import upstream maccel baseline
Tests / test_core_function (push) Failing after 12s

This commit is contained in:
2026-03-24 12:10:31 +00:00
parent 6e948d7b39
commit 5f1254d11a
108 changed files with 18930 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
accel_test
+75
View File
@@ -0,0 +1,75 @@
#include <linux/types.h>
#ifndef __KERNEL__
#include <stdint.h>
#endif // !__KERNEL__
/**
* [Yeetmouse](https://github.com/AndyFilter/YeetMouse/blob/master/driver/FixedMath/Fixed64.h#L1360)
* has better string utils. FP64_ToString there doesn't require
* `__udivmodti4` in 64-bit mode.
*
*/
typedef int32_t FP_INT;
typedef int64_t FP_LONG;
#define FP64_Shift 32
static inline FP_LONG FP64_Mul(FP_LONG a, FP_LONG b) {
return (FP_LONG)(((__int128_t)a * (__int128_t)b) >> FP64_Shift);
}
static char *FP_64_itoa_loop(char *buf, uint64_t scale, uint64_t value,
int skip) {
while (scale) {
unsigned digit = (value / scale);
if (!skip || digit || scale == 1) {
skip = 0;
*buf++ = '0' + digit;
value %= scale;
}
scale /= 10;
}
return buf;
}
void FP64_ToString(FP_LONG value, char *buf);
void FP64_ToString(FP_LONG value, char *buf) {
uint64_t uvalue = (value >= 0) ? value : -value;
if (value < 0)
*buf++ = '-';
#define SCALE 9
static const uint64_t FP_64_scales[10] = {
/* 18 decimals is enough for full 64bit fixed point precision */
1, 10, 100, 1000, 10000,
100000, 1000000, 10000000, 100000000, 1000000000};
/* Separate the integer and decimal parts of the value */
uint64_t intpart = uvalue >> 32;
uint64_t fracpart = uvalue & 0xFFFFFFFF;
uint64_t scale = FP_64_scales[SCALE];
fracpart = FP64_Mul(fracpart, scale);
if (fracpart >= scale) {
/* Handle carry from decimal part */
intpart++;
fracpart -= scale;
}
/* Format integer part */
buf = FP_64_itoa_loop(buf, 1000000000, intpart, 1);
/* Format decimal part (if any) */
if (scale != 1) {
*buf++ = '.';
buf = FP_64_itoa_loop(buf, scale / 10, fracpart, 0);
}
*buf = '\0';
}
+31
View File
@@ -0,0 +1,31 @@
ifneq ($(KERNELRELEASE),)
obj-m := maccel.o
ccflags-y += $(DRIVER_CFLAGS)
endif
KVER ?= $(shell uname -r)
KDIR ?= /lib/modules/$(KVER)/build
DRIVER_CFLAGS ?= -DFIXEDPT_BITS=$(shell getconf LONG_BIT)
ifneq ($(CC),clang)
CC=gcc
else
export LLVM=1
endif
build:
$(MAKE) CC=$(CC) -C $(KDIR) M=$(CURDIR)
build_debug: DRIVER_CFLAGS += -g -DDEBUG
build_debug: build
clean:
$(MAKE) -C $(KDIR) M=$(CURDIR) clean
test_debug: DRIVER_CFLAGS += -g -DDEBUG
test_debug: test
test: **/*.test.c
@mkdir -p tests/snapshots
DRIVER_CFLAGS="$(DRIVER_CFLAGS)" TEST_NAME=$(name) sh tests/run_tests.sh
+140
View File
@@ -0,0 +1,140 @@
#ifndef _ACCEL_H_
#define _ACCEL_H_
#include "accel/linear.h"
#include "accel/mode.h"
#include "accel/natural.h"
#include "accel/synchronous.h"
#include "dbg.h"
#include "fixedptc.h"
#include "math.h"
#include "speed.h"
struct no_accel_curve_args {};
union __accel_args {
struct natural_curve_args natural;
struct linear_curve_args linear;
struct synchronous_curve_args synchronous;
struct no_accel_curve_args no_accel;
};
struct accel_args {
fpt sens_mult;
fpt yx_ratio;
fpt input_dpi;
fpt angle_rotation_deg;
enum accel_mode tag;
union __accel_args args;
};
const fpt NORMALIZED_DPI = fpt_fromint(1000);
/**
* Calculate the factor by which to multiply the input vector
* in order to get the desired output speed.
*
*/
static inline struct vector sensitivity(fpt input_speed,
struct accel_args args) {
fpt sens;
switch (args.tag) {
case synchronous:
dbg("accel mode %d: synchronous", args.tag);
sens = __synchronous_sens_fun(input_speed, args.args.synchronous);
break;
case natural:
dbg("accel mode %d: natural", args.tag);
sens = __natural_sens_fun(input_speed, args.args.natural);
break;
case linear:
dbg("accel mode %d: linear", args.tag);
sens = __linear_sens_fun(input_speed, args.args.linear);
break;
case no_accel:
dbg("accel mode %d: no_accel", args.tag);
sens = FIXEDPT_ONE;
break;
default:
sens = FIXEDPT_ONE;
}
sens = fpt_mul(sens, args.sens_mult);
return (struct vector){sens, fpt_mul(sens, args.yx_ratio)};
}
const fpt DEG_TO_RAD_FACTOR = fpt_xdiv(FIXEDPT_PI, fpt_rconst(180));
static inline void f_accelerate(int *x, int *y, fpt time_interval_ms,
struct accel_args args) {
static fpt carry_x = 0;
static fpt carry_y = 0;
fpt dx = fpt_fromint(*x);
fpt dy = fpt_fromint(*y);
{
if (args.angle_rotation_deg == 0) {
goto accel_routine;
}
// Add rotation of input vector
fpt degrees = args.angle_rotation_deg;
fpt radians =
fpt_mul(degrees, DEG_TO_RAD_FACTOR); // Convert degrees to radians
fpt cos_angle = fpt_cos(radians);
fpt sin_angle = fpt_sin(radians);
dbg("rotation angle(deg): %s deg", fptoa(degrees));
dbg("rotation angle(rad): %s rad", fptoa(radians));
dbg("cosine of rotation: %s", fptoa(cos_angle));
dbg("sine of rotation: %s", fptoa(sin_angle));
// Rotate input vector
fpt dx_rot = fpt_mul(dx, cos_angle) - fpt_mul(dy, sin_angle);
fpt dy_rot = fpt_mul(dx, sin_angle) + fpt_mul(dy, cos_angle);
dbg("rotated x: %s", fptoa(dx_rot));
dbg("rotated y: %s", fptoa(dy_rot));
dx = dx_rot;
dy = dy_rot;
}
accel_routine:
dbg("in (%d, %d)", *x, *y);
dbg("in: x (fpt conversion) %s", fptoa(dx));
dbg("in: y (fpt conversion) %s", fptoa(dy));
fpt dpi_factor = fpt_div(NORMALIZED_DPI, args.input_dpi);
dbg("dpi adjustment factor: %s", fptoa(dpi_factor));
dx = fpt_mul(dx, dpi_factor);
dy = fpt_mul(dy, dpi_factor);
fpt speed_in = input_speed(dx, dy, time_interval_ms);
struct vector sens = sensitivity(speed_in, args);
dbg("scale x %s", fptoa(sens.x));
dbg("scale y %s", fptoa(sens.y));
fpt dx_out = fpt_mul(dx, sens.x);
fpt dy_out = fpt_mul(dy, sens.y);
dx_out = fpt_add(dx_out, carry_x);
dy_out = fpt_add(dy_out, carry_y);
dbg("out: x %s", fptoa(dx_out));
dbg("out: y %s", fptoa(dy_out));
*x = fpt_toint(dx_out);
*y = fpt_toint(dy_out);
dbg("out (int conversion) (%d, %d)", *x, *y);
carry_x = fpt_sub(dx_out, fpt_fromint(*x));
carry_y = fpt_sub(dy_out, fpt_fromint(*y));
dbg("carry (%s, %s)", fptoa(carry_x), fptoa(carry_x));
}
#endif
+50
View File
@@ -0,0 +1,50 @@
#ifndef __ACCEL_LINEAR_H_
#define __ACCEL_LINEAR_H_
#include "../dbg.h"
#include "../fixedptc.h"
#include "../math.h"
struct linear_curve_args {
fpt accel;
fpt offset;
fpt output_cap;
};
static inline fpt linear_base_fn(fpt x, fpt accel,
fpt input_offset) {
fpt _x = x - input_offset;
fpt _x_square = fpt_mul(
_x, _x); // because linear in rawaccel is classic with exponent = 2
return fpt_mul(accel, fpt_div(_x_square, x));
}
/**
* Sensitivity Function for Linear Acceleration
*/
static inline fpt __linear_sens_fun(fpt input_speed,
struct linear_curve_args args) {
dbg("linear: accel %s", fptoa(args.accel));
dbg("linear: offset %s", fptoa(args.offset));
dbg("linear: output_cap %s", fptoa(args.output_cap));
if (input_speed <= args.offset) {
return FIXEDPT_ONE;
}
fpt sens = linear_base_fn(input_speed, args.accel, args.offset);
dbg("linear: base_fn sens %s", fptoa(args.accel));
fpt sign = FIXEDPT_ONE;
if (args.output_cap > 0) {
fpt cap = fpt_sub(args.output_cap, FIXEDPT_ONE);
if (cap < 0) {
cap = -cap;
sign = -sign;
}
sens = minsd(sens, cap);
}
return fpt_add(FIXEDPT_ONE, fpt_mul(sign, sens));
}
#endif // !__ACCEL_LINEAR_H_
+6
View File
@@ -0,0 +1,6 @@
#ifndef __ACCEL_MODE_H
#define __ACCEL_MODE_H
enum accel_mode : unsigned char { linear, natural, synchronous, no_accel };
#endif // !__ACCEL_MODE_H
+48
View File
@@ -0,0 +1,48 @@
#ifndef __ACCEL_NATURAL_H_
#define __ACCEL_NATURAL_H_
#include "../fixedptc.h"
struct natural_curve_args {
fpt decay_rate;
fpt offset;
fpt limit;
};
/**
* Gain Function for Natural Acceleration
*/
static inline fpt __natural_sens_fun(fpt input_speed,
struct natural_curve_args args) {
dbg("natural: decay_rate %s", fptoa(args.decay_rate));
dbg("natural: offset %s", fptoa(args.offset));
dbg("natural: limit %s", fptoa(args.limit));
if (input_speed <= args.offset) {
return FIXEDPT_ONE;
}
if (args.limit <= FIXEDPT_ONE) {
return FIXEDPT_ONE;
}
if (args.decay_rate <= 0) {
return FIXEDPT_ONE;
}
fpt limit = args.limit - FIXEDPT_ONE;
fpt accel = fpt_div(args.decay_rate, fpt_abs(limit));
fpt constant = fpt_div(-limit, accel);
dbg("natural: constant %s", fptoa(constant));
fpt offset_x = args.offset - input_speed;
fpt decay = fpt_exp(fpt_mul(accel, offset_x));
dbg("natural: decay %s", fptoa(decay));
fpt output_denom = fpt_div(decay, accel) - offset_x;
fpt output = fpt_mul(limit, output_denom) + constant;
return fpt_div(output, input_speed) + FIXEDPT_ONE;
}
#endif
+64
View File
@@ -0,0 +1,64 @@
#ifndef __ACCEL_SYNCHRONOUS_H_
#define __ACCEL_SYNCHRONOUS_H_
#include "../fixedptc.h"
struct synchronous_curve_args {
fpt gamma;
fpt smooth;
fpt motivity;
fpt sync_speed;
};
/**
* Sensitivity Function for `Synchronous` Acceleration
*/
static inline fpt __synchronous_sens_fun(fpt input_speed,
struct synchronous_curve_args args) {
fpt log_motivity = fpt_ln(args.motivity);
fpt gamma_const = fpt_div(args.gamma, log_motivity);
fpt log_syncspeed = fpt_ln(args.sync_speed);
fpt syncspeed = args.sync_speed;
fpt sharpness = args.smooth == 0 ? fpt_rconst(16.0)
: fpt_div(fpt_rconst(0.5), args.smooth);
int use_linear_clamp = sharpness >= fpt_rconst(16.0);
fpt sharpness_recip = fpt_div(FIXEDPT_ONE, sharpness);
fpt minimum_sens = fpt_div(FIXEDPT_ONE, args.motivity);
fpt maximum_sens = args.motivity;
// if sharpness >= 16, use linear clamp for activation function.
// linear clamp means: fpt_clamp(input_speed, -1, 1).
if (use_linear_clamp) {
fpt log_space = fpt_mul(gamma_const, (fpt_ln(input_speed) - log_syncspeed));
if (log_space < -FIXEDPT_ONE) {
return minimum_sens;
}
if (log_space > FIXEDPT_ONE) {
return maximum_sens;
}
return fpt_exp(fpt_mul(log_space, log_motivity));
}
if (input_speed == syncspeed) {
return FIXEDPT_ONE;
}
fpt log_x = fpt_ln(input_speed);
fpt log_diff = log_x - log_syncspeed;
if (log_diff > 0) {
fpt log_space = fpt_mul(gamma_const, log_diff);
fpt exponent =
fpt_pow(fpt_tanh(fpt_pow(log_space, sharpness)), sharpness_recip);
return fpt_exp(fpt_mul(exponent, log_motivity));
} else {
fpt log_space = fpt_mul(-gamma_const, log_diff);
fpt exponent =
-fpt_pow(fpt_tanh(fpt_pow(log_space, sharpness)), sharpness_recip);
return fpt_exp(fpt_mul(exponent, log_motivity));
}
}
#endif
+87
View File
@@ -0,0 +1,87 @@
#ifndef _ACCELK_H_
#define _ACCELK_H_
#include "accel.h"
#include "accel/linear.h"
#include "accel/mode.h"
#include "fixedptc.h"
#include "linux/ktime.h"
#include "params.h"
#include "speed.h"
static struct accel_args collect_args(void) {
struct accel_args accel = {0};
enum accel_mode mode = PARAM_MODE;
accel.tag = mode;
accel.sens_mult = atofp(PARAM_SENS_MULT);
accel.yx_ratio = atofp(PARAM_YX_RATIO);
accel.input_dpi = atofp(PARAM_INPUT_DPI);
accel.angle_rotation_deg = atofp(PARAM_ANGLE_ROTATION);
switch (mode) {
case synchronous: {
accel.args.synchronous.gamma = atofp(PARAM_GAMMA);
accel.args.synchronous.smooth = atofp(PARAM_SMOOTH);
accel.args.synchronous.motivity = atofp(PARAM_MOTIVITY);
accel.args.synchronous.sync_speed = atofp(PARAM_SYNC_SPEED);
break;
}
case natural: {
accel.args.natural.decay_rate = atofp(PARAM_DECAY_RATE);
accel.args.natural.offset = atofp(PARAM_OFFSET);
accel.args.natural.limit = atofp(PARAM_LIMIT);
break;
}
case linear: {
accel.args.linear.accel = atofp(PARAM_ACCEL);
accel.args.linear.offset = atofp(PARAM_OFFSET);
accel.args.linear.output_cap = atofp(PARAM_OUTPUT_CAP);
break;
}
case no_accel:
default: {
}
};
return accel;
}
#if FIXEDPT_BITS == 64
const fpt UNIT_PER_MS = fpt_rconst(1000000); // 1 million nanoseconds
#else
const fpt UNIT_PER_MS = fpt_rconst(1000); // 1 thousand microsends
#endif
static inline void accelerate(int *x, int *y) {
dbg("FIXEDPT_BITS = %d", FIXEDPT_BITS);
static ktime_t last_time;
ktime_t now = ktime_get();
#if FIXEDPT_BITS == 64
s64 unit_time = ktime_to_ns(now - last_time);
dbg("ktime interval -> now (%llu) vs last_ktime (%llu), diff = %llins", now,
last_time, unit_time);
#else
s64 unit_time = ktime_to_us(now - last_time);
dbg("ktime interval -> now (%llu) vs last_ktime (%llu), diff = %llius", now,
last_time, unit_time);
#endif
last_time = now;
fpt _unit_time = fpt_fromint(unit_time);
fpt millisecond = fpt_div(_unit_time, UNIT_PER_MS);
#if FIXEDPT_BITS == 64
dbg("ktime interval -> converting to ns: %lluns -> %sms", unit_time,
fptoa(millisecond));
#else
dbg("ktime interval, converting to us: %lluus -> %sms", unit_time,
fptoa(millisecond));
#endif
return f_accelerate(x, y, millisecond, collect_args());
}
#endif // !_ACCELK_H_
+6
View File
@@ -0,0 +1,6 @@
#include "accel.h"
extern inline struct vector sensitivity_rs(fpt input_speed,
struct accel_args args) {
return sensitivity(input_speed, args);
}
+39
View File
@@ -0,0 +1,39 @@
#ifdef DEBUG
#define DEBUG_TEST 1
#else
#define DEBUG_TEST 0
#endif
#ifdef __KERNEL__
#include <linux/printk.h>
#define dbg(fmt, ...) dbg_k(fmt, __VA_ARGS__)
#else
#include <stdio.h>
#define dbg(fmt, ...) dbg_std(fmt, __VA_ARGS__)
#endif
#if defined __KERNEL__ && defined __clang__
#define dbg_k(fmt, ...) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wstatic-local-in-inline\"") do { \
if (DEBUG_TEST) \
printk(KERN_INFO "%s:%d:%s(): " #fmt "\n", __FILE__, __LINE__, __func__, \
__VA_ARGS__); \
} \
while (0) \
_Pragma("clang diagnostic pop")
#elif defined __KERNEL__
#define dbg_k(fmt, ...) \
do { \
if (DEBUG_TEST) \
printk(KERN_INFO "%s:%d:%s(): " #fmt "\n", __FILE__, __LINE__, __func__, \
__VA_ARGS__); \
} while (0)
#else
#define dbg_std(fmt, ...) \
do { \
if (DEBUG_TEST) \
fprintf(stderr, "%s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, \
__VA_ARGS__); \
} while (0)
#endif
+437
View File
@@ -0,0 +1,437 @@
#ifndef _FIXEDPTC_H_
#define _FIXEDPTC_H_
/*
* fixedptc.h is a 32-bit or 64-bit fixed point numeric library.
*
* The symbol FIXEDPT_BITS, if defined before this library header file
* is included, determines the number of bits in the data type (its "width").
* The default width is 32-bit (FIXEDPT_BITS=32) and it can be used
* on any recent C99 compiler. The 64-bit precision (FIXEDPT_BITS=64) is
* available on compilers which implement 128-bit "long long" types. This
* precision has been tested on GCC 4.2+.
*
* The FIXEDPT_WBITS symbols governs how many bits are dedicated to the
* "whole" part of the number (to the left of the decimal point). The larger
* this width is, the larger the numbers which can be stored in the fixedpt
* number. The rest of the bits (available in the FIXEDPT_FBITS symbol) are
* dedicated to the fraction part of the number (to the right of the decimal
* point).
*
* Since the number of bits in both cases is relatively low, many complex
* functions (more complex than div & mul) take a large hit on the precision
* of the end result because errors in precision accumulate.
* This loss of precision can be lessened by increasing the number of
* bits dedicated to the fraction part, but at the loss of range.
*
* Adventurous users might utilize this library to build two data types:
* one which has the range, and one which has the precision, and carefully
* convert between them (including adding two number of each type to produce
* a simulated type with a larger range and precision).
*
* The ideas and algorithms have been cherry-picked from a large number
* of previous implementations available on the Internet.
* Tim Hartrick has contributed cleanup and 64-bit support patches.
*
* == Special notes for the 32-bit precision ==
* Signed 32-bit fixed point numeric library for the 24.8 format.
* The specific limits are -8388608.999... to 8388607.999... and the
* most precise number is 0.00390625. In practice, you should not count
* on working with numbers larger than a million or to the precision
* of more than 2 decimal places. Make peace with the fact that PI
* is 3.14 here. :)
*/
/*-
* Copyright (c) 2010-2012 Ivan Voras <ivoras@freebsd.org>
* Copyright (c) 2012 Tim Hartrick <tim@edgecast.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "utils.h"
#ifndef FIXEDPT_BITS
#define FIXEDPT_BITS 64
#endif
#ifdef __KERNEL__
#include <linux/math64.h>
#include <linux/stddef.h>
#include <linux/types.h>
#else
#include <stddef.h>
#include <stdint.h>
#endif
#if FIXEDPT_BITS == 32
typedef int32_t fpt;
typedef int64_t fptd;
typedef uint32_t fptu;
typedef uint64_t fptud;
#define FIXEDPT_WBITS 16
#elif FIXEDPT_BITS == 64
#include "Fixed64.utils.h"
typedef int64_t fpt;
typedef __int128_t fptd;
typedef uint64_t fptu;
typedef __uint128_t fptud;
#define FIXEDPT_WBITS 32
#else
#error "FIXEDPT_BITS must be equal to 32 or 64"
#endif
#if FIXEDPT_WBITS >= FIXEDPT_BITS
#error "FIXEDPT_WBITS must be less than or equal to FIXEDPT_BITS"
#endif
#define FIXEDPT_VCSID "$Id$"
#define FIXEDPT_FBITS (FIXEDPT_BITS - FIXEDPT_WBITS)
#define FIXEDPT_FMASK (((fpt)1 << FIXEDPT_FBITS) - 1)
#define fpt_rconst(R) ((fpt)((R) * FIXEDPT_ONE + ((R) >= 0 ? 0.5 : -0.5)))
#define fpt_fromint(I) ((fptd)(I) << FIXEDPT_FBITS)
#define fpt_toint(F) ((F) >> FIXEDPT_FBITS)
#define fpt_add(A, B) ((A) + (B))
#define fpt_sub(A, B) ((A) - (B))
#define fpt_xmul(A, B) ((fpt)(((fptd)(A) * (fptd)(B)) >> FIXEDPT_FBITS))
#define fpt_xdiv(A, B) ((fpt)(((fptd)(A) << FIXEDPT_FBITS) / (fptd)(B)))
#define fpt_fracpart(A) ((fpt)(A) & FIXEDPT_FMASK)
#define FIXEDPT_ONE ((fpt)((fpt)1 << FIXEDPT_FBITS))
#define FIXEDPT_ONE_HALF (FIXEDPT_ONE >> 1)
#define FIXEDPT_TWO (FIXEDPT_ONE + FIXEDPT_ONE)
#define FIXEDPT_PI fpt_rconst(3.14159265358979323846)
#define FIXEDPT_TWO_PI fpt_rconst(2 * 3.14159265358979323846)
#define FIXEDPT_HALF_PI fpt_rconst(3.14159265358979323846 / 2)
#define FIXEDPT_E fpt_rconst(2.7182818284590452354)
#define fpt_abs(A) ((A) < 0 ? -(A) : (A))
/* fpt is meant to be usable in environments without floating point support
* (e.g. microcontrollers, kernels), so we can't use floating point types
* directly. Putting them only in macros will effectively make them optional. */
#define fpt_tofloat(T) \
((float)((T) * ((float)(1) / (float)(1L << FIXEDPT_FBITS))))
#define fpt_todouble(T) \
((double)((T) * ((double)(1) / (double)(1L << FIXEDPT_FBITS))))
/* Multiplies two fpt numbers, returns the result. */
static inline fpt fpt_mul(fpt A, fpt B) {
return (((fptd)A * (fptd)B) >> FIXEDPT_FBITS);
}
#if FIXEDPT_BITS == 64
static inline fpt div128_s64_s64(fpt dividend, fpt divisor) {
fpt high = dividend >> FIXEDPT_FBITS;
fpt low = dividend << FIXEDPT_FBITS;
fpt result = div128_s64_s64_s64(high, low, divisor);
return result;
}
#endif
/* Divides two fpt numbers, returns the result. */
static inline fpt fpt_div(fpt A, fpt B) {
#if FIXEDPT_BITS == 64
return div128_s64_s64(A, B);
#endif
return (((fptd)A << FIXEDPT_FBITS) / (fptd)B);
}
/*
* Note: adding and subtracting fpt numbers can be done by using
* the regular integer operators + and -.
*/
/**
* Convert the given fpt number to a decimal string.
* The max_dec argument specifies how many decimal digits to the right
* of the decimal point to generate. If set to -1, the "default" number
* of decimal digits will be used (2 for 32-bit fpt width, 10 for
* 64-bit fpt width); If set to -2, "all" of the digits will
* be returned, meaning there will be invalid, bogus digits outside the
* specified precisions.
*/
static inline void fpt_str(fpt A, char *str, int max_dec) {
int ndec = 0, slen = 0;
char tmp[12] = {0};
fptud fr, ip;
const fptud one = (fptud)1 << FIXEDPT_BITS;
const fptud mask = one - 1;
if (max_dec == -1)
#if FIXEDPT_BITS == 32
#if FIXEDPT_WBITS > 16
max_dec = 2;
#else
max_dec = 4;
#endif
#elif FIXEDPT_BITS == 64
max_dec = 10;
#else
#error Invalid width
#endif
else if (max_dec == -2)
max_dec = 15;
if (A < 0) {
str[slen++] = '-';
A *= -1;
}
ip = fpt_toint(A);
do {
tmp[ndec++] = '0' + ip % 10;
ip /= 10;
} while (ip != 0);
while (ndec > 0)
str[slen++] = tmp[--ndec];
str[slen++] = '.';
fr = (fpt_fracpart(A) << FIXEDPT_WBITS) & mask;
do {
fr = (fr & mask) * 10;
str[slen++] = '0' + (fr >> FIXEDPT_BITS) % 10;
ndec++;
} while (fr != 0 && ndec < max_dec);
if (ndec > 1 && str[slen - 1] == '0')
str[slen - 1] = '\0'; /* cut off trailing 0 */
else
str[slen] = '\0';
}
/* Converts the given fpt number into a string, using a static
* (non-threadsafe) string buffer */
static inline char *fptoa(const fpt A) {
static char str[25];
#if FIXEDPT_BITS == 64
FP64_ToString(A, str);
#else
fpt_str(A, str, 10);
#endif
return (str);
}
static fpt atofp(char *num_string) {
fptu n = 0;
int sign = 0;
for (int idx = 0; num_string[idx] != '\0'; idx++) {
char c = num_string[idx];
switch (c) {
case ' ':
continue;
case '\n':
continue;
case '-':
sign = 1;
continue;
default:
if (is_digit(c)) {
int digit = c - '0';
n = n * 10 + digit;
} else {
dbg("Hit an unsupported character while parsing a number: '%c'", c);
}
}
}
return sign ? -n : n;
}
/* Returns the square root of the given number, or -1 in case of error */
static inline fpt fpt_sqrt(fpt A) {
int invert = 0;
int iter = FIXEDPT_FBITS;
int i;
fpt l;
if (A < 0)
return (-1);
if (A == 0 || A == FIXEDPT_ONE)
return (A);
if (A < FIXEDPT_ONE && A > 6) {
invert = 1;
A = fpt_div(FIXEDPT_ONE, A);
}
if (A > FIXEDPT_ONE) {
fpt s = A;
iter = 0;
while (s > 0) {
s >>= 2;
iter++;
}
}
/* Newton's iterations */
l = (A >> 1) + 1;
for (i = 0; i < iter; i++)
l = (l + fpt_div(A, l)) >> 1;
if (invert)
return (fpt_div(FIXEDPT_ONE, l));
return (l);
}
/* Returns the sine of the given fpt number.
* Note: the loss of precision is extraordinary! */
static inline fpt fpt_sin(fpt fp) {
int sign = 1;
fpt sqr, result;
const fpt SK[2] = {fpt_rconst(7.61e-03), fpt_rconst(1.6605e-01)};
fp %= 2 * FIXEDPT_PI;
if (fp < 0)
fp = FIXEDPT_PI * 2 + fp;
if ((fp > FIXEDPT_HALF_PI) && (fp <= FIXEDPT_PI))
fp = FIXEDPT_PI - fp;
else if ((fp > FIXEDPT_PI) && (fp <= (FIXEDPT_PI + FIXEDPT_HALF_PI))) {
fp = fp - FIXEDPT_PI;
sign = -1;
} else if (fp > (FIXEDPT_PI + FIXEDPT_HALF_PI)) {
fp = (FIXEDPT_PI << 1) - fp;
sign = -1;
}
sqr = fpt_mul(fp, fp);
result = SK[0];
result = fpt_mul(result, sqr);
result -= SK[1];
result = fpt_mul(result, sqr);
result += FIXEDPT_ONE;
result = fpt_mul(result, fp);
return sign * result;
}
/* Returns the cosine of the given fpt number */
static inline fpt fpt_cos(fpt A) { return (fpt_sin(FIXEDPT_HALF_PI - A)); }
/* Returns the tangent of the given fpt number */
static inline fpt fpt_tan(fpt A) { return fpt_div(fpt_sin(A), fpt_cos(A)); }
/* Returns the value exp(x), i.e. e^x of the given fpt number. */
static inline fpt fpt_exp(fpt fp) {
fpt xabs, k, z, R, xp;
const fpt LN2 = fpt_rconst(0.69314718055994530942);
const fpt LN2_INV = fpt_rconst(1.4426950408889634074);
const fpt EXP_P[5] = {
fpt_rconst(1.66666666666666019037e-01),
fpt_rconst(-2.77777777770155933842e-03),
fpt_rconst(6.61375632143793436117e-05),
fpt_rconst(-1.65339022054652515390e-06),
fpt_rconst(4.13813679705723846039e-08),
};
if (fp == 0)
return (FIXEDPT_ONE);
xabs = fpt_abs(fp);
k = fpt_mul(xabs, LN2_INV);
k += FIXEDPT_ONE_HALF;
k &= ~FIXEDPT_FMASK;
if (fp < 0)
k = -k;
fp -= fpt_mul(k, LN2);
z = fpt_mul(fp, fp);
/* Taylor */
R = FIXEDPT_TWO +
fpt_mul(
z,
EXP_P[0] +
fpt_mul(
z, EXP_P[1] +
fpt_mul(z, EXP_P[2] +
fpt_mul(z, EXP_P[3] +
fpt_mul(z, EXP_P[4])))));
xp = FIXEDPT_ONE + fpt_div(fpt_mul(fp, FIXEDPT_TWO), R - fp);
if (k < 0)
k = FIXEDPT_ONE >> (-k >> FIXEDPT_FBITS);
else
k = FIXEDPT_ONE << (k >> FIXEDPT_FBITS);
return (fpt_mul(k, xp));
}
/* Returns the hyperbolic tangent of the given fpt number */
static inline fpt fpt_tanh(fpt X) {
fpt e_to_the_2_x = fpt_exp(fpt_mul(FIXEDPT_TWO, X));
fpt sinh = e_to_the_2_x - FIXEDPT_ONE;
fpt cosh = e_to_the_2_x + FIXEDPT_ONE;
return fpt_div(sinh, cosh);
}
/* Returns the natural logarithm of the given fpt number. */
static inline fpt fpt_ln(fpt x) {
fpt log2, xi;
fpt f, s, z, w, R;
const fpt LN2 = fpt_rconst(0.69314718055994530942);
const fpt LG[7] = {fpt_rconst(6.666666666666735130e-01),
fpt_rconst(3.999999999940941908e-01),
fpt_rconst(2.857142874366239149e-01),
fpt_rconst(2.222219843214978396e-01),
fpt_rconst(1.818357216161805012e-01),
fpt_rconst(1.531383769920937332e-01),
fpt_rconst(1.479819860511658591e-01)};
if (x < 0)
return (0);
if (x == 0)
return 0xffffffff;
log2 = 0;
xi = x;
while (xi > FIXEDPT_TWO) {
xi >>= 1;
log2++;
}
f = xi - FIXEDPT_ONE;
s = fpt_div(f, FIXEDPT_TWO + f);
z = fpt_mul(s, s);
w = fpt_mul(z, z);
R = fpt_mul(w, LG[1] + fpt_mul(w, LG[3] + fpt_mul(w, LG[5]))) +
fpt_mul(z, LG[0] +
fpt_mul(w, LG[2] + fpt_mul(w, LG[4] + fpt_mul(w, LG[6]))));
return (fpt_mul(LN2, (log2 << FIXEDPT_FBITS)) + f - fpt_mul(s, f - R));
}
/* Returns the logarithm of the given base of the given fpt number */
static inline fpt fpt_log(fpt x, fpt base) {
return (fpt_div(fpt_ln(x), fpt_ln(base)));
}
/* Return the power value (n^exp) of the given fpt numbers */
static inline fpt fpt_pow(fpt n, fpt exp) {
if (exp == 0)
return (FIXEDPT_ONE);
if (n < 0)
return 0;
return (fpt_exp(fpt_mul(fpt_ln(n), exp)));
}
#endif
+93
View File
@@ -0,0 +1,93 @@
#ifndef _INPUT_ECHO_
#define _INPUT_ECHO_
#include "fixedptc.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include "speed.h"
#include <linux/version.h>
int create_char_device(void);
void destroy_char_device(void);
static struct cdev device;
static struct class *device_class;
static dev_t device_number;
/*
* Convert an int into an array of four/eight bytes, in big endian (MSB first)
*/
static void fpt_to_int_be_bytes(fpt num, char bytes[sizeof(fpt)]) {
#define byte(i) (FIXEDPT_BITS - (i * 8))
bytes[0] = (num >> byte(1)) & 0xFF; // Most significant byte
bytes[1] = (num >> byte(2)) & 0xFF;
bytes[2] = (num >> byte(3)) & 0xFF;
#if FIXEDPT_BITS == 64
if (sizeof(fpt) == 8) {
bytes[3] = (num >> byte(4)) & 0xFF;
bytes[4] = (num >> byte(5)) & 0xFF;
bytes[5] = (num >> byte(6)) & 0xFF;
bytes[6] = (num >> byte(7)) & 0xFF;
bytes[7] = num & 0xFF; // Least significant byte
return;
}
#else
bytes[3] = num & 0xFF; // Least significant byte
#endif
}
static ssize_t read(struct file *f, char __user *user_buffer, size_t size,
loff_t *offset) {
dbg("echoing speed to userspace: %s", fptoa(LAST_INPUT_MOUSE_SPEED));
char be_bytes_for_int[sizeof(fpt)] = {0};
fpt_to_int_be_bytes(LAST_INPUT_MOUSE_SPEED, be_bytes_for_int);
int err =
copy_to_user(user_buffer, be_bytes_for_int, sizeof(be_bytes_for_int));
if (err)
return -EFAULT;
return sizeof(be_bytes_for_int);
}
struct file_operations fops = {.owner = THIS_MODULE, .read = read};
int create_char_device(void) {
int err;
err = alloc_chrdev_region(&device_number, 0, 1, "maccel");
if (err)
return -EIO;
cdev_init(&device, &fops);
cdev_add(&device, device_number, 1);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0))
device_class = class_create(THIS_MODULE, "maccel");
#else
device.owner = THIS_MODULE;
device_class = class_create("maccel");
#endif
if (IS_ERR(device_class)) {
goto err_free_cdev;
}
device_create(device_class, NULL, device_number, NULL, "maccel");
return 0;
err_free_cdev:
cdev_del(&device);
unregister_chrdev_region(device_number, 1);
return -EIO;
}
void destroy_char_device(void) {
device_destroy(device_class, device_number);
class_destroy(device_class);
cdev_del(&device);
unregister_chrdev_region(device_number, 1);
}
#endif // !_INPUT_ECHO_
+231
View File
@@ -0,0 +1,231 @@
#include "./accel_k.h"
#include "linux/input.h"
#include "mouse_move.h"
#include <linux/hid.h>
#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0))
#define __cleanup_events 0
#else
#define __cleanup_events 1
#endif
static inline bool rotation_is_enabled(void) {
/* Fast check: the default is "0", so skip atofp unless the string differs */
return PARAM_ANGLE_ROTATION[0] != '0' || PARAM_ANGLE_ROTATION[1] != '\0';
}
/*
* Collect the events EV_REL REL_X and EV_REL REL_Y, once we have both then
* we accelerate the (x, y) vector and set the EV_REL event's value
* through the `value_ptr` of each collected event.
*/
static void event(struct input_handle *handle, struct input_value *value_ptr) {
/* printk(KERN_INFO "type %d, code %d, value %d", type, code, value); */
switch (value_ptr->type) {
case EV_REL: {
dbg("EV_REL => code %d, value %d", value_ptr->code, value_ptr->value);
update_mouse_move(value_ptr);
return;
}
case EV_SYN: {
int x = get_x(MOVEMENT);
int y = get_y(MOVEMENT);
if (x || y) {
dbg("EV_SYN => code %d", value_ptr->code);
/*
* When rotation is active and one axis is missing from this frame,
* point the missing axis to synthetic storage so f_accelerate can
* write the rotated cross-axis component into it. The synthetic
* value is later injected into the event stream by maccel_events().
*
* Only on >= 6.11.0: injection into the event buffer is not
* possible on older kernels, so there is no point in computing
* the cross-axis component.
*/
#if __cleanup_events
if (rotation_is_enabled()) {
ensure_axes_for_rotation();
}
#endif
accelerate(&x, &y);
dbg("accelerated -> (%d, %d)", x, y);
set_x_move(x);
set_y_move(y);
clear_mouse_move();
}
return;
}
default:
return;
}
}
#if __cleanup_events
static unsigned int maccel_events(struct input_handle *handle,
struct input_value *vals,
unsigned int count) {
#else
static void maccel_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count) {
#endif
struct input_value *v;
for (v = vals; v != vals + count; v++) {
event(handle, v);
}
struct input_value *end = vals;
for (v = vals; v != vals + count; v++) {
if (v->type == EV_REL && v->value == NONE_EVENT_VALUE)
continue;
if (end != v) {
*end = *v;
}
end++;
}
int _count = end - vals;
#if __cleanup_events
/*
* Inject synthetic EV_REL events for axes that rotation produced
* but weren't present in the original frame. Insert before SYN_REPORT
* so downstream handlers see a valid event sequence.
*
* Gated to >= 6.11.0 only: on older kernels the handler returns void
* and cannot communicate a new event count to the input subsystem.
* Writing extra events into the buffer on those kernels is undefined
* behavior — the kernel won't deliver them and may behave erratically.
*/
{
struct input_value *syn_pos = NULL;
unsigned int max = handle->dev->max_vals;
/* Find the last SYN_REPORT so we can insert before it */
for (v = vals; v != end; v++) {
if (v->type == EV_SYN && v->code == SYN_REPORT)
syn_pos = v;
}
if (injected_x && synthetic_x_val != NONE_EVENT_VALUE && _count < max) {
if (syn_pos) {
/* Shift SYN_REPORT and everything after it forward by one */
memmove(syn_pos + 1, syn_pos, (end - syn_pos) * sizeof(*syn_pos));
syn_pos->type = EV_REL;
syn_pos->code = REL_X;
syn_pos->value = synthetic_x_val;
syn_pos++;
end++;
_count++;
}
dbg("rotation: injected synthetic REL_X = %d", synthetic_x_val);
}
if (injected_y && synthetic_y_val != NONE_EVENT_VALUE && _count < max) {
if (syn_pos) {
memmove(syn_pos + 1, syn_pos, (end - syn_pos) * sizeof(*syn_pos));
syn_pos->type = EV_REL;
syn_pos->code = REL_Y;
syn_pos->value = synthetic_y_val;
end++;
_count++;
}
dbg("rotation: injected synthetic REL_Y = %d", synthetic_y_val);
}
}
#endif
handle->dev->num_vals = _count;
#if __cleanup_events
return _count;
#endif
}
/* Same as Linux's input_register_handle but we always add the handle to the
* head of handlers */
static int input_register_handle_head(struct input_handle *handle) {
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 7))
/* In 6.11.7 an additional handler pointer was added:
* https://github.com/torvalds/linux/commit/071b24b54d2d05fbf39ddbb27dee08abd1d713f3
*/
if (handler->events)
handle->handle_events = handler->events;
#endif
int error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
list_add_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
list_add_tail_rcu(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
return 0;
}
static int maccel_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id) {
struct input_handle *handle;
int error;
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
handle->dev = input_get_device(dev);
handle->handler = handler;
handle->name = "maccel";
error = input_register_handle_head(handle);
if (error)
goto err_free_mem;
error = input_open_device(handle);
if (error)
goto err_unregister_handle;
printk(KERN_INFO pr_fmt("maccel flags: DEBUG=%s; FIXEDPT_BITS=%d"),
DEBUG_TEST ? "true" : "false", FIXEDPT_BITS);
printk(KERN_INFO pr_fmt("maccel connecting to device: %s (%s at %s)"),
dev_name(&dev->dev), dev->name ?: "unknown", dev->phys ?: "unknown");
return 0;
err_unregister_handle:
input_unregister_handle(handle);
err_free_mem:
kfree(handle);
return error;
}
static void maccel_disconnect(struct input_handle *handle) {
input_close_device(handle);
input_unregister_handle(handle);
kfree(handle);
}
static const struct input_device_id my_ids[] = {
{.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
.evbit = {BIT_MASK(EV_REL)}}, // Match all relative pointer values
{},
};
MODULE_DEVICE_TABLE(input, my_ids);
struct input_handler maccel_handler = {.events = maccel_events,
.connect = maccel_connect,
.disconnect = maccel_disconnect,
.name = "maccel",
.id_table = my_ids};
+35
View File
@@ -0,0 +1,35 @@
#include "./input_handler.h"
#include "input_echo.h"
/*
* We initialize the character driver for the userspace visualizations,
* and we register the input_handler.
*/
static int __init driver_initialization(void) {
int error;
error = create_char_device();
if (error)
return error;
error = input_register_handler(&maccel_handler);
if (error)
goto err_free_chrdev;
return 0;
err_free_chrdev:
destroy_char_device();
return error;
}
static void __exit driver_exit(void) {
input_unregister_handler(&maccel_handler);
destroy_char_device();
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gnarus-G");
MODULE_DESCRIPTION("Mouse acceleration driver.");
module_init(driver_initialization);
module_exit(driver_exit);
+25
View File
@@ -0,0 +1,25 @@
#ifndef _MATH_H_
#define _MATH_H_
#include "fixedptc.h"
struct vector {
fpt x;
fpt y;
};
static inline fpt magnitude(struct vector v) {
fpt x_square = fpt_mul(v.x, v.x);
fpt y_square = fpt_mul(v.y, v.y);
fpt x_square_plus_y_square = fpt_add(x_square, y_square);
dbg("dx^2 (in) %s", fptoa(x_square));
dbg("dy^2 (in) %s", fptoa(y_square));
dbg("square modulus (in) %s", fptoa(x_square_plus_y_square));
return fpt_sqrt(x_square_plus_y_square);
}
static inline fpt minsd(fpt a, fpt b) { return (a < b) ? a : b; }
#endif // !_MATH_H_
+93
View File
@@ -0,0 +1,93 @@
#include "dbg.h"
#include "linux/input.h"
#include <linux/stddef.h>
#define NONE_EVENT_VALUE 0
typedef struct {
int *x;
int *y;
} mouse_move;
static mouse_move MOVEMENT = {.x = NULL, .y = NULL};
/*
* Track whether we injected synthetic storage for a missing axis.
* When rotation is active and the mouse only reports one axis (e.g. pure
* horizontal movement -> only REL_X), we need a place for f_accelerate
* to write the rotated cross-axis component. These synthetic values
* are later injected into the event stream by maccel_events().
*/
static bool injected_x = false;
static bool injected_y = false;
static int synthetic_x_val = 0;
static int synthetic_y_val = 0;
static inline void update_mouse_move(struct input_value *value) {
switch (value->code) {
case REL_X:
MOVEMENT.x = &value->value;
break;
case REL_Y:
MOVEMENT.y = &value->value;
break;
default:
dbg("bad movement input_value: (code, value) = (%d, %d)", value->code,
value->value);
}
}
static inline int get_x(mouse_move movement) {
if (movement.x == NULL) {
return NONE_EVENT_VALUE;
}
return *movement.x;
}
static inline int get_y(mouse_move movement) {
if (movement.y == NULL) {
return NONE_EVENT_VALUE;
}
return *movement.y;
}
static inline void set_x_move(int value) {
if (MOVEMENT.x == NULL) {
return;
}
*MOVEMENT.x = value;
}
static inline void set_y_move(int value) {
if (MOVEMENT.y == NULL) {
return;
}
*MOVEMENT.y = value;
}
/*
* When rotation is active and one axis is missing from the frame,
* point the missing axis to synthetic storage so f_accelerate can
* write the rotated component into it.
*/
static inline void ensure_axes_for_rotation(void) {
if (MOVEMENT.x == NULL) {
synthetic_x_val = 0;
MOVEMENT.x = &synthetic_x_val;
injected_x = true;
dbg("rotation: injecting synthetic REL_X storage (x=%d)", 0);
}
if (MOVEMENT.y == NULL) {
synthetic_y_val = 0;
MOVEMENT.y = &synthetic_y_val;
injected_y = true;
dbg("rotation: injecting synthetic REL_Y storage (y=%d)", 0);
}
}
static inline void clear_mouse_move(void) {
MOVEMENT.x = NULL;
MOVEMENT.y = NULL;
injected_x = false;
injected_y = false;
}
+90
View File
@@ -0,0 +1,90 @@
#ifndef _PARAM_H_
#define _PARAM_H_
#include "accel/mode.h"
#include "fixedptc.h"
#include "linux/moduleparam.h"
#define RW_USER_GROUP 0664
#define PARAM(param, default_value, desc) \
char *PARAM_##param = #default_value; \
module_param_named(param, PARAM_##param, charp, RW_USER_GROUP); \
MODULE_PARM_DESC(param, desc);
#if FIXEDPT_BITS == 64
PARAM(
SENS_MULT, 4294967296, // 1 << 32
"A factor applied by the sensitivity calculation after ACCEL is applied.");
PARAM(YX_RATIO, 4294967296, // 1 << 32
"A factor (Y/X) by which the final sensitivity calculated is multiplied "
"to produce the sensitivity applied to the Y axis.");
PARAM(INPUT_DPI, 4294967296000, // 1000 << 32
"The DPI of the mouse, used to normalize input to 1000 DPI equivalent "
"for consistent acceleration across different mice.");
#else
PARAM(SENS_MULT, 65536, // 1 << 16
"A factor applied the sensitivity calculation after ACCEL is applied.");
PARAM(YX_RATIO, 65536, // 1 << 16
"A factor (Y/X) by which the final sensitivity calculated is multiplied "
"to produce the sensitivity applied to the Y axis.");
PARAM(INPUT_DPI, 65536000, // 1000 << 16
"The DPI of the mouse, used to normalize input to 1000 DPI equivalent "
"for consistent acceleration across different mice.");
#endif
PARAM(ANGLE_ROTATION, 0,
"Apply rotation (degrees) to the mouse movement input");
// For Linear Mode
PARAM(ACCEL, 0, "Control the sensitivity calculation.");
PARAM(OFFSET, 0, "Input speed threshold (counts/ms) before acceleration begins.");
PARAM(OUTPUT_CAP, 0, "Control the maximum sensitivity.");
// For Natural Mode
#if FIXEDPT_BITS == 64
PARAM(DECAY_RATE, 429496730, // 0.1 << 32
"Decay rate of the Natural curve.");
PARAM(LIMIT, 6442450944, // 1.5 << 32
"Limit of the Natural curve.");
#else
PARAM(DECAY_RATE, 6554, // 0.1 << 16
"Decay rate of the Natural curve");
PARAM(LIMIT, 98304, // 1.5 << 16
"Limit of the Natural curve");
#endif
// For Synchronous Mode
#if FIXEDPT_BITS == 64
PARAM(GAMMA, 4294967296, // 1 << 32
"Control how fast you get from low to fast around the midpoint");
PARAM(SMOOTH, 2147483648, // 0.5 << 32
"Control the suddeness of the sensitivity increase.");
PARAM(MOTIVITY, 6442450944, // 1.5 << 32
"Set the maximum sensitivity while also setting the minimum to "
"1/MOTIVITY");
PARAM(SYNC_SPEED, 21474836480, // 5 << 32
"Set The middle sensitivity between you min and max sensitivity");
#else
PARAM(GAMMA, 65536, // 1 << 16
"Control how fast you get from low to fast around the midpoint");
PARAM(SMOOTH, 32768, // 0.5 << 16
"Control the suddeness of the sensitivity increase.");
PARAM(MOTIVITY, 98304, // 1.5 << 16
"Set the maximum sensitivity while also setting the minimum to "
"1/MOTIVITY");
PARAM(SYNC_SPEED, 327680, // 5 << 16
"Set The middle sensitivity between you min and max sensitivity");
#endif
// Flags
#define PARAM_FLAG(param, default_value, desc) \
unsigned char PARAM_##param = default_value; \
module_param_named(param, PARAM_##param, byte, RW_USER_GROUP); \
MODULE_PARM_DESC(param, desc);
PARAM_FLAG(MODE, linear, "Desired type of acceleration.");
#endif // !_PARAM_H_
+34
View File
@@ -0,0 +1,34 @@
#ifndef __SPEED_H__
#define __SPEED_H__
#include "dbg.h"
#include "fixedptc.h"
#include "math.h"
/**
* Track this to enable the UI to show the last noted
* input counts/ms (speed).
*/
static fpt LAST_INPUT_MOUSE_SPEED = 0;
static inline fpt input_speed(fpt dx, fpt dy, fpt time_ms) {
fpt distance = magnitude((struct vector){dx, dy});
if (distance == -1) {
dbg("distance calculation failed: t = %s", fptoa(time_ms));
return 0;
}
dbg("distance (in) %s", fptoa(distance));
fpt speed = fpt_div(distance, time_ms);
LAST_INPUT_MOUSE_SPEED = speed;
dbg("time interval %s", fptoa(time_ms));
dbg("speed (in) %s", fptoa(speed));
return speed;
}
#endif // !__SPEED_H__
+187
View File
@@ -0,0 +1,187 @@
#include "../accel.h"
#include "test_utils.h"
#include <stdio.h>
static int test_acceleration(const char *filename, struct accel_args args) {
const int LINE_LEN = 26;
const int MIN = -128;
const int MAX = 127;
char content[256 * 256 * LINE_LEN + 1];
strcpy(content, ""); // initialize as an empty string
for (int x = MIN; x < MAX; x++) {
for (int y = MIN; y < MAX; y++) {
int x_out = x;
int y_out = y;
f_accelerate(&x_out, &y_out, FIXEDPT_ONE, args);
char curr_debug_print[LINE_LEN];
sprintf(curr_debug_print, "(%d, %d) => (%d, %d)\n", x, y, x_out, y_out);
strcat(content, curr_debug_print);
}
}
assert_snapshot(filename, content);
return 0;
}
static int test_linear_acceleration(const char *filename, fpt param_sens_mult,
fpt param_yx_ratio, fpt param_accel,
fpt param_offset, fpt param_output_cap) {
struct linear_curve_args _args =
(struct linear_curve_args){.accel = param_accel,
.offset = param_offset,
.output_cap = param_output_cap};
struct accel_args args = {
.sens_mult = param_sens_mult,
.yx_ratio = param_yx_ratio,
.input_dpi = fpt_fromint(1000),
.tag = linear,
.args = (union __accel_args){.linear = _args},
};
return test_acceleration(filename, args);
}
static int test_natural_acceleration(const char *filename, fpt param_sens_mult,
fpt param_yx_ratio, fpt param_decay_rate,
fpt param_offset, fpt param_limit) {
struct natural_curve_args _args =
(struct natural_curve_args){.decay_rate = param_decay_rate,
.offset = param_offset,
.limit = param_limit};
struct accel_args args = {
.sens_mult = param_sens_mult,
.yx_ratio = param_yx_ratio,
.input_dpi = fpt_fromint(1000),
.tag = natural,
.args = (union __accel_args){.natural = _args},
};
return test_acceleration(filename, args);
}
static int test_synchronous_acceleration(const char *filename,
fpt param_sens_mult,
fpt param_yx_ratio, fpt param_gamma,
fpt param_smooth, fpt param_motivity,
fpt param_sync_speed) {
struct synchronous_curve_args _args =
(struct synchronous_curve_args){.gamma = param_gamma,
.smooth = param_smooth,
.motivity = param_motivity,
.sync_speed = param_sync_speed};
struct accel_args args = {
.sens_mult = param_sens_mult,
.yx_ratio = param_yx_ratio,
.input_dpi = fpt_fromint(1000),
.tag = synchronous,
.args = (union __accel_args){.synchronous = _args},
};
return test_acceleration(filename, args);
}
static int test_no_accel_acceleration(const char *filename, fpt param_sens_mult,
fpt param_yx_ratio) {
struct no_accel_curve_args _args = (struct no_accel_curve_args){};
struct accel_args args = {
.sens_mult = param_sens_mult,
.yx_ratio = param_yx_ratio,
.input_dpi = fpt_fromint(1000),
.tag = no_accel,
.args = (union __accel_args){.no_accel = _args},
};
return test_acceleration(filename, args);
}
static int test_rotation_no_accel(const char *filename, fpt param_sens_mult,
fpt param_angle_deg) {
struct no_accel_curve_args _args = (struct no_accel_curve_args){};
struct accel_args args = {
.sens_mult = param_sens_mult,
.yx_ratio = FIXEDPT_ONE,
.input_dpi = fpt_fromint(1000),
.angle_rotation_deg = param_angle_deg,
.tag = no_accel,
.args = (union __accel_args){.no_accel = _args},
};
return test_acceleration(filename, args);
}
#define test_linear(sens_mult, yx_ratio, accel, offset, cap) \
assert(test_linear_acceleration( \
"SENS_MULT-" #sens_mult "-ACCEL-" #accel "-OFFSET" #offset \
"-OUTPUT_CAP-" #cap ".snapshot", \
fpt_rconst(sens_mult), fpt_rconst(yx_ratio), fpt_rconst(accel), \
fpt_rconst(offset), fpt_rconst(cap)) == 0);
#define test_natural(sens_mult, yx_ratio, decay_rate, offset, limit) \
assert(test_natural_acceleration( \
"Natural__SENS_MULT-" #sens_mult "-DECAY_RATE-" #decay_rate \
"-OFFSET" #offset "-LIMIT-" #limit ".snapshot", \
fpt_rconst(sens_mult), fpt_rconst(yx_ratio), \
fpt_rconst(decay_rate), fpt_rconst(offset), \
fpt_rconst(limit)) == 0);
#define test_synchronous(sens_mult, yx_ratio, gamma, smooth, motivity, \
sync_speed) \
assert(test_synchronous_acceleration( \
"Synchronous__SENS_MULT-" #sens_mult "-GAMMA-" #gamma \
"-SMOOTH" #smooth "-MOTIVITY-" #motivity \
"-SYNC_SPEED-" #sync_speed ".snapshot", \
fpt_rconst(sens_mult), fpt_rconst(yx_ratio), fpt_rconst(gamma), \
fpt_rconst(smooth), fpt_rconst(motivity), \
fpt_rconst(sync_speed)) == 0);
#define test_no_accel(sens_mult, yx_ratio) \
assert(test_no_accel_acceleration( \
"NoAccel__SENS_MULT-" #sens_mult "-YX_RATIO-" #yx_ratio \
".snapshot", \
fpt_rconst(sens_mult), fpt_rconst(yx_ratio)) == 0);
#define test_rotation(sens_mult, angle_deg) \
assert(test_rotation_no_accel( \
"Rotation__SENS_MULT-" #sens_mult "-ANGLE-" #angle_deg \
".snapshot", \
fpt_rconst(sens_mult), fpt_rconst(angle_deg)) == 0);
int main(void) {
test_linear(1, 1, 0, 0, 0);
test_linear(1, 1, 0.3, 2, 2);
test_linear(0.1325, 1, 0.3, 21.333333, 2);
test_linear(0.1875, 1, 0.05625, 10.6666666, 2);
test_linear(0.0917, 1, 0.002048, 78.125, 2.0239);
test_linear(0.07, 1.15, 0.055, 21, 3);
test_natural(1, 1, 0, 0, 0);
test_natural(1, 1, 0.1, 0, 0);
test_natural(1, 1, 0.1, 8, 0);
test_natural(1, 1, 0.03, 8, 1.5);
test_synchronous(1, 1.15, 0.8, 0.5, 1.5, 32);
test_no_accel(1, 1);
test_no_accel(0.5, 1.5);
/* Rotation tests: verify cross-axis output when one axis is 0.
* At 45 degrees, (10, 0) should produce roughly (7, 7) - not (10, 0).
* At 90 degrees, (10, 0) should produce roughly (0, 10). */
test_rotation(1, 45);
test_rotation(1, 90);
print_success;
}
+41
View File
@@ -0,0 +1,41 @@
#include "../fixedptc.h"
#include "./test_utils.h"
#include <assert.h>
#include <stdio.h>
#include <time.h>
void test_eq(char *value, double expected) {
fpt n = atofp(value);
double actual = fpt_todouble(n);
dbg("actual: (%li) %.15f, vs expected: %.15f\n", n, actual, expected);
assert(actual == expected);
}
void super_tiny_micro_minuscule_bench() {
int iterations = 100000;
double sum = 0;
for (int i = 0; i < iterations; i++) {
struct timespec begin, end;
clock_gettime(CLOCK_MONOTONIC_RAW, &begin);
fpt n = atofp("1826512586328");
dbg("atofp(\"1826512586328\") = %li\n", n);
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
sum += (double)(end.tv_nsec - begin.tv_nsec);
}
double avg = sum / iterations;
printf(" Avg run time is %fns\n", avg);
}
int main(void) {
test_eq("1073741824", 0.25);
test_eq("536870912", 0.125);
test_eq("1342177280", 0.3125);
test_eq("-335007449088", -78);
print_success;
super_tiny_micro_minuscule_bench();
}
+45
View File
@@ -0,0 +1,45 @@
#include "../dbg.h"
#include "../fixedptc.h"
#include "test_utils.h"
#include <assert.h>
#include <stdint.h>
void test_custom_division_against_fixedpt(double a, double b) {
#if FIXEDPT_BITS == 32
return;
#else
fpt n = fpt_rconst(a);
fpt divisor = fpt_rconst(b);
fpt quotient = div128_s64_s64(n, divisor);
fpt quotient1 = fpt_xdiv(n, divisor);
double actual = fpt_todouble(quotient);
double expected = fpt_todouble(quotient1);
dbg("actual = (%li) -> %.10f", quotient, actual);
dbg("expect = (%li) -> %.10f", quotient1, expected);
assert(actual == expected);
#endif
}
int main(void) {
test_custom_division_against_fixedpt(57, 5.5);
test_custom_division_against_fixedpt(0, 2.57);
test_custom_division_against_fixedpt(-1, 3);
test_custom_division_against_fixedpt(-128, 4);
test_custom_division_against_fixedpt(127, 1.5);
/* test_custom_division_against_fixedpth(135, 0); */ // You only crash once!
print_success;
return 0;
}
+27
View File
@@ -0,0 +1,27 @@
#include "../fixedptc.h"
#include "test_utils.h"
#include <assert.h>
#include <linux/limits.h>
#include <unistd.h>
int assert_string_value(char *filename, double value) {
fpt v = fpt_rconst(value);
char *_v = fptoa(v);
dbg("to_string %f = %s", value, _v);
assert_snapshot(filename, _v);
return 0;
}
#define test_str(value) \
assert(assert_string_value(__FILE_NAME__ "_" #value ".snapshot", value) == 0)
int main(void) {
test_str(0.25);
test_str(0.125);
test_str(0.3125);
test_str(-785);
print_success;
}
+48
View File
@@ -0,0 +1,48 @@
#include "../speed.h"
#include "./test_utils.h"
#include <stdio.h>
int assert_string_value(char *filename, double x, double y, double t) {
fpt dx = fpt_rconst(x);
fpt dy = fpt_rconst(y);
fpt dt = fpt_rconst(t);
dbg("in (%f, %f)", x, y);
dbg("in: x (fpt conversion) %s", fptoa(x));
dbg("in: y (fpt conversion) %s", fptoa(y));
fpt s = input_speed(dx, dy, dt);
double res = fpt_todouble(s);
dbg("(%f, %f) dt = %f -> %f\n", x, y, t, res);
char content[100];
sprintf(content, "(sqrt(%f, %f) / %f) = %f\n", x, y, t, res);
assert_snapshot(filename, content);
return 0;
}
#define test(x, y, time) \
assert(assert_string_value(__FILE_NAME__ "_sqrt_" #x "_" #y "_" #time \
".snapshot", \
x, y, time) == 0)
int main(void) {
test(1, 1, 1);
test(1, 21, 1);
test(64, -37, 1);
test(1, 4, 1);
test(-1, 1, 4);
test(1, 0, 100);
test(1, -1, 100);
test(-1, -24, 1);
print_success;
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
for test in tests/*.test.c; do
if [[ ! $test =~ $TEST_NAME ]]; then
continue
fi
gcc ${test} -o maccel_test -lm $DRIVER_CFLAGS || exit 1
./maccel_test || exit 1
rm maccel_test
done
@@ -0,0 +1 @@
-785.000000000
@@ -0,0 +1 @@
0.125000000
@@ -0,0 +1 @@
0.250000000
@@ -0,0 +1 @@
0.312500000
@@ -0,0 +1 @@
(sqrt(-1.000000, -24.000000) / 1.000000) = 24.020824
@@ -0,0 +1 @@
(sqrt(-1.000000, 1.000000) / 4.000000) = 0.353553
@@ -0,0 +1 @@
(sqrt(1.000000, -1.000000) / 100.000000) = 0.014142
@@ -0,0 +1 @@
(sqrt(1.000000, 0.000000) / 100.000000) = 0.010000
@@ -0,0 +1 @@
(sqrt(1.000000, 1.000000) / 1.000000) = 1.414214
@@ -0,0 +1 @@
(sqrt(1.000000, 21.000000) / 1.000000) = 21.023796
@@ -0,0 +1 @@
(sqrt(1.000000, 4.000000) / 1.000000) = 4.123106
@@ -0,0 +1 @@
(sqrt(64.000000, -37.000000) / 1.000000) = 73.925638
+137
View File
@@ -0,0 +1,137 @@
#include <assert.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
static int diff(const char *content, const char *filename) {
int pipe_fd[2];
pid_t child_pid;
// Create a pipe for communication
if (pipe(pipe_fd) == -1) {
perror("Pipe creation failed");
exit(EXIT_FAILURE);
}
// Fork the process
if ((child_pid = fork()) == -1) {
perror("Fork failed");
exit(EXIT_FAILURE);
}
if (child_pid == 0) { // Child process
// Close the write end of the pipe
close(pipe_fd[1]);
// Redirect stdin to read from the pipe
dup2(pipe_fd[0], STDIN_FILENO);
// Execute a command (e.g., "wc -l")
execlp("diff", "diff", "-u", "--color", filename, "-", NULL);
// If execlp fails
perror("Exec failed");
exit(EXIT_FAILURE);
} else { // Parent process
// Close the read end of the pipe
close(pipe_fd[0]);
// Write data to the child process
if (write(pipe_fd[1], content, strlen(content)) == -1) {
perror("failed to write content to the pipe for diff");
}
close(pipe_fd[1]);
// Wait for the child process to finish
wait(NULL);
}
return 0;
}
static int get_current_working_dir(char *buf, size_t buf_size) {
if (getcwd(buf, buf_size) != NULL) {
return 0;
}
perror("getcwd() error");
return 1;
}
static char *create_snapshot_file_path(const char *filename) {
char cwd[PATH_MAX];
if (get_current_working_dir(cwd, PATH_MAX)) {
return NULL;
};
static char filepath[PATH_MAX];
sprintf(filepath, "%s/tests/snapshots/%s", cwd, filename);
return filepath;
}
static void assert_snapshot(const char *__filename, const char *content) {
char *filename = create_snapshot_file_path(__filename);
if (filename == NULL) {
fprintf(stderr, "failed to create snapshot file: %s\n", filename);
exit(1);
}
int snapshot_file_exists = access(filename, F_OK) != -1;
FILE *snapshot_file;
if (snapshot_file_exists) {
snapshot_file = fopen(filename, "r");
} else {
snapshot_file = fopen(filename, "w");
}
if (snapshot_file == NULL) {
fprintf(stderr, "failed to open or create the snapshot file: %s\n",
filename);
exit(1);
}
if (snapshot_file_exists) {
struct stat stats;
int file_size;
stat(filename, &stats);
file_size = stats.st_size;
char *snapshot = (char *)malloc(stats.st_size + 1);
if (snapshot == NULL) {
fprintf(stderr,
"failed to allocate %zd bytes of a string for the snapshot "
"content in file: %s\n",
stats.st_size, filename);
exit(1);
}
size_t bytes_read = fread(snapshot, 1, file_size, snapshot_file);
if (bytes_read != file_size) {
fprintf(stderr, "failed to read a snapshot file %s\n", filename);
exit(1);
}
snapshot[file_size] = 0; // null byte terminator
int string_test_diff = strcmp(snapshot, content);
diff(content, filename);
/* dbg("diff in content = %d: snapshot '%s' vs now '%s'", string_test, */
/* snapshot, content); */
assert(string_test_diff == 0);
} else {
fprintf(snapshot_file, "%s", content);
printf("created a snapshot file %s\n", filename);
}
fclose(snapshot_file);
}
#define print_success printf("[%s]\t\tAll tests passed!\n", __FILE_NAME__)
+28
View File
@@ -0,0 +1,28 @@
#ifndef _UTILS_H_
#define _UTILS_H_
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
#include "dbg.h"
static inline int64_t div128_s64_s64_s64(int64_t high, int64_t low,
int64_t divisor) {
int64_t result;
// s.high.low
// high -> rdx
// low -> rax
uint64_t remainder;
__asm__("idivq %[B]"
: "=a"(result), "=d"(remainder)
: [B] "r"(divisor), "a"(low), "d"(high));
return result;
}
static inline int is_digit(char c) { return '0' <= c && c <= '9'; }
#endif