Developers/hackers thread

So I’ve been researching into how it all works and figured to keep the info in one thread, so people interested in development can read here.

First of all this is the general way of communication between Steam and the headset:

Steam system <-> pvr_driver.dll <-> LibPVRClient64.dll <-> pi_server.exe <-> headset

So how it works is that SteamVR, upon opening, tries to load all drivers that it finds in its C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers directory. This is where the pvr_driver.dll is located and this is how Steam loads this PIMAX driver. The driver dll dynamically loads “LibPVRClient64.dll” which is located in your windows directory. This dll contains RPC code that searches for a RPC server, located at “\pipe\pvr_api”. This pipe is opened by pi_server.exe, which thus functions as a server to the LibPVRClient dll. The pi_server communicates with the headset.

So in short, for Steam to function, all you need is that pi_server to be running, the pvr_driver in the windows directory and the LibPVRClient dll in your windows directory.

Version 1.1.x uses a different communication method between the server as v1.2.x, so you can’t mix the v1.1.x server with the v1.2.x client for example (v1.1.x uses for example shared file mapping which is not used in v1.2.x)

About the driver itself: the driver needs to expose a ‘driverfactory function’ to Steam, which in turn exposes 2 methods that Steam uses. There are example drivers out there, from “OSVR” (Open Source VR) which explain it very well:

In fact, Pimax is based on this Open source driver too ! So it should be possible to modify the Source code to get it going with Pimax.

Anyway, I’ll keep updating this thread with any relevant info that I find.


Really good stuff from you my friend, now that you are in deep with this (Piplay), is there a way how to apply the native 4K resolution??? at 30 hz in extended mode ??? at the end this is the main point … maybe is more easy, you do not have to get to deep and fight againts steamvr… I am not expert like you in this, just to show you another way to go…

Well as I described in that other thread, the headset resolution is being changed with a the ‘feature report’ API command and we know the layout of the packet, as I described here:

So we should be able to find the handler for that in the headset’s FW, because we can disassemble it, like I’ve described here:

However I took a quick look at the FW to look for a cmp with 0xf0 (which would probably be the first thing the handler would do) but haven’t really found the code I’m looking for. I don’t have too much time for it neither at this moment. Besides I’m not sure how difficult it would be to adapt it for a new resolution. I might look into this in the future though.

Anyway something else that I’ve found very helpful is the API/SDK ! It actually uses that same LibPVRCLIent64.dll directly, so you can run all kinds of tests and pull all kinds of info directly from the headset. Very useful for anyone who wants to research into this.

Ure doing awesome stuff @_@ Wish I could help but I only know Python + super basic C++…

If u ever find out how to set up resolution to run at proper 2k + 60/75fps that would be epic. So far I hear that latest drivers are messing it up. to 1.6k or something like that :- (

I think I’ve figured out why Pimax is using the pi_server.exe. I’ve been wondering: why does Pimax use this ? From an efficiency/speed perspective it would make a lot more sense to just integrate everything into the driver.dll, more so because RPC (which is used to communicate with the server) is not fast at all. I also saw Oculus is using the same idea (also with a server).

But I now think I know why. It’s because the Oculus runs in different modes (direct mode vs extended mode). With a server you can just change the behavior ‘behind’ the dll very easily ! Without a server it would be possible too I guess, with settings that are being read by the DLL, but a server seems more flexible here. So I guess that’s the reason why they’re using this client/server model.


Is there some way of passing an external tracker to SteamVR for the Pimax? I’m trying to use the EDtracker with Elite Dangerous, but havent managed to get the combo working. I suspect I need something that patches SteamVR for the ED Tracker.

Some memory locations in the FW:
0x20000708 = IPD
0x2000070C = HDmode on/off (varies between 0-1). This is the AND #3 result of the displayParam (see below)
0x2000070D = DisplayMode (varies between 0-2). This is displayParam/4 (see below)

0x20000714 = serial number

0x20000725 = work mode param1
0x20000726 = work mode param 2, varies between 0 and 2. 0 = pimax mode, 1 = extended mode.
0x2000072C = LCD type (0 or 1)

0x20000744 = brightness
0x20000745 = colour (warm/cold)
0x20000722 = region ID (2 bytes)

0x2000074C = config param 1
0x2000074D = config param 2

0x200008AC = LCD config info: Array of 5 bytes:
byte 0 = work mode param 1
byte 1 = LCD type (0 or 1)
byte 2 = color mode (warm/cold)
byte 3 = the HD mode
byte 4 = work mode param 2

0x200008B1 = Here starts the bulk of the LCD configuration data that is being sent to the LCD panel via LCD commands
0x20002C60 = EEPROM was inited bool (ensures EEPROM only inits once)
0x200033DC = 0x64 bytes of ‘calibration data’

0x20002BE0 = color mode related
0x20002BE1 = color mode related
0x20002BDF = color mode related

So the “HD mode” and “display mode” together form the “display param” as sent in the 'feature_report" when changing between 1080p and 1440p:

DisplayParam = 4*DisplayMode + AND #3 (HD_mode)

So there are currently 6 valid DisplayParams:

0: HD off, Displaymode 0
1: HD on, Displaymode 0

4: HD off, Displaymode 1 (currently used for 1080p)
5: HD on, Displaymode 1 (currently used for 1440p)

8: HD off, Displaymode 2
9: HD on, Displaymode 2

So, HD seems to switch between 1080p and 1440p, ‘display mode’ is maybe direct/extended/pimax ? Will investigate

1 Like

Like explained, to communicate with the headset ‘feature reports’ are being used. First byte of the report is always 0xF0, the 4th byte indicates the type of message and then the rest of the packet are params, usually 1-2 bytes only (rest zero’s). Feature report’s packet length is always 0x40.

Types valid for Feature_report_send (so to change a configuration param)
0x1 = enter Pimax DFU mode. No Params. (btw to leave the DFU mode, a dos command is issued: “dfusecommand 0 return”, so no feature reports are being used here !)
0x2 = Display mode set. Param byte 40: 0x4 = 1080p, 0x5 = 1440p.
0x3 = not sure yet. Gets sent after resolution change. Maybe reboot command
0x4 = “Work mode Set”. Byte 26 set to 0= pimax mode. Extended mode: byte 26 set to 0x11
0x8 = change brightness, byte 30: 0xb0 = level 1 high, 0xc0 = 2 high, 0x0 = normal, 0x10 = low 1, 0x20 = low 2
0xB = Sensor config set. Gets sent before changing brightness. Here byte 31 = 0x4 when sending brightness
0xC = change IPD, param byte 37 and 38 = IPD * 1000

Types valid to receive a configuration param:
0x0 = get serialnr
0x2 = displaymode get
0x4 = work mode get
0x5 = LCD type get
0x6 = version get
0x7 = sys state get
0x8 = brightness get
0x9 = regionID get
0xA = sys warn get
0xB = sensor config get
0xC = IPD get

1 Like

So, what happens if you for example change the IPD ?

  1. piservice.exe signals server.exe to kill itself, via an openevent/setevent. piserver.exe disappears and led goes red
  2. piservice.exe sends the featurereport type 0xC to tell the headset to change the IPD to the new value, included in the report
  3. piservice.exe starts piserver.exe and headset led changes to blue again.

So at this moment I THINK that changing values like the IPD are nothing more storing the new (IPD) value in EEPROM and then the headset gets rebooted with using the new IPD value. This also seems to correspond with what I’ve seen in the firmware, but going to look more into this.

1 Like

Some more about the communication with the headset:

  1. piservice.exe communicates with the headset via the hid api: hidapi/hidapi.h at master · signal11/hidapi · GitHub

It uses “hid_send_feature_report” like explained before, to send data to the HMD and it uses “hid_get_feature_report” to retrieve data.

On startup of the service it calls that get_feature_report with only 0xF0 as first byte, rest zeroes. The headset returns with a packet (also 0x40 bytes long), including the serial number, the FW version, the brightness setting, the IPD and a few more bytes which I’m not sure yet what they are. It also returns 0x4 for 1080p setting and 0x5 for 1440p as outlined above

  1. piserver.exe also uses the same 2 hidapi functions to communicate directly with the HMD, BUT also seems to use the “regular” hid.dll api, with api calls like HidD_GetFeature, HidD_SetFeature, HidD_GetAttributes etc.

So, I’ve made huge progress last few days, I’ve updated some of the previous posts in this thread with new info the last few days. I’ve also figured out how the FW communicates with the LCD panel !!

Before calling ‘lcd init’, the FW makes an array of 5 bytes and saves it to 0x200008AC. These 5 bytes are the working mode param 1, the LCD type, the color mode, the HD mode and the work mode (2nd param). These 5 bytes are used to configure the LCD panel. The driver in the FW writes bytes over the I2C bus to the LCD panel to configure it. In the FW, these configuration modes are always 6 bytes, where the first 2 bytes form the ‘address’ in the LCD panel and the last 4 bytes form the parameters. Or think of it as a variable name (that the LCD panel understands) and the value of that parameter. Now sometimes only 1 byte is sent, sometimes 2 bytes and sometimes 4 bytes. If only one byte it used, then the first of the 4 byte is used and the other 3 bytes are ignored.

An example of this would be:
0x8 0x8C 0x4E 0x0 0x0 0x0

so this is address 0x8c08 and the value is 0x4E. This specific command tells the LCD panel to set byte 8 of the EDID info to 0x4E. In fact the first byte of the EDID info is 0x8c00 and the last byte (there are 0x7f bytes in the EDID block) would be 0x8C7F

The FW does this to set the manufacturer ID and product ID (to Oculus DK2 and OSVR HK2) and to set the checksum byte in the EDID block (the last byte).

So this is how the whole process works ! The LCD driver in the FW writes bytes via the I2C bus to the LCD panel, which it uses to configure itself.

Next step is trying to figure out how to configure the LCD panel for 4k@23hz :slight_smile: It seems it is NOT changing the info in the EDID table with hard writes like this, but the LCD panel SEEMS to change the EDID info itself, based on the configuration it receives from the driver (because I don’t see any more 0x8cXX instructions). But I might be wrong here, still quite a bit of work is needed …


Why only 23hz but not 30?

Really good news man !!!.. hope you can do it.

If I succeed, then it’s most likely easy to configure it to any of those, IF the LCD supports it.

It’s still a quite a bit of work from here and I’m also not sure if it will lead to a success. It’s possible that the LCD commands are hard to deduce, I mean it are custom commands and I might not be able to figure out what each command exactly does so I might not be able to figure out how to change the commands to get the higher resolution and lower frequency.

I’ll try though !


The hero we need, but don’t deserve !

Can we get this thing supported for UE4? As a hoobist game developer I d really love some efforts in that direction. Would buy instantly but a bit scared atm it would just sit in the box

UE4 should be fine. Vanishing of Ethan Carter with vr dlc works fine. One of the PiPlay market has UE in it… Might be bullet time (a slo mo demo)

For the most part if it supports vive/rift then should run in PiMax

UE4 is not a game, is game ENGINE and it needs to support and get data from the device. If you search on the Epic games forums the world Pimax does not even exisist. They support Vive and rift. I would Love to toy around with a device like this instead of the Vive and so would a ton of other Ue4 developers I am sure. Send a couple dev kits to Epic games!