Compare commits

..

186 commits
enomem ... main

Author SHA1 Message Date
Denis
16f1c52025
Added support for real-time kernels 2024-03-04 03:12:54 +01:00
Denis
f64a336138
By default do not manage epp and leave that to asusctl 2024-03-04 02:57:21 +01:00
Denis
5947da5f2d
Use high frequency when available 2024-02-20 02:23:14 +01:00
Denis
c68e06a88b
moved inline_read_file and inline_write_file 2024-02-20 02:02:40 +01:00
Denis
3834651c0e
move dmi board read 2024-02-20 01:56:37 +01:00
Denis
2166c6a337
Use only two modes until Luke finishes upstreaming his patch 2024-02-14 02:22:56 +01:00
Denis
5b007e36d2
Change default config to comply with what will soon be upstreamed 2024-02-14 02:16:58 +01:00
Denis
e3fa85d6c8
fix spelling of a variable 2024-02-14 02:16:32 +01:00
Denis
98e64bb864
Fix sampling rate of iio and reduce reporting frequency from devices 2024-02-13 01:12:05 +01:00
Denis
9da2278640
Implement more performance modes 2024-02-13 00:37:22 +01:00
Denis
288a1bce18
Avoid timers cross-referencing 2024-01-15 21:16:18 +01:00
Denis
75c19eace1
Allow inverting x axis 2024-01-15 19:24:41 +01:00
Denis
8c8a488466
Fix sleep issie: allow using IMU polling interface in bmi323-imu too 2024-01-15 03:43:35 +01:00
Denis
c8e81eb272
fix build issue 2024-01-15 03:13:49 +01:00
Denis
749b184d13
Read data from the old bmc150-accel-i2c modified driver 2024-01-15 03:11:32 +01:00
Denis
35bbaa4f2d
prepare to work in backward-comaptible mode 2024-01-15 02:32:21 +01:00
Denis
1866d65b9e
Ability to disable IMU 2024-01-15 02:15:06 +01:00
Denis
f4f638ea24
add a * to match every input device 2024-01-08 03:42:34 +01:00
Denis
d608d07d89
Allow to set a specific thermal profile at start-up time 2024-01-07 18:02:09 +01:00
Denis
cb9b5e5c4a
Improve device grabbing based on configuration options 2024-01-07 15:50:59 +01:00
Denis
f75351fa36
Allow disabling thermal profiles switching and leds management 2024-01-07 15:44:47 +01:00
Denis
e0d36026e7
set thermal profile to quiet as default 2024-01-07 03:28:57 +01:00
Denis
50d36ba6fe
this was missing 2024-01-07 02:40:32 +01:00
Denis
5b7b977ec4
Implement thermal profile switch over asusctl 2024-01-07 02:37:09 +01:00
Denis
57ca777219
revert to hidraw leds setting 2024-01-07 00:25:57 +01:00
Denis
6873e50f00
Merge branch 'main' into devel 2024-01-07 00:05:16 +01:00
Denis
a64c7680fd
Prepare to switch thermal profiles 2024-01-07 00:04:35 +01:00
Denis
612fb701c5
use asusctl command to set leds 2024-01-06 23:49:08 +01:00
Denis
8a97652d05
use a shell command to set leds 2024-01-06 23:41:42 +01:00
Denis
658551fac1
Disregard the haptics select flag for vibration 2024-01-06 21:46:55 +01:00
Denis
4ab34c4547
gyro-to-analog should finally work at this point 2024-01-06 17:55:53 +01:00
Denis
34ba98d12d
fix gyro-to-analogs 2024-01-06 17:45:32 +01:00
Denis
f4a4efd201
pass settings 2024-01-06 17:01:26 +01:00
Denis
3bd22ad542
Improve gyro-to-analog(s) 2024-01-06 16:59:41 +01:00
Denis
0853d0cdc6
Add no libinput udev rule 2024-01-06 16:39:52 +01:00
Denis
6055034bf6
Improve dualsense output 2024-01-06 16:10:04 +01:00
Denis
79fe2cf9dd
fix up gyro-to-analog 2024-01-06 16:01:00 +01:00
Denis
dde8b0f152
Do not set bytes reserved for edge model while emulating a normal dualsense 2024-01-06 15:39:15 +01:00
Denis
39cd08d01a
Add rdesc for bluetooth normal dualsense 2024-01-06 15:35:16 +01:00
Denis
ee02e47df0
improve gyro-to-joy 2024-01-06 03:43:40 +01:00
Denis
ae3fac6301
swap y and z, improve gyro-to-joystick 2024-01-06 03:32:50 +01:00
Denis
ec2d777245
try something different 2024-01-06 03:07:36 +01:00
Denis
6edf68376d
debug 2024-01-06 02:34:37 +01:00
Denis
479b671733
I got indexes wrong... ofc. 2024-01-06 02:29:45 +01:00
Denis
6572e07bcb
debugging 2024-01-06 02:22:38 +01:00
Denis
984e735d06
Do not invert gyro/accel axis 2024-01-06 00:28:58 +01:00
Denis
1a268f1333
rdesc for USB dualsense 2024-01-06 00:22:55 +01:00
Denis
ef12d04dc8
Prepare to emulate the normal model 2024-01-06 00:02:26 +01:00
Denis Benato
b27f1778e3 Prepare to support normal dualsense 2024-01-05 23:37:05 +01:00
Denis Benato
43b3871a7a Remove useless header 2024-01-04 14:27:07 +01:00
Denis Benato
eed329b829 Use function buttons for back paddles 2024-01-04 14:26:16 +01:00
Denis
06454661bf
Fix Y 2024-01-03 16:17:18 +01:00
Denis
42141c3058
Better join 2024-01-03 16:13:22 +01:00
Denis
48e9c261a0
join analogs and gyroscope 2024-01-03 15:59:00 +01:00
Denis
bcee26dcfd
Swap back buttons 2024-01-03 15:48:31 +01:00
Denis
93cfd21f11
linking error 2024-01-03 15:45:45 +01:00
Denis
026e970615
join analogs and gyroscope 2024-01-03 15:39:19 +01:00
Denis
d208f5dc2a
Print bluetooth emulation info 2024-01-03 15:11:39 +01:00
Denis
9234403297
change default config 2024-01-03 04:09:58 +01:00
Denis
778082077c
controller over bluetooth settings parameter added 2024-01-03 04:05:34 +01:00
Denis
eec5cadf8c
Remove dead code 2024-01-03 03:36:55 +01:00
Denis
b38563243f
Do not use bluetooth for dualshock for now 2024-01-03 03:16:39 +01:00
Denis
4b6183eeb6
Remove the finger emulation for m1m2 2024-01-03 03:16:18 +01:00
Denis
2c3d7de0e4
kernel does it this way... 2024-01-03 02:56:07 +01:00
Denis
7b50fbfca1
Fix dualshock emulating over bluetooth 2024-01-03 02:41:51 +01:00
Denis
337244b0de
oh uniq must be 16 bytes 2024-01-03 02:26:45 +01:00
Denis
d091e54ecb
dualshock4 over bluetooth as well 2024-01-03 02:09:51 +01:00
Denis
358fba86d6
allow disabling touchbar + use timestamp from the bmi323 2024-01-03 00:59:57 +01:00
Denis
bb4ff25130
pressing touchbar should also count as touchbar pressed because on the real hardware you cannot avoid that. 2024-01-02 23:06:51 +01:00
Denis
6b5aeaa9ef
Rumble should work better... 2024-01-02 22:54:13 +01:00
Denis
d777f284b0
udev rules 2024-01-02 22:24:03 +01:00
Denis
6640812315
restore Y too 2024-01-02 22:13:06 +01:00
Denis
31fc20de9a
X MSB works, compose the rest ox X 2024-01-02 22:09:53 +01:00
Denis
c68ddd85b8
I have no idea... 2024-01-02 22:03:11 +01:00
Denis
9cf162681e
I do have no words to express how stupid I am 2024-01-02 21:58:21 +01:00
Denis
1c401c9dbf
debug 2024-01-02 21:51:41 +01:00
Denis
e85322e38c
debug 2024-01-02 21:47:35 +01:00
Denis
f3ac4dc285
I have finished messages 2024-01-02 21:35:54 +01:00
Denis
42832e0b0d
dafuq? 2024-01-02 21:30:49 +01:00
Denis
782865dbb6
no idea... 2024-01-02 21:24:12 +01:00
Denis
58d7d7c553
better? 2024-01-02 21:20:07 +01:00
Denis
c668a9406c
what about this? 2024-01-02 21:16:53 +01:00
Denis
922dd90831
let's exclude the other part of X too 2024-01-02 21:11:00 +01:00
Denis
c400ee7d22
let's fix X alone 2024-01-02 21:08:08 +01:00
Denis
a755e2a18c
default to touchpad button for m1 and m2 buttons 2024-01-02 21:04:09 +01:00
Denis
e36ad2e47d
try this way 2024-01-02 21:02:42 +01:00
Denis
e3e2a7e83e
wtf... 2024-01-02 20:56:29 +01:00
Denis
ae2e50cc2c
whoopsie... 2024-01-02 20:53:04 +01:00
Denis
ebf83d6350
Add touchbar to dualsense 2024-01-02 19:28:58 +01:00
Denis
8870020cab
Fix one axis improperly inverted 2024-01-02 19:17:56 +01:00
Denis
72df979cf6
Can use m1 and m2 to simulate touchpad 2024-01-02 17:58:10 +01:00
Denis
62c903830e
Remove problematic pressis based on trigger values 2023-12-29 02:25:38 +01:00
Denis
089e289003
crank up sampling frequency to the max 2023-12-28 18:01:34 +01:00
Denis
f7ee474659
Warn about failures hiding controllers 2023-12-28 15:36:50 +01:00
Denis
b9ea79a558
hide opened input 2023-12-28 15:22:34 +01:00
Denis
6ff835405e
Removed benchmarking stuff 2023-12-28 15:09:41 +01:00
Denis
10c3ce4fe7
fix nsecs used as usecs 2023-12-27 22:14:21 +01:00
Denis
6f60b53745
check if this solves the problem 2023-12-26 23:42:12 +01:00
Denis
bca99cefca
check if the problem is constant timeouts 2023-12-26 22:27:39 +01:00
Denis
34aa554e29
exclude the rest 2023-12-26 21:21:37 +01:00
Denis
1055f4d796
try to exclude iio 2023-12-26 20:59:57 +01:00
Denis
309609a377
Lower CPU usage 2023-12-26 18:52:40 +01:00
Denis
a0a5eba0f4
note 2023-12-26 15:46:19 +01:00
Denis
5b5592b0a7
Restore hidraw set leds 2023-12-26 15:27:57 +01:00
Denis
2bcbb4d9fa
.... 2023-12-26 15:05:54 +01:00
Denis
8021eba1e4
wtf... 2023-12-26 15:00:57 +01:00
Denis
6abee8e0e8
well... 2023-12-26 14:55:09 +01:00
Denis
95ce2a687b
hopefully this works 2023-12-26 14:52:26 +01:00
Denis
acd59cecf3
retry to change properties 2023-12-26 14:40:21 +01:00
Denis
fe11a9f5b6
Change set method 2023-12-25 23:03:52 +01:00
Denis
566ab8b93a
changed the problematic interface name 2023-12-25 21:35:11 +01:00
Denis
17afabd24c
Try to connect to dbus 2023-12-25 20:31:25 +01:00
Denis
82fab22cff
restore vibration and brightness value 2023-12-25 20:25:46 +01:00
Denis
cca9a4b375
restore virtual mouse and keyboard 2023-12-24 02:58:01 +01:00
Denis
7498f90f1a
too much time 2023-12-24 02:08:12 +01:00
Denis
f351427a05
do not fail on no fd 2023-12-24 00:08:18 +01:00
Denis
458ef9f07d
works with both kernel and sdl2 while using bt 2023-12-23 23:59:33 +01:00
Denis
4825ee8e17
perf testing: exclude mouse and keyboard 2023-12-21 22:55:58 +01:00
Denis
80331fa85a
additional checks for fd > 0 2023-12-21 22:55:00 +01:00
Denis
6f9a16e694
Remove performance measurement printf 2023-12-21 22:08:08 +01:00
Denis
464d257a23
performance test 2023-12-21 21:48:00 +01:00
Denis
2aa79bb180
Use monotonic clock for gyroscope data 2023-12-21 21:42:12 +01:00
Denis
a4981b2ad3
fix the command recognition while in bluetooth mode 2023-12-21 03:13:36 +01:00
Denis
a4b98139ee
Use bluetooth dualshock 2023-12-21 02:04:54 +01:00
Denis
8857d30a58
Revert "use the ACPI mount matrix"
This reverts commit 8637035ab2.
2023-12-20 23:21:43 +01:00
Denis
8637035ab2
use the ACPI mount matrix 2023-12-20 19:17:48 +01:00
Denis
628e803834
better imu startup 2023-12-20 15:32:43 +01:00
Denis
566d89df29
Allow the user to disable gamepad rumble and leds control 2023-12-20 14:35:46 +01:00
Denis
646ed984de
wrong if statement 2023-12-19 22:54:57 +01:00
Denis
9aa38f416c
Change default configuration 2023-12-19 22:23:19 +01:00
Denis
793f9e7e56
fix both dpad up in mouse mode and lctrl 2023-12-19 21:23:01 +01:00
Denis
18c1e27c3a
fix reported USB name 2023-12-19 21:22:39 +01:00
Denis
0324cd2760
make it feel like a phone toggle 2023-12-19 02:08:32 +01:00
Denis
23dad1ba60
use mkdir -p 2023-12-19 01:46:43 +01:00
Denis
c4b6616675
reference bash scripts for start buffered mode 2023-12-19 01:25:33 +01:00
Denis
68412b3318
quickly vibrate 2023-12-19 00:37:56 +01:00
Denis
8fc2bd42b5
1s is too much... 2023-12-19 00:15:27 +01:00
Denis
e6a940dc4d
Why do I get uneven rumbles? 2023-12-19 00:12:26 +01:00
Denis
96b47c6e49
this might work better... 2023-12-19 00:08:51 +01:00
Denis
9397aa2348
whoopsie... 2023-12-18 23:41:32 +01:00
Denis
692ff53ff7
Rumble on mode switch is ON by default 2023-12-18 23:37:36 +01:00
Denis
0336bf3777
maybe this fixes the too long rumble on the dualsense? 2023-12-18 23:13:38 +01:00
Denis
89f32b5eb1
Rumble on mode switch 2023-12-18 23:12:59 +01:00
Denis
4623413b5a
Placeholder code to get rumble working on mode switch 2023-12-18 02:39:30 +01:00
Denis
a89d9b5ad1
Fix timer only firing up once 2023-12-18 02:24:53 +01:00
Denis
ff7b0d918c
Prepare the logic for the rumble-on-switch 2023-12-18 01:29:08 +01:00
Denis
a9eb18319d
Read and discard bytes from Asus MCU 2023-12-18 00:39:49 +01:00
Denis
bc8911e180
fix an error 2023-12-17 23:14:26 +01:00
Denis
f6e0173854
whoopsie... 2023-12-17 23:03:41 +01:00
Denis
ea5f026e04
timer timeout to evdev 2023-12-17 17:37:58 +01:00
Denis
6eb6847444
using nanoseconds as such 2023-12-17 16:43:45 +01:00
Denis
71603dc4d6
WIP: timer implementation 2023-12-17 15:48:17 +01:00
Denis
466f1a4db6
improving devices support 2023-12-17 03:50:36 +01:00
Denis
660f73c26d
fixed arrows and lctrl 2023-12-17 03:12:21 +01:00
Denis
c331a4e139
Avoid fooding with unnecessary messages 2023-12-17 03:05:34 +01:00
Denis
0105251e34
Also sends arrows 2023-12-17 02:36:23 +01:00
Denis
c9354dc357
keyboard should work now 2023-12-17 02:22:29 +01:00
Denis
c6ecb53bd3
chatgpt blatantly refused to write this so I had to do it myself. 2023-12-17 01:38:06 +01:00
Denis
046f635de6
Removed useless files 2023-12-17 00:35:23 +01:00
Denis
5e3a1e4e98
whoopsie 2023-12-17 00:11:45 +01:00
Denis
f9f5b67690
use hidraw dev to change leds 2023-12-17 00:05:09 +01:00
Denis
00cc6adf7d
whoopsie... 2023-12-16 23:33:19 +01:00
Denis
5548eb806f
Improved mode switch 2023-12-16 23:19:32 +01:00
Denis
587fe7ffcf
testing mouse mode and swtich 2023-12-16 23:15:04 +01:00
Denis
4bdff1a50d
bugfix 2023-12-16 22:35:26 +01:00
Denis
cc16d4983e
mode switch should work 2023-12-16 22:26:37 +01:00
Denis
0b7f888a2f
Implementing virtual mouse and mode switch 2023-12-16 22:10:57 +01:00
Denis
6072f88002
reimplement BTN_MODE 2023-12-16 19:08:03 +01:00
Denis
0797e2e326
discarding hidraw mode change and initial setup 2023-12-16 18:39:15 +01:00
Denis
563da79b2b
add buttons references 2023-12-16 14:58:33 +01:00
Denis
b69d2d7198
better formatting 2023-12-15 00:55:17 +01:00
Denis
3ca8c59837
whoopsie 2023-12-15 00:15:00 +01:00
Denis
13f35d6924
text for when controller mode is switched 2023-12-15 00:12:36 +01:00
Denis
75b4bb1912
toggle controller mode on the rog ally 2023-12-15 00:06:46 +01:00
Denis
fb36302883
as a temporary workaround swap z and y and also do -1 where needed 2023-12-14 23:53:52 +01:00
Denis
8b57c19d0a
default_gamepad is an int: load config as such 2023-12-14 23:38:51 +01:00
Denis
bcabb2732b
honor settings in output server 2023-12-14 23:34:20 +01:00
Denis
f55c055ad5
honor the enable qam setting 2023-12-14 23:28:28 +01:00
Denis
a3b2eb0e41
Configuration refactor 2023-12-14 23:16:36 +01:00
Denis
917ec565f8
Can you please start working as indentended? please.... 2023-12-14 14:05:13 +01:00
Denis
49ba884623
try with SOCK_STREAM 2023-12-14 13:58:50 +01:00
Denis
971b661c5e
watch for messages on the receiving socket 2023-12-14 13:53:48 +01:00
Denis
c2e3a7704a
make logic work with both SOCK_STREAM and SEQ_PACKET 2023-12-14 13:41:33 +01:00
Denis
4d2c9c2e36
Fixed a bug that prevented the gyro from being registered 2023-12-14 03:15:34 +01:00
Denis
e580cca85c
fix iio and hidraw reads 2023-12-14 03:04:29 +01:00
Denis
f87028f8f7
report gyro & accel buffer to the output device 2023-12-14 02:55:46 +01:00
48 changed files with 6117 additions and 1619 deletions

View file

@ -0,0 +1,2 @@
ACTION=="add|change", KERNEL=="event[0-9]*", ATTRS{name}=="Wireless Controller Touchpad", ENV{LIBINPUT_IGNORE_DEVICE}="1"
ACTION=="add|change", KERNEL=="event[0-9]*", ATTRS{name}=="Wireless Edge Controller Touchpad", ENV{LIBINPUT_IGNORE_DEVICE}="1"

1
99-js-block.rules Normal file
View file

@ -0,0 +1 @@
KERNEL=="js[0-9]*|event[0-9]*", SUBSYSTEM=="input", ATTRS{idVendor}=="045e", ATTRS{idProduct}=="028e", MODE="000", GROUP="root", TAG="", RUN+="/bin/chmod 000 /dev/input/%k"

1
99-xbox360-block.rules Normal file
View file

@ -0,0 +1 @@
ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="045E", ATTRS{idProduct}=="028E", RUN+="/bin/sh -c 'echo 0 > /sys$DEVPATH/authorized'"

View file

@ -13,10 +13,13 @@ set(ROGUE_EXECUTABLE_NAME "rogue-enemy")
set(STRAY_EXECUTABLE_NAME "stray-ally")
set(ALLINONE_EXECUTABLE_NAME "allynone")
find_package(PkgConfig REQUIRED) # Include functions provided by PkgConfig module.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
add_executable(${ROGUE_EXECUTABLE_NAME}
dev_timer.c
dev_evdev.c
dev_iio.c
dev_hidraw.c
@ -35,7 +38,10 @@ add_executable(${STRAY_EXECUTABLE_NAME}
settings.c
virt_ds4.c
virt_ds5.c
virt_mouse.c
virt_kbd.c
devices_status.c
rogue_enemy.c
)
add_executable(${ALLINONE_EXECUTABLE_NAME}
@ -44,7 +50,10 @@ add_executable(${ALLINONE_EXECUTABLE_NAME}
settings.c
virt_ds4.c
virt_ds5.c
virt_mouse.c
virt_kbd.c
devices_status.c
dev_timer.c
dev_evdev.c
dev_iio.c
dev_hidraw.c
@ -58,7 +67,7 @@ add_executable(${ALLINONE_EXECUTABLE_NAME}
set_property(TARGET ${ALLINONE_EXECUTABLE_NAME} PROPERTY C_STANDARD 17)
target_link_libraries(${ALLINONE_EXECUTABLE_NAME} PRIVATE Threads::Threads -levdev -ludev -lconfig -lm)
target_link_libraries(${ALLINONE_EXECUTABLE_NAME} PRIVATE Threads::Threads -levdev -ludev -lconfig -lm -lz)
set_target_properties(${ALLINONE_EXECUTABLE_NAME} PROPERTIES LINKER_LANGUAGE C)
@ -74,7 +83,7 @@ install(TARGETS ${ROGUE_EXECUTABLE_NAME} DESTINATION bin)
set_property(TARGET ${STRAY_EXECUTABLE_NAME} PROPERTY C_STANDARD 17)
target_link_libraries(${STRAY_EXECUTABLE_NAME} PRIVATE Threads::Threads -levdev -ludev -lconfig -lm)
target_link_libraries(${STRAY_EXECUTABLE_NAME} PRIVATE Threads::Threads -levdev -ludev -lconfig -lm -lz)
set_target_properties(${STRAY_EXECUTABLE_NAME} PROPERTIES LINKER_LANGUAGE C)

View file

@ -0,0 +1,22 @@
Event: time 1702734480.395212, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70069
Event: time 1702734480.395212, type 1 (EV_KEY), code 184 (KEY_F14), value 1
Event: time 1702734480.395212, -------------- SYN_REPORT ------------
Event: time 1702734480.666881, type 1 (EV_KEY), code 184 (KEY_F14), value 2
Event: time 1702734480.666881, -------------- SYN_REPORT ------------
Event: time 1702734480.703572, type 1 (EV_KEY), code 184 (KEY_F14), value 2
Event: time 1702734480.703572, -------------- SYN_REPORT ------------
Event: time 1702734480.740210, type 1 (EV_KEY), code 184 (KEY_F14), value 2
Event: time 1702734480.740210, -------------- SYN_REPORT ------------
Event: time 1702734480.776877, type 1 (EV_KEY), code 184 (KEY_F14), value 2
Event: time 1702734480.776877, -------------- SYN_REPORT ------------
Event: time 1702734480.813547, type 1 (EV_KEY), code 184 (KEY_F14), value 2
Event: time 1702734480.813547, -------------- SYN_REPORT ------------
Event: time 1702734480.850210, type 1 (EV_KEY), code 184 (KEY_F14), value 2
Event: time 1702734480.850210, -------------- SYN_REPORT ------------
Event: time 1702734480.890215, type 1 (EV_KEY), code 184 (KEY_F14), value 2
Event: time 1702734480.890215, -------------- SYN_REPORT ------------
Event: time 1702734480.930212, type 1 (EV_KEY), code 184 (KEY_F14), value 2
Event: time 1702734480.930212, -------------- SYN_REPORT ------------
Event: time 1702734480.943181, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70069
Event: time 1702734480.943181, type 1 (EV_KEY), code 184 (KEY_F14), value 0
Event: time 1702734480.943181, -------------- SYN_REPORT ------------

View file

@ -0,0 +1,18 @@
Event: time 1702734659.559525, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e0
Event: time 1702734659.559525, type 1 (EV_KEY), code 29 (KEY_LEFTCTRL), value 1
Event: time 1702734659.559525, -------------- SYN_REPORT ------------
Event: time 1702734659.560490, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e2
Event: time 1702734659.560490, type 1 (EV_KEY), code 56 (KEY_LEFTALT), value 1
Event: time 1702734659.560490, -------------- SYN_REPORT ------------
Event: time 1702734659.561435, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004c
Event: time 1702734659.561435, type 1 (EV_KEY), code 111 (KEY_DELETE), value 1
Event: time 1702734659.561435, -------------- SYN_REPORT ------------
Event: time 1702734659.562485, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e0
Event: time 1702734659.562485, type 1 (EV_KEY), code 29 (KEY_LEFTCTRL), value 0
Event: time 1702734659.562485, -------------- SYN_REPORT ------------
Event: time 1702734659.563486, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e2
Event: time 1702734659.563486, type 1 (EV_KEY), code 56 (KEY_LEFTALT), value 0
Event: time 1702734659.563486, -------------- SYN_REPORT ------------
Event: time 1702734659.565433, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7004c
Event: time 1702734659.565433, type 1 (EV_KEY), code 111 (KEY_DELETE), value 0
Event: time 1702734659.565433, -------------- SYN_REPORT ------------

View file

@ -0,0 +1,6 @@
Event: time 1702734692.300847, type 4 (EV_MSC), code 4 (MSC_SCAN), value ff3100a6
Event: time 1702734692.300847, type 1 (EV_KEY), code 186 (KEY_F16), value 1
Event: time 1702734692.300847, -------------- SYN_REPORT ------------
Event: time 1702734692.301778, type 4 (EV_MSC), code 4 (MSC_SCAN), value ff3100a6
Event: time 1702734692.301778, type 1 (EV_KEY), code 186 (KEY_F16), value 0
Event: time 1702734692.301778, -------------- SYN_REPORT ------------

View file

@ -0,0 +1,14 @@
Event: time 1702734541.227670, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7006a
Event: time 1702734541.227670, type 1 (EV_KEY), code 185 (KEY_F15), value 1
Event: time 1702734541.227670, -------------- SYN_REPORT ------------
Event: time 1702734541.490206, type 1 (EV_KEY), code 185 (KEY_F15), value 2
Event: time 1702734541.490206, -------------- SYN_REPORT ------------
Event: time 1702734541.530208, type 1 (EV_KEY), code 185 (KEY_F15), value 2
Event: time 1702734541.530208, -------------- SYN_REPORT ------------
Event: time 1702734541.570205, type 1 (EV_KEY), code 185 (KEY_F15), value 2
Event: time 1702734541.570205, -------------- SYN_REPORT ------------
Event: time 1702734541.606872, type 1 (EV_KEY), code 185 (KEY_F15), value 2
Event: time 1702734541.606872, -------------- SYN_REPORT ------------
Event: time 1702734541.640678, type 4 (EV_MSC), code 4 (MSC_SCAN), value 7006a
Event: time 1702734541.640678, type 1 (EV_KEY), code 185 (KEY_F15), value 0
Event: time 1702734541.640678, -------------- SYN_REPORT ------------

View file

@ -0,0 +1,17 @@
Event: time 1702734610.835523, type 4 (EV_MSC), code 4 (MSC_SCAN), value ff3100a7
Event: time 1702734610.835523, type 1 (EV_KEY), code 187 (KEY_F17), value 1
Event: time 1702734610.835523, -------------- SYN_REPORT ------------
Event: time 1702734610.836480, type 4 (EV_MSC), code 4 (MSC_SCAN), value ff3100a7
Event: time 1702734610.836480, type 1 (EV_KEY), code 187 (KEY_F17), value 0
Event: time 1702734610.836480, -------------- SYN_REPORT ------------
F17 appears before release
Event: time 1702734611.433499, type 4 (EV_MSC), code 4 (MSC_SCAN), value ff3100a8
Event: time 1702734611.433499, type 1 (EV_KEY), code 188 (KEY_F18), value 1
Event: time 1702734611.433499, -------------- SYN_REPORT ------------
Event: time 1702734611.434434, type 4 (EV_MSC), code 4 (MSC_SCAN), value ff3100a8
Event: time 1702734611.434434, type 1 (EV_KEY), code 188 (KEY_F18), value 0
Event: time 1702734611.434434, -------------- SYN_REPORT ------------

View file

@ -0,0 +1,6 @@
Event: time 1702734588.908929, type 4 (EV_MSC), code 4 (MSC_SCAN), value ff310038
Event: time 1702734588.908929, type 1 (EV_KEY), code 148 (KEY_PROG1), value 1
Event: time 1702734588.908929, -------------- SYN_REPORT ------------
Event: time 1702734588.909906, type 4 (EV_MSC), code 4 (MSC_SCAN), value ff310038
Event: time 1702734588.909906, type 1 (EV_KEY), code 148 (KEY_PROG1), value 0
Event: time 1702734588.909906, -------------- SYN_REPORT ------------

View file

@ -10,51 +10,66 @@
#include "rog_ally.h"
#include "legion_go.h"
/*
logic_t global_logic;
static output_dev_t out_gamepadd_dev = {
.logic = &global_logic,
};
void sig_handler(int signo)
{
if (signo == SIGINT) {
logic_request_termination(&global_logic);
printf("Received SIGINT\n");
}
}
*/
#include <sys/mman.h>
static const char* configuration_file = "/etc/ROGueENEMY/config.cfg";
controller_settings_t settings;
int main(int argc, char ** argv) {
// Lock all current and future pages from preventing of being paged to swap
const int lockall_res = mlockall( MCL_CURRENT | MCL_FUTURE );
if (lockall_res) {
fprintf(stderr, "mlockall failed: %d", lockall_res);
}
int ret = 0;
init_config(&settings);
fill_config(&settings, configuration_file);
// fill in configuration from file: automatic fallback to default
dev_in_settings_t in_settings = {
.enable_qam = true,
.ff_gain = 0xFFFF,
.rumble_on_mode_switch = true,
.m1m2_mode = 0,
.touchbar = true,
.enable_thermal_profiles_switching = false,
.default_thermal_profile = -1,
.enable_leds_commands = false,
.enable_imu = true,
.imu_polling_interface = true,
};
load_in_config(&in_settings, configuration_file);
dev_out_settings_t out_settings = {
.default_gamepad = 0,
.nintendo_layout = false,
.gamepad_leds_control = true,
.gamepad_rumble_control = true,
.controller_bluetooth = false,
.dualsense_edge = false,
.swap_y_z = false,
.invert_x = false,
.gyro_to_analog_activation_treshold = 16,
.gyro_to_analog_mapping = 4,
};
load_out_config(&out_settings, configuration_file);
input_dev_composite_t* in_devs = NULL;
int dmi_name_fd = open("/sys/class/dmi/id/board_name", O_RDONLY | O_NONBLOCK);
if (dmi_name_fd < 0) {
char bname[256];
if (dmi_board_name(bname, sizeof(bname)) < 0) {
fprintf(stderr, "Cannot get the board name\n");
return EXIT_FAILURE;
}
char bname[256];
memset(bname, 0, sizeof(bname));
read(dmi_name_fd, bname, sizeof(bname));
if (strstr(bname, "RC71L") != NULL) {
printf("Running in an Asus ROG Ally device\n");
in_devs = rog_ally_device_def(&settings);
in_devs = rog_ally_device_def(&in_settings);
} else if (strstr(bname, "LNVNB161216")) {
printf("Running in an Lenovo Legion Go device\n");
in_devs = legion_go_device_def(&settings);
in_devs = legion_go_device_def();
}
close(dmi_name_fd);
int dev_in_thread_creation = -1;
int dev_out_thread_creation = -1;
@ -105,12 +120,12 @@ int main(int argc, char ** argv) {
.out_message_pipe_fd = out_message_pipes[0],
}
}
}
},
.settings = in_settings,
};
// populate the output device thread data
dev_out_data_t dev_out_thread_data = {
.gamepad = GAMEPAD_DUALSENSE,
.flags = 0x00000000U,
.communication = {
.type = ipc_unix_pipe,
@ -120,7 +135,8 @@ int main(int argc, char ** argv) {
.out_message_pipe_fd = out_message_pipes[1],
}
}
}
},
.settings = out_settings,
};
pthread_t dev_in_thread;

View file

@ -1,5 +1,20 @@
enable_qam = true;
ff_gain = 100;
ff_gain = 255;
nintendo_layout = false;
gamepad_output_device = 1;
rumble_dedicated_thread = false;
default_gamepad = 1;
rumble_on_mode_switch = true;
gamepad_rumble_control = true;
gamepad_leds_control = true;
m1m2_mode = 1;
gyro_to_analog_mapping = 5;
gyro_to_analog_activation_treshold = 1;
touchbar = true;
controller_bluetooth = true;
dualsense_edge = false;
swap_y_z = true;
invert_x = true;
enable_thermal_profiles_switching = true;
default_thermal_profile = 3;
enable_leds_commands = true;
enable_imu = true;
imu_polling_interface = true;

View file

@ -2,6 +2,7 @@
#include <libevdev-1.0/libevdev/libevdev.h>
static const char *input_path = "/dev/input/";
static const char *hidden_input_path = "/dev/input/.hidden/";
static pthread_mutex_t input_acquire_mutex = PTHREAD_MUTEX_INITIALIZER;
@ -133,6 +134,17 @@ int dev_evdev_open(
int open_sysfs_idx = -1;
struct stat st = {0};
if (stat(hidden_input_path, &st) == -1) {
printf("Directory %s does not exits -- creating it\n", hidden_input_path);
if (mkdir(hidden_input_path, 0700) == 0) {
printf("Directory %s creates successfully", hidden_input_path);
} else {
fprintf(stderr, "Error creating %s directory: %d\n", hidden_input_path, errno);
}
}
const int mutex_lock_res = pthread_mutex_lock(&input_acquire_mutex);
if (mutex_lock_res != 0) {
fprintf(stderr, "Cannot lock input mutex: %d\n", mutex_lock_res);
@ -146,6 +158,13 @@ int dev_evdev_open(
goto dev_evdev_open_err;
}
char *const hidden_path = malloc(MAX_PATH_LEN);
if (path == NULL) {
free(path);
res = -ENOMEM;
goto dev_evdev_open_err;
}
DIR *d;
struct dirent *dir;
d = opendir(input_path);
@ -159,6 +178,7 @@ int dev_evdev_open(
continue;
}
snprintf(hidden_path, MAX_PATH_LEN - 1, "%s%s", hidden_input_path, dir->d_name);
snprintf(path, MAX_PATH_LEN - 1, "%s%s", input_path, dir->d_name);
//printf("Testing for device %s\n", path);
@ -199,6 +219,17 @@ int dev_evdev_open(
continue;
}
if (rename(path, hidden_path) != 0) {
fprintf(stderr, "Unable to move the device: %d\n", errno);
libevdev_free(*out_evdev);
close(fd);
continue;
} else {
if (chmod(hidden_path, 000) != 0) {
fprintf(stderr, "Unable to perform chmod 000 to opened device\n");
}
}
// register the device as being opened already
open_paths[open_sysfs_idx].sysfs_path = path;
open_paths[open_sysfs_idx].fd = fd;

View file

@ -82,7 +82,7 @@ int dev_hidraw_open(
//printf("Testing for device %s\n", path);
// try to open the device, if it cannot be opened to go the next
int fd = open(path, O_RDWR);
int fd = open(path, O_RDWR | O_NONBLOCK);
if (fd < 0) {
//fprintf(stderr, "Cannot open %s, device skipped.\n", path);
continue;

375
dev_in.c
View file

@ -5,14 +5,16 @@
#include "message.h"
#include "dev_evdev.h"
#include "dev_iio.h"
#include <libevdev-1.0/libevdev/libevdev.h>
#include <linux/input-event-codes.h>
#include "dev_timer.h"
#include <libconfig.h>
typedef enum dev_in_type {
DEV_IN_TYPE_NONE,
DEV_IN_TYPE_HIDRAW,
DEV_IN_TYPE_IIO,
DEV_IN_TYPE_EV,
DEV_IN_TYPE_TIMER,
} dev_in_type_t;
typedef struct dev_in_iio {
@ -23,6 +25,10 @@ typedef struct dev_in_iio {
typedef struct dev_in_hidraw {
dev_hidraw_t *hidrawdev;
hidraw_callbacks_t callbacks;
void* user_data;
} dev_in_hidraw_t;
typedef struct dev_in_ev {
@ -40,12 +46,29 @@ typedef struct dev_in_ev {
struct ff_effect ff_effect;
ev_callbacks_t callbacks;
void* user_data;
} dev_in_ev_t;
typedef struct dev_in_timer {
dev_timer_t* timer;
const char* name;
timer_callbacks_t callbacks;
void* user_data;
} dev_in_timer_t;
typedef union dev_in_aggr {
dev_in_ev_t evdev;
dev_in_iio_t iio;
dev_in_hidraw_t hidraw;
dev_in_timer_t timer;
} dev_in_aggr_t;
typedef struct dev_in {
@ -68,7 +91,30 @@ static int map_message_from_iio(dev_in_iio_t *const in_iio, in_message_t *const
goto send_message_from_iio_err;
}
res = 0;
if (res != 24) {
fprintf(stderr, "Invalid read lenght\n");
res = -EIO;
goto send_message_from_iio_err;
}
uint16_t *const scan_elements = (uint16_t*)&data[0];
int64_t *const timestamp = (int64_t*)&data[16]; // either that or (int64_t*)&data[12]
messages[0].type = GAMEPAD_SET_ELEMENT;
messages[0].data.gamepad_set.element = GAMEPAD_ACCELEROMETER;
messages[1].data.gamepad_set.status.accel.sample_timestamp_ns = *timestamp;
messages[0].data.gamepad_set.status.accel.x = scan_elements[0];
messages[0].data.gamepad_set.status.accel.y = (uint16_t)(-1) * scan_elements[2];
messages[0].data.gamepad_set.status.accel.z = scan_elements[1];
messages[1].type = GAMEPAD_SET_ELEMENT;
messages[1].data.gamepad_set.element = GAMEPAD_GYROSCOPE;
messages[1].data.gamepad_set.status.gyro.sample_timestamp_ns = *timestamp;
messages[1].data.gamepad_set.status.gyro.x = scan_elements[3];
messages[1].data.gamepad_set.status.gyro.y = (uint16_t)(-1) * scan_elements[5];
messages[1].data.gamepad_set.status.gyro.z = scan_elements[4];
res = 2;
send_message_from_iio_err:
return res;
@ -120,7 +166,25 @@ fill_message_from_evdev_err_completed:
return res;
}
static int timer_open_device(
const dev_in_settings_t *const in_settings,
const timer_filters_t *const in_filters,
dev_in_timer_t *const out_dev
) {
int res = dev_timer_open(in_filters, &out_dev->timer);
if (res != 0) {
fprintf(stderr, "Unable to open the timer device: %d\n", res);
goto timer_open_device_err;
}
printf("Opened timer device: %s\n", in_filters->name);
timer_open_device_err:
return res;
}
static int hidraw_open_device(
const dev_in_settings_t *const in_settings,
const hidraw_filters_t *const in_filters,
dev_in_hidraw_t *const out_dev
) {
@ -141,6 +205,7 @@ iio_open_device_err:
}
static int iio_open_device(
const dev_in_settings_t *const in_settings,
const iio_filters_t *const in_filters,
dev_in_iio_t *const out_dev
) {
@ -164,6 +229,7 @@ iio_open_device_err:
}
static int evdev_open_device(
const dev_in_settings_t *const in_settings,
const uinput_filters_t *const in_filters,
dev_in_ev_t *const out_dev
) {
@ -196,14 +262,14 @@ static int evdev_open_device(
out_dev->ff_effect.type = FF_RUMBLE;
out_dev->ff_effect.id = -1;
out_dev->ff_effect.replay.delay = 0;
out_dev->ff_effect.replay.length = 5000;
out_dev->ff_effect.replay.length = 1000;
out_dev->ff_effect.u.rumble.strong_magnitude = 0x0000;
out_dev->ff_effect.u.rumble.weak_magnitude = 0x0000;
const struct input_event gain = {
.type = EV_FF,
.code = FF_GAIN,
.value = 0xFFFF, // TODO: give the user the ability to change this
.value = in_settings->ff_gain,
};
const int gain_set_res = write(libevdev_get_fd(out_dev->evdev), (const void*)&gain, sizeof(gain));
@ -232,7 +298,11 @@ static void hidraw_close_device(dev_in_hidraw_t *const out_hidraw) {
dev_hidraw_close(out_hidraw->hidrawdev);
}
static void handle_rumble_device(dev_in_ev_t *const in_dev, const out_message_rumble_t *const in_rumble_msg) {
static void timer_close_device(dev_in_timer_t *const out_hidraw) {
dev_timer_close(out_hidraw->timer);
}
static void handle_rumble_device(const dev_in_settings_t *const conf, dev_in_ev_t *const in_dev, const out_message_rumble_t *const in_rumble_msg) {
if (!in_dev->has_rumble_support) {
return;
}
@ -281,10 +351,57 @@ static void handle_rumble_device(dev_in_ev_t *const in_dev, const out_message_ru
}
}
static void handle_rumble(dev_in_t *const in_devs, size_t in_devs_count, const out_message_rumble_t *const in_rumble_msg) {
static void handle_rumble(const dev_in_settings_t *const conf, dev_in_t *const in_devs, size_t in_devs_count, const out_message_rumble_t *const in_rumble_msg) {
for (size_t i = 0; i < in_devs_count; ++i) {
if (in_devs[i].type == DEV_IN_TYPE_EV) {
handle_rumble_device(&in_devs[i].dev.evdev, in_rumble_msg);
handle_rumble_device(
conf,
&in_devs[i].dev.evdev,
in_rumble_msg
);
}
}
}
static void handle_leds(const dev_in_settings_t *const conf, dev_in_t *const in_devs, size_t in_devs_count, const out_message_leds_t *const in_leds_msg) {
for (size_t i = 0; i < in_devs_count; ++i) {
if (in_devs[i].type == DEV_IN_TYPE_HIDRAW) {
in_devs[i].dev.hidraw.callbacks.leds_callback(
conf,
in_devs[i].dev.hidraw.hidrawdev->fd,
in_leds_msg->r,
in_leds_msg->g,
in_leds_msg->b,
in_devs[i].dev.hidraw.user_data
);
}
}
}
static void handle_timeout(
const dev_in_settings_t *const conf,
dev_in_t *const in_devs,
size_t in_devs_count,
const char* name,
uint64_t expirations
) {
for (size_t i = 0; i < in_devs_count; ++i) {
if (in_devs[i].type == DEV_IN_TYPE_EV) {
in_devs[i].dev.evdev.callbacks.timeout_callback(
conf,
in_devs[i].dev.evdev.evdev,
name,
expirations,
in_devs[i].dev.evdev.user_data
);
} else if (in_devs[i].type == DEV_IN_TYPE_HIDRAW) {
in_devs[i].dev.hidraw.callbacks.timeout_callback(
conf,
dev_hidraw_get_fd(in_devs[i].dev.hidraw.hidrawdev),
name,
expirations,
in_devs[i].dev.hidraw.user_data
);
}
}
}
@ -292,7 +409,7 @@ static void handle_rumble(dev_in_t *const in_devs, size_t in_devs_count, const o
static int open_socket(struct sockaddr_un *serveraddr) {
int res = -ENODEV;
int sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
int sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd < 0)
{
res = sd;
@ -314,23 +431,15 @@ void* dev_in_thread_func(void *ptr) {
dev_in_data_t *const dev_in_data = (dev_in_data_t*)ptr;
void* platform_data;
const int platform_init_res = dev_in_data->input_dev_decl->init_fn(&platform_data);
const int platform_init_res = dev_in_data->input_dev_decl->init_fn(&dev_in_data->settings, &platform_data);
if (platform_init_res != 0) {
fprintf(stderr, "Error setting up platform data: %d\n", platform_init_res);
}
struct timeval timeout = {
.tv_sec = (__time_t)dev_in_data->timeout_ms / (__time_t)1000,
.tv_usec = ((__suseconds_t)dev_in_data->timeout_ms % (__suseconds_t)1000) * (__suseconds_t)1000000,
};
fd_set read_fds;
const size_t max_devices = dev_in_data->input_dev_decl->dev_count;
in_message_t* controller_msg = malloc(sizeof(in_message_t) * 4);
size_t controller_msg_avail = controller_msg == NULL ? 0 : 4;
dev_in_t* const devices = malloc(sizeof(dev_in_t) * max_devices);
if (devices == NULL) {
fprintf(stderr, "Unable to allocate memory to hold devices -- aborting input thread\n");
@ -364,6 +473,8 @@ void* dev_in_thread_func(void *ptr) {
continue;
}
}
FD_SET(dev_in_data->communication.endpoint.socket.fd, &read_fds);
}
for (size_t i = 0; i < max_devices; ++i) {
@ -374,36 +485,86 @@ void* dev_in_thread_func(void *ptr) {
FD_SET(dev_iio_get_buffer_fd(devices[i].dev.iio.iiodev), &read_fds);
} else if (devices[i].type == DEV_IN_TYPE_HIDRAW) {
FD_SET(dev_hidraw_get_fd(devices[i].dev.hidraw.hidrawdev), &read_fds);
} else if (devices[i].type == DEV_IN_TYPE_TIMER) {
FD_SET(dev_timer_get_fd(devices[i].dev.timer.timer), &read_fds);
} else if (devices[i].type == DEV_IN_TYPE_NONE) {
const input_dev_type_t d_type = dev_in_data->input_dev_decl->dev[i]->dev_type;
if (d_type == input_dev_type_uinput) {
fprintf(stderr, "Device (evdev) %zu not found -- Attempt reconnection for device named %s\n", i, dev_in_data->input_dev_decl->dev[i]->filters.ev.name);
const int open_res = evdev_open_device(&dev_in_data->input_dev_decl->dev[i]->filters.ev, &devices[i].dev.evdev);
const int open_res = evdev_open_device(
&dev_in_data->settings,
&dev_in_data->input_dev_decl->dev[i]->filters.ev,
&devices[i].dev.evdev
);
if (open_res == 0) {
devices[i].type = DEV_IN_TYPE_EV;
devices[i].dev.evdev.user_data = dev_in_data->input_dev_decl->dev[i]->user_data;
devices[i].dev.evdev.callbacks = dev_in_data->input_dev_decl->dev[i]->map.ev_callbacks;
// device is now connected, query it in select
FD_SET(libevdev_get_fd(devices[i].dev.evdev.evdev), &read_fds);
}
} else if (d_type == input_dev_type_iio) {
fprintf(stderr, "Device (iio) %zu not found -- Attempt reconnection for device named %s\n", i, dev_in_data->input_dev_decl->dev[i]->filters.iio.name);
const int open_res = iio_open_device(&dev_in_data->input_dev_decl->dev[i]->filters.iio, &devices[i].dev.iio);
const int open_res = iio_open_device(
&dev_in_data->settings,
&dev_in_data->input_dev_decl->dev[i]->filters.iio,
&devices[i].dev.iio
);
if (open_res == 0) {
devices[i].type = DEV_IN_TYPE_IIO;
// device is now connected, query it in select
FD_SET(dev_iio_get_buffer_fd(devices[i].dev.iio.iiodev), &read_fds);
}
} else if (d_type == input_dev_type_hidraw) {
fprintf(stderr, "Device (hidraw) %zu not found -- Attempt reconnection for device %x:%x\n", i, dev_in_data->input_dev_decl->dev[i]->filters.hidraw.pid, dev_in_data->input_dev_decl->dev[i]->filters.hidraw.vid);
const int open_res = hidraw_open_device(&dev_in_data->input_dev_decl->dev[i]->filters.hidraw, &devices[i].dev.hidraw);
const int open_res = hidraw_open_device(
&dev_in_data->settings,
&dev_in_data->input_dev_decl->dev[i]->filters.hidraw,
&devices[i].dev.hidraw
);
if (open_res == 0) {
devices[i].dev.hidraw.callbacks = dev_in_data->input_dev_decl->dev[i]->map.hidraw_callbacks;
devices[i].dev.hidraw.user_data = dev_in_data->input_dev_decl->dev[i]->user_data;
devices[i].type = DEV_IN_TYPE_HIDRAW;
// device is now connected, query it in select
FD_SET(dev_hidraw_get_fd(devices[i].dev.hidraw.hidrawdev), &read_fds);
}
} else if (d_type == input_dev_type_timer) {
fprintf(stderr, "Device (timer) %zu not found -- Attempt to create it with name %s\n", i, dev_in_data->input_dev_decl->dev[i]->filters.timer.name);
const int open_res = timer_open_device(
&dev_in_data->settings,
&dev_in_data->input_dev_decl->dev[i]->filters.timer,
&devices[i].dev.timer
);
if (open_res == 0) {
devices[i].dev.timer.callbacks = dev_in_data->input_dev_decl->dev[i]->map.timer_callbacks;
devices[i].dev.timer.user_data = dev_in_data->input_dev_decl->dev[i]->user_data;
devices[i].dev.timer.name = dev_in_data->input_dev_decl->dev[i]->filters.timer.name;
devices[i].type = DEV_IN_TYPE_TIMER;
// device is now connected, query it in select
FD_SET(dev_timer_get_fd(devices[i].dev.timer.timer), &read_fds);
}
}
}
}
struct timeval timeout = {
.tv_sec = (__time_t)dev_in_data->timeout_ms / (__time_t)1000,
.tv_usec = ((__suseconds_t)dev_in_data->timeout_ms % (__suseconds_t)1000) * (__suseconds_t)1000000,
};
int ready_fds = select(FD_SETSIZE, &read_fds, NULL, NULL, &timeout);
if (ready_fds == -1) {
@ -412,6 +573,7 @@ void* dev_in_thread_func(void *ptr) {
continue;
} else if (ready_fds == 0) {
// Timeout... simply retry
printf("TIMEOUT\n");
continue;
}
@ -428,18 +590,22 @@ void* dev_in_thread_func(void *ptr) {
const ssize_t out_message_pipe_read_res = read(out_message_fd, (void*)&out_msg, sizeof(out_message_t));
if (out_message_pipe_read_res == sizeof(out_message_t)) {
if (out_msg.type == OUT_MSG_TYPE_RUMBLE) {
handle_rumble(devices, max_devices, &out_msg.data.rumble);
handle_rumble(&dev_in_data->settings, devices, max_devices, &out_msg.data.rumble);
} else if (out_msg.type == OUT_MSG_TYPE_LEDS) {
// first inform the platform
const int platform_leds_res = dev_in_data->input_dev_decl->leds_fn(out_msg.data.leds.r, out_msg.data.leds.g, out_msg.data.leds.b, platform_data);
const int platform_leds_res = dev_in_data->input_dev_decl->leds_fn(
&dev_in_data->settings,
out_msg.data.leds.r, out_msg.data.leds.g,
out_msg.data.leds.b, platform_data
);
if (platform_leds_res != 0) {
fprintf(stderr, "Error in changing platform LEDs: %d\n", platform_leds_res);
}
// TODO: handle_leds()
handle_leds(&dev_in_data->settings, devices, max_devices, &out_msg.data.leds);
}
} else {
fprintf(stderr, "Error reading from out_message_pipe_fd: got %zu bytes, expected %zu butes\n", out_message_pipe_read_res, sizeof(out_message_t));
fprintf(stderr, "Error reading from out_message_pipe_fd: got %zu bytes, expected %zu bytes\n", out_message_pipe_read_res, sizeof(out_message_t));
// in case of an error reschedule to socket for reconnection
if (dev_in_data->communication.type == ipc_client_socket) {
@ -458,6 +624,8 @@ void* dev_in_thread_func(void *ptr) {
fd = dev_iio_get_buffer_fd(devices[i].dev.iio.iiodev);
} else if (devices[i].type == DEV_IN_TYPE_HIDRAW) {
fd = dev_hidraw_get_fd(devices[i].dev.hidraw.hidrawdev);
} else if (devices[i].type == DEV_IN_TYPE_TIMER) {
fd = dev_timer_get_fd(devices[i].dev.timer.timer);
} else {
continue;
}
@ -466,55 +634,92 @@ void* dev_in_thread_func(void *ptr) {
continue;
}
int controller_msg_count = -ENOMEM;
while (controller_msg_count == -ENOMEM) {
// the following part fills controller_msg and writes in controller_msg_count an error or the number of messages to be sent to the output device
if (devices[i].type == DEV_IN_TYPE_EV) {
evdev_collected_t coll = {
.ev_count = 0
};
in_message_t controller_msg[MAX_IN_MESSAGES];
size_t controller_msg_avail = sizeof(controller_msg) / sizeof(in_message_t);
int controller_msg_count = -EIO;
controller_msg_count = fill_message_from_evdev(&devices[i].dev.evdev, &coll);
if (controller_msg_count != 0) {
fprintf(stderr, "Unable to fill input_event(s) for device %zd: %d -- Will reconnect the device\n", i, controller_msg_count);
evdev_close_device(&devices[i].dev.evdev);
devices[i].type = DEV_IN_TYPE_NONE;
continue;
}
// the following part fills controller_msg and writes in controller_msg_count an error or the number of messages to be sent to the output device
if (devices[i].type == DEV_IN_TYPE_EV) {
evdev_collected_t coll = {
.ev_count = 0
};
controller_msg_count = dev_in_data->input_dev_decl->dev[i]->map.ev_input_map_fn(&coll, controller_msg, controller_msg_avail, dev_in_data->input_dev_decl->dev[i]->user_data);
} else if (devices[i].type == DEV_IN_TYPE_IIO) {
controller_msg_count = map_message_from_iio(&devices[i].dev.iio, controller_msg, controller_msg_avail);
if (controller_msg_count != 0) {
fprintf(stderr, "Error in performing operations for device %zd: %d -- Will reconnect to the device\n", i, controller_msg_count);
iio_close_device(&devices[i].dev.iio);
devices[i].type = DEV_IN_TYPE_NONE;
continue;
}
} else if (devices[i].type == DEV_IN_TYPE_HIDRAW) {
controller_msg_count = dev_in_data->input_dev_decl->dev[i]->map.hidraw_input_map_fn(dev_hidraw_get_fd(devices[i].dev.hidraw.hidrawdev), controller_msg, controller_msg_avail, dev_in_data->input_dev_decl->dev[i]->user_data);
if (controller_msg_count != 0) {
fprintf(stderr, "Error in performing operations for device %zd: %d -- Will reconnect to the device\n", i, controller_msg_count);
hidraw_close_device(&devices[i].dev.hidraw);
devices[i].type = DEV_IN_TYPE_NONE;
continue;
}
controller_msg_count = fill_message_from_evdev(&devices[i].dev.evdev, &coll);
if (controller_msg_count != 0) {
fprintf(stderr, "Unable to fill input_event(s) for device %zd: %d -- Will reconnect the device\n", i, controller_msg_count);
evdev_close_device(&devices[i].dev.evdev);
devices[i].type = DEV_IN_TYPE_NONE;
continue;
}
// attempt to acquire more memory
if (controller_msg_count == -ENOMEM) {
printf("Map function reported not enough memory, allocating new one...\n");
const size_t tmp_controller_msg_avail = controller_msg_avail * 2;
in_message_t *tmp_controller_msg = malloc(sizeof(in_message_t) * tmp_controller_msg_avail);
if (tmp_controller_msg == NULL) {
fprintf(stderr, "Could not allocate new memory -- will retry\n");
} else {
printf("Allocated new memory to fill the buffer\n");
free(controller_msg);
controller_msg = tmp_controller_msg;
controller_msg_avail = tmp_controller_msg_avail;
}
controller_msg_count = devices[i].dev.evdev.callbacks.input_map_fn(
&dev_in_data->settings,
&coll,
&controller_msg[0],
controller_msg_avail,
devices[i].dev.evdev.user_data
);
} else if (devices[i].type == DEV_IN_TYPE_IIO) {
controller_msg_count = map_message_from_iio(
&devices[i].dev.iio,
&controller_msg[0],
controller_msg_avail
);
if (controller_msg_count < 0) {
fprintf(stderr, "Error in reading iio buffer for device %zd: %d -- Will reconnect to the device\n", i, controller_msg_count);
iio_close_device(&devices[i].dev.iio);
devices[i].type = DEV_IN_TYPE_NONE;
continue;
}
} else if (devices[i].type == DEV_IN_TYPE_HIDRAW) {
controller_msg_count = devices[i].dev.hidraw.callbacks.map_callback(
&dev_in_data->settings,
fd,
&controller_msg[0],
controller_msg_avail,
devices[i].dev.hidraw.user_data
);
if (controller_msg_count < 0) {
fprintf(stderr, "Error in performing operations for device %zd: %d -- Will reconnect to the device\n", i, controller_msg_count);
hidraw_close_device(&devices[i].dev.hidraw);
devices[i].type = DEV_IN_TYPE_NONE;
continue;
}
} else if (devices[i].type == DEV_IN_TYPE_TIMER) {
uint64_t expirations;
ssize_t num_read = read(fd, &expirations, sizeof(uint64_t));
if (num_read != sizeof(uint64_t)) {
fprintf(stderr, "Error in reading expirations from timer device %zd: %d -- Will reconnect to the device\n", i, controller_msg_count);
timer_close_device(&devices[i].dev.timer);
devices[i].type = DEV_IN_TYPE_NONE;
continue;
}
controller_msg_count = devices[i].dev.timer.callbacks.map_fn(
&dev_in_data->settings,
fd,
expirations,
&controller_msg[0],
controller_msg_avail,
devices[i].dev.timer.user_data
);
if (controller_msg_count < 0) {
fprintf(stderr, "Error in timer device %zd: %d -- Will reconnect to the device\n", i, controller_msg_count);
timer_close_device(&devices[i].dev.timer);
devices[i].type = DEV_IN_TYPE_NONE;
continue;
}
handle_timeout(
&dev_in_data->settings,
devices,
max_devices,
devices[i].dev.timer.name,
expirations
);
}
// send messages (if any)
@ -523,18 +728,22 @@ void* dev_in_thread_func(void *ptr) {
}
if (dev_in_data->communication.type == ipc_client_socket) {
const int write_res = write(dev_in_data->communication.endpoint.socket.fd, (void*)&controller_msg[0], sizeof(in_message_t) * controller_msg_count);
if (write_res < 0) {
fprintf(stderr, "Error in writing input event messages: %d -- connection will be drop and retried\n", write_res);
for (int msg_idx = 0; msg_idx < controller_msg_count; ++msg_idx) {
const int write_res = write(dev_in_data->communication.endpoint.socket.fd, (void*)&controller_msg[msg_idx], sizeof(in_message_t));
if (write_res < 0) {
fprintf(stderr, "Error in writing input event messages: %d -- connection will be drop and retried\n", write_res);
// in case of an error reschedule to socket for reconnection
close(dev_in_data->communication.endpoint.socket.fd);
dev_in_data->communication.endpoint.socket.fd = -1;
// in case of an error reschedule to socket for reconnection
close(dev_in_data->communication.endpoint.socket.fd);
dev_in_data->communication.endpoint.socket.fd = -1;
}
}
} else if (dev_in_data->communication.type == ipc_unix_pipe) {
const int write_res = write(dev_in_data->communication.endpoint.pipe.in_message_pipe_fd, (void*)&controller_msg[0], sizeof(in_message_t) * controller_msg_count);
if (write_res < 0) {
fprintf(stderr, "Error in writing input event messages: %d\n", write_res);
for (int msg_idx = 0; msg_idx < controller_msg_count; ++msg_idx) {
const int write_res = write(dev_in_data->communication.endpoint.pipe.in_message_pipe_fd, (void*)&controller_msg[msg_idx], sizeof(in_message_t));
if (write_res < 0) {
fprintf(stderr, "Error in writing input event messages: %d\n", write_res);
}
}
}
}
@ -570,14 +779,16 @@ void* dev_in_thread_func(void *ptr) {
} else if (devices[i].type == DEV_IN_TYPE_HIDRAW) {
hidraw_close_device(&devices[i].dev.hidraw);
devices[i].type = DEV_IN_TYPE_NONE;
} else if (devices[i].type == DEV_IN_TYPE_TIMER) {
timer_close_device(&devices[i].dev.timer);
devices[i].type = DEV_IN_TYPE_NONE;
}
}
// TODO: free every fd
free(devices);
if (platform_init_res != 0) {
dev_in_data->input_dev_decl->deinit_fn(&platform_data);
dev_in_data->input_dev_decl->deinit_fn(&dev_in_data->settings, &platform_data);
}
return NULL;

View file

@ -3,6 +3,9 @@
#include "ipc.h"
#include "message.h"
#include "input_dev.h"
#include "settings.h"
#define MAX_IN_MESSAGES 8
#define DEV_IN_FLAG_EXIT 0x00000001U
@ -17,6 +20,8 @@ typedef struct dev_in_data {
ipc_t communication;
dev_in_settings_t settings;
volatile uint32_t flags;
} dev_in_data_t;

684
dev_out.c
View file

@ -5,8 +5,11 @@
#include "message.h"
#include "virt_ds4.h"
#include "virt_ds5.h"
#include "virt_mouse.h"
#include "virt_kbd.h"
static void handle_incoming_message_gamepad_action(
const dev_out_settings_t *const in_settings,
const in_message_gamepad_action_t *const msg_payload,
gamepad_status_t *const inout_gamepad
) {
@ -17,25 +20,64 @@ static void handle_incoming_message_gamepad_action(
}
}
static void handle_incoming_message_mouse_event(
const dev_out_settings_t *const in_settings,
const in_message_mouse_event_t *const msg_payload,
mouse_status_t *const inout_mouse
) {
if (msg_payload->type == MOUSE_ELEMENT_X) {
inout_mouse->x += msg_payload->value;
} else if (msg_payload->type == MOUSE_ELEMENT_Y) {
inout_mouse->y += msg_payload->value;
} else if (msg_payload->type == MOUSE_BTN_LEFT) {
inout_mouse->btn_left = msg_payload->value;
} else if (msg_payload->type == MOUSE_BTN_MIDDLE) {
inout_mouse->btn_middle = msg_payload->value;
} else if (msg_payload->type == MOUSE_BTN_RIGHT) {
inout_mouse->btn_right = msg_payload->value;
}
}
static void handle_incoming_message_gamepad_set(
const dev_out_settings_t *const in_settings,
const in_message_gamepad_set_element_t *const msg_payload,
gamepad_status_t *const inout_gamepad
) {
switch (msg_payload->element) {
case GAMEPAD_BTN_CROSS: {
inout_gamepad->cross = msg_payload->status.btn;
if (!in_settings->nintendo_layout) {
inout_gamepad->cross = msg_payload->status.btn;
} else {
inout_gamepad->circle = msg_payload->status.btn;
}
break;
}
case GAMEPAD_BTN_CIRCLE: {
inout_gamepad->circle = msg_payload->status.btn;
if (in_settings->nintendo_layout) {
inout_gamepad->cross = msg_payload->status.btn;
} else {
inout_gamepad->circle = msg_payload->status.btn;
}
break;
}
case GAMEPAD_BTN_SQUARE: {
inout_gamepad->square = msg_payload->status.btn;
if (in_settings->nintendo_layout) {
inout_gamepad->triangle = msg_payload->status.btn;
} else {
inout_gamepad->square = msg_payload->status.btn;
}
break;
}
case GAMEPAD_BTN_TRIANGLE: {
inout_gamepad->triangle = msg_payload->status.btn;
if (!in_settings->nintendo_layout) {
inout_gamepad->triangle = msg_payload->status.btn;
} else {
inout_gamepad->square = msg_payload->status.btn;
}
break;
}
case GAMEPAD_BTN_OPTION: {
@ -90,6 +132,14 @@ static void handle_incoming_message_gamepad_set(
inout_gamepad->touchpad_press = msg_payload->status.btn;
break;
}
case GAMEPAD_BTN_JOIN_LEFT_ANALOG_AND_GYROSCOPE: {
inout_gamepad->join_left_analog_and_gyroscope = msg_payload->status.btn;
break;
}
case GAMEPAD_BTN_JOIN_RIGHT_ANALOG_AND_GYROSCOPE: {
inout_gamepad->join_right_analog_and_gyroscope = msg_payload->status.btn;
break;
}
case GAMEPAD_LEFT_JOYSTICK_X: {
inout_gamepad->joystick_positions[0][0] = msg_payload->status.joystick_pos;
break;
@ -134,6 +184,32 @@ static void handle_incoming_message_gamepad_set(
break;
}
case GAMEPAD_GYROSCOPE: {
inout_gamepad->last_gyro_motion_timestamp_ns = msg_payload->status.gyro.sample_timestamp_ns;
inout_gamepad->raw_gyro[0] = in_settings->invert_x ? (int16_t)(-1) * msg_payload->status.gyro.x : msg_payload->status.gyro.x;
inout_gamepad->raw_gyro[1] = in_settings->swap_y_z ? msg_payload->status.gyro.z : msg_payload->status.gyro.y;
inout_gamepad->raw_gyro[2] = in_settings->swap_y_z ? msg_payload->status.gyro.y : msg_payload->status.gyro.z;
break;
}
case GAMEPAD_ACCELEROMETER: {
inout_gamepad->last_accel_motion_timestamp_ns = msg_payload->status.accel.sample_timestamp_ns;
inout_gamepad->raw_accel[0] = in_settings->invert_x ? (int16_t)(-1) * msg_payload->status.accel.x : msg_payload->status.accel.x;
inout_gamepad->raw_accel[1] = in_settings->swap_y_z ? msg_payload->status.accel.z : msg_payload->status.accel.y;
inout_gamepad->raw_accel[2] = in_settings->swap_y_z ? msg_payload->status.accel.y : msg_payload->status.accel.z;
break;
}
case GAMEPAD_TOUCHPAD_TOUCH_ACTIVE: {
inout_gamepad->touchpad_touch_num = msg_payload->status.touchpad_active.status;
break;
}
case GAMEPAD_TOUCHPAD_X: {
inout_gamepad->touchpad_x = msg_payload->status.touchpad_x.value;
break;
}
case GAMEPAD_TOUCHPAD_Y: {
inout_gamepad->touchpad_y = msg_payload->status.touchpad_y.value;
break;
}
default: {
fprintf(stderr, "Unknown gamepad element: %d\n", msg_payload->element);
break;
@ -141,24 +217,174 @@ static void handle_incoming_message_gamepad_set(
}
}
static void handle_incoming_message_keyboard_set(
const dev_out_settings_t *const in_settings,
const in_message_keyboard_set_element_t *const msg_payload,
keyboard_status_t *const inout_kbd
) {
switch (msg_payload->type) {
case KEYBOARD_KEY_Q:
inout_kbd->q = msg_payload->value;
break;
case KEYBOARD_KEY_W:
inout_kbd->w = msg_payload->value;
break;
case KEYBOARD_KEY_E:
inout_kbd->e = msg_payload->value;
break;
case KEYBOARD_KEY_R:
inout_kbd->r = msg_payload->value;
break;
case KEYBOARD_KEY_T:
inout_kbd->t = msg_payload->value;
break;
case KEYBOARD_KEY_Y:
inout_kbd->y = msg_payload->value;
break;
case KEYBOARD_KEY_U:
inout_kbd->u = msg_payload->value;
break;
case KEYBOARD_KEY_I:
inout_kbd->i = msg_payload->value;
break;
case KEYBOARD_KEY_O:
inout_kbd->o = msg_payload->value;
break;
case KEYBOARD_KEY_P:
inout_kbd->p = msg_payload->value;
break;
case KEYBOARD_KEY_A:
inout_kbd->a = msg_payload->value;
break;
case KEYBOARD_KEY_S:
inout_kbd->s = msg_payload->value;
break;
case KEYBOARD_KEY_D:
inout_kbd->d = msg_payload->value;
break;
case KEYBOARD_KEY_F:
inout_kbd->f = msg_payload->value;
break;
case KEYBOARD_KEY_G:
inout_kbd->g = msg_payload->value;
break;
case KEYBOARD_KEY_H:
inout_kbd->h = msg_payload->value;
break;
case KEYBOARD_KEY_J:
inout_kbd->j = msg_payload->value;
break;
case KEYBOARD_KEY_K:
inout_kbd->k = msg_payload->value;
break;
case KEYBOARD_KEY_L:
inout_kbd->l = msg_payload->value;
break;
case KEYBOARD_KEY_Z:
inout_kbd->z = msg_payload->value;
break;
case KEYBOARD_KEY_X:
inout_kbd->x = msg_payload->value;
break;
case KEYBOARD_KEY_C:
inout_kbd->c = msg_payload->value;
break;
case KEYBOARD_KEY_V:
inout_kbd->v = msg_payload->value;
break;
case KEYBOARD_KEY_B:
inout_kbd->b = msg_payload->value;
break;
case KEYBOARD_KEY_N:
inout_kbd->n = msg_payload->value;
break;
case KEYBOARD_KEY_M:
inout_kbd->m = msg_payload->value;
break;
case KEYBOARD_KEY_UP:
inout_kbd->up = msg_payload->value;
break;
case KEYBOARD_KEY_DOWN:
inout_kbd->down = msg_payload->value;
break;
case KEYBOARD_KEY_LEFT:
inout_kbd->left = msg_payload->value;
break;
case KEYBOARD_KEY_RIGHT:
inout_kbd->right = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_0:
inout_kbd->num_0 = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_1:
inout_kbd->num_1 = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_2:
inout_kbd->num_2 = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_3:
inout_kbd->num_3 = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_4:
inout_kbd->num_4 = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_5:
inout_kbd->num_5 = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_6:
inout_kbd->num_6 = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_7:
inout_kbd->num_7 = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_8:
inout_kbd->num_8 = msg_payload->value;
break;
case KEYBOARD_KEY_NUM_9:
inout_kbd->num_9 = msg_payload->value;
break;
case KEYBOARD_KEY_LCRTL:
inout_kbd->lctrl = msg_payload->value;
break;
default:
fprintf(stderr, "key not implemented\n");
}
}
static void handle_incoming_message(
const dev_out_settings_t *const in_settings,
const in_message_t *const msg,
devices_status_t *const dev_stats
) {
if (msg->type == GAMEPAD_SET_ELEMENT) {
handle_incoming_message_gamepad_set(&msg->data.gamepad_set, &dev_stats->gamepad);
handle_incoming_message_gamepad_set(
in_settings,
&msg->data.gamepad_set,
&dev_stats->gamepad
);
} else if (msg->type == GAMEPAD_ACTION) {
handle_incoming_message_gamepad_action(&msg->data.action, &dev_stats->gamepad);
handle_incoming_message_gamepad_action(
in_settings,
&msg->data.action,
&dev_stats->gamepad
);
} else if (msg->type == MOUSE_EVENT) {
handle_incoming_message_mouse_event(
in_settings,
&msg->data.mouse_event,
&dev_stats->mouse
);
} else if (msg->type == KEYBOARD_SET_ELEMENT) {
handle_incoming_message_keyboard_set(
in_settings,
&msg->data.kbd_set,
&dev_stats->kbd
);
}
}
int64_t get_timediff_usec(const struct timeval *const past, const struct timeval *const now) {
struct timeval tdiff;
timersub(now, past, &tdiff);
//const int64_t sgn = ((now->tv_sec > past->tv_sec) || ((now->tv_sec == past->tv_sec) && (now->tv_usec > past->tv_usec))) ? -1 : +1;
return (int64_t)(tdiff.tv_sec) * (int64_t)1000000 + (int64_t)(tdiff.tv_usec);
int64_t get_timediff_nsec(const struct timespec *const start, const struct timespec *const end) {
return (end->tv_sec - start->tv_sec) * 1000000000LL + (end->tv_nsec - start->tv_nsec);
}
void *dev_out_thread_func(void *ptr) {
@ -167,41 +393,96 @@ void *dev_out_thread_func(void *ptr) {
// Initialize device
devices_status_init(&dev_out_data->dev_stats);
dev_out_gamepad_device_t current_gamepad = dev_out_data->gamepad;
dev_out_gamepad_device_t current_gamepad = GAMEPAD_DUALSENSE;
switch (dev_out_data->settings.default_gamepad) {
case 1:
current_gamepad = GAMEPAD_DUALSENSE;
break;
case 2:
current_gamepad = GAMEPAD_DUALSHOCK;
break;
default:
current_gamepad = GAMEPAD_DUALSENSE;
break;
}
int current_gamepad_fd = -1;
int current_keyboard_fd = -1;
int current_mouse_fd = -1;
union {
virt_dualshock_t ds4;
virt_dualsense_t ds5;
} controller_data;
int current_gamepad_fd = -1;
//int current_keyboard_fd = -1;
//int current_mouse_fd = -1;
virt_mouse_t mouse_data;
const int mouse_init_res = virt_mouse_init(&mouse_data);
if (mouse_init_res < 0) {
fprintf(stderr, "Unable to initialize virtual mouse -- will continue regardless\n");
} else {
current_mouse_fd = virt_mouse_get_fd(&mouse_data);
printf("Mouse initialized: fd=%d\n", current_mouse_fd);
}
virt_kbd_t keyboard_data;
const int kbd_init_res = virt_kbd_init(&keyboard_data);
if (kbd_init_res < 0) {
fprintf(stderr, "Unable to initialize virtual keyboard -- will continue regardless\n");
} else {
current_keyboard_fd = virt_kbd_get_fd(&keyboard_data);
printf("Keyboard initialized: fd=%d\n", current_keyboard_fd);
}
bool high_hz_avail = false;
const char *const avail_freq = inline_read_file("/sys/bus/iio/devices/iio:device0/", "name");
if ((avail_freq != NULL) && (strstr(avail_freq, "1600.0") != NULL)) {
printf("High iio sampling frequency available: 1600.0 Hz");
high_hz_avail = true;
}
const int64_t kbd_report_timing_us = high_hz_avail ? 1125 : 2025;
const int64_t mouse_report_timing_us = high_hz_avail ? 950 : 1650;
const int64_t gamepad_report_timing_us = high_hz_avail ? 1250 : 2500;
if (current_gamepad == GAMEPAD_DUALSENSE) {
const int ds5_init_res = virt_dualsense_init(&controller_data.ds5);
const int ds5_init_res = virt_dualsense_init(
&controller_data.ds5,
dev_out_data->settings.controller_bluetooth,
dev_out_data->settings.dualsense_edge,
dev_out_data->settings.gyro_to_analog_activation_treshold,
dev_out_data->settings.gyro_to_analog_mapping
);
if (ds5_init_res != 0) {
fprintf(stderr, "Unable to initialize the DualSense device: %d\n", ds5_init_res);
} else {
current_gamepad_fd = virt_dualsense_get_fd(&controller_data.ds5);
printf("DualSense initialized: fd=%d\n", current_gamepad_fd);
printf("DualSense initialized: fd=%d, bluetooth=%s\n", current_gamepad_fd, dev_out_data->settings.controller_bluetooth ? "true" : "false");
}
} else if (current_gamepad == GAMEPAD_DUALSHOCK) {
const int ds4_init_res = virt_dualshock_init(&controller_data.ds4);
const int ds4_init_res = virt_dualshock_init(
&controller_data.ds4,
dev_out_data->settings.controller_bluetooth,
dev_out_data->settings.gyro_to_analog_activation_treshold,
dev_out_data->settings.gyro_to_analog_mapping
);
if (ds4_init_res != 0) {
fprintf(stderr, "Unable to initialize the DualShock device: %d\n", ds4_init_res);
} else {
current_gamepad_fd = virt_dualshock_get_fd(&controller_data.ds4);
printf("DualShock initialized: fd=%d\n", current_gamepad_fd);
printf("DualShock initialized: fd=%d, bluetooth=%s\n", current_gamepad_fd, dev_out_data->settings.controller_bluetooth ? "true" : "false");
}
}
struct timeval now = {0};
gettimeofday(&now, NULL);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timeval gamepad_last_hid_report_sent = now;
//struct timeval mouse_last_hid_report_sent = now;
//struct timeval keyboard_last_hid_report_sent = now;
struct timespec gamepad_last_hid_report_sent = now;
struct timespec mouse_last_hid_report_sent = now;
struct timespec keyboard_last_hid_report_sent = now;
uint8_t tmp_buf[256];
@ -212,9 +493,13 @@ void *dev_out_thread_func(void *ptr) {
break;
}
gettimeofday(&now, NULL);
int64_t gamepad_time_diff_usecs = get_timediff_usec(&gamepad_last_hid_report_sent, &now);
if (gamepad_time_diff_usecs >= 1250) {
clock_gettime(CLOCK_MONOTONIC, &now);
const int64_t gamepad_time_diff_usecs = get_timediff_nsec(&gamepad_last_hid_report_sent, &now) / 1000;
const int64_t mouse_time_diff_usecs = get_timediff_nsec(&mouse_last_hid_report_sent, &now) / 1000;
const int64_t kbd_time_diff_usecs = get_timediff_nsec(&keyboard_last_hid_report_sent, &now) / 1000;
if ((current_gamepad_fd > 0) && (gamepad_time_diff_usecs >= gamepad_report_timing_us)) {
gamepad_last_hid_report_sent = now;
if (current_gamepad == GAMEPAD_DUALSENSE) {
@ -224,159 +509,234 @@ void *dev_out_thread_func(void *ptr) {
virt_dualshock_compose(&controller_data.ds4, &dev_out_data->dev_stats.gamepad, tmp_buf);
virt_dualshock_send(&controller_data.ds4, tmp_buf);
}
} else {
FD_ZERO(&read_fds);
}
if ((current_mouse_fd > 0) && (mouse_time_diff_usecs >= mouse_report_timing_us)) {
mouse_last_hid_report_sent = now;
virt_mouse_send(&mouse_data, &dev_out_data->dev_stats.mouse, NULL);
// reset mouse movements now
dev_out_data->dev_stats.mouse.x = 0;
dev_out_data->dev_stats.mouse.y = 0;
}
if ((current_keyboard_fd > 0) && (kbd_time_diff_usecs >= kbd_report_timing_us)) {
keyboard_last_hid_report_sent = now;
virt_kbd_send(&keyboard_data, &dev_out_data->dev_stats.kbd, NULL);
}
// once here no output device needs to send out its report
FD_ZERO(&read_fds);
if (dev_out_data->communication.type == ipc_unix_pipe) {
FD_SET(dev_out_data->communication.endpoint.pipe.in_message_pipe_fd, &read_fds);
} else if (dev_out_data->communication.type == ipc_server_sockets) {
if (pthread_mutex_lock(&dev_out_data->communication.endpoint.ssocket.mutex) == 0) {
for (int i = 0; i < MAX_CONNECTED_CLIENTS; ++i) {
const int fd = dev_out_data->communication.endpoint.ssocket.clients[i];
if (fd > 0) {
FD_SET(fd, &read_fds);
}
}
pthread_mutex_unlock(&dev_out_data->communication.endpoint.ssocket.mutex);
}
}
if (current_mouse_fd > 0) {
FD_SET(current_mouse_fd, &read_fds);
}
if (current_keyboard_fd > 0) {
FD_SET(current_keyboard_fd, &read_fds);
}
if (current_gamepad_fd > 0) {
FD_SET(current_gamepad_fd, &read_fds);
}
const int64_t timeout_gamepad_time_diff_usecs = (current_gamepad_fd > 0) ? gamepad_report_timing_us - gamepad_time_diff_usecs : 5000;
const int64_t timeout_mouse_time_diff_usecs = (current_mouse_fd > 0) ? mouse_report_timing_us - mouse_time_diff_usecs : 5000;
const int64_t timeout_kbd_time_diff_usecs = (current_keyboard_fd > 0) ? kbd_report_timing_us - kbd_time_diff_usecs : 5000;
int64_t next_timing_out_device_diff_usecs = 5000;
if ((timeout_kbd_time_diff_usecs > 0) && (timeout_kbd_time_diff_usecs < next_timing_out_device_diff_usecs)) {
next_timing_out_device_diff_usecs = timeout_kbd_time_diff_usecs;
}
if ((timeout_mouse_time_diff_usecs > 0) && (timeout_mouse_time_diff_usecs < next_timing_out_device_diff_usecs)) {
next_timing_out_device_diff_usecs = timeout_mouse_time_diff_usecs;
}
if ((timeout_gamepad_time_diff_usecs > 0) && (timeout_gamepad_time_diff_usecs < next_timing_out_device_diff_usecs)) {
next_timing_out_device_diff_usecs = timeout_gamepad_time_diff_usecs;
}
// calculate the shortest timeout between one of the multiple device will needs to send out its hid report
struct timeval timeout = {
.tv_sec = (__time_t)next_timing_out_device_diff_usecs / (__time_t)1000000,
.tv_usec = (__suseconds_t)next_timing_out_device_diff_usecs % (__suseconds_t)1000000,
};
int ready_fds = select(FD_SETSIZE, &read_fds, NULL, NULL, &timeout);
gamepad_status_qam_quirk_ext_time(&dev_out_data->dev_stats.gamepad);
if (ready_fds == -1) {
const int err = errno;
fprintf(stderr, "Error reading events for output devices: %d\n", err);
usleep(1000);
continue;
} else if (ready_fds == 0) {
// timeout: do nothing but continue. next iteration will take care
continue;
}
if ((current_gamepad_fd > 0) && (FD_ISSET(current_gamepad_fd, &read_fds))) {
const uint64_t prev_leds_events_count = dev_out_data->dev_stats.gamepad.leds_events_count;
const uint64_t prev_motors_events_count = dev_out_data->dev_stats.gamepad.rumble_events_count;
out_message_t out_msgs[4];
size_t out_msgs_count = 0;
if (current_gamepad == GAMEPAD_DUALSENSE) {
virt_dualsense_event(&controller_data.ds5, &dev_out_data->dev_stats.gamepad);
} else if (current_gamepad == GAMEPAD_DUALSHOCK) {
virt_dualshock_event(&controller_data.ds4, &dev_out_data->dev_stats.gamepad);
}
const uint64_t current_leds_events_count = dev_out_data->dev_stats.gamepad.leds_events_count;
const uint64_t current_motors_events_count = dev_out_data->dev_stats.gamepad.rumble_events_count;
if (current_leds_events_count != prev_leds_events_count) {
const out_message_t msg = {
.type = OUT_MSG_TYPE_LEDS,
.data = {
.leds = {
.r = dev_out_data->dev_stats.gamepad.leds_colors[0],
.g = dev_out_data->dev_stats.gamepad.leds_colors[1],
.b = dev_out_data->dev_stats.gamepad.leds_colors[2],
}
}
};
if (dev_out_data->settings.gamepad_leds_control) {
out_msgs[out_msgs_count++] = msg;
}
}
if (current_motors_events_count != prev_motors_events_count) {
const out_message_t msg = {
.type = OUT_MSG_TYPE_RUMBLE,
.data = {
.rumble = {
.motors_left = dev_out_data->dev_stats.gamepad.motors_intensity[0],
.motors_right = dev_out_data->dev_stats.gamepad.motors_intensity[1],
}
}
};
if (dev_out_data->settings.gamepad_rumble_control) {
out_msgs[out_msgs_count++] = msg;
}
}
// send out game-generated events to sockets
if (dev_out_data->communication.type == ipc_unix_pipe) {
FD_SET(dev_out_data->communication.endpoint.pipe.in_message_pipe_fd, &read_fds);
for (int msg_idx = 0; msg_idx < out_msgs_count; ++msg_idx) {
const int write_res = write(dev_out_data->communication.endpoint.pipe.out_message_pipe_fd, (void*)&out_msgs[msg_idx], sizeof(out_message_t));
if (write_res != sizeof(out_message_t)) {
fprintf(stderr, "Error in writing out_message to out_message_pipe: %d\n", write_res);
}
}
} else if (dev_out_data->communication.type == ipc_server_sockets) {
if (pthread_mutex_lock(&dev_out_data->communication.endpoint.ssocket.mutex) == 0) {
for (int i = 0; i < MAX_CONNECTED_CLIENTS; ++i) {
const int fd = dev_out_data->communication.endpoint.ssocket.clients[i];
if (fd > 0) {
FD_SET(fd, &read_fds);
}
}
pthread_mutex_unlock(&dev_out_data->communication.endpoint.ssocket.mutex);
}
}
// TODO: FD_SET(current_mouse_fd, &read_fds);
// TODO: FD_SET(current_keyboard_fd, &read_fds);
FD_SET(current_gamepad_fd, &read_fds);
// calculate the shortest timeout between one of the multiple device will needs to send out its hid report
struct timeval timeout = {
.tv_sec = (__time_t)gamepad_time_diff_usecs / (__time_t)1000000,
.tv_usec = (__suseconds_t)gamepad_time_diff_usecs % (__suseconds_t)1000000,
};
int ready_fds = select(FD_SETSIZE, &read_fds, NULL, NULL, &timeout);
gamepad_status_qam_quirk_ext_time(&dev_out_data->dev_stats.gamepad, &now);
if (ready_fds == -1) {
const int err = errno;
fprintf(stderr, "Error reading events for output devices: %d\n", err);
continue;
} else if (ready_fds == 0) {
// timeout: do nothing but continue. next iteration will take care
continue;
}
if (FD_ISSET(current_gamepad_fd, &read_fds)) {
const uint64_t prev_leds_events_count = dev_out_data->dev_stats.gamepad.leds_events_count;
const uint64_t prev_motors_events_count = dev_out_data->dev_stats.gamepad.rumble_events_count;
out_message_t out_msgs[4];
size_t out_msgs_count = 0;
if (current_gamepad == GAMEPAD_DUALSENSE) {
virt_dualsense_event(&controller_data.ds5, &dev_out_data->dev_stats.gamepad);
} else if (current_gamepad == GAMEPAD_DUALSHOCK) {
virt_dualshock_event(&controller_data.ds4, &dev_out_data->dev_stats.gamepad);
}
const uint64_t current_leds_events_count = dev_out_data->dev_stats.gamepad.leds_events_count;
const uint64_t current_motors_events_count = dev_out_data->dev_stats.gamepad.rumble_events_count;
if (current_leds_events_count != prev_leds_events_count) {
const out_message_t msg = {
.type = OUT_MSG_TYPE_LEDS,
.data = {
.leds = {
.r = dev_out_data->dev_stats.gamepad.leds_colors[0],
.g = dev_out_data->dev_stats.gamepad.leds_colors[1],
.b = dev_out_data->dev_stats.gamepad.leds_colors[2],
}
}
};
out_msgs[out_msgs_count++] = msg;
}
if (current_motors_events_count != prev_motors_events_count) {
const out_message_t msg = {
.type = OUT_MSG_TYPE_RUMBLE,
.data = {
.rumble = {
.motors_left = dev_out_data->dev_stats.gamepad.motors_intensity[0],
.motors_right = dev_out_data->dev_stats.gamepad.motors_intensity[1],
}
}
};
out_msgs[out_msgs_count++] = msg;
}
// send out game-generated events to sockets
int fd = -1;
const size_t bytes_to_send = sizeof(out_message_t) * out_msgs_count;
if (dev_out_data->communication.type == ipc_unix_pipe) {
const int write_res = write(dev_out_data->communication.endpoint.pipe.out_message_pipe_fd, (void*)&out_msgs, bytes_to_send);
if (write_res != bytes_to_send) {
fprintf(stderr, "Error in writing out_message to out_message_pipe: %d\n", write_res);
}
} else if (dev_out_data->communication.type == ipc_server_sockets) {
if (pthread_mutex_lock(&dev_out_data->communication.endpoint.ssocket.mutex) == 0) {
for (int i = 0; i < MAX_CONNECTED_CLIENTS; ++i) {
if (dev_out_data->communication.endpoint.ssocket.clients[i] > 0) {
const int write_res = write(dev_out_data->communication.endpoint.ssocket.clients[i], (void*)&out_msgs, bytes_to_send);
if (write_res != bytes_to_send) {
if (dev_out_data->communication.endpoint.ssocket.clients[i] > 0) {
for (int msg_idx = 0; msg_idx < out_msgs_count; ++msg_idx) {
const int write_res = write(dev_out_data->communication.endpoint.ssocket.clients[i], (void*)&out_msgs[msg_idx], sizeof(out_message_t));
if (write_res != sizeof(out_message_t)) {
fprintf(stderr, "Error in writing out_message to socket number %d: %d\n", i, write_res);
close(dev_out_data->communication.endpoint.ssocket.clients[i]);
dev_out_data->communication.endpoint.ssocket.clients[i] = -1;
}
}
}
}
pthread_mutex_unlock(&dev_out_data->communication.endpoint.ssocket.mutex);
}
}
}
// read and handle incoming data: this data is packed into in_message_t
if (dev_out_data->communication.type == ipc_unix_pipe) {
if (FD_ISSET(dev_out_data->communication.endpoint.pipe.in_message_pipe_fd, &read_fds)) {
in_message_t incoming_message;
const size_t in_message_pipe_read_res = read(dev_out_data->communication.endpoint.pipe.in_message_pipe_fd, (void*)&incoming_message, sizeof(in_message_t));
if (in_message_pipe_read_res == sizeof(in_message_t)) {
handle_incoming_message(&incoming_message, &dev_out_data->dev_stats);
} else {
fprintf(stderr, "Error reading from in_message_pipe_fd: got %zu bytes, expected %zu butes\n", in_message_pipe_read_res, sizeof(in_message_t));
}
}
} else if (dev_out_data->communication.type == ipc_server_sockets) {
if (pthread_mutex_lock(&dev_out_data->communication.endpoint.ssocket.mutex) == 0) {
for (int i = 0; i < MAX_CONNECTED_CLIENTS; ++i) {
const int fd = dev_out_data->communication.endpoint.ssocket.clients[i];
if ((fd > 0) && (FD_ISSET(fd, &read_fds))) {
in_message_t incoming_message;
const size_t in_message_pipe_read_res = read(fd, (void*)&incoming_message, sizeof(in_message_t));
if (in_message_pipe_read_res == sizeof(in_message_t)) {
handle_incoming_message(&incoming_message, &dev_out_data->dev_stats);
} else {
fprintf(stderr, "Error reading from socket number %d: got %zu bytes, expected %zu butes\n", i, in_message_pipe_read_res, sizeof(in_message_t));
close(dev_out_data->communication.endpoint.ssocket.clients[i]);
dev_out_data->communication.endpoint.ssocket.clients[i] = -1;
}
}
}
pthread_mutex_unlock(&dev_out_data->communication.endpoint.ssocket.mutex);
}
}
}
if ((current_keyboard_fd > 0) && (FD_ISSET(current_keyboard_fd, &read_fds))) {
// TODO: read keyboard events
}
if ((current_mouse_fd > 0) && (FD_ISSET(current_mouse_fd, &read_fds))) {
// TODO: read mouse events
}
// read and handle incoming data: this data is packed into in_message_t
if (dev_out_data->communication.type == ipc_unix_pipe) {
if (FD_ISSET(dev_out_data->communication.endpoint.pipe.in_message_pipe_fd, &read_fds)) {
in_message_t incoming_message;
const size_t in_message_pipe_read_res = read(dev_out_data->communication.endpoint.pipe.in_message_pipe_fd, (void*)&incoming_message, sizeof(in_message_t));
if (in_message_pipe_read_res == sizeof(in_message_t)) {
handle_incoming_message(
&dev_out_data->settings,
&incoming_message,
&dev_out_data->dev_stats
);
} else {
fprintf(stderr, "Error reading from in_message_pipe_fd: got %zu bytes, expected %zu bytes\n", in_message_pipe_read_res, sizeof(in_message_t));
}
}
} else if (dev_out_data->communication.type == ipc_server_sockets) {
if (pthread_mutex_lock(&dev_out_data->communication.endpoint.ssocket.mutex) == 0) {
for (int i = 0; i < MAX_CONNECTED_CLIENTS; ++i) {
const int fd = dev_out_data->communication.endpoint.ssocket.clients[i];
if ((fd > 0) && (FD_ISSET(fd, &read_fds))) {
in_message_t incoming_message;
const size_t in_message_pipe_read_res = read(fd, (void*)&incoming_message, sizeof(in_message_t));
if (in_message_pipe_read_res == sizeof(in_message_t)) {
handle_incoming_message(
&dev_out_data->settings,
&incoming_message,
&dev_out_data->dev_stats
);
} else {
fprintf(stderr, "Error reading from socket number %d: got %zu bytes, expected %zu bytes\n", i, in_message_pipe_read_res, sizeof(in_message_t));
close(dev_out_data->communication.endpoint.ssocket.clients[i]);
dev_out_data->communication.endpoint.ssocket.clients[i] = -1;
}
}
}
pthread_mutex_unlock(&dev_out_data->communication.endpoint.ssocket.mutex);
}
}
}
// close the output device
if (current_gamepad == GAMEPAD_DUALSENSE) {
virt_dualsense_close(&controller_data.ds5);
} else if (current_gamepad == GAMEPAD_DUALSHOCK) {
virt_dualshock_close(&controller_data.ds4);
// close the gamepad output device
if (current_gamepad_fd > 0) {
if (current_gamepad == GAMEPAD_DUALSENSE) {
virt_dualsense_close(&controller_data.ds5);
} else if (current_gamepad == GAMEPAD_DUALSHOCK) {
virt_dualshock_close(&controller_data.ds4);
}
}
// close the mouse device
if (current_mouse_fd > 0) {
virt_mouse_close(&mouse_data);
}
// close the keyboard device
if (current_keyboard_fd > 0) {
virt_kbd_close(&keyboard_data);
}
// end communication

View file

@ -3,6 +3,7 @@
#include "ipc.h"
#include "message.h"
#include "devices_status.h"
#include "settings.h"
#define DEV_OUT_FLAG_EXIT 0x00000001U
@ -16,10 +17,10 @@ typedef struct dev_out_data {
ipc_t communication;
dev_out_gamepad_device_t gamepad;
devices_status_t dev_stats;
dev_out_settings_t settings;
volatile uint32_t flags;
} dev_out_data_t;

67
dev_timer.c Normal file
View file

@ -0,0 +1,67 @@
#include "dev_timer.h"
int dev_timer_open(
const timer_filters_t *const in_filters,
dev_timer_t **const out_dev
) {
int res = -ENODEV;
*out_dev = malloc(sizeof(dev_timer_t));
if (*out_dev == NULL) {
res = -ENOMEM;
goto dev_timer_open_err;
}
memset(*out_dev, 0, sizeof(dev_timer_t));
const int fd = timerfd_create(CLOCK_MONOTONIC, 0);
if (fd < 0) {
res = errno < 0 ? errno : -1 * errno;
if (res == 0) {
res = fd;
}
goto dev_timer_open_err;
}
(*out_dev)->fd = fd;
if (in_filters->ticktime_ms != 0) {
(*out_dev)->timer_spec.it_value.tv_sec = in_filters->ticktime_ms / (__time_t)1000;
(*out_dev)->timer_spec.it_value.tv_nsec = (in_filters->ticktime_ms % (__syscall_slong_t)1000) * (__syscall_slong_t)1000000;
(*out_dev)->timer_spec.it_interval.tv_sec = in_filters->ticktime_ms / (__time_t)1000;
(*out_dev)->timer_spec.it_interval.tv_nsec = (in_filters->ticktime_ms % (__syscall_slong_t)1000) * (__syscall_slong_t)1000000;
} else {
(*out_dev)->timer_spec.it_value.tv_sec = 0;
(*out_dev)->timer_spec.it_value.tv_nsec = in_filters->ticktime_ns;
(*out_dev)->timer_spec.it_interval.tv_sec = 0;
(*out_dev)->timer_spec.it_interval.tv_nsec = in_filters->ticktime_ns;
}
if (timerfd_settime((*out_dev)->fd, 0, &(*out_dev)->timer_spec, NULL) < 0) {
res = errno < 0 ? errno : -1 * errno;
if (res == 0) {
res = -EIO;
}
goto dev_timer_open_err;
}
res = 0;
dev_timer_open_err:
if (res != 0) {
if (fd > 0) {
close(fd);
}
free(*out_dev);
}
return res;
}
void dev_timer_close(dev_timer_t *const inout_dev) {
close(inout_dev->fd);
}
int dev_timer_get_fd(const dev_timer_t *const in_dev) {
return in_dev->fd;
}

18
dev_timer.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#include "input_dev.h"
typedef struct dev_timer {
struct itimerspec timer_spec;
int fd;
} dev_timer_t;
int dev_timer_open(
const timer_filters_t *const in_filters,
dev_timer_t **const out_dev
);
void dev_timer_close(dev_timer_t *const inout_dev);
int dev_timer_get_fd(const dev_timer_t *const in_dev);

View file

@ -1,7 +1,63 @@
#include "devices_status.h"
#include <pthread.h>
void kbd_status_init(keyboard_status_t *const stats) {
stats->connected = true;
stats->q = 0;
stats->w = 0;
stats->e = 0;
stats->r = 0;
stats->t = 0;
stats->y = 0;
stats->u = 0;
stats->i = 0;
stats->o = 0;
stats->p = 0;
stats->a = 0;
stats->s = 0;
stats->d = 0;
stats->f = 0;
stats->g = 0;
stats->h = 0;
stats->j = 0;
stats->k = 0;
stats->l = 0;
stats->z = 0;
stats->x = 0;
stats->c = 0;
stats->v = 0;
stats->b = 0;
stats->n = 0;
stats->m = 0;
stats->num_1 = 0;
stats->num_2 = 0;
stats->num_3 = 0;
stats->num_4 = 0;
stats->num_5 = 0;
stats->num_6 = 0;
stats->num_7 = 0;
stats->num_8 = 0;
stats->num_9 = 0;
stats->num_0 = 0;
stats->up = 0;
stats->down = 0;
stats->left = 0;
stats->right = 0;
stats->lctrl = 0;
}
void mouse_status_init(mouse_status_t *const stats) {
stats->connected = true;
stats->x = 0;
stats->y = 0;
stats->btn_left = 0;
stats->btn_middle = 0;
stats->btn_right = 0;
}
void gamepad_status_init(gamepad_status_t *const stats) {
@ -39,13 +95,19 @@ void gamepad_status_init(gamepad_status_t *const stats) {
stats->leds_colors[0] = 0;
stats->leds_colors[1] = 0;
stats->leds_colors[2] = 0;
stats->touchpad_touch_num = -1;
stats->touchpad_x = 0;
stats->touchpad_y = 0;
stats->join_left_analog_and_gyroscope = 0;
stats->join_right_analog_and_gyroscope = 0;
stats->flags = 0;
}
void devices_status_init(devices_status_t *const stats) {
pthread_mutex_init(&stats->mutex, NULL);
gamepad_status_init(&stats->gamepad);
kbd_status_init(&stats->kbd);
// TODO: mouse init
mouse_status_init(&stats->mouse);
}
void gamepad_status_qam_quirk(gamepad_status_t *const gamepad_stats) {
@ -106,13 +168,15 @@ void gamepad_status_qam_quirk(gamepad_status_t *const gamepad_stats) {
}
}
void gamepad_status_qam_quirk_ext_time(gamepad_status_t *const gamepad_stats, struct timeval *now) {
void gamepad_status_qam_quirk_ext_time(gamepad_status_t *const gamepad_stats) {
static struct timeval press_time;
if (gamepad_stats->flags & GAMEPAD_STATUS_FLAGS_PRESS_AND_REALEASE_CENTER) {
struct timeval now;
gettimeofday(&now, NULL);
// Calculate elapsed time in milliseconds
const int64_t elapsed_time = (now->tv_sec - press_time.tv_sec) * 1000 +
(now->tv_usec - press_time.tv_usec) / 1000;
const int64_t elapsed_time = (now.tv_sec - press_time.tv_sec) * 1000 +
(now.tv_usec - press_time.tv_usec) / 1000;
if (gamepad_stats->center) {
// If the center button is pressed and at least X ms have passed

View file

@ -46,8 +46,12 @@ typedef struct gamepad_status {
uint8_t touchpad_press;
struct timeval last_gyro_motion_time;
struct timeval last_accel_motion_time;
int16_t touchpad_touch_num; // touchpad is inactive when this is -1
int16_t touchpad_x; // 0 to 1920
int16_t touchpad_y; // 0 to 1080
int64_t last_gyro_motion_timestamp_ns;
int64_t last_accel_motion_timestamp_ns;
double gyro[3]; // | x, y, z| right-hand-rules -- in rad/s
double accel[3]; // | x, y, z| positive: right, up, towards player -- in m/s^2
@ -61,14 +65,39 @@ typedef struct gamepad_status {
uint64_t leds_events_count;
uint8_t leds_colors[3]; // r | g | b
uint8_t join_left_analog_and_gyroscope;
uint8_t join_right_analog_and_gyroscope;
volatile uint32_t flags;
} gamepad_status_t;
typedef struct keyboard_status {
bool connected;
uint8_t q,w,e,r,t,y,u,i,o,p,a,s,d,f,g,h,j,k,l,z,x,c,v,b,n,m;
uint8_t num_1, num_2, num_3, num_4, num_5, num_6, num_7, num_8, num_9, num_0;
uint8_t up, down, left, right;
uint8_t lctrl;
} keyboard_status_t;
typedef struct mouse_status {
bool connected;
int32_t x;
int32_t y;
uint8_t btn_left;
uint8_t btn_middle;
uint8_t btn_right;
} mouse_status_t;
typedef struct devices_status {
// this mutex MUST be grabbed when reading and/or writing below properties
pthread_mutex_t mutex;
@ -77,8 +106,12 @@ typedef struct devices_status {
keyboard_status_t kbd;
mouse_status_t mouse;
} devices_status_t;
void mouse_status_init(mouse_status_t *const stats);
void kbd_status_init(keyboard_status_t *const stats);
void gamepad_status_init(gamepad_status_t *const stats);
@ -87,4 +120,4 @@ void devices_status_init(devices_status_t *const stats);
void gamepad_status_qam_quirk(gamepad_status_t *const gamepad_stats);
void gamepad_status_qam_quirk_ext_time(gamepad_status_t *const gamepad_stats, struct timeval *now);
void gamepad_status_qam_quirk_ext_time(gamepad_status_t *const gamepad_stats);

View file

@ -1,6 +1,7 @@
#pragma once
#include "message.h"
#include "settings.h"
#undef INCLUDE_INPUT_DEBUG
#undef IGNORE_INPUT_SCAN
@ -17,13 +18,26 @@ typedef struct evdev_collected {
* A function with this signature grapbs input_event data and sends to the pipe messages
* constructed from that data.
*/
typedef int (*ev_map)(const evdev_collected_t *const e, in_message_t *const messages, size_t messages_len, void* user_data);
typedef int (*hidraw_map)(int hidraw_fd, in_message_t *const messages, size_t messages_len, void* user_data);
typedef int (*ev_map)(
const dev_in_settings_t *const conf,
const evdev_collected_t *const e,
in_message_t *const messages,
size_t messages_len,
void* user_data
);
typedef void (*ev_timer)(
const dev_in_settings_t *const conf,
struct libevdev* evdev,
const char* const timer_name,
uint64_t expired,
void* user_data
);
typedef enum input_dev_type {
input_dev_type_uinput,
input_dev_type_iio,
input_dev_type_hidraw,
input_dev_type_timer,
} input_dev_type_t;
typedef struct hidraw_filters {
@ -40,13 +54,44 @@ typedef struct iio_filters {
const char name[256];
} iio_filters_t;
typedef int (*hidraw_set_leds)(uint8_t r, uint8_t g, uint8_t b, void* user_data);
typedef int (*hidraw_map)(
const dev_in_settings_t *const conf,
int hidraw_fd,
in_message_t *const messages,
size_t messages_len,
void* user_data
);
typedef int (*hidraw_rumble)(uint8_t left_motor, uint8_t right_motor, void* user_data);
typedef int (*hidraw_set_leds)(
const dev_in_settings_t *const conf,
int hidraw_fd,
uint8_t r,
uint8_t g,
uint8_t b,
void* user_data
);
typedef int (*hidraw_rumble)(
const dev_in_settings_t *const conf,
int hidraw_fd,
uint8_t left_motor,
uint8_t right_motor,
void* user_data
);
typedef void (*hidraw_timer)(
const dev_in_settings_t *const conf,
int fd,
const char* const timer_name,
uint64_t expired,
void* user_data
);
typedef struct hidraw_callbacks {
hidraw_set_leds leds_callback;
hidraw_rumble rumble_callback;
hidraw_map map_callback;
hidraw_timer timeout_callback;
} hidraw_callbacks_t;
typedef struct iio_settings {
@ -54,6 +99,23 @@ typedef struct iio_settings {
int8_t post_matrix[3][3];
} iio_settings_t;
typedef int (*timer_map)(const dev_in_settings_t *const conf, int timer_fd, uint64_t expirations, in_message_t *const messages, size_t messages_len, void* user_data);
typedef struct timer_callbacks {
timer_map map_fn;
} timer_callbacks_t;
typedef struct ev_callbacks {
ev_map input_map_fn;
ev_timer timeout_callback;
} ev_callbacks_t;
typedef struct timer_filters {
char name[128];
uint64_t ticktime_ms;
uint64_t ticktime_ns;
} timer_filters_t;
typedef struct input_dev {
input_dev_type_t dev_type;
@ -61,23 +123,25 @@ typedef struct input_dev {
uinput_filters_t ev;
iio_filters_t iio;
hidraw_filters_t hidraw;
timer_filters_t timer;
} filters;
void* user_data;
union input_dev_map {
iio_settings_t iio_settings;
ev_map ev_input_map_fn;
hidraw_map hidraw_input_map_fn;
ev_callbacks_t ev_callbacks;
hidraw_callbacks_t hidraw_callbacks;
timer_callbacks_t timer_callbacks;
} map;
} input_dev_t;
typedef int (*platform_init)(void** platform_data);
typedef int (*platform_init)(const dev_in_settings_t *const conf, void** platform_data);
typedef void (*platform_deinit)(void** platform_data);
typedef void (*platform_deinit)(const dev_in_settings_t *const conf, void** platform_data);
typedef int (*platform_leds)(uint8_t r, uint8_t g, uint8_t b, void* platform_data);
typedef int (*platform_leds)(const dev_in_settings_t *const conf, uint8_t r, uint8_t g, uint8_t b, void* platform_data);
typedef struct input_dev_composite {

View file

@ -11,12 +11,12 @@ static input_dev_t in_xbox_dev = {
}
},
.map = {
.ev_input_map_fn = xbox360_ev_map,
.ev_callbacks = {
.input_map_fn = xbox360_ev_map,
},
}
};
static xbox360_settings_t x360_cfg;
static input_dev_t in_iio_dev = {
.dev_type = input_dev_type_iio,
.filters = {
@ -40,7 +40,7 @@ static struct llg_hidraw_data {
uint8_t last_packet[64];
} llg_hidraw_user_data;
static int llg_hidraw_map(int hidraw_fd, in_message_t *const messages, size_t messages_len, void* user_data) {
static int llg_hidraw_map(const dev_in_settings_t *const conf, int hidraw_fd, in_message_t *const messages, size_t messages_len, void* user_data) {
struct llg_hidraw_data *const llg_data = (struct llg_hidraw_data*)user_data;
int msg_count = 0;
@ -84,7 +84,9 @@ static input_dev_t in_hidraw_dev = {
},
.user_data = (void*)&llg_hidraw_user_data,
.map = {
.hidraw_input_map_fn = llg_hidraw_map,
.hidraw_callbacks = {
.map_callback = llg_hidraw_map,
}
},
};
@ -92,7 +94,7 @@ typedef struct legion_go_platform {
int _pad;
} legion_go_platform_t;
static int legion_platform_init(void** platform_data) {
static int legion_platform_init(const dev_in_settings_t *const conf, void** platform_data) {
int res = -EINVAL;
legion_go_platform_t *const llg_platform = malloc(sizeof(legion_go_platform_t));
@ -109,12 +111,12 @@ legion_platform_init_err:
return res;
}
static void legion_platform_deinit(void** platform_data) {
static void legion_platform_deinit(const dev_in_settings_t *const conf, void** platform_data) {
free(*platform_data);
*platform_data = NULL;
}
int legion_platform_leds(uint8_t r, uint8_t g, uint8_t b, void* platform_data) {
int legion_platform_leds(const dev_in_settings_t *const conf, uint8_t r, uint8_t g, uint8_t b, void* platform_data) {
return 0;
}
@ -131,10 +133,7 @@ input_dev_composite_t legion_composite = {
};
input_dev_composite_t* legion_go_device_def(const controller_settings_t *const settings) {
x360_cfg.nintendo_layout = settings->nintendo_layout;
in_xbox_dev.user_data = (void*)&x360_cfg;
input_dev_composite_t* legion_go_device_def(void) {
return &legion_composite;
}

View file

@ -3,4 +3,4 @@
#include "input_dev.h"
#include "settings.h"
input_dev_composite_t* legion_go_device_def(const controller_settings_t *const settings);
input_dev_composite_t* legion_go_device_def(void);

75
main.c
View file

@ -10,15 +10,33 @@
#include "rog_ally.h"
#include "legion_go.h"
#include <sys/mman.h>
static const char* configuration_file = "/etc/ROGueENEMY/config.cfg";
controller_settings_t settings;
int main(int argc, char ** argv) {
// Lock all current and future pages from preventing of being paged to swap
const int lockall_res = mlockall( MCL_CURRENT | MCL_FUTURE );
if (lockall_res) {
fprintf(stderr, "mlockall failed: %d", lockall_res);
}
int ret = 0;
init_config(&settings);
fill_config(&settings, configuration_file);
dev_in_settings_t in_settings = {
.enable_qam = true,
.ff_gain = 0xFFFF,
.rumble_on_mode_switch = true,
.m1m2_mode = 1,
.touchbar = true,
.enable_thermal_profiles_switching = false,
.default_thermal_profile = -1,
.enable_leds_commands = false,
.enable_imu = true,
.imu_polling_interface = true,
};
load_in_config(&in_settings, configuration_file);
input_dev_composite_t* in_devs = NULL;
@ -33,10 +51,10 @@ int main(int argc, char ** argv) {
read(dmi_name_fd, bname, sizeof(bname));
if (strstr(bname, "RC71L") != NULL) {
printf("Running in an Asus ROG Ally device\n");
in_devs = rog_ally_device_def(&settings);
in_devs = rog_ally_device_def(&in_settings);
} else if (strstr(bname, "LNVNB161216")) {
printf("Running in an Lenovo Legion Go device\n");
in_devs = legion_go_device_def(&settings);
in_devs = legion_go_device_def();
}
close(dmi_name_fd);
@ -55,13 +73,54 @@ int main(int argc, char ** argv) {
}
},
}
}
},
.settings = in_settings,
};
// fill in configuration from file: automatic fallback to default
load_in_config(&dev_in_thread_data.settings, configuration_file);
//memset(&dev_in_thread_data.communication.endpoint.socket.serveraddr, 0, sizeof(dev_in_thread_data.communication.endpoint.socket.serveraddr));
// Initialize pthread attributes (default values)
struct sched_param param;
pthread_attr_t attr;
ret = pthread_attr_init(&attr);
if (ret) {
printf("init pthread attributes failed\n");
goto main_err;
}
// Set a specific stack size
ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + 8192);
if (ret) {
printf("pthread setstacksize failed\n");
goto main_err;
}
// Set scheduler policy and priority of pthread
ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if (ret) {
printf("pthread setschedpolicy failed\n");
goto main_err;
}
param.sched_priority = 80;
ret = pthread_attr_setschedparam(&attr, &param);
if (ret) {
printf("pthread setschedparam failed\n");
goto main_err;
}
// Use scheduling parameters of attr
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret) {
printf("pthread setinheritsched failed\n");
goto main_err;
}
pthread_t dev_in_thread;
const int dev_in_thread_creation = pthread_create(&dev_in_thread, NULL, dev_in_thread_func, (void*)(&dev_in_thread_data));
const int dev_in_thread_creation = pthread_create(&dev_in_thread, &attr, dev_in_thread_func, (void*)(&dev_in_thread_data));
if (dev_in_thread_creation != 0) {
fprintf(stderr, "Error creating dev_in thread: %d\n", dev_in_thread_creation);
ret = -1;

112
message.h
View file

@ -24,6 +24,8 @@ typedef enum in_message_gamepad_btn {
GAMEPAD_BTN_L5,
GAMEPAD_BTN_R5,
GAMEPAD_BTN_TOUCHPAD,
GAMEPAD_BTN_JOIN_LEFT_ANALOG_AND_GYROSCOPE,
GAMEPAD_BTN_JOIN_RIGHT_ANALOG_AND_GYROSCOPE,
GAMEPAD_LEFT_JOYSTICK_X,
GAMEPAD_LEFT_JOYSTICK_Y,
@ -32,36 +34,142 @@ typedef enum in_message_gamepad_btn {
GAMEPAD_DPAD_X,
GAMEPAD_DPAD_Y,
GAMEPAD_GYROSCOPE,
GAMEPAD_ACCELEROMETER,
GAMEPAD_TOUCHPAD_X,
GAMEPAD_TOUCHPAD_Y,
GAMEPAD_TOUCHPAD_TOUCH_ACTIVE,
} in_gamepad_element_t;
typedef struct in_message_gamepad_touchpad_x {
int16_t value;
} in_message_gamepad_touchpad_x_t;
typedef struct in_message_gamepad_touchpad_y {
int16_t value;
} in_message_gamepad_touchpad_y_t;
typedef struct in_message_gamepad_touchpad_active {
int16_t status;
} in_message_gamepad_touchpad_active_t;
typedef struct in_message_gamepad_gyro {
int64_t sample_timestamp_ns;
uint16_t x;
uint16_t y;
uint16_t z;
} in_message_gamepad_gyro_t;
typedef struct in_message_gamepad_accel {
int64_t sample_timestamp_ns;
uint16_t x;
uint16_t y;
uint16_t z;
} in_message_gamepad_accel_t;
typedef struct in_message_gamepad_set_element {
in_gamepad_element_t element;
union {
uint8_t btn;
int32_t joystick_pos;
int8_t dpad; // -1 | 0 | +1
in_message_gamepad_accel_t accel;
in_message_gamepad_gyro_t gyro;
in_message_gamepad_touchpad_active_t touchpad_active;
in_message_gamepad_touchpad_x_t touchpad_x;
in_message_gamepad_touchpad_y_t touchpad_y;
} status;
} in_message_gamepad_set_element_t;
typedef enum mouse_element {
MOUSE_ELEMENT_X,
MOUSE_ELEMENT_Y,
MOUSE_BTN_LEFT,
MOUSE_BTN_MIDDLE,
MOUSE_BTN_RIGHT,
} mouse_element_t;
typedef struct in_message_mouse_event {
mouse_element_t type;
int32_t value;
} in_message_mouse_event_t;
typedef enum in_message_gamepad_action {
GAMEPAD_ACTION_PRESS_AND_RELEASE_CENTER,
GAMEPAD_ACTION_OPEN_STEAM_QAM,
} in_message_gamepad_action_t;
typedef enum kbd_element {
KEYBOARD_KEY_Q,
KEYBOARD_KEY_W,
KEYBOARD_KEY_E,
KEYBOARD_KEY_R,
KEYBOARD_KEY_T,
KEYBOARD_KEY_Y,
KEYBOARD_KEY_U,
KEYBOARD_KEY_I,
KEYBOARD_KEY_O,
KEYBOARD_KEY_P,
KEYBOARD_KEY_A,
KEYBOARD_KEY_S,
KEYBOARD_KEY_D,
KEYBOARD_KEY_F,
KEYBOARD_KEY_G,
KEYBOARD_KEY_H,
KEYBOARD_KEY_J,
KEYBOARD_KEY_K,
KEYBOARD_KEY_L,
KEYBOARD_KEY_Z,
KEYBOARD_KEY_X,
KEYBOARD_KEY_C,
KEYBOARD_KEY_V,
KEYBOARD_KEY_B,
KEYBOARD_KEY_N,
KEYBOARD_KEY_M,
KEYBOARD_KEY_UP,
KEYBOARD_KEY_DOWN,
KEYBOARD_KEY_LEFT,
KEYBOARD_KEY_RIGHT,
KEYBOARD_KEY_NUM_1,
KEYBOARD_KEY_NUM_2,
KEYBOARD_KEY_NUM_3,
KEYBOARD_KEY_NUM_4,
KEYBOARD_KEY_NUM_5,
KEYBOARD_KEY_NUM_6,
KEYBOARD_KEY_NUM_7,
KEYBOARD_KEY_NUM_8,
KEYBOARD_KEY_NUM_9,
KEYBOARD_KEY_NUM_0,
KEYBOARD_KEY_LCRTL,
} kbd_element_t;
typedef struct in_message_keyboard_set_element {
kbd_element_t type;
uint8_t value;
} in_message_keyboard_set_element_t;
typedef enum in_in_message_type {
GAMEPAD_SET_ELEMENT,
GAMEPAD_ACTION,
MOUSE_EVENT,
KEYBOARD_SET_ELEMENT,
} in_message_type_t;
typedef struct in_message {
in_message_type_t type;
union {
//imu_in_message_t imu;
in_message_gamepad_action_t action;
in_message_gamepad_set_element_t gamepad_set;
in_message_mouse_event_t mouse_event;
in_message_keyboard_set_element_t kbd_set;
} data;
} in_message_t;

View file

@ -1,276 +0,0 @@
#include <asm-generic/errno-base.h>
#include <stdlib.h>
#include <libudev.h>
#include <dirent.h>
#include "platform.h"
static const char* const platform_input_path = "/sys/devices/platform/asus-mcu.0/input/mode";
static int hidraw_cycle_to_mode(const char* const path, int controller_mode) {
int res = 0;
if ((controller_mode < 0) || (controller_mode > 2)) {
res = -EINVAL;
goto hidraw_cycle_to_mode_err_mode;
}
const char* hidraw_subdir = "/hidraw/";
const unsigned long len = strlen(path) + strlen(hidraw_subdir) + 64;
char* hidraw_path = malloc(len + 1);
if (hidraw_path == NULL) {
res = -ENOMEM;
goto hidraw_cycle_to_mode_err_path;
}
memset(hidraw_path, 0, len + 1);
strcat(hidraw_path, path);
strcat(hidraw_path, hidraw_subdir);
DIR *d;
struct dirent *dir;
d = opendir(hidraw_path);
if (d) {
while ((dir = readdir(d)) != NULL) {
if (strstr(dir->d_name, "hidraw") == NULL) { // h as in hidraw
continue;
}
memset(hidraw_path, 0, len + 1);
strcat(hidraw_path, "/dev/");
strcat(hidraw_path, dir->d_name);
//strcat(hidraw_path, dir->d_name);
//strcat(hidraw_path, "/dev");
printf("Using hidraw located at: %s\n", hidraw_path);
int fd = open(hidraw_path, O_RDWR);
if (fd == -1) {
fprintf(stderr, "Error opening hidraw device %s\n", hidraw_path);
res = -EIO;
goto hidraw_cycle_to_mode_err;
}
for (int i = 0; i < 23; ++i) {
const int write_res = write(fd, &rc71l_mode_switch_commands[controller_mode][i][0], 64);
if (write_res != 64) {
fprintf(stderr, "Error writing packet %d/23: %d bytes sent, 64 expected\n", i, write_res);
break;
}
}
close(fd);
if (res == 0) {
printf("Control messages sent successfully.\n");
} else {
goto hidraw_cycle_to_mode_err;
}
}
}
hidraw_cycle_to_mode_err:
free(hidraw_path);
hidraw_cycle_to_mode_err_path:
hidraw_cycle_to_mode_err_mode:
return res;
}
static char* find_device(struct udev *udev) {
struct udev_enumerate *const enumerate = udev_enumerate_new(udev);
if (enumerate == NULL) {
fprintf(stderr, "Error in udev_enumerate_new: mode switch will not be available.\n");
return NULL;
}
const int add_match_subsystem_res = udev_enumerate_add_match_subsystem(enumerate, "hid");
if (add_match_subsystem_res != 0) {
fprintf(stderr, "Error in udev_enumerate_add_match_subsystem: %d\n", add_match_subsystem_res);
udev_enumerate_unref(enumerate);
return NULL;
}
const int add_match_sysattr_res = udev_enumerate_add_match_sysattr(enumerate, "gamepad_mode", NULL);
if (add_match_sysattr_res != 0) {
fprintf(stderr, "Error in udev_enumerate_add_match_sysattr: %d\n", add_match_sysattr_res);
udev_enumerate_unref(enumerate);
return NULL;
}
const int enumerate_scan_devices_res = udev_enumerate_scan_devices(enumerate);
if (enumerate_scan_devices_res != 0) {
fprintf(stderr, "Error in udev_enumerate_scan_devices: %d\n", enumerate_scan_devices_res);
udev_enumerate_unref(enumerate);
return NULL;
}
struct udev_list_entry *const udev_lst_frst = udev_enumerate_get_list_entry(enumerate);
struct udev_list_entry *list_entry = NULL;
udev_list_entry_foreach(list_entry, udev_lst_frst) {
const char* const name = udev_list_entry_get_name(list_entry);
const unsigned long len = strlen(name) + 1;
char *const result = malloc(len);
memset(result, 0, len);
strncat(result, name, len - 1);
udev_enumerate_unref(enumerate);
return result;
}
udev_enumerate_unref(enumerate);
return NULL;
}
int init_platform(rc71l_platform_t *const platform) {
platform->udev = NULL;
if (access(platform_input_path, F_OK) != 0) {
fprintf(stderr, "Unable to find the MCU platform mode file %s: asus-mcu not found.\n", platform_input_path);
/* create udev object */
platform->udev = udev_new();
if (platform->udev == NULL) {
fprintf(stderr, "Cannot create udev context: mode switch will not be available.\n");
platform->mode = -1;
return -ENOENT;
}
char *const dev_name = find_device(platform->udev);
if (dev_name == NULL) {
fprintf(stderr, "Cannot locate asus-mcu device: mode switch will not be available.\n");
platform->mode = -1;
return -ENOENT;
} else {
printf("Asus MCU over hidraw: %s -- mode will be reset\n", dev_name);
platform->platform_mode = rc71l_platform_mode_hidraw;
platform->modes_count = 2;
platform->mode = 0;
// reset to mode 0: game mode
const int reset_res = hidraw_cycle_to_mode(dev_name, 0);
if (reset_res != 0) {
fprintf(stderr, "Unable to reset Asus MCU over hidraw: %d -- Asus MCU will be unavailable.\n", reset_res);
free(dev_name);
return -EIO;
}
// find_device does malloc
free(dev_name);
return 0;
}
return -ENOENT;
}
FILE* mode_file = fopen(platform_input_path, "r");
if (mode_file == NULL) {
fprintf(stderr, "Unable to open the MCU platform mode file %s: modes cannot be switched.\n", platform_input_path);
platform->mode = -1;
return -EINVAL;
}
char mode_str[12];
unsigned long read_bytes = fread((void*)&mode_str[0], 1, sizeof(mode_str), mode_file);
if (read_bytes < 1) {
fprintf(stderr, "Unable to read the MCU platform mode file %s: no bytes.\n", platform_input_path);
fclose(mode_file);
platform->mode = -1;
return -EINVAL;
}
platform->mode = strtoul(&mode_str[0], NULL, 10);
fclose(mode_file);
printf("Asus MCU platform found: current mode %lu\n", platform->mode);
platform->modes_count = 2;
platform->platform_mode = rc71l_platform_mode_asus_mcu;
return 0;
}
int cycle_mode(rc71l_platform_t *const platform) {
if (platform == NULL) {
return -ENOENT;
}
char new_mode_str[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
unsigned long new_mode = (platform->mode + 1) % platform->modes_count;
sprintf(new_mode_str, "%lu\n", new_mode);
if (platform->platform_mode == rc71l_platform_mode_hidraw) {
char *const dev_name = find_device(platform->udev);
if (dev_name == NULL) {
fprintf(stderr, "Unable to locate Asus MCU hidraw to mode-switch. Mode will not be switched.\n");
return -ENOENT;
}
const int next_mode = (platform->mode + 1) % platform->modes_count;
const int reset_res = hidraw_cycle_to_mode(dev_name, next_mode);
if (reset_res != 0) {
fprintf(stderr, "Unable to change mode of Asus MCU over hidraw: %d.\n", reset_res);
}
free(dev_name);
platform->mode = next_mode;
printf("Used hidraw to switch Asus MCU to mode %lu\n", platform->mode);
return reset_res;
} else if (platform->platform_mode == rc71l_platform_mode_asus_mcu) {
FILE* mode_file = fopen(platform_input_path, "w");
if (mode_file == NULL) {
fprintf(stderr, "Unable to open the MCU platform mode file %s: modes cannot be switched.\n", platform_input_path);
return -1;
}
size_t len = strlen(new_mode_str);
const int write_bytes = fwrite((void*)&new_mode_str[0], 1, len, mode_file);
if (write_bytes < len) {
fprintf(stderr, "Error writing new mode: expected to write %d bytes, %d written.\n", (int)len, (int)write_bytes);
return -EIO;
}
platform->mode = new_mode;
printf("Used asus-mcu to switch Asus MCU to mode %lu\n", platform->mode);
fclose(mode_file);
}
return -ENOENT;
}
int is_mouse_mode(rc71l_platform_t *const platform) {
return platform != NULL ? platform->mode == 1 : 0;
}

View file

@ -1,23 +0,0 @@
#pragma once
#include "rogue_enemy.h"
typedef enum rc71l_platform_mode {
rc71l_platform_mode_hidraw,
rc71l_platform_mode_asus_mcu,
} rc71l_platform_mode_t;
typedef struct rc71l_platform {
struct udev *udev;
rc71l_platform_mode_t platform_mode;
unsigned long mode;
unsigned int modes_count;
} rc71l_platform_t;
int init_platform(rc71l_platform_t *const platform);
int cycle_mode(rc71l_platform_t *const platform);
int is_mouse_mode(rc71l_platform_t *const platform);

2645
rog_ally.c

File diff suppressed because it is too large Load diff

View file

@ -3,4 +3,4 @@
#include "input_dev.h"
#include "settings.h"
input_dev_composite_t* rog_ally_device_def(const controller_settings_t *const settings);
input_dev_composite_t* rog_ally_device_def(const dev_in_settings_t *const settings);

View file

@ -0,0 +1,18 @@
#!/bin/bash
for i in /sys/bus/iio/devices/* ; do
if [ -d "$i" ]; then
if [ -f "$i/name" ]; then
name=$(cat "$i/name")
if [ "$name" = "bmi323-imu" ]; then
# bind fake hrtimer to to the iio device
echo "void" > "$i/trigger/current_trigger"
# enable the buffer
echo 0 > "$i/buffer0/enable"
echo "bmi323-imu buffer started"
fi
fi
fi
done

View file

@ -0,0 +1,60 @@
#!/bin/bash
modprobe industrialio-sw-trigger
modprobe iio-trig-sysfs
modprobe iio-trig-hrtimer
# hrtimer
if [ ! -d "/home/config" ]; then
mkdir -p /home/config
fi
mount -t configfs none /home/config
mkdir -p /home/config/iio/triggers/hrtimer/rogue
# set sampling frequency for rogue
for i in /sys/bus/iio/devices/* ; do
if [ -d "$i" ]; then
if [ -f "$i/name" ]; then
name=$(cat "$i/name")
if [ "$name" = "rogue" ]; then
echo "1600" > "$i/sampling_frequency"
fi
fi
fi
done
# set the gyroscope
for i in /sys/bus/iio/devices/* ; do
if [ -d "$i" ]; then
if [ -f "$i/name" ]; then
name=$(cat "$i/name")
if [ "$name" = "bmi323-imu" ]; then
# change chip sampling frequency
echo "1600.000000" > "$i/in_accel_sampling_frequency"
echo "1600.000000" > "$i/in_anglvel_sampling_frequency"
# enable accel data acquisition
echo 1 > "$i/scan_elements/in_accel_x_en"
echo 1 > "$i/scan_elements/in_accel_y_en"
echo 1 > "$i/scan_elements/in_accel_z_en"
# enable gyroscope data acquisition
echo 1 > "$i/scan_elements/in_anglvel_x_en"
echo 1 > "$i/scan_elements/in_anglvel_y_en"
echo 1 > "$i/scan_elements/in_anglvel_z_en"
# enable timestamp reporting
echo 1 > "$i/scan_elements/in_timestamp_en"
# bind rogue hrtimer to to the iio device
echo "rogue" > "$i/trigger/current_trigger"
# enable the buffer
echo 1 > "$i/buffer0/enable"
echo "bmi323-imu buffer started"
fi
fi
fi
done

View file

@ -5,3 +5,124 @@ int32_t div_round_closest(int32_t x, int32_t divisor) {
const int32_t __d = divisor;
return ((__x) > 0) == ((__d) > 0) ? (((__x) + ((__d) / 2)) / (__d)) : (((__x) - ((__d) / 2)) / (__d));
}
int64_t div_round_closest_i64(int64_t x, int64_t divisor) {
const int64_t __x = x;
const int64_t __d = divisor;
return ((__x) > 0) == ((__d) > 0) ? (((__x) + ((__d) / 2)) / (__d)) : (((__x) - ((__d) / 2)) / (__d));
}
int64_t min_max_clamp(int64_t value, int64_t min, int64_t max) {
if (value <= min) {
return min;
} else if (value >= max) {
return max;
}
return value;
}
int64_t absolute_value(int64_t value) {
if (value < 0) {
return (int64_t)-1 * value;
}
return value;
}
ssize_t dmi_board_name(char *const buf, size_t buf_len) {
int dmi_name_fd = open("/sys/class/dmi/id/board_name", O_RDONLY | O_NONBLOCK);
if (dmi_name_fd < 0) {
return -1;
}
memset(buf, 0, buf_len);
const ssize_t ret = read(dmi_name_fd, buf, buf_len);
close(dmi_name_fd);
return ret;
}
char* inline_read_file(const char* base_path, const char *file) {
char* res = NULL;
char* fdir = NULL;
long len = 0;
len = strlen(base_path) + strlen(file) + 1;
fdir = malloc(len);
if (fdir == NULL) {
fprintf(stderr, "Cannot allocate %ld bytes for device path, device skipped.\n", len);
goto read_file_err;
}
strcpy(fdir, base_path);
strcat(fdir, file);
if (access(fdir, F_OK) == 0) {
FILE* fp = fopen(fdir, "r");
if (fp != NULL) {
fseek(fp, 0L, SEEK_END);
len = ftell(fp);
rewind(fp);
len += 1;
res = malloc(len);
if (res != NULL) {
unsigned long read_bytes = fread(res, 1, len, fp);
printf("Read %lu bytes from file %s\n", read_bytes, fdir);
} else {
fprintf(stderr, "Cannot allocate %ld bytes for %s content.\n", len, fdir);
}
fclose(fp);
} else {
fprintf(stderr, "Cannot open file %s.\n", fdir);
}
} else {
fprintf(stderr, "File %s does not exists.\n", fdir);
}
free(fdir);
fdir = NULL;
read_file_err:
return res;
}
int inline_write_file(const char* base_path, const char *file, const void* buf, size_t buf_sz) {
char* fdir = NULL;
int res = 0;
const size_t len = strlen(base_path) + strlen(file) + 1;
fdir = malloc(len);
if (fdir == NULL) {
fprintf(stderr, "Cannot allocate %ld bytes for device path, device skipped.\n", len);
goto inline_write_file_err;
}
strcpy(fdir, base_path);
strcat(fdir, file);
if (access(fdir, F_OK) == 0) {
FILE* fp = fopen(fdir, "w");
if (fp != NULL) {
res = fwrite(buf, 1, buf_sz, fp);
if (res >= buf_sz) {
printf("Written %d bytes to file %s\n", res, fdir);
} else {
fprintf(stderr, "Cannot write to %s: %d.\n", fdir, res);
}
fclose(fp);
} else {
fprintf(stderr, "Cannot open file %s.\n", fdir);
}
} else {
fprintf(stderr, "File %s does not exists.\n", fdir);
}
free(fdir);
fdir = NULL;
inline_write_file_err:
return res;
}

View file

@ -28,6 +28,7 @@
#include <sys/syscall.h>
#include <sys/socket.h>
#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <sys/un.h>
#include <linux/hidraw.h>
@ -41,12 +42,22 @@
#include <libevdev-1.0/libevdev/libevdev.h>
#include <libudev.h>
#include <zlib.h>
#define LSB_PER_RAD_S_2000_DEG_S ((double)0.001064724)
#define LSB_PER_RAD_S_2000_DEG_S_STR "0.001064724"
#define LSB_PER_16G ((double)0.004785)
#define LSB_PER_16G_STR "0.004785"
#define PREFERRED_SAMPLING_FREQ ((double)800.000000)
#define PREFERRED_SAMPLING_FREQ_STR "800.000000"
#define PREFERRED_SAMPLING_FREQ_HIGH_HZ ((double)1600.000000)
#define PREFERRED_SAMPLING_FREQ_HIGH_HZ_STR "1600.000000"
// courtesy of linux kernel
#ifndef __packed
#define __packed __attribute__((packed))
@ -54,3 +65,15 @@
// also courtesy of linux kernel
int32_t div_round_closest(int32_t x, int32_t divisor);
int64_t div_round_closest_i64(int64_t x, int64_t divisor);
int64_t min_max_clamp(int64_t value, int64_t min, int64_t max);
int64_t absolute_value(int64_t value);
ssize_t dmi_board_name(char *const buf, size_t buf_len);
char* inline_read_file(const char* base_path, const char *file);
int inline_write_file(const char* base_path, const char *file, const void* buf, size_t buf_sz);

View file

@ -2,68 +2,182 @@
#include <libconfig.h>
void init_config(controller_settings_t *const conf) {
conf->ff_gain = 100;
conf->enable_qam = 0;
conf->nintendo_layout = 0;
conf->gamepad_output_device = 1;
conf->rumble_dedicated_thread = 0;
}
int fill_config(controller_settings_t *const conf, const char* file) {
int res = 0;
void load_in_config(dev_in_settings_t *const out_conf, const char* const filepath) {
config_t cfg;
config_init(&cfg);
const int config_read_res = config_read_file(&cfg, file);
const int config_read_res = config_read_file(&cfg, filepath);
if (config_read_res != CONFIG_TRUE) {
fprintf(stderr, "Error in reading config file: %s\n", config_error_text(&cfg));
goto fill_config_err;
goto load_in_config_err;
}
int enable_qam;
if (config_lookup_bool(&cfg, "enable_qam", &enable_qam) != CONFIG_FALSE) {
conf->enable_qam = enable_qam;
out_conf->enable_qam = enable_qam;
} else {
fprintf(stderr, "enable_qam (bool) configuration not found. Default value will be used.\n");
}
int rumble_on_mode_switch;
if (config_lookup_bool(&cfg, "rumble_on_mode_switch", &rumble_on_mode_switch) != CONFIG_FALSE) {
out_conf->rumble_on_mode_switch = rumble_on_mode_switch;
} else {
fprintf(stderr, "rumble_on_mode_switch (bool) configuration not found. Default value will be used.\n");
}
int ff_gain;
if (config_lookup_int(&cfg, "ff_gain", &ff_gain) != CONFIG_FALSE) {
if (ff_gain <= 100) {
conf->ff_gain = (ff_gain == 100) ? 0xFFFF : ((int)0xFFFF / 100) * ff_gain;
if (ff_gain <= 0xFF) {
out_conf->ff_gain = (ff_gain == 0) ? 0x0000 : ((uint16_t)ff_gain << (uint16_t)8) | (uint16_t)0x00FF;
} else {
fprintf(stderr, "ff_gain (int) must be a number between 0 and 100");
fprintf(stderr, "ff_gain (int) must be a number between 0 and 255");
}
} else {
fprintf(stderr, "ff_gain (int) configuration not found. Default value will be used.\n");
}
int nintendo_layout;
if (config_lookup_bool(&cfg, "nintendo_layout", &nintendo_layout) != CONFIG_FALSE) {
conf->nintendo_layout = nintendo_layout;
int m1m2_mode;
if (config_lookup_int(&cfg, "m1m2_mode", &m1m2_mode) != CONFIG_FALSE) {
if (m1m2_mode <= 2) {
out_conf->m1m2_mode = m1m2_mode;
} else {
fprintf(stderr, "m1m2_mode (int) must be a number between 0 and 2");
}
} else {
fprintf(stderr, "nintendo_layout (bool) configuration not found. Default value will be used.\n");
fprintf(stderr, "m1m2_mode (int) configuration not found. Default value will be used.\n");
}
int gamepad_output_device;
if (config_lookup_int(&cfg, "gamepad_output_device", &gamepad_output_device) != CONFIG_FALSE) {
conf->gamepad_output_device = gamepad_output_device;
int touchbar;
if (config_lookup_bool(&cfg, "touchbar", &touchbar) != CONFIG_FALSE) {
out_conf->touchbar = touchbar;
} else {
fprintf(stderr, "gamepad_output_device (int) configuration not found. Default value will be used.\n");
fprintf(stderr, "touchbar (bool) configuration not found. Default value will be used.\n");
}
int rumble_dedicated_thread;
if (config_lookup_bool(&cfg, "rumble_dedicated_thread", &rumble_dedicated_thread) != CONFIG_FALSE) {
conf->rumble_dedicated_thread = rumble_dedicated_thread;
int enable_thermal_profiles_switching;
if (config_lookup_bool(&cfg, "enable_thermal_profiles_switching", &enable_thermal_profiles_switching) != CONFIG_FALSE) {
out_conf->enable_thermal_profiles_switching = enable_thermal_profiles_switching;
} else {
fprintf(stderr, "rumble_dedicated_thread (bool) configuration not found. Default value will be used.\n");
fprintf(stderr, "enable_thermal_profiles_switching (bool) configuration not found. Default value will be used.\n");
}
int default_thermal_profile;
if (config_lookup_int(&cfg, "default_thermal_profile", &default_thermal_profile) != CONFIG_FALSE) {
out_conf->default_thermal_profile = default_thermal_profile;
} else {
fprintf(stderr, "default_thermal_profile (int) configuration not found. Default value will be used.\n");
}
int enable_leds_commands;
if (config_lookup_bool(&cfg, "enable_leds_commands", &enable_leds_commands) != CONFIG_FALSE) {
out_conf->enable_leds_commands = enable_leds_commands;
} else {
fprintf(stderr, "enable_leds_commands (bool) configuration not found. Default value will be used.\n");
}
int enable_imu;
if (config_lookup_bool(&cfg, "enable_imu", &enable_imu) != CONFIG_FALSE) {
out_conf->enable_imu = enable_imu;
} else {
fprintf(stderr, "enable_imu (bool) configuration not found. Default value will be used.\n");
}
int imu_polling_interface;
if (config_lookup_bool(&cfg, "imu_polling_interface", &imu_polling_interface) != CONFIG_FALSE) {
out_conf->imu_polling_interface = imu_polling_interface;
} else {
fprintf(stderr, "imu_polling_interface (bool) configuration not found. Default value will be used.\n");
}
config_destroy(&cfg);
fill_config_err:
return res;
load_in_config_err:
return;
}
void load_out_config(dev_out_settings_t *const out_conf, const char* const filepath) {
config_t cfg;
config_init(&cfg);
const int config_read_res = config_read_file(&cfg, filepath);
if (config_read_res != CONFIG_TRUE) {
fprintf(stderr, "Error in reading config file: %s\n", config_error_text(&cfg));
goto load_out_config_err;
}
int nintendo_layout;
if (config_lookup_bool(&cfg, "nintendo_layout", &nintendo_layout) != CONFIG_FALSE) {
out_conf->nintendo_layout = nintendo_layout;
} else {
fprintf(stderr, "nintendo_layout (bool) configuration not found. Default value will be used.\n");
}
int default_gamepad;
if (config_lookup_int(&cfg, "default_gamepad", &default_gamepad) != CONFIG_FALSE) {
out_conf->default_gamepad = default_gamepad % 3;
} else {
fprintf(stderr, "default_gamepad (int) configuration not found. Default value will be used.\n");
}
int gamepad_leds_control;
if (config_lookup_bool(&cfg, "gamepad_leds_control", &gamepad_leds_control) != CONFIG_FALSE) {
out_conf->gamepad_leds_control = gamepad_leds_control;
} else {
fprintf(stderr, "gamepad_leds_control (bool) configuration not found. Default value will be used.\n");
}
int gamepad_rumble_control;
if (config_lookup_bool(&cfg, "gamepad_rumble_control", &gamepad_rumble_control) != CONFIG_FALSE) {
out_conf->gamepad_rumble_control = gamepad_rumble_control;
} else {
fprintf(stderr, "gamepad_rumble_control (bool) configuration not found. Default value will be used.\n");
}
int controller_bluetooth;
if (config_lookup_bool(&cfg, "controller_bluetooth", &controller_bluetooth) != CONFIG_FALSE) {
out_conf->controller_bluetooth = controller_bluetooth;
} else {
fprintf(stderr, "controller_bluetooth (bool) configuration not found. Default value will be used.\n");
}
int dualsense_edge;
if (config_lookup_bool(&cfg, "dualsense_edge", &dualsense_edge) != CONFIG_FALSE) {
out_conf->dualsense_edge = dualsense_edge;
} else {
fprintf(stderr, "dualsense_edge (bool) configuration not found. Default value will be used.\n");
}
int swap_y_z;
if (config_lookup_bool(&cfg, "swap_y_z", &swap_y_z) != CONFIG_FALSE) {
out_conf->swap_y_z = swap_y_z;
} else {
fprintf(stderr, "swap_y_z (bool) configuration not found. Default value will be used.\n");
}
int invert_x;
if (config_lookup_bool(&cfg, "invert_x", &invert_x) != CONFIG_FALSE) {
out_conf->invert_x = invert_x;
} else {
fprintf(stderr, "invert_x (bool) configuration not found. Default value will be used.\n");
}
int gyro_to_analog_activation_treshold;
if (config_lookup_int(&cfg, "gyro_to_analog_activation_treshold", &gyro_to_analog_activation_treshold) != CONFIG_FALSE) {
out_conf->gyro_to_analog_activation_treshold = gyro_to_analog_activation_treshold;
} else {
fprintf(stderr, "gyro_to_analog_activation_treshold (int) configuration not found. Default value will be used.\n");
}
int gyro_to_analog_mapping;
if (config_lookup_int(&cfg, "gyro_to_analog_mapping", &gyro_to_analog_mapping) != CONFIG_FALSE) {
out_conf->gyro_to_analog_mapping = gyro_to_analog_mapping == 0 ? 1 : gyro_to_analog_mapping;
} else {
fprintf(stderr, "gyro_to_analog_mapping (int) configuration not found. Default value will be used.\n");
}
config_destroy(&cfg);
load_out_config_err:
return;
}

View file

@ -2,22 +2,32 @@
#include "rogue_enemy.h"
typedef struct controller_settings {
typedef struct dev_in_settings {
bool enable_qam;
bool rumble_on_mode_switch;
uint16_t ff_gain;
int enable_qam;
int nintendo_layout;
uint8_t m1m2_mode;
bool touchbar;
bool enable_thermal_profiles_switching;
int default_thermal_profile;
bool enable_leds_commands;
bool enable_imu;
bool imu_polling_interface;
} dev_in_settings_t;
/**
* 0 is virtual evdev
* 1 is DualSense
* 2 is DualShock
* 3 is Xbox one
*/
int gamepad_output_device;
void load_in_config(dev_in_settings_t *const out_conf, const char* const filepath);
int rumble_dedicated_thread;
} controller_settings_t;
typedef struct dev_out_settings {
bool nintendo_layout;
uint8_t default_gamepad;
bool gamepad_leds_control;
bool gamepad_rumble_control;
bool controller_bluetooth;
bool dualsense_edge;
bool swap_y_z;
bool invert_x;
int gyro_to_analog_activation_treshold;
int gyro_to_analog_mapping;
} dev_out_settings_t;
void init_config(controller_settings_t *const conf);
int fill_config(controller_settings_t *const conf, const char* file);
void load_out_config(dev_out_settings_t *const out_conf, const char* const filepath);

View file

@ -7,18 +7,33 @@
#include "dev_out.h"
#include "settings.h"
#include "rog_ally.h"
#include "legion_go.h"
#include <sys/mman.h>
static const char* configuration_file = "/etc/ROGueENEMY/config.cfg";
controller_settings_t settings;
int main(int argc, char ** argv) {
// Lock all current and future pages from preventing of being paged to swap
const int lockall_res = mlockall( MCL_CURRENT | MCL_FUTURE );
if (lockall_res) {
fprintf(stderr, "mlockall failed: %d", lockall_res);
}
int ret = 0;
init_config(&settings);
fill_config(&settings, configuration_file);
dev_out_settings_t out_settings = {
.default_gamepad = 0,
.nintendo_layout = false,
.gamepad_leds_control = true,
.gamepad_rumble_control = true,
.controller_bluetooth = false,
.dualsense_edge = false,
.swap_y_z = false,
.invert_x = false,
.gyro_to_analog_activation_treshold = 16,
.gyro_to_analog_mapping = 4,
};
load_out_config(&out_settings, configuration_file);
// Create a signal set containing only SIGTERM
sigset_t mask;
@ -50,11 +65,49 @@ int main(int argc, char ** argv) {
}
}
},
.gamepad = GAMEPAD_DUALSENSE,
.settings = out_settings,
};
load_out_config(&dev_out_thread_data.settings, configuration_file);
// Initialize pthread attributes (default values)
struct sched_param param;
pthread_attr_t attr;
ret = pthread_attr_init(&attr);
if (ret) {
printf("init pthread attributes failed\n");
goto main_err;
}
// Set a specific stack size
ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + 8192);
if (ret) {
printf("pthread setstacksize failed\n");
goto main_err;
}
// Set scheduler policy and priority of pthread
ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if (ret) {
printf("pthread setschedpolicy failed\n");
goto main_err;
}
param.sched_priority = 80;
ret = pthread_attr_setschedparam(&attr, &param);
if (ret) {
printf("pthread setschedparam failed\n");
goto main_err;
}
// Use scheduling parameters of attr
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (ret) {
printf("pthread setinheritsched failed\n");
goto main_err;
}
pthread_t dev_out_thread;
const int dev_out_thread_creation = pthread_create(&dev_out_thread, NULL, dev_out_thread_func, (void*)(&dev_out_thread_data));
const int dev_out_thread_creation = pthread_create(&dev_out_thread, &attr, dev_out_thread_func, (void*)(&dev_out_thread_data));
if (dev_out_thread_creation != 0) {
fprintf(stderr, "Error creating dev_out thread: %d\n", dev_out_thread_creation);
ret = -1;
@ -71,7 +124,7 @@ int main(int argc, char ** argv) {
int sd=-1;
struct sockaddr_un serveraddr;
do {
sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd < 0)
{
fprintf(stderr, "socket() failed");

View file

@ -1,12 +1,7 @@
#include "virt_ds4.h"
#include "message.h"
#include <bits/types/time_t.h>
#include <linux/uhid.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <stdint.h>
#define DS4_GYRO_RES_PER_DEG_S 1024
#define DS4_ACC_RES_PER_G 8192
@ -17,6 +12,24 @@
#define DS4_OUTPUT_VALID_FLAG0_LED 0x02
#define DS4_OUTPUT_VALID_FLAG0_LED_BLINK 0x04
#define DS4_INPUT_REPORT_USB 0x01
#define DS4_INPUT_REPORT_USB_SIZE 64
#define DS4_INPUT_REPORT_BT 0x11
#define DS4_INPUT_REPORT_BT_SIZE 78
#define DS4_OUTPUT_REPORT_USB 0x05
#define DS4_OUTPUT_REPORT_USB_SIZE 32
#define DS4_OUTPUT_REPORT_BT 0x11
#define DS4_OUTPUT_REPORT_BT_SIZE 78
#define DS4_FEATURE_REPORT_CALIBRATION 0x02
#define DS4_FEATURE_REPORT_CALIBRATION_SIZE 37
#define DS4_FEATURE_REPORT_CALIBRATION_BT 0x05
#define DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE 41
#define DS4_FEATURE_REPORT_FIRMWARE_INFO 0xa3
#define DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE 49
#define DS4_FEATURE_REPORT_PAIRING_INFO 0x12
#define DS4_FEATURE_REPORT_PAIRING_INFO_SIZE 16
static const uint16_t gyro_pitch_bias = 0xfff9;
static const uint16_t gyro_yaw_bias = 0x0009;
static const uint16_t gyro_roll_bias = 0xfff9;
@ -37,6 +50,17 @@ static const uint16_t acc_z_minus = 0xe086;
static const char* path = "/dev/uhid";
/* Seed values for DualShock4 / DualSense CRC32 for different report types. */
static uint8_t PS_INPUT_CRC32_SEED = 0xA1;
static uint8_t PS_OUTPUT_CRC32_SEED = 0xA2;
static uint8_t PS_FEATURE_CRC32_SEED = 0xA3;
static uint32_t crc32_le(uint32_t crc_initial, const uint8_t *const buf, size_t len) {
return crc32(crc_initial ^ 0xffffffff, buf, len) ^ 0xffffffff;
}
static const uint8_t MAC_ADDR[] = { 0xf2, 0xa5, 0x71, 0x68, 0xaf, 0xdc };
static unsigned char rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x05, /* Usage (Gamepad), */
@ -291,6 +315,37 @@ static unsigned char rdesc[] = {
0xC0 /* End Collection */
};
static unsigned char rdesc_bt[] = {
0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x32, 0x09, 0x35,
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x04, 0x81, 0x02, 0x09, 0x39, 0x15, 0x00, 0x25,
0x07, 0x75, 0x04, 0x95, 0x01, 0x81, 0x42, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0e, 0x15, 0x00, 0x25,
0x01, 0x75, 0x01, 0x95, 0x0e, 0x81, 0x02, 0x75, 0x06, 0x95, 0x01, 0x81, 0x01, 0x05, 0x01, 0x09,
0x33, 0x09, 0x34, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02, 0x06, 0x04,
0xff, 0x85, 0x02, 0x09, 0x24, 0x95, 0x24, 0xb1, 0x02, 0x85, 0xa3, 0x09, 0x25, 0x95, 0x30, 0xb1,
0x02, 0x85, 0x05, 0x09, 0x26, 0x95, 0x28, 0xb1, 0x02, 0x85, 0x06, 0x09, 0x27, 0x95, 0x34, 0xb1,
0x02, 0x85, 0x07, 0x09, 0x28, 0x95, 0x30, 0xb1, 0x02, 0x85, 0x08, 0x09, 0x29, 0x95, 0x2f, 0xb1,
0x02, 0x85, 0x09, 0x09, 0x2a, 0x95, 0x13, 0xb1, 0x02, 0x06, 0x03, 0xff, 0x85, 0x03, 0x09, 0x21,
0x95, 0x26, 0xb1, 0x02, 0x85, 0x04, 0x09, 0x22, 0x95, 0x2e, 0xb1, 0x02, 0x85, 0xf0, 0x09, 0x47,
0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf1, 0x09, 0x48, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0xf2, 0x09, 0x49,
0x95, 0x0f, 0xb1, 0x02, 0x06, 0x00, 0xff, 0x85, 0x11, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00,
0x75, 0x08, 0x95, 0x4d, 0x81, 0x02, 0x09, 0x21, 0x91, 0x02, 0x85, 0x12, 0x09, 0x22, 0x95, 0x8d,
0x81, 0x02, 0x09, 0x23, 0x91, 0x02, 0x85, 0x13, 0x09, 0x24, 0x95, 0xcd, 0x81, 0x02, 0x09, 0x25,
0x91, 0x02, 0x85, 0x14, 0x09, 0x26, 0x96, 0x0d, 0x01, 0x81, 0x02, 0x09, 0x27, 0x91, 0x02, 0x85,
0x15, 0x09, 0x28, 0x96, 0x4d, 0x01, 0x81, 0x02, 0x09, 0x29, 0x91, 0x02, 0x85, 0x16, 0x09, 0x2a,
0x96, 0x8d, 0x01, 0x81, 0x02, 0x09, 0x2b, 0x91, 0x02, 0x85, 0x17, 0x09, 0x2c, 0x96, 0xcd, 0x01,
0x81, 0x02, 0x09, 0x2d, 0x91, 0x02, 0x85, 0x18, 0x09, 0x2e, 0x96, 0x0d, 0x02, 0x81, 0x02, 0x09,
0x2f, 0x91, 0x02, 0x85, 0x19, 0x09, 0x30, 0x96, 0x22, 0x02, 0x81, 0x02, 0x09, 0x31, 0x91, 0x02,
0x06, 0x80, 0xff, 0x85, 0x82, 0x09, 0x22, 0x95, 0x3f, 0xb1, 0x02, 0x85, 0x83, 0x09, 0x23, 0xb1,
0x02, 0x85, 0x84, 0x09, 0x24, 0xb1, 0x02, 0x85, 0x90, 0x09, 0x30, 0xb1, 0x02, 0x85, 0x91, 0x09,
0x31, 0xb1, 0x02, 0x85, 0x92, 0x09, 0x32, 0xb1, 0x02, 0x85, 0x93, 0x09, 0x33, 0xb1, 0x02, 0x85,
0x94, 0x09, 0x34, 0xb1, 0x02, 0x85, 0xa0, 0x09, 0x40, 0xb1, 0x02, 0x85, 0xa4, 0x09, 0x44, 0xb1,
0x02, 0x85, 0xa7, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xa8, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xa9, 0x09,
0x45, 0xb1, 0x02, 0x85, 0xaa, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xab, 0x09, 0x45, 0xb1, 0x02, 0x85,
0xac, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xad, 0x09, 0x45, 0xb1, 0x02, 0x85, 0xb3, 0x09, 0x45, 0xb1,
0x02, 0x85, 0xb4, 0x09, 0x46, 0xb1, 0x02, 0x85, 0xb5, 0x09, 0x47, 0xb1, 0x02, 0x85, 0xd0, 0x09,
0x40, 0xb1, 0x02, 0x85, 0xd4, 0x09, 0x44, 0xb1, 0x02, 0xc0, 0x00
};
static int uhid_write(int fd, const struct uhid_event *ev)
{
ssize_t ret;
@ -308,21 +363,25 @@ static int uhid_write(int fd, const struct uhid_event *ev)
}
}
static int create(int fd)
static int create(int fd, bool bluetooth)
{
struct uhid_event ev;
memset(&ev, 0, sizeof(ev));
ev.type = UHID_CREATE;
strcpy((char*)ev.u.create.name, "Sony Corp. DualShock 4 [CUH-ZCT2x]");
ev.u.create.rd_data = rdesc;
ev.u.create.rd_size = sizeof(rdesc);
ev.u.create.bus = BUS_USB;
ev.u.create.rd_data = bluetooth ? rdesc_bt : rdesc;
ev.u.create.rd_size = bluetooth ? sizeof(rdesc_bt) : sizeof(rdesc);
ev.u.create.bus = bluetooth ? BUS_BLUETOOTH : BUS_USB;
ev.u.create.vendor = 0x054C;
ev.u.create.product = 0x09CC;
ev.u.create.version = 0;
ev.u.create.country = 0;
sprintf((char*)ev.u.create.uniq, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
MAC_ADDR[5], MAC_ADDR[4], MAC_ADDR[3], MAC_ADDR[2], MAC_ADDR[1], MAC_ADDR[0]
);
return uhid_write(fd, &ev);
}
@ -370,15 +429,23 @@ static ds4_dpad_status_t ds4_dpad_from_gamepad(uint8_t dpad) {
return DPAD_RELEASED;
}
int virt_dualshock_init(virt_dualshock_t *const out_gamepad) {
int virt_dualshock_init(
virt_dualshock_t *const out_gamepad,
bool bluetooth,
int64_t gyro_to_analog_activation_treshold,
int64_t gyro_to_analog_mapping
) {
int ret = 0;
out_gamepad->gyro_to_analog_activation_treshold = absolute_value(gyro_to_analog_activation_treshold);
out_gamepad->gyro_to_analog_mapping = gyro_to_analog_mapping;
out_gamepad->dt_sum = 0;
out_gamepad->dt_buffer_current = 0;
memset(out_gamepad->dt_buffer, 0, sizeof(out_gamepad->dt_buffer));
out_gamepad->debug = false;
out_gamepad->empty_reports = 0;
out_gamepad->last_time = 0;
out_gamepad->bluetooth = bluetooth;
out_gamepad->fd = open(path, O_RDWR | O_CLOEXEC /* | O_NONBLOCK */);
if (out_gamepad->fd < 0) {
@ -387,7 +454,7 @@ int virt_dualshock_init(virt_dualshock_t *const out_gamepad) {
goto virt_dualshock_init_err;
}
ret = create(out_gamepad->fd);
ret = create(out_gamepad->fd, out_gamepad->bluetooth);
if (ret) {
fprintf(stderr, "Error creating uhid device: %d\n", ret);
close(out_gamepad->fd);
@ -452,57 +519,57 @@ int virt_dualshock_event(virt_dualshock_t *const gamepad, gamepad_status_t *cons
if (ev.u.output.rtype != UHID_OUTPUT_REPORT)
return 0;
/*
if (ds4->update_rumble) {
* Select classic rumble style haptics and enable it. *
common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_MOTOR;
common->motor_left = ds4->motor_left;
common->motor_right = ds4->motor_right;
ds4->update_rumble = false;
}
if (ds4->update_lightbar) {
common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED;
* Comptabile behavior with hid-sony, which used a dummy global LED to
* allow enabling/disabling the lightbar. The global LED maps to
* lightbar_enabled.
*
common->lightbar_red = ds4->lightbar_enabled ? ds4->lightbar_red : 0;
common->lightbar_green = ds4->lightbar_enabled ? ds4->lightbar_green : 0;
common->lightbar_blue = ds4->lightbar_enabled ? ds4->lightbar_blue : 0;
ds4->update_lightbar = false;
}
if (ds4->update_lightbar_blink) {
common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED_BLINK;
common->lightbar_blink_on = ds4->lightbar_blink_on;
common->lightbar_blink_off = ds4->lightbar_blink_off;
ds4->update_lightbar_blink = false;
}
*/
if (ev.u.output.size != 32) {
fprintf(stderr, "Invalid data length: got %d, expected 32\n", (int)ev.u.output.size);
if (
(!gamepad->bluetooth) && (ev.u.output.size != DS4_OUTPUT_REPORT_USB_SIZE) &&
(gamepad->bluetooth) && (ev.u.output.size != DS4_OUTPUT_REPORT_BT_SIZE)
) {
fprintf(
stderr,
"Invalid data length: got %d, expected %d\n",
(int)ev.u.output.size,
(gamepad->bluetooth) ? DS4_OUTPUT_REPORT_BT_SIZE : DS4_OUTPUT_REPORT_USB_SIZE
);
return 0;
}
// first byte is report-id which is 0x01
if (ev.u.output.data[0] != 0x05) {
fprintf(stderr, "Unrecognised report-id: %d\n", (int)ev.u.output.data[0]);
if (
(!gamepad->bluetooth) && (ev.u.output.data[0] != DS4_OUTPUT_REPORT_USB) &&
(gamepad->bluetooth) && ((ev.u.output.data[0] != DS4_OUTPUT_REPORT_BT) && (ev.u.output.data[0] < 0x10))
) {
fprintf(
stderr,
"Unrecognised report-id: got 0x%x expected 0x%x\n",
(int)ev.u.output.data[0],
(gamepad->bluetooth) ? DS4_OUTPUT_REPORT_BT : DS4_OUTPUT_REPORT_USB
);
return 0;
}
const uint8_t valid_flag0 = ev.u.output.data[1];
const uint8_t valid_flag1 = ev.u.output.data[2];
const uint8_t reserved = ev.u.output.data[3];
const uint8_t motor_right = ev.u.output.data[4];
const uint8_t motor_left = ev.u.output.data[5];
const uint8_t lightbar_red = ev.u.output.data[6];
const uint8_t lightbar_green = ev.u.output.data[7];
const uint8_t lightbar_blue = ev.u.output.data[8];
const uint8_t lightbar_blink_on = ev.u.output.data[9];
const uint8_t lightbar_blink_off = ev.u.output.data[10];
// When using bluetooth, the first byte after the reportID is uint8_t seq_tag,
// while the next one is uint8_t tag, following bytes are the same.
size_t offset = 1;
if ((gamepad->bluetooth) && (ev.u.output.data[0] > 0x10)) {
offset = 2;
} else if ((gamepad->bluetooth) && (ev.u.output.data[0] == 0x02)) {
offset = 3;
} else if ((gamepad->bluetooth) && (ev.u.output.data[0] == 0x01)) {
offset = 1;
}
const uint8_t *const common_report = &ev.u.output.data[offset];
const uint8_t valid_flag0 = common_report[0];
const uint8_t valid_flag1 = common_report[1];
const uint8_t reserved = common_report[2];
const uint8_t motor_right = common_report[3];
const uint8_t motor_left = common_report[4];
const uint8_t lightbar_red = common_report[5];
const uint8_t lightbar_green = common_report[6];
const uint8_t lightbar_blue = common_report[7];
const uint8_t lightbar_blink_on = common_report[8];
const uint8_t lightbar_blink_off = common_report[9];
if ((valid_flag0 & DS4_OUTPUT_VALID_FLAG0_LED)) {
out_device_status->leds_colors[0] = lightbar_red;
@ -543,32 +610,40 @@ int virt_dualshock_event(virt_dualshock_t *const gamepad, gamepad_status_t *cons
}
if (ev.u.get_report.rnum == 18) {
const struct uhid_event mac_addr_response = {
struct uhid_event mac_addr_response = {
.type = UHID_GET_REPORT_REPLY,
.u = {
.get_report_reply = {
.size = 16,
.size = DS4_FEATURE_REPORT_PAIRING_INFO_SIZE,
.id = ev.u.get_report.id,
.err = 0,
.data = {
0x12, 0xf2, 0xa5, 0x71, 0x68, 0xaf, 0xdc, 0x08,
DS4_FEATURE_REPORT_PAIRING_INFO,
MAC_ADDR[0], MAC_ADDR[1], MAC_ADDR[2], MAC_ADDR[3], MAC_ADDR[4], MAC_ADDR[5],
0x08,
0x25, 0x00, 0x4c, 0x46, 0x49, 0x0e, 0x41, 0x00
}
}
}
};
if (gamepad->bluetooth) {
uint32_t crc = crc32_le(0xFFFFFFFFU, (const uint8_t*)&PS_FEATURE_CRC32_SEED, sizeof(PS_FEATURE_CRC32_SEED));
crc = ~crc32_le(crc, (const Bytef *)&mac_addr_response.u.get_report_reply.data[0], mac_addr_response.u.get_report_reply.size - 4);
memcpy(&mac_addr_response.u.get_report_reply.data[mac_addr_response.u.get_report_reply.size - sizeof(crc)], &crc, sizeof(crc));
}
uhid_write(gamepad->fd, &mac_addr_response);
} else if (ev.u.get_report.rnum == 0xa3) {
const struct uhid_event firmware_info_response = {
} else if (ev.u.get_report.rnum == DS4_FEATURE_REPORT_FIRMWARE_INFO) {
struct uhid_event firmware_info_response = {
.type = UHID_GET_REPORT_REPLY,
.u = {
.get_report_reply = {
.size = 49,
.size = DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE,
.id = ev.u.get_report.id,
.err = 0,
.data = {
0xa3, 0x53, 0x65, 0x70, 0x20, 0x32, 0x31, 0x20,
DS4_FEATURE_REPORT_FIRMWARE_INFO, 0x53, 0x65, 0x70, 0x20, 0x32, 0x31, 0x20,
0x32, 0x30, 0x31, 0x38, 0x00, 0x00, 0x00, 0x00,
0x00, 0x30, 0x34, 0x3a, 0x35, 0x30, 0x3a, 0x35,
0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@ -580,22 +655,32 @@ int virt_dualshock_event(virt_dualshock_t *const gamepad, gamepad_status_t *cons
}
};
if (gamepad->bluetooth) {
uint32_t crc = crc32_le(0xFFFFFFFFU, (const uint8_t*)&PS_FEATURE_CRC32_SEED, sizeof(PS_FEATURE_CRC32_SEED));
crc = ~crc32_le(crc, (const Bytef *)&firmware_info_response.u.get_report_reply.data[0], firmware_info_response.u.get_report_reply.size - 4);
memcpy(&firmware_info_response.u.get_report_reply.data[firmware_info_response.u.get_report_reply.size - sizeof(crc)], &crc, sizeof(crc));
}
uhid_write(gamepad->fd, &firmware_info_response);
} else if (ev.u.get_report.rnum == 0x02) { // dualshock4_get_calibration_data
struct uhid_event firmware_info_response = {
} else if (
((gamepad->bluetooth) && (ev.u.get_report.rnum == DS4_FEATURE_REPORT_CALIBRATION_BT)) ||
((!gamepad->bluetooth) && (ev.u.get_report.rnum == DS4_FEATURE_REPORT_CALIBRATION))
) { // dualshock4_get_calibration_data
struct uhid_event calibration_response = {
.type = UHID_GET_REPORT_REPLY,
.u = {
.get_report_reply = {
.size = 37,
.size = gamepad->bluetooth ? DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE : DS4_FEATURE_REPORT_CALIBRATION_SIZE,
.id = ev.u.get_report.id,
.err = 0,
.data = {
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
gamepad->bluetooth ? DS4_FEATURE_REPORT_CALIBRATION_BT : DS4_FEATURE_REPORT_CALIBRATION,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06, 0x00,
0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
0x00,
}
}
}
@ -605,25 +690,31 @@ int virt_dualshock_event(virt_dualshock_t *const gamepad, gamepad_status_t *cons
// speed_2x = speed_2x*DS4_GYRO_RES_PER_DEG_S; calculated by the kernel will be 1080.
// As a consequence sens_numer (for every axis) is 1080*1024.
// that number will be 1105920
memcpy((void*)&firmware_info_response.u.get_report_reply.data[1], (const void*)&gyro_pitch_bias, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[3], (const void*)&gyro_yaw_bias, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[5], (const void*)&gyro_roll_bias, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[7], (const void*)&gyro_pitch_plus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[9], (const void*)&gyro_pitch_minus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[11], (const void*)&gyro_yaw_plus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[13], (const void*)&gyro_yaw_minus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[15], (const void*)&gyro_roll_plus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[17], (const void*)&gyro_roll_minus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[19], (const void*)&gyro_speed_plus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[21], (const void*)&gyro_speed_minus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[23], (const void*)&acc_x_plus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[25], (const void*)&acc_x_minus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[27], (const void*)&acc_y_plus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[29], (const void*)&acc_y_minus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[31], (const void*)&acc_z_plus, sizeof(int16_t));
memcpy((void*)&firmware_info_response.u.get_report_reply.data[33], (const void*)&acc_z_minus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[1], (const void*)&gyro_pitch_bias, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[3], (const void*)&gyro_yaw_bias, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[5], (const void*)&gyro_roll_bias, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[7], (const void*)&gyro_pitch_plus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[9], (const void*)&gyro_pitch_minus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[11], (const void*)&gyro_yaw_plus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[13], (const void*)&gyro_yaw_minus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[15], (const void*)&gyro_roll_plus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[17], (const void*)&gyro_roll_minus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[19], (const void*)&gyro_speed_plus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[21], (const void*)&gyro_speed_minus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[23], (const void*)&acc_x_plus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[25], (const void*)&acc_x_minus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[27], (const void*)&acc_y_plus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[29], (const void*)&acc_y_minus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[31], (const void*)&acc_z_plus, sizeof(int16_t));
memcpy((void*)&calibration_response.u.get_report_reply.data[33], (const void*)&acc_z_minus, sizeof(int16_t));
uhid_write(gamepad->fd, &firmware_info_response);
if (gamepad->bluetooth) {
uint32_t crc = crc32_le(0xFFFFFFFFU, (const uint8_t*)&PS_FEATURE_CRC32_SEED, sizeof(PS_FEATURE_CRC32_SEED));
crc = ~crc32_le(crc, (const Bytef *)&calibration_response.u.get_report_reply.data[0], calibration_response.u.get_report_reply.size - 4);
memcpy(&calibration_response.u.get_report_reply.data[calibration_response.u.get_report_reply.size - sizeof(crc)], &crc, sizeof(crc));
}
uhid_write(gamepad->fd, &calibration_response);
}
break;
@ -642,9 +733,9 @@ void virt_dualshock_close(virt_dualshock_t *const out_gamepad) {
* This function arranges HID packets as described on https://www.psdevwiki.com/ps4/DS4-USB
*/
void virt_dualshock_compose(virt_dualshock_t *const gamepad, gamepad_status_t *const in_device_status, uint8_t *const out_buf) {
const int64_t time_us = in_device_status->last_gyro_motion_time.tv_sec * 1000000 + in_device_status->last_gyro_motion_time.tv_usec;
const int64_t time_us = in_device_status->last_gyro_motion_timestamp_ns / (int64_t)1000;
const int delta_time = time_us - gamepad->last_time;
const int64_t delta_time = time_us - gamepad->last_time;
gamepad->last_time = time_us;
// find the average Δt in the last 30 non-zero inputs;
@ -695,78 +786,82 @@ void virt_dualshock_compose(virt_dualshock_t *const gamepad, gamepad_status_t *c
*
* as we know sens_numer is 0, hence calib_data is zero.
*/
/*
const int16_t g_x = ((in_device_status->gyro[0]) * ((double)(180.0)/(double)(M_PI))) / (double)DS4_GYRO_RES_PER_DEG_S;
const int16_t g_y = ((in_device_status->gyro[1]) * ((double)(180.0)/(double)(M_PI))) / (double)DS4_GYRO_RES_PER_DEG_S;
const int16_t g_z = ((in_device_status->gyro[2]) * ((double)(180.0)/(double)(M_PI))) / (double)DS4_GYRO_RES_PER_DEG_S;
const int16_t a_x = ((in_device_status->accel[0]) / ((double)9.8)) / (double)DS4_ACC_RES_PER_G; // TODO: IDK how to test...
const int16_t a_y = ((in_device_status->accel[1]) / ((double)9.8)) / (double)DS4_ACC_RES_PER_G; // TODO: IDK how to test...
const int16_t a_z = ((in_device_status->accel[2]) / ((double)9.8)) / (double)DS4_ACC_RES_PER_G; // TODO: IDK how to test...
*/
/*
const int16_t g_x = (in_device_status->gyro[0]) / LSB_PER_RAD_S_2000_DEG_S;
const int16_t g_y = (in_device_status->gyro[1]) / LSB_PER_RAD_S_2000_DEG_S;
const int16_t g_z = (in_device_status->gyro[2]) / LSB_PER_RAD_S_2000_DEG_S;
const int16_t a_x = (in_device_status->accel[0]) / LSB_PER_16G; // TODO: IDK how to test...
const int16_t a_y = (in_device_status->accel[1]) / LSB_PER_16G; // TODO: IDK how to test...
const int16_t a_z = (in_device_status->accel[2]) / LSB_PER_16G; // TODO: IDK how to test...
*/
const int16_t g_x = in_device_status->raw_gyro[0];
const int16_t g_y = (int16_t)(-1) * in_device_status->raw_gyro[1]; // Swap Y and Z
const int16_t g_z = (int16_t)(-1) * in_device_status->raw_gyro[2]; // Swap Y and Z
const int16_t g_y = in_device_status->raw_gyro[1];
const int16_t g_z = in_device_status->raw_gyro[2];
const int16_t a_x = in_device_status->raw_accel[0];
const int16_t a_y = (int16_t)(-1) * in_device_status->raw_accel[1]; // Swap Y and Z
const int16_t a_z = (int16_t)(-1) * in_device_status->raw_accel[2]; // Swap Y and Z
const int16_t a_y = in_device_status->raw_accel[1];
const int16_t a_z = in_device_status->raw_accel[2];
out_buf[0] = 0x01; // [00] report ID (0x01)
out_buf[1] = ((uint64_t)((int64_t)in_device_status->joystick_positions[0][0] + (int64_t)32768) >> (uint64_t)8); // L stick, X axis
out_buf[2] = ((uint64_t)((int64_t)in_device_status->joystick_positions[0][1] + (int64_t)32768) >> (uint64_t)8); // L stick, Y axis
out_buf[3] = ((uint64_t)((int64_t)in_device_status->joystick_positions[1][0] + (int64_t)32768) >> (uint64_t)8); // R stick, X axis
out_buf[4] = ((uint64_t)((int64_t)in_device_status->joystick_positions[1][1] + (int64_t)32768) >> (uint64_t)8); // R stick, Y axis
out_buf[5] =
const int64_t contrib_x = ((int64_t)g_y / (int64_t)gamepad->gyro_to_analog_mapping);
const int64_t contrib_y = ((int64_t)g_x / (int64_t)gamepad->gyro_to_analog_mapping);
out_buf[0] = gamepad->bluetooth ? DS4_INPUT_REPORT_BT : DS4_INPUT_REPORT_USB; // [00] report ID (0x01)
uint8_t *const out_shifted_buf = gamepad->bluetooth ? &out_buf[2] : &out_buf[0];
out_shifted_buf[1] = ((uint64_t)((int64_t)in_device_status->joystick_positions[0][0] + (int64_t)32768) >> (uint64_t)8); // L stick, X axis
out_shifted_buf[2] = ((uint64_t)((int64_t)in_device_status->joystick_positions[0][1] + (int64_t)32768) >> (uint64_t)8); // L stick, Y axis
out_shifted_buf[3] = ((uint64_t)((int64_t)in_device_status->joystick_positions[1][0] + (int64_t)32768) >> (uint64_t)8); // R stick, X axis
out_shifted_buf[4] = ((uint64_t)((int64_t)in_device_status->joystick_positions[1][1] + (int64_t)32768) >> (uint64_t)8); // R stick, Y axis
out_shifted_buf[5] =
(in_device_status->triangle ? 0x80 : 0x00) |
(in_device_status->circle ? 0x40 : 0x00) |
(in_device_status->cross ? 0x20 : 0x00) |
(in_device_status->square ? 0x10 : 0x00) |
(uint8_t)ds4_dpad_from_gamepad(in_device_status->dpad);
out_buf[6] =
out_shifted_buf[6] =
(in_device_status->r3 ? 0x80 : 0x00) |
(in_device_status->l3 ? 0x40 : 0x00) |
(in_device_status->share ? 0x20 : 0x00) |
(in_device_status->option ? 0x10 : 0x00) |
(in_device_status->r2_trigger > 200 ? 0x08 : 0x00) |
(in_device_status->l2_trigger > 200 ? 0x04 : 0x00) |
/*(in_device_status->r2_trigger > 200 ? 0x08 : 0x00)*/ 0x00 |
/*(in_device_status->l2_trigger > 200 ? 0x04 : 0x00)*/ 0x00 |
(in_device_status->r1 ? 0x02 : 0x00) |
(in_device_status->l1 ? 0x01 : 0x00);
/*
static uint8_t counter = 0;
buf[7] = (((counter++) % (uint8_t)64) << ((uint8_t)2)) | get_buttons_byte3_by_gs(&gs);
*/
if (in_device_status->join_left_analog_and_gyroscope) {
if (absolute_value(contrib_x) > gamepad->gyro_to_analog_activation_treshold) {
out_shifted_buf[1] = min_max_clamp((int64_t)127 + (((int64_t)out_shifted_buf[1] - (int64_t)127) + contrib_x), 0, 255);
}
if (absolute_value(contrib_y) >= gamepad->gyro_to_analog_activation_treshold) {
out_shifted_buf[2] = min_max_clamp((int64_t)127 + (((int64_t)out_shifted_buf[2] - (int64_t)127) + contrib_y), 0, 255);
}
}
if (in_device_status->join_right_analog_and_gyroscope) {
if (absolute_value(contrib_x) >= gamepad->gyro_to_analog_activation_treshold) {
out_shifted_buf[3] = min_max_clamp((int64_t)127 + (((int64_t)out_shifted_buf[3] - (int64_t)127) + contrib_x), 0, 255);
}
if (absolute_value(contrib_y) >= gamepad->gyro_to_analog_activation_treshold) {
out_shifted_buf[4] = min_max_clamp((int64_t)127 + (((int64_t)out_shifted_buf[4] - (int64_t)127) + contrib_y), 0, 255);
}
}
out_buf[7] = in_device_status->center ? 0x01 : 0x00;
out_shifted_buf[7] = in_device_status->center ? 0x01 : 0x00;
out_buf[8] = in_device_status->l2_trigger;
out_buf[9] = in_device_status->r2_trigger;
memcpy(&out_buf[10], &timestamp, sizeof(timestamp));
out_buf[12] = 0x20; // [12] battery level | this is called sensor_temparature in the kernel driver but is never used...
memcpy(&out_buf[13], &g_x, sizeof(int16_t));
memcpy(&out_buf[15], &g_y, sizeof(int16_t));
memcpy(&out_buf[17], &g_z, sizeof(int16_t));
memcpy(&out_buf[19], &a_x, sizeof(int16_t));
memcpy(&out_buf[21], &a_y, sizeof(int16_t));
memcpy(&out_buf[23], &a_z, sizeof(int16_t));
out_shifted_buf[8] = in_device_status->l2_trigger;
out_shifted_buf[9] = in_device_status->r2_trigger;
memcpy(&out_shifted_buf[10], &timestamp, sizeof(timestamp));
out_shifted_buf[12] = 0x20; // [12] battery level | this is called sensor_temparature in the kernel driver but is never used...
memcpy(&out_shifted_buf[13], &g_x, sizeof(int16_t));
memcpy(&out_shifted_buf[15], &g_y, sizeof(int16_t));
memcpy(&out_shifted_buf[17], &g_z, sizeof(int16_t));
memcpy(&out_shifted_buf[19], &a_x, sizeof(int16_t));
memcpy(&out_shifted_buf[21], &a_y, sizeof(int16_t));
memcpy(&out_shifted_buf[23], &a_z, sizeof(int16_t));
out_buf[30] = 0x1b; // no headset attached
out_shifted_buf[30] = 0x1b; // no headset attached
out_buf[62] = 0x80; // IDK... it seems constant...
out_buf[57] = 0x80; // IDK... it seems constant...
out_buf[53] = 0x80; // IDK... it seems constant...
out_buf[48] = 0x80; // IDK... it seems constant...
out_buf[35] = 0x80; // IDK... it seems constant...
out_buf[44] = 0x80; // IDK... it seems constant...
out_shifted_buf[62] = 0x80; // IDK... it seems constant...
out_shifted_buf[57] = 0x80; // IDK... it seems constant...
out_shifted_buf[53] = 0x80; // IDK... it seems constant...
out_shifted_buf[48] = 0x80; // IDK... it seems constant...
out_shifted_buf[35] = 0x80; // IDK... it seems constant...
out_shifted_buf[44] = 0x80; // IDK... it seems constant...
}
int virt_dualshock_send(virt_dualshock_t *const gamepad, uint8_t *const out_buf) {
@ -774,12 +869,18 @@ int virt_dualshock_send(virt_dualshock_t *const gamepad, uint8_t *const out_buf)
.type = UHID_INPUT2,
.u = {
.input2 = {
.size = 64,
.size = gamepad->bluetooth ? DS4_INPUT_REPORT_BT_SIZE : DS4_INPUT_REPORT_USB_SIZE,
}
}
};
memcpy(&l.u.input2.data[0], &out_buf[0], l.u.input2.size);
if (gamepad->bluetooth) {
uint32_t crc = crc32_le(0xFFFFFFFFU, (const uint8_t*)&PS_INPUT_CRC32_SEED, sizeof(PS_INPUT_CRC32_SEED));
crc = ~crc32_le(crc, (const uint8_t *)&l.u.input2.data[0], l.u.input2.size - 4);
memcpy(&l.u.input2.data[l.u.input2.size - sizeof(crc)], &crc, sizeof(crc));
}
return uhid_write(gamepad->fd, &l);
}

View file

@ -13,22 +13,46 @@ typedef struct virt_dualshock {
bool debug;
bool bluetooth;
uint32_t dt_sum;
uint8_t dt_buffer_current;
uint32_t dt_buffer[30];
uint32_t empty_reports;
uint64_t last_time;
int64_t last_time;
int64_t gyro_to_analog_activation_treshold;
int64_t gyro_to_analog_mapping;
} virt_dualshock_t;
int virt_dualshock_init(virt_dualshock_t *const gamepad);
int virt_dualshock_init(
virt_dualshock_t *const gamepad,
bool bluetooth,
int64_t gyro_to_analog_activation_treshold,
int64_t gyro_to_analog_mapping
);
int virt_dualshock_get_fd(virt_dualshock_t *const gamepad);
int virt_dualshock_get_fd(
virt_dualshock_t *const gamepad
);
int virt_dualshock_event(virt_dualshock_t *const gamepad, gamepad_status_t *const out_device_status);
int virt_dualshock_event(
virt_dualshock_t *const gamepad,
gamepad_status_t *const out_device_status
);
void virt_dualshock_compose(virt_dualshock_t *const gamepad, gamepad_status_t *const in_device_status, uint8_t *const out_buf);
void virt_dualshock_compose(
virt_dualshock_t *const gamepad,
gamepad_status_t *const in_device_status,
uint8_t *const out_buf
);
int virt_dualshock_send(virt_dualshock_t *const gamepad, uint8_t *const out_buf);
int virt_dualshock_send(
virt_dualshock_t *const gamepad,
uint8_t *const out_buf
);
void virt_dualshock_close(virt_dualshock_t *const gamepad);
void virt_dualshock_close(
virt_dualshock_t *const gamepad
);

1330
virt_ds5.c

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,10 @@ typedef struct virt_dualsense {
bool debug;
bool bluetooth;
bool edge_model;
uint8_t seq_num;
uint32_t dt_sum;
@ -20,18 +24,41 @@ typedef struct virt_dualsense {
uint32_t dt_buffer[30];
uint32_t empty_reports;
uint64_t last_time;
int64_t last_time;
int64_t gyro_to_analog_activation_treshold;
int64_t gyro_to_analog_mapping;
} virt_dualsense_t;
int virt_dualsense_init(virt_dualsense_t *const gamepad);
int virt_dualsense_init(
virt_dualsense_t *const gamepad,
bool bluetooth,
bool dualsense_edge,
int64_t gyro_to_analog_activation_treshold,
int64_t gyro_to_analog_mapping
);
int virt_dualsense_get_fd(virt_dualsense_t *const gamepad);
int virt_dualsense_get_fd(
virt_dualsense_t *const gamepad
);
int virt_dualsense_event(virt_dualsense_t *const gamepad, gamepad_status_t *const out_device_status);
int virt_dualsense_event(
virt_dualsense_t *const gamepad,
gamepad_status_t *const out_device_status
);
void virt_dualsense_compose(virt_dualsense_t *const gamepad, gamepad_status_t *const in_device_status, uint8_t *const out_buf);
void virt_dualsense_compose(
virt_dualsense_t *const gamepad,
gamepad_status_t *const in_device_status,
uint8_t *const out_buf
);
int virt_dualsense_send(virt_dualsense_t *const gamepad, uint8_t *const out_buf);
int virt_dualsense_send(
virt_dualsense_t *const gamepad,
uint8_t *const out_buf
);
void virt_dualsense_close(virt_dualsense_t *const gamepad);
void virt_dualsense_close(
virt_dualsense_t *const gamepad
);

411
virt_kbd.c Normal file
View file

@ -0,0 +1,411 @@
#include "virt_kbd.h"
#include "message.h"
#include <linux/input-event-codes.h>
int virt_kbd_init(virt_kbd_t *const kbd) {
int ret = -EINVAL;
memset(kbd, 0, sizeof(virt_kbd_t));
int fd = open("/dev/uinput", O_RDWR);
if(fd < 0) {
ret = errno;
goto virt_mouse_init_err;
}
ioctl(fd, UI_SET_EVBIT, EV_REL);
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_EVBIT, EV_MSC);
ioctl(fd, UI_SET_EVBIT, EV_SYN);
ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP);
ioctl(fd, UI_SET_KEYBIT, KEY_Q);
ioctl(fd, UI_SET_KEYBIT, KEY_W);
ioctl(fd, UI_SET_KEYBIT, KEY_E);
ioctl(fd, UI_SET_KEYBIT, KEY_R);
ioctl(fd, UI_SET_KEYBIT, KEY_T);
ioctl(fd, UI_SET_KEYBIT, KEY_Y);
ioctl(fd, UI_SET_KEYBIT, KEY_U);
ioctl(fd, UI_SET_KEYBIT, KEY_I);
ioctl(fd, UI_SET_KEYBIT, KEY_O);
ioctl(fd, UI_SET_KEYBIT, KEY_P);
ioctl(fd, UI_SET_KEYBIT, KEY_A);
ioctl(fd, UI_SET_KEYBIT, KEY_S);
ioctl(fd, UI_SET_KEYBIT, KEY_D);
ioctl(fd, UI_SET_KEYBIT, KEY_F);
ioctl(fd, UI_SET_KEYBIT, KEY_G);
ioctl(fd, UI_SET_KEYBIT, KEY_H);
ioctl(fd, UI_SET_KEYBIT, KEY_J);
ioctl(fd, UI_SET_KEYBIT, KEY_K);
ioctl(fd, UI_SET_KEYBIT, KEY_L);
ioctl(fd, UI_SET_KEYBIT, KEY_Z);
ioctl(fd, UI_SET_KEYBIT, KEY_X);
ioctl(fd, UI_SET_KEYBIT, KEY_C);
ioctl(fd, UI_SET_KEYBIT, KEY_V);
ioctl(fd, UI_SET_KEYBIT, KEY_B);
ioctl(fd, UI_SET_KEYBIT, KEY_N);
ioctl(fd, UI_SET_KEYBIT, KEY_M);
ioctl(fd, UI_SET_KEYBIT, KEY_0);
ioctl(fd, UI_SET_KEYBIT, KEY_1);
ioctl(fd, UI_SET_KEYBIT, KEY_2);
ioctl(fd, UI_SET_KEYBIT, KEY_3);
ioctl(fd, UI_SET_KEYBIT, KEY_4);
ioctl(fd, UI_SET_KEYBIT, KEY_5);
ioctl(fd, UI_SET_KEYBIT, KEY_6);
ioctl(fd, UI_SET_KEYBIT, KEY_7);
ioctl(fd, UI_SET_KEYBIT, KEY_8);
ioctl(fd, UI_SET_KEYBIT, KEY_9);
ioctl(fd, UI_SET_KEYBIT, KEY_UP);
ioctl(fd, UI_SET_KEYBIT, KEY_DOWN);
ioctl(fd, UI_SET_KEYBIT, KEY_LEFT);
ioctl(fd, UI_SET_KEYBIT, KEY_RIGHT);
//ioctl(fd, UI_SET_KEYBIT, KEY_);
struct uinput_setup dev = {0};
strncpy(dev.name, VIRT_KBD_DEV_NAME, UINPUT_MAX_NAME_SIZE-1);
dev.id.bustype = BUS_VIRTUAL;
dev.id.vendor = VIRT_KBD_DEV_VENDOR_ID;
dev.id.product = VIRT_KBD_DEV_PRODUCT_ID;
dev.id.version = VIRT_KBD_DEV_VERSION;
if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) {
ret = errno > 0 ? errno : -1 * errno;
ret = ret == 0 ? -EIO : ret;
goto virt_mouse_init_err;
}
if(ioctl(fd, UI_DEV_CREATE) < 0) {
ret = errno > 0 ? errno : -1 * errno;
ret = ret == 0 ? -EIO : ret;
goto virt_mouse_init_err;
}
// initialization ok
kbd->fd = fd;
ret = 0;
virt_mouse_init_err:
if (ret != 0) {
kbd->fd = -1;
close(fd);
}
return ret;
}
int virt_kbd_get_fd(virt_kbd_t *const kbd) {
return kbd->fd;
}
int virt_kbd_send(virt_kbd_t *const kbd, keyboard_status_t *const status, struct timeval *const now) {
int res = 0;
size_t events_count = 0;
struct input_event events[12];
struct input_event tmp_ev;
tmp_ev.type = EV_KEY;
if (status->q != kbd->prev_q) {
tmp_ev.code = KEY_Q;
tmp_ev.value = kbd->prev_q = status->q;
events[events_count++] = tmp_ev;
}
if (status->w != kbd->prev_w) {
tmp_ev.code = KEY_W;
tmp_ev.value = kbd->prev_w = status->w;
events[events_count++] = tmp_ev;
}
if (status->e != kbd->prev_e) {
tmp_ev.code = KEY_E;
tmp_ev.value = kbd->prev_e = status->e;
events[events_count++] = tmp_ev;
}
if (status->r != kbd->prev_r) {
tmp_ev.code = KEY_R;
tmp_ev.value = kbd->prev_r = status->r;
events[events_count++] = tmp_ev;
}
if (status->t != kbd->prev_t) {
tmp_ev.code = KEY_T;
tmp_ev.value = kbd->prev_t = status->t;
events[events_count++] = tmp_ev;
}
if (status->y != kbd->prev_y) {
tmp_ev.code = KEY_Y;
tmp_ev.value = kbd->prev_y = status->y;
events[events_count++] = tmp_ev;
}
if (status->u != kbd->prev_u) {
tmp_ev.code = KEY_U;
tmp_ev.value = kbd->prev_u = status->u;
events[events_count++] = tmp_ev;
}
if (status->i != kbd->prev_i) {
tmp_ev.code = KEY_I;
tmp_ev.value = kbd->prev_i = status->i;
events[events_count++] = tmp_ev;
}
if (status->o != kbd->prev_o) {
tmp_ev.code = KEY_O;
tmp_ev.value = kbd->prev_o = status->o;
events[events_count++] = tmp_ev;
}
if (status->p != kbd->prev_p) {
tmp_ev.code = KEY_P;
tmp_ev.value = kbd->prev_p = status->p;
events[events_count++] = tmp_ev;
}
if (status->a != kbd->prev_a) {
tmp_ev.code = KEY_A;
tmp_ev.value = kbd->prev_a = status->a;
events[events_count++] = tmp_ev;
}
if (status->s != kbd->prev_s) {
tmp_ev.code = KEY_S;
tmp_ev.value = kbd->prev_s = status->s;
events[events_count++] = tmp_ev;
}
if (status->d != kbd->prev_d) {
tmp_ev.code = KEY_D;
tmp_ev.value = kbd->prev_d = status->d;
events[events_count++] = tmp_ev;
}
if (status->f != kbd->prev_f) {
tmp_ev.code = KEY_F;
tmp_ev.value = kbd->prev_f = status->f;
events[events_count++] = tmp_ev;
}
if (status->g != kbd->prev_g) {
tmp_ev.code = KEY_G;
tmp_ev.value = kbd->prev_g = status->g;
events[events_count++] = tmp_ev;
}
if (status->h != kbd->prev_h) {
tmp_ev.code = KEY_H;
tmp_ev.value = kbd->prev_h = status->h;
events[events_count++] = tmp_ev;
}
if (status->j != kbd->prev_j) {
tmp_ev.code = KEY_J;
tmp_ev.value = kbd->prev_j = status->j;
events[events_count++] = tmp_ev;
}
if (status->k != kbd->prev_k) {
tmp_ev.code = KEY_K;
tmp_ev.value = kbd->prev_k = status->k;
events[events_count++] = tmp_ev;
}
if (status->l != kbd->prev_l) {
tmp_ev.code = KEY_L;
tmp_ev.value = kbd->prev_l = status->l;
events[events_count++] = tmp_ev;
}
if (status->z != kbd->prev_z) {
tmp_ev.code = KEY_Z;
tmp_ev.value = kbd->prev_z = status->z;
events[events_count++] = tmp_ev;
}
if (status->x != kbd->prev_x) {
tmp_ev.code = KEY_X;
tmp_ev.value = kbd->prev_x = status->x;
events[events_count++] = tmp_ev;
}
if (status->c != kbd->prev_c) {
tmp_ev.code = KEY_C;
tmp_ev.value = kbd->prev_c = status->c;
events[events_count++] = tmp_ev;
}
if (status->v != kbd->prev_v) {
tmp_ev.code = KEY_V;
tmp_ev.value = kbd->prev_v = status->v;
events[events_count++] = tmp_ev;
}
if (status->b != kbd->prev_b) {
tmp_ev.code = KEY_B;
tmp_ev.value = kbd->prev_b = status->b;
events[events_count++] = tmp_ev;
}
if (status->n != kbd->prev_n) {
tmp_ev.code = KEY_N;
tmp_ev.value = kbd->prev_n = status->n;
events[events_count++] = tmp_ev;
}
if (status->m != kbd->prev_m) {
tmp_ev.code = KEY_M;
tmp_ev.value = kbd->prev_m = status->m;
events[events_count++] = tmp_ev;
}
if (status->num_0 != kbd->prev_num_0) {
tmp_ev.code = KEY_0;
tmp_ev.value = kbd->prev_num_0 = status->num_0;
events[events_count++] = tmp_ev;
}
if (status->num_1 != kbd->prev_num_1) {
tmp_ev.code = KEY_1;
tmp_ev.value = kbd->prev_num_1 = status->num_1;
events[events_count++] = tmp_ev;
}
if (status->num_2 != kbd->prev_num_2) {
tmp_ev.code = KEY_2;
tmp_ev.value = kbd->prev_num_2 = status->num_2;
events[events_count++] = tmp_ev;
}
if (status->num_3 != kbd->prev_num_3) {
tmp_ev.code = KEY_3;
tmp_ev.value = kbd->prev_num_3 = status->num_3;
events[events_count++] = tmp_ev;
}
if (status->num_4 != kbd->prev_num_4) {
tmp_ev.code = KEY_4;
tmp_ev.value = kbd->prev_num_4 = status->num_4;
events[events_count++] = tmp_ev;
}
if (status->num_5 != kbd->prev_num_5) {
tmp_ev.code = KEY_5;
tmp_ev.value = kbd->prev_num_5 = status->num_5;
events[events_count++] = tmp_ev;
}
if (status->num_6 != kbd->prev_num_6) {
tmp_ev.code = KEY_6;
tmp_ev.value = kbd->prev_num_6 = status->num_6;
events[events_count++] = tmp_ev;
}
if (status->num_7 != kbd->prev_num_7) {
tmp_ev.code = KEY_7;
tmp_ev.value = kbd->prev_num_7 = status->num_7;
events[events_count++] = tmp_ev;
}
if (status->num_8 != kbd->prev_num_8) {
tmp_ev.code = KEY_8;
tmp_ev.value = kbd->prev_num_8 = status->num_8;
events[events_count++] = tmp_ev;
}
if (status->num_9 != kbd->prev_num_9) {
tmp_ev.code = KEY_9;
tmp_ev.value = kbd->prev_num_9 = status->num_9;
events[events_count++] = tmp_ev;
}
if (status->lctrl != kbd->prev_lctrl) {
tmp_ev.code = KEY_LEFTCTRL;
tmp_ev.value = kbd->prev_lctrl = status->lctrl;
events[events_count++] = tmp_ev;
}
if (status->up != kbd->prev_up) {
tmp_ev.code = KEY_UP;
tmp_ev.value = kbd->prev_up = status->up;
events[events_count++] = tmp_ev;
}
if (status->down != kbd->prev_down) {
tmp_ev.code = KEY_DOWN;
tmp_ev.value = kbd->prev_down = status->down;
events[events_count++] = tmp_ev;
}
if (status->left != kbd->prev_left) {
tmp_ev.code = KEY_LEFT;
tmp_ev.value = kbd->prev_left = status->left;
events[events_count++] = tmp_ev;
}
if (status->right != kbd->prev_right) {
tmp_ev.code = KEY_RIGHT;
tmp_ev.value = kbd->prev_right = status->right;
if (write(kbd->fd, &tmp_ev, sizeof(tmp_ev)) != sizeof(struct input_event)) {
res = errno < 0 ? errno : -1 * errno;
goto virt_kbd_send_err;
}
}
#if 0
const struct input_event timestamp_ev = {
.code = MSC_TIMESTAMP,
.type = EV_MSC,
.value = (now.tv_sec - secAtInit)*1000000 + (now.tv_usec - usecAtInit),
.time = now,
};
const ssize_t timestamp_written = write(fd, (void*)&timestamp_ev, sizeof(timestamp_ev));
if (timestamp_written != sizeof(timestamp_ev)) {
fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", timestamp_written, sizeof(timestamp_ev));
}
#endif
if (events_count > 0) {
struct timeval t;
if (now != NULL) {
t = *now;
} else {
gettimeofday(&t, NULL);
}
struct input_event syn_ev = {
.code = SYN_REPORT,
.type = EV_SYN,
.value = 0,
.time = t,
};
syn_ev.time.tv_usec += 1;
events[events_count++] = syn_ev;
for (size_t i = 0; i < events_count; ++i) {
if (i != (events_count - 1)) {
events[i].time = t;
}
const ssize_t sync_written = write(kbd->fd, (void*)&events[i], sizeof(struct input_event));
if (sync_written != sizeof(syn_ev)) {
fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", sync_written, sizeof(syn_ev));
res = errno;
res = res < 0 ? res : -1*res;
res = res == 0 ? -EIO : res;
goto virt_kbd_send_err;
}
}
}
virt_kbd_send_err:
return res;
}
void virt_kbd_close(virt_kbd_t *const kbd) {
close(kbd->fd);
}

67
virt_kbd.h Normal file
View file

@ -0,0 +1,67 @@
#pragma once
#include "message.h"
#include "devices_status.h"
#define VIRT_KBD_DEV_NAME "ROGueENEMY - kbd"
#define VIRT_KBD_DEV_VENDOR_ID 0x108c
#define VIRT_KBD_DEV_PRODUCT_ID 0x0323
#define VIRT_KBD_DEV_VERSION 0x0111
typedef struct virt_kbd {
int fd;
uint8_t prev_q;
uint8_t prev_w;
uint8_t prev_e;
uint8_t prev_r;
uint8_t prev_t;
uint8_t prev_y;
uint8_t prev_u;
uint8_t prev_i;
uint8_t prev_o;
uint8_t prev_p;
uint8_t prev_a;
uint8_t prev_s;
uint8_t prev_d;
uint8_t prev_f;
uint8_t prev_g;
uint8_t prev_h;
uint8_t prev_j;
uint8_t prev_k;
uint8_t prev_l;
uint8_t prev_z;
uint8_t prev_x;
uint8_t prev_c;
uint8_t prev_v;
uint8_t prev_b;
uint8_t prev_n;
uint8_t prev_m;
uint8_t prev_num_1;
uint8_t prev_num_2;
uint8_t prev_num_3;
uint8_t prev_num_4;
uint8_t prev_num_5;
uint8_t prev_num_6;
uint8_t prev_num_7;
uint8_t prev_num_8;
uint8_t prev_num_9;
uint8_t prev_num_0;
uint8_t prev_up;
uint8_t prev_down;
uint8_t prev_left;
uint8_t prev_right;
uint8_t prev_lctrl;
} virt_kbd_t;
int virt_kbd_init(virt_kbd_t *const mouse);
int virt_kbd_get_fd(virt_kbd_t *const mouse);
int virt_kbd_send(virt_kbd_t *const mouse, keyboard_status_t *const status, struct timeval *const now);
void virt_kbd_close(virt_kbd_t *const mouse);

163
virt_mouse.c Normal file
View file

@ -0,0 +1,163 @@
#include "virt_mouse.h"
int virt_mouse_init(virt_mouse_t *const mouse) {
int ret = -EINVAL;
mouse->status_recv = 0;
int fd = open("/dev/uinput", O_RDWR);
if(fd < 0) {
ret = errno;
goto virt_mouse_init_err;
}
ioctl(fd, UI_SET_EVBIT, EV_REL);
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_EVBIT, EV_MSC);
ioctl(fd, UI_SET_EVBIT, EV_SYN);
ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP);
ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
ioctl(fd, UI_SET_RELBIT, REL_X);
ioctl(fd, UI_SET_RELBIT, REL_Y);
ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
struct uinput_setup dev = {0};
strncpy(dev.name, VIRT_MOUSE_DEV_NAME, UINPUT_MAX_NAME_SIZE-1);
dev.id.bustype = BUS_VIRTUAL;
dev.id.vendor = VIRT_MOUSE_DEV_VENDOR_ID;
dev.id.product = VIRT_MOUSE_DEV_PRODUCT_ID;
dev.id.version = VIRT_MOUSE_DEV_VERSION;
if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) {
ret = errno > 0 ? errno : -1 * errno;
ret = ret == 0 ? -EIO : ret;
goto virt_mouse_init_err;
}
if(ioctl(fd, UI_DEV_CREATE) < 0) {
ret = errno > 0 ? errno : -1 * errno;
ret = ret == 0 ? -EIO : ret;
goto virt_mouse_init_err;
}
// initialization ok
mouse->prev_btn_left = 0;
mouse->prev_btn_right = 0;
mouse->prev_btn_middle = 0;
mouse->fd = fd;
ret = 0;
virt_mouse_init_err:
if (ret != 0) {
mouse->fd = -1;
close(fd);
}
return ret;
}
int virt_mouse_get_fd(virt_mouse_t *const mouse) {
return mouse->fd;
}
int virt_mouse_send(virt_mouse_t *const mouse, mouse_status_t *const status, struct timeval *const now) {
int res = 0;
size_t events_count = 0;
struct input_event events[12];
struct input_event tmp_ev;
tmp_ev.type = EV_REL;
if (status->x != 0) {
tmp_ev.code = REL_X;
tmp_ev.value = status->x;
events[events_count++] = tmp_ev;
}
if (status->y != 0) {
tmp_ev.code = REL_Y;
tmp_ev.value = status->y;
events[events_count++] = tmp_ev;
}
tmp_ev.type = EV_KEY;
if (status->btn_left != mouse->prev_btn_left) {
mouse->prev_btn_left = status->btn_left;
tmp_ev.code = BTN_LEFT;
tmp_ev.value = status->btn_left;
events[events_count++] = tmp_ev;
}
if (status->btn_middle != mouse->prev_btn_middle) {
mouse->prev_btn_middle = status->btn_middle;
tmp_ev.code = BTN_MIDDLE;
tmp_ev.value = status->btn_middle;
events[events_count++] = tmp_ev;
}
if (status->btn_right != mouse->prev_btn_right) {
mouse->prev_btn_right = status->btn_right;
tmp_ev.code = BTN_RIGHT;
tmp_ev.value = status->btn_right;
events[events_count++] = tmp_ev;
}
#if 0
const struct input_event timestamp_ev = {
.code = MSC_TIMESTAMP,
.type = EV_MSC,
.value = (now.tv_sec - secAtInit)*1000000 + (now.tv_usec - usecAtInit),
.time = now,
};
const ssize_t timestamp_written = write(fd, (void*)&timestamp_ev, sizeof(timestamp_ev));
if (timestamp_written != sizeof(timestamp_ev)) {
fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", timestamp_written, sizeof(timestamp_ev));
}
#endif
if (events_count > 0) {
struct timeval t;
if (now != NULL) {
t = *now;
} else {
gettimeofday(&t, NULL);
}
struct input_event syn_ev = {
.code = SYN_REPORT,
.type = EV_SYN,
.value = 0,
.time = t,
};
syn_ev.time.tv_usec += 1;
events[events_count++] = syn_ev;
for (size_t i = 0; i < events_count; ++i) {
if (i != (events_count - 1)) {
events[i].time = t;
}
const ssize_t sync_written = write(mouse->fd, (void*)&events[i], sizeof(struct input_event));
if (sync_written != sizeof(syn_ev)) {
fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", sync_written, sizeof(syn_ev));
res = errno;
res = res < 0 ? res : -1*res;
res = res == 0 ? -EIO : res;
goto virt_mouse_send_err;
}
}
}
virt_mouse_send_err:
return res;
}
void virt_mouse_close(virt_mouse_t *const mouse) {
close(mouse->fd);
}

28
virt_mouse.h Normal file
View file

@ -0,0 +1,28 @@
#pragma once
#include "message.h"
#include "devices_status.h"
#define VIRT_MOUSE_DEV_NAME "ROGueENEMY - mouse"
#define VIRT_MOUSE_DEV_VENDOR_ID 0x108c
#define VIRT_MOUSE_DEV_PRODUCT_ID 0x0323
#define VIRT_MOUSE_DEV_VERSION 0x0111
typedef struct virt_mouse {
int fd;
uint8_t prev_btn_left;
uint8_t prev_btn_right;
uint8_t prev_btn_middle;
uint64_t status_recv;
} virt_mouse_t;
int virt_mouse_init(virt_mouse_t *const mouse);
int virt_mouse_get_fd(virt_mouse_t *const mouse);
int virt_mouse_send(virt_mouse_t *const mouse, mouse_status_t *const status, struct timeval *const now);
void virt_mouse_close(virt_mouse_t *const mouse);

View file

@ -1,8 +1,13 @@
#include "xbox360.h"
#include "message.h"
int xbox360_ev_map(const evdev_collected_t *const coll, in_message_t *const messages, size_t messages_len, void* user_data) {
const xbox360_settings_t *const settings = (xbox360_settings_t*)user_data;
int xbox360_ev_map(
const dev_in_settings_t *const conf,
const evdev_collected_t *const coll,
in_message_t *const messages,
size_t messages_len,
void* user_data
) {
int written_msg = 0;
for (uint32_t i = 0; i < coll->ev_count; ++i) {
@ -12,16 +17,16 @@ int xbox360_ev_map(const evdev_collected_t *const coll, in_message_t *const mess
};
if (coll->ev[i].code == BTN_EAST) {
current_message.data.gamepad_set.element = (settings->nintendo_layout) ? GAMEPAD_BTN_CROSS : GAMEPAD_BTN_CIRCLE;
current_message.data.gamepad_set.element = GAMEPAD_BTN_CIRCLE;
current_message.data.gamepad_set.status.btn = coll->ev[i].value;
} else if (coll->ev[i].code == BTN_NORTH) {
current_message.data.gamepad_set.element = (settings->nintendo_layout) ? GAMEPAD_BTN_TRIANGLE : GAMEPAD_BTN_SQUARE;
current_message.data.gamepad_set.element = GAMEPAD_BTN_SQUARE;
current_message.data.gamepad_set.status.btn = coll->ev[i].value;
} else if (coll->ev[i].code == BTN_SOUTH) {
current_message.data.gamepad_set.element = (settings->nintendo_layout) ? GAMEPAD_BTN_CIRCLE : GAMEPAD_BTN_CROSS;
current_message.data.gamepad_set.element = GAMEPAD_BTN_CROSS;
current_message.data.gamepad_set.status.btn = coll->ev[i].value;
} else if (coll->ev[i].code == BTN_WEST) {
current_message.data.gamepad_set.element = (settings->nintendo_layout) ? GAMEPAD_BTN_SQUARE : GAMEPAD_BTN_TRIANGLE;
current_message.data.gamepad_set.element = GAMEPAD_BTN_TRIANGLE;
current_message.data.gamepad_set.status.btn = coll->ev[i].value;
} else if (coll->ev[i].code == BTN_SELECT) {
current_message.data.gamepad_set.element = GAMEPAD_BTN_OPTION;

View file

@ -2,8 +2,10 @@
#include "input_dev.h"
typedef struct xbox360_settings {
bool nintendo_layout;
} xbox360_settings_t;
int xbox360_ev_map(const evdev_collected_t *const coll, in_message_t *const messages, size_t messages_len, void* user_data);
int xbox360_ev_map(
const dev_in_settings_t *const conf,
const evdev_collected_t *const coll,
in_message_t *const messages,
size_t messages_len,
void* user_data
);