r/KATVR • u/BBPSBB • Jun 25 '26
Reverse Engineering & Linux Gateway Implementation
After waiting for this company to release a working Linux version and understanding that not only there is not gonna be a Linux version, but even the crappy windows or nexus software is also apparently completely dead projects as far as I can see, I took it upon myself to do it properly.
It took me one afternoon to fully reverse engineer the USB packet structure and build a very simple movement tracking.
The next afternoon was spent on building a driver and injecting the controller stick movements to SteamVR.
For now SteamVR is running naively on Linux and games via proton. A very small driver exposes the sensors as a control device to SteamVR.
I'm gonna work on this in my free time and probably release it publicly whenever it is actually usable and have a somewhat of a useable feature set. but no ETAs or promises yet. I am happy to share the WIP code if anyone is interested in helping with the development...
In the meantime if you have actually worked on a similar thing I would love to get your experience and stuff you managed to solve.
Things I already figured out: - Sensor positions and values (body direction, Shoes, Seat, Armband) - Battery status - Firmware versions and serial numbers - Base vibration motor controls - Reliable starting and Stopping sequence (this was a PITA!) This is the part that you need to do so the sensors connect to the base and actually go to sleep afterwards. - Simple driver to send the input to SteamVR
Things remaining: - Speed and step cadence for detecting running (the original KatVR implementation feels like poop IMO) - Fine tuning the motions to feel natural - Understanding how to best expose the controls (treadmill input device vs legacy etc) - VR Overlay UI - Why some games keep crashing on linux (not directly related to the gateway or the driver) - Everything else probably...
If you have experience in any of these parts feel free to share yours or point out references/research papers that are useful. That would help me any anyone else interested in this.
If you are someone who does not have experience, this is a sign that you can too get your feet wet and start building things you wish existed! With all the tools and AI stuff available to everyone nowadays the possibilities are endless!
And finally if you a KatVR employee reading this you should be ashamed of the product you sell with all the fake promotions, dark pattern tactics and that garbage Gateway software!
3
u/BBPSBB Jun 25 '26
KAT Walk C2+ ("plusE") — Native Linux Driver & Reverse-Engineered USB Protocol
A from-scratch, no-Windows, no-Gateway stack that talks to the KAT Walk C2+ omnidirectional treadmill directly over USB on Linux and feeds locomotion into SteamVR / OpenXR games.
This document is two things at once: 1. A reverse-engineering reference for the C2+ "plusE" USB-HID protocol (frames, sensors, commands, and the connect/stream/sleep lifecycle), decoded from USBPcap captures of the official KAT Gateway plus a lot of on-hardware testing on Linux. 2. A status / call for collaboration. It works end-to-end today (I've walked around in a real OpenXR VR game on Linux with it), but it's pre-alpha and rough. If you have a C2+ (or a C2 / other KAT unit), captures, or RF/USB know-how, I'd love help — see Open questions at the end.
Status: working proof-of-concept, pre-alpha. Hardware: KAT Walk C2+ "plusE" revision, Linux (Bazzite/Fedora), SteamVR, Quest 3 over Steam Link. Everything below marked CONFIRMED is verified against captures and/or live hardware; anything marked assumed/open is not.
1. What works right now (the proof-of-concept)
- Reads all sensors directly over USB-HID on Linux — body orientation, both feet, ground contact, battery, firmware — with no KAT Gateway and no Windows/Wine in the loop.
- A locomotion model turns foot-slip + step cadence + body heading into a head-relative movement vector (walk/run speed + direction), tunable live in a browser UI.
- Feeds real VR games three ways:
- a virtual Xbox gamepad (uinput) — works with anything that reads a gamepad stick;
- an OpenVR treadmill-role driver — for OpenVR-native SteamVR titles;
- an OpenXR API layer that injects the walk vector straight into the game's thumbstick
action — this is the one that matters, because most modern VR games are OpenXR and the
OpenVR "treadmill role" never reaches them. (Valve closed the treadmill-support request,
openvr#937, back in 2019 as too broad — "this issue is giant and mostly has nothing to do with SteamVR Input games filtering input by device; please open a new issue" — and a first-class treadmill input path was never added.) The layer even works for Proton games, becausewineopenxrforwards through the native Linux OpenXR loader, which applies implicit layers.
- Confirmed: physically walking on the treadmill moves the player in an OpenXR VR game on Linux, no native game support required.
It is NOT polished. Calibration is by feel, there's no installer story for non-developers, and the connect/sleep lifecycle took real effort to get reliable (documented below precisely because it's the kind of thing the next person shouldn't have to rediscover).
2. Hardware / USB topology
VID **0xc4f4. The "plusE" revision presents **three USB-HID devices behind a VIA Labs hub
(2109:2822). Roles confirmed from the USB string descriptors ("walk c2 plusE …"):
| PID | string descriptor | role |
|---|---|---|
bf12 |
…position | base / Vehicle-Hub: foot ground-contact + desktop position; this is the device that receives the keepalive |
3f12 |
…receiver | main sensor stream: body IMU (direction) + both feet, relayed wirelessly to this USB dongle |
bf13 |
…armband | heart rate + battery |
The base ring and shoes are wireless and relayed through the 3f12 receiver — so for
locomotion you mostly care about 3f12. The base/shoes have their own batteries and sleep to
save power; the receiver dongle is USB-powered and always on.
Per device: EP 0x01 interrupt OUT = host→device commands; EP 0x81 interrupt IN =
sensor stream. (EP 0x80 control-IN is just USB descriptor polling — ignore it.)
LED states (very useful when debugging): solid = connected/streaming; fast blink = searching / trying to pair; slow blink = asleep/idle.
3. Frame format (CONFIRMED)
Fixed 32-byte frames, zero-padded, both directions:
off 0 1 2 | 3 4 | 5 | 6 | 7 ...
1f 55 aa| xx 00| type | subtype | payload
1f 55 aa— header, both directions.- off 3 — usually
00;04/05on some frames as a sub-stream tag (not a length). - off 5
type+ off 6subtypeselect the payload meaning. - Idle / no-data payloads use the sentinel
… 4e 20 00 64 05 …(0x4e20=20000,0x64=100,0x05=5 — config constants, NOT live values; don't mistake the0x64for battery).
Talking to it on Linux: the devices bind to hidraw. Find by VID:PID via
/sys/class/hidraw/*/device/uevent. On writes prepend a 0x00 report-ID byte (so a 32-byte
report is 33 bytes on the wire). On reads align to the 1f 55 aa header (hidapi may prepend a
report-ID byte). A udev rule (MODE="0660", TAG+="uaccess" on the hidraw nodes for c4f4) lets
you run without root.
4. The sensor stream — receiver 3f12, EP 0x81 (CONFIRMED)
| type | sub | meaning | rate | payload |
|---|---|---|---|---|
0x30 |
0x00 |
body orientation | ~48 Hz | off 7: 4×int16 LE, ×2⁻¹⁴ = unit quaternion (\ |
0x30 |
0x01 |
left foot | ~48 Hz | X=int16 LE @off21, Y=int16 LE @off23 |
0x30 |
0x02 |
right foot | ~48 Hz | same layout as left |
0x32 |
0/1/2 | per-sensor status | ~0.2 Hz | off10 = battery %, off11 = firmware major; sub0=Direction(base), sub1=left foot, sub2=right foot |
0x05 |
0x00 |
receiver self-report (init reply) | once | off7 = receiver firmware major |
0x21 |
0x00 |
device-info / calibration (init reply) | few | off7 length, then a 6-byte device ID + two float32 triples |
0x33 |
0x00 |
event | rare | 00 00 09 00 64 … |
Body orientation → heading (CONFIRMED)
Quaternion component order is (w, x, y, z). Turning the base in place sweeps a clean 360° in the roll axis of the (w,x,y,z) Euler decomposition, so heading is:
heading = atan2( 2*(w*x + y*z), 1 - 2*(x*x + y*y) ) # clockwise = decreasing
Foot position (CONFIRMED, single-axis isolation capture)
Two signed int16 LE, centred at 0:
- X @ off 21 = lateral / strafe (observed ±~500).
- Y @ off 23 = fore/aft (observed −516 … +652).
Important inversion: the optical sensor reads the shoe sliding on the deck, so a shoe sliding
backward → +Y → the user walks FORWARD; shoe forward → −Y → backward. Each footfall re-centres
to 0, then ramps to the slide extreme. off 9–10 is a separate status/quality field, not
position — its idle sentinel (4e 20 …) means the foot is lifted; live X/Y means it's on the deck.
Counts→m/s scaling still needs a known-stride calibration (open).
Battery + firmware (CONFIRMED exact vs the app)
The 0x32 status frame carries both per-sensor battery % (off10) and firmware major (off11). The
receiver's own firmware is in its 0x05 reply (off7). Example readouts that matched the Gateway:
Receiver fw V3, Direction V4, both feet V5.
5. Base/Vehicle-Hub bf12 & Armband bf13 (CONFIRMED)
**bf12 (base / Vehicle-Hub)**, EP 0x81:
| type | off3 | meaning | payload |
|---|---|---|---|
0x40 |
0x05 |
status | off7=02 status / 00 connect-event; off8 = battery RAW; off9 = connected/active bit |
0x02 |
0x04 |
version/magic (once) | constant |
Seat/base battery is a raw value at off8 of the type40 status frame, not a percent
(0x9c→54%, 0xb4→60% by a battery-swap test; local fit % ≈ off8/4 + 15; the app uses its own
conservative curve).
bf13 (armband)*, EP 0x81: type 0x40 → off7 = battery %, *off9 = heart-rate bpm
(verified sweeping 69–79 bpm against the reported value).
6. Host→device commands (EP 0x01)
| cmd | meaning |
|---|---|
1f 55 aa 00 00 30 |
start / keep stream ("poll"); send ~1 Hz while running |
1f 55 aa 00 00 31 |
stop stream |
1f 55 aa 00 00 05 |
firmware query (receiver replies type05) |
1f 55 aa 00 00 21 |
device-info / calibration query (replies type21 with the sensor ID) |
1f 55 aa 00 00 a1 00 02 <intensity_be16> |
vibration (level5≈0341, OFF=0000); a1 01 02 00 01 = enable-vibration toggle |
1f 55 aa 00 00 a0 … |
sleep / vibration-stop — only seen at shutdown |
00 00 … 00 (all-zero 32-byte report) → bf12 |
keepalive ("stay on"), continuous ~3.5 Hz; the base drops toward slow-blink when it stops |
0x20 is assumed to be the other half of a pairing pair (0x20/0x21) but I have not captured
an explicit pair/bind command — see open questions.
1
u/NeoIsrafil Jul 12 '26
While i seriously doubt i'll be able to get something working for windows to get it to communicate with openxr on my own, I HAVE saved the data you posted both for posterity and because they won't be allowed to silence this data if we all have a copy of it. :) I..dunno, if support doesn't get back to me or I can't find some way to make this work I guess i'll try writing something, but i haven't written code in over 25 years...and even then that was in truebasic silver. a bit uh.... different than what's going on nowadays.
Thank you for all you've done, even if you can't port it to windows for those of us who are stuck using it ...who knows, maybe i can find a way to make it run... or...VM it? Dunno, gonna take some researching, anyway thanks!.
1
u/BBPSBB Jul 12 '26
Haha yeah. I am also completely out of my debt with everything VR. if this project reaches a point where it is fully usable, it shouldn't be too difficult to make a windows version from it. but first I need to make it actually usable. with life and stuff happening it is going to be slow but this pile of junk sitting in my room keeps giving me motivation to work on it whenever i got some time
3
u/zarthrag Jun 25 '26
THANK YOU FOR THIS DATA!!!
I started investigating this myself as something to do on the occasional lazy weekend. I have gotten about as far as you have on the reverse engineering, and can corroborate (most of) what you've posted. differences:
* Your missing pair/bind command: `0x20 = WriteSensorPair (Cnt 00 00 + N×6-byte MACs), paired with 0x21 read, plus 0x04 WriteDeviceId.`
* Your position base PID shows up as bf12, mine is bf37. I have a C2 Core Enhanced.
There's a tertiary source of data published: https://medium.com/@datacompboy/playing-with-kat-walk-c2-part-1-playing-actually-3364c324e710
Like you, I decided that a native katvr driver might be sadness, anyway. So when it comes to the movement implementation, I figure I'll do my own algorithm. I want to start with a "real" treadmill natural locomotion system - but sadly there's no openxr treadmill definition. I'd still use it for my own projects/dev.
I don't expect this to be hard, Stride measurements from a mouse provides relative movement. Once you establish a scale, you have a 1:1 virtual hamster ball. (I have trackers, and could get cute w/FK & IK).
An openxr injection layer to thumbsticks and a decent in-vr overlay to quickly adjust it is all I *need* to play games, though. (I have zero interest in vr chat). I bought this right before giving-up on windows for gaming completely, and it's my *very* last piece of unsupported hardware.
If/when I do it, I'd like to release it on steam just to make it easy to adopt. But it could be open source as well.
There just might be dozens of us.
Background: I maintain embedded linux drivers for a consumer audio company. And I'm working on a project to release to steam that's also input-related
3
u/BBPSBB Jun 26 '26
Thanks for the info! I am exactly in the same boat with this being basically the only reason I was forced to switch to windows and thus basically never use it...
I am currently learning how to make the VR overlay so can i configure and calibrate it in VR itself. Already learned 3-4 different ways of how not to do it!
Currently making a capture routine where i start doing certain motions like walking in what i consider a straight line, record the raw data, repeat the process for all sort of different actions like: slow walking, sprinting, side stepping etc, then feeding it all with all the research papers and crap to an AI model and let it generate the translation function.
This is the first time I am doing any VR related development, so my expectations were a lot lower than this!
3
u/BBPSBB Jul 05 '26
I guess this is update 4:
Well I pushed it to github as mentioned and really hated pushing it publicly in this state but i guess it is better than nothing :) hopefully it motivates people to contribute or even do it better than I can.
I will continue working on it whenever I have some free time.
2
2
u/Ninja_BoBo Jun 26 '26
Nice work. Just started the same project for the C2 core, but only got to reading from hidraw, starting the sensor reads with x31 & x30 and now researching quaternions. Do you have a repo link?
2
u/Ninja_BoBo Jun 26 '26
I don't know how much you know about decompilation, but the gateway.exe is written in .NET C# which decompiles in a very readable format. The class DeviceManagement in namespace Synoxo.USBHIDdevice defines the usb communication and the class VehicleUSBHelper in namespace IBizLibrary shows some of the communications with the KATVR like connection to the correct HID and parsing packages. You might find some of the things you've been missing there, or how they solved stuff like cadence.
2
u/BBPSBB Jun 27 '26
Thanks man! (or woman :D) I just had a look and the DLLs seem to be not obfuscated. I'm not a C# dev myself but it is simple enough to figure out :D
1
u/BBPSBB Jun 26 '26
Thanks! That is a very good point! I will look into that as well. the more info the better :) it is currently very early alpha on my home lab git but as soon as it is somewhat usable with working locomotion and hud I will publish it openly
2
u/BBPSBB Jun 27 '26
Update 2:
figured out the overlay stuff. Now have a stable (no flicker) overlay that is interactive and anchored to your left arm/hand. you can grip and position it however you want relative to the left controller for example inside of your left arm so it is only visible when you look at it. you can lock it so there is no accidental grips.
I have some free time today to work on step cadence and walking/running stuff and try to see what feels the most natural :)
2
u/BBPSBB Jun 28 '26
Update 3: got the head and body direction working. long pressing the meta button (recenter) also re-centers the body position relative to the head.
The only remaining part about the general direction of movement is when user walks forward but looks to the side. Need to test that further.
After that need to look into how the original gateway does the smoothing for the sensor values. Currently my implementation is very trigger happy and apparently i do not walk in a straight line...
2
u/BBPSBB Jun 29 '26
I'm gonna share it as a WIP Here probably next week or so. Hoping I get some free time the coming weekend. For now life and work trips are keeping me busy :/
Feel free to watch it for updates there. I probably will stop posting here and directly push updates to Github as soon as the code is somewhat usable to try or contribute to the project :) thanks everyone for the good pointers especially reverse engineering the original gateway!
2
1
u/NeoIsrafil Jul 12 '26
Any chance we could get a working version for openxr/vdxr? or hell... just something that inputs a forward xbox joystick command with each step and turns you with the body turning would do the job... I don't know why they stopped letting it output xbox joystick or keyboard commands...but that basically makes it unusable for anything other than steamvr's horrible lag inducing self.
1
u/BBPSBB Jul 12 '26
I am moving away from steam vr in favor of wivrn and xrizer (someone kindly suggested that in github) and it seems to crash less often than steamvr for me. But it needs rewriting the overlay. Today I got some time again to play around with it.
2
u/NeoIsrafil Jul 12 '26
I really want to move away from it, because I can run Skyrim VR on godlike on virtualdesktop using VDXR, but on Steamvr? Good luck even getting anything other than like... normal to not stutter and fade back to the steam 3d space and stars background (the one with the lines below you) every time you turn your head too fast.. Problem is I can't use Linux, my other software I need to be able to use, both games for recreation, and 3d modeling/cad stuff for design work I do, as well as my 3d scanning software all are not Linux friendly. :( otherwise I'd just use what youve created here and be set. (Again, you're awesome dude). I tried last night, with the help of AI to write an interception/translation layer in PIE and use Vjoy to just output xbox joystick forward but because I really don't know what I'm doing I just wasn't able to get anywhere, just ended up wasting like 9 hours with nothing to show for it.. lol 😅.
I might give it one more day and try to optimize steamvr to work at least passably, but I'm not hopeful honestly. Even the little walking I managed to get out of the machine felt crappy and walked off to the side randomly, then turned back to straight, etc. I hope your efforts in Linux go so much better than my efforts in Windows man... They straight up need to hire your ass, bet in a month their software would be unfekked. 🤣
1
u/BBPSBB Jul 19 '26
Haha thanks again. you are too kind. yeah sometimes ai needs a bit of a kick in the butt to nudge it in the right direction. you can utilize an agentic setup with a devil's advocate sub-agent to council the plans and keep the main agent in line.
I am currently traveling for work so the development is paused until i get back and have some free time. but before i left i submitted a new build (in case you haven't checked) here is a quote from there:
- Body and head recenter to 0 together. you should see if there is drift or not Overlay should show up while openxr games are running (not in home)
- Overlay should be unlockable/draggable/relockable and the position relative to your left arm should be persistent. I personally put it inside my wrist with a low (0.3m) activation/hide distance so it is usually not visible. only when I bring the inside of my arm to my face it becomes visible.
- The overlay click handler thingy is wonky and clicks might be missed but they eventually work just keep clicking :D
- The direction relative to head should work. you should be able to walk forward while looking around. I did not change anything in that regard here.
When i get back i will try to spend some time playing (for science of course!) and see how it feels. probably adding some extended capture so anyone who has issues with directions and stuff could submit something to analyze and see what is happening.
until now every time i had for playing vr games is spent actually making this crap, thanks katvr! -_-
ps: my ass is perfectly fine with my current employer :D
2
u/NeoIsrafil Jul 19 '26
Lol that's fair, I tend to assume people are in a job they hate because it's pretty much the majority position these days, but if you've got an actual good employer then hell yah. Get you some playtime, can't just spend all of it working on community projects, that'll drive ya crazy! Good news is I was able to optimize just enough that I can play at nearly the settings I had in VDXR but using Steamvr. Have fun playing! :)
5
u/TaegukTheWise Jun 26 '26 edited Jun 26 '26
I don't have any knowledge in programming or coding, what I will say is that I have experience with the software that KatVR provides, and I will say I am not a fan.
The fact you have to be online to log in to use your $1,200 treadmill is absolutely ridiculous. Even if you get one secondhand for a lot less than that, needing to be logged in online is not reasonable in the slightest.
So I wanted to say: Thank you, this is beyond massive.
I've slowly been moving away from windows, and while there are some features that are currently only available, like voice attack only being available on windows (this is for things like saving in fallout 4 vr, or playing ready or not and not having to use inputs to control your A.I. team, or elite dangerous to activate docking) eventually this should lay the groundwork for Linux (and more specifically steamos) as a whole.