Bluetooth Filter Driver for DS3-compatibility - research notes

Going the extra mile today: testing the PS Move Navigation controller 😈 Connection established:

2019/02/10-18:32:19.445	TRACE_LEVEL_VERBOSE	BthPS3IndicationCallback Entry
2019/02/10-18:32:19.445	TRACE_LEVEL_INFORMATION	New connection for PSM 0x5053 from 70401E341 arrived
2019/02/10-18:32:19.445	TRACE_LEVEL_VERBOSE	L2CAP_PS3_HandleRemoteConnect Entry
2019/02/10-18:32:19.446	TRACE_LEVEL_ERROR	BTHPS3_GET_DEVICE_NAME failed with status STATUS_INVALID_PARAMETER (0xC000000D), dropping connection
2019/02/10-18:32:19.446	TRACE_LEVEL_VERBOSE	L2CAP_PS3_DenyRemoteConnect Entry
2019/02/10-18:32:19.446	TRACE_LEVEL_VERBOSE	L2CAP_PS3_DenyRemoteConnectCompleted Entry (STATUS_SUCCESS (0x00000000))
2019/02/10-18:32:19.446	TRACE_LEVEL_VERBOSE	L2CAP_PS3_DenyRemoteConnectCompleted Exit
2019/02/10-18:32:19.446	TRACE_LEVEL_VERBOSE	L2CAP_PS3_DenyRemoteConnect Exit
2019/02/10-18:32:19.446	TRACE_LEVEL_VERBOSE	BthPS3IndicationCallback Exit
2019/02/10-18:32:20.247	TRACE_LEVEL_VERBOSE	BthPS3IndicationCallback Entry
2019/02/10-18:32:20.247	TRACE_LEVEL_INFORMATION	New connection for PSM 0x5053 from 70401E341 arrived
2019/02/10-18:32:20.247	TRACE_LEVEL_VERBOSE	L2CAP_PS3_HandleRemoteConnect Entry
2019/02/10-18:32:20.247	TRACE_LEVEL_INFORMATION	++ Device 70401E341 name: Navigation Controller
2019/02/10-18:32:20.247	TRACE_LEVEL_VERBOSE	L2CAP_PS3_HandleRemoteConnect Exit (STATUS_SUCCESS (0x00000000))
2019/02/10-18:32:20.247	TRACE_LEVEL_VERBOSE	BthPS3IndicationCallback Exit
2019/02/10-18:32:20.247	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ConnectionIndicationCallback Entry (Indication: 0x0, Context: 0xFFFFFA80074DE0C0)
2019/02/10-18:32:20.247	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ConnectionIndicationCallback Exit
2019/02/10-18:32:20.497	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ControlConnectResponseCompleted Entry
2019/02/10-18:32:20.497	TRACE_LEVEL_INFORMATION	Connection completion, status: STATUS_SUCCESS (0x00000000)
2019/02/10-18:32:20.497	TRACE_LEVEL_INFORMATION	HID Control Channel connection established
2019/02/10-18:32:20.497	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ControlConnectResponseCompleted Exit
2019/02/10-18:32:20.689	TRACE_LEVEL_VERBOSE	BthPS3IndicationCallback Entry
2019/02/10-18:32:20.689	TRACE_LEVEL_INFORMATION	New connection for PSM 0x5055 from 70401E341 arrived
2019/02/10-18:32:20.689	TRACE_LEVEL_VERBOSE	L2CAP_PS3_HandleRemoteConnect Entry
2019/02/10-18:32:20.689	TRACE_LEVEL_INFORMATION	++ Device 70401E341 name: Navigation Controller
2019/02/10-18:32:20.689	TRACE_LEVEL_VERBOSE	++ Found desired connection item in connection list
2019/02/10-18:32:20.689	TRACE_LEVEL_VERBOSE	L2CAP_PS3_HandleRemoteConnect Exit (STATUS_SUCCESS (0x00000000))
2019/02/10-18:32:20.689	TRACE_LEVEL_VERBOSE	BthPS3IndicationCallback Exit
2019/02/10-18:32:20.689	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ConnectionIndicationCallback Entry (Indication: 0x0, Context: 0xFFFFFA80074DE0C0)
2019/02/10-18:32:20.689	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ConnectionIndicationCallback Exit
2019/02/10-18:32:20.783	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ConnectionIndicationCallback Entry (Indication: 0x4, Context: 0xFFFFFA80074DE0C0)
2019/02/10-18:32:20.783	TRACE_LEVEL_INFORMATION	L2CAP_PS3_ConnectionIndicationCallback ++ IndicationRemoteConfigRequest
2019/02/10-18:32:20.783	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ConnectionIndicationCallback Exit
2019/02/10-18:32:20.824	TRACE_LEVEL_VERBOSE	L2CAP_PS3_InterruptConnectResponseCompleted Entry
2019/02/10-18:32:20.824	TRACE_LEVEL_INFORMATION	Connection completion, status: STATUS_SUCCESS (0x00000000)
2019/02/10-18:32:20.824	TRACE_LEVEL_INFORMATION	HID Interrupt Channel connection established
2019/02/10-18:32:20.824	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ConnectionStateConnected Entry
2019/02/10-18:32:20.824	TRACE_LEVEL_VERBOSE	L2CAP_PS3_ConnectionStateConnected Exit
2019/02/10-18:32:20.824	TRACE_LEVEL_VERBOSE	L2CAP_PS3_InterruptConnectResponseCompleted Exit
2019/02/10-18:32:20.824	TRACE_LEVEL_VERBOSE	BthPS3_EvtWdfChildListCreateDevice Entry
2019/02/10-18:32:20.824	TRACE_LEVEL_VERBOSE	BthPS3_EvtWdfChildListCreateDevice Exit

And there we have the HID Input Report in the function driver 🎉

2019/02/10-18:34:23.749	TRACE_LEVEL_INFORMATION	InputReport_EvtTimerFunc Entry
2019/02/10-18:34:23.757	TRACE_LEVEL_INFORMATION	A1 01 00 00 00 00 00 80 80 7D 85 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 03 14 FF C2 00 00 23 D6 77 00 40 02 00 02 04 01 A3 01 FF 
2019/02/10-18:34:23.757	TRACE_LEVEL_INFORMATION	InputReport_EvtTimerFunc Exit

Alright, time to properly polish and fix power management.

I've implemented dropping all connections in case of bthenum surprise removal (unplugging the USB Bluetooth dongle without warning), freeing memory and instructing child device (PDO) removal. This works well and allows unloading the driver, so no dangling objects here. But as soon as the function driver attaches, everything stalls on surprise removal. I haven't yet had the time to debug/implement it but I can document a few assumptions to help me later when I pick it up again.

  • EvtWdfIoQueueIoInternalDeviceControl sends BRBs synchronously out of convenience, this probably isn't the brightest idea, will adjust that to asynchronous
  • PDO might be missing PNP/Power capabilities
  • Queue I/O stop callback isn't invoked
  • Maybe implement self-managed I/O flush callback?

I bet it's an easy fix and I'm just overthinking the issue. Will pick up development again in a few days 👋

Surprise removal power-down sequence fixed!

Two things had to be done (properly):

Another step closer to finishing and hardening the profile/bus driver 🎉

Goddammit, I managed to make bthport.sys leak memory ☠


Time for code review! 🤦



Aha! It only happens when I send data to the HID Control Channel, how interesting 🤔 We'll see about that... :male-detective:

Seriously, how did I do that 😦


Pool tag details:

BTHP - bthport.sys  - Bluetooth port driver (generic)
HCIT - bthport.sys  - Bluetooth port driver (HCI)

Now if the symbols could download faster than over 56k I could actually see more 💤

Better graph, still no clue 🙄


Ha, found it! 🎉 Now that's more like it:


Purple is non-paged memory from HCI-specific pool now only slightly jittering up and down as expected. Green is the BTHP pool which is now steady as well. The uneven bumps are probably owed to timing in the driver and probably latency on the radio link.

So what was the issue? Well, previously I was only writing to the HID Control Channel (periodically) because that's how the HID Output Report travels to the remote device. Now since I couldn't find any issues with my memory handling in my own drivers I was starting to experiment and implemented reading from the control channel (where there shouldn't be any data coming back because the DS3 sends data over the HID Interrupt Channel) as well and look at that: with every packet I send out there's one byte of "response" available to read as well! The content is always a solid 0x00 but still, this was allocating and holding memory in the lower sections of bthport.sys and the reason for the rise in non-paged memory usage. I'm now also periodically reading and therefore "emptying" the control channel and now it's not growing anymore. Nice!

Time for a break ☀

Guess some sort of Writer's Block got me, but for coding. No worries though, will do something else then until the spirits are up for it again 😄

Cheers 🥂

I've currently "interrupted" further coding with building my WHQL lab. The progress on it gets its own thread here but since my Windows 7 test boxes are ready I'll throw the current revision of the profile driver into it and see what happens 😈


Can't wait to connect my Ps3 controller via bluetooth to pc without installing malicious software 😄 (it's this project all about this right?, if yes, when it will be released? 😇 )

@epikvigem pretty much sums it up, yeah 😛 This set of drivers will allow you to use the popular PS3 peripherals (DualShock 3, Navigation & Move Controllers) transparently with little overhead on Windows 7 up to 10.

As for the estimated release schedule... If the current pace stays where it is I aim for early or mid May?


HCK had its run over night on the profile driver and look how happy it is 😅


Next I have to polish and test the filter driver and extend it with a few management features (like turning it on or off from user-land).

@nefarius this looks awesome! Really looking forward to seeing this in production! Is there anything we as a community can do or provide to assist your work? Beta testing, experiment with various BT dongles/pads, or just show interest in your product? 🙂 What kind of licensing are you thinking about once this is ready? Propriety paid/free, open source and all its variations, dual - community version open source (without binaries) VS paid WHQL certified?


@Locksmith pardon the delay, I wanted to answer this in detail as it contains a lot of interesting and valid questions.

Community involvement

I will be getting to a stage where the full stack can be tested and I would greatly appreciate it if I get a few owners of those obviously "fake" and aftermarket DualShock "PANHAI" controllers since I can't order them in my country and back in SCP-days a rather large demographic was interested in getting them to work as they typically only cost around 10$. I'm not a fan of those personally but if they work on the stack with ease, be my guest 😉 As I own the genuine Sony hardware I can guarantee a 100% compatibility for those obviously. Just a heads-up: I won't give out production-signed binaries to beta testers anymore though as that's irresponsible. So anybody interested would be required to put his or her machine into test mode. Usually not a big deal but I've heard that a few anti-cheat and DRM solutions out there won't let you launch certain games anymore until reverted.

Dongle compatibility shouldn't be much of an issue anymore as the vendor software stack is kept in place and isn't touched anymore (unlike SCP) so e.g. Intel Wireless cards will keep operating with the Intel vendor driver. Tests welcome though nonetheless.

Last but not least I ofc. welcome interest in my post-apocalypticSCP shenanigans 😉 I've launched into this mess out of personal motivation; getting comfortable with yet another kernel-mode driver, the Windows Bluetooth Driver Interface, WHQL-fun and maybe finally getting a finished plug-and-play solution so I can visit my couch again and play Castlevania 😆 A lot of what's happening here currently is straight out research and development. As a lot of people are apparently not understanding (or accepting) what "work in progress" means I keep "advertising" for the forum etc. to a minimum as long as there's not enough resources for me to research, develop, give support, regularly update news etc. This doesn't mean I want to stay in my own bubble here, I welcome anybody interested but in return I expect acceptance if things don't quite go according to plan.


Despite a few "hiccups" I've had with larger corporations taking my stuff being a bit clumsy in the past I'm still a big advocate and believer in open-source. First and foremost because I'd probably not be in this "business" of dealing with gaming peripherals if the mad lad creating SCP hadn't open-sourced his or her work plus all of the other fabulous projects and samples I profited of by studying the sources. This also includes non-Windows sources like bits and pieces in the Linux kernel or Arduino projects. So I'll definitely open-source this stack as well once I tag it production-ready.

Now one might ask why this time I didn't go for GitHub from the very beginning but use a private Git repository instead. It makes developing the back-doors so much easier! 😆 Jokes aside, once again this is another measure I had to take to protect myself from idiocy. What I've noticed over the years is that non-developers who find something interesting on GitHub usually ignore the README and other helpful resources completely, get confused because there is no "download EXE here now" button and then come haunt and annoy me and my team with questions that could've been avoided from the very start. I have no problem with answering questions but if I get used as a search engine replacement I take that as a personal offense as you clearly show me that you don't value my time. Phew, that went sour fast 😅

Back to licensing. With upcoming releases I'm gonna include an EULA to both protect myself (you know, warranty, haha) and probably place some restrictions on it regarding commercial use. Maybe dual-licensing it. Again, there are reasonable motivations behind it. To private individuals I wanna ensure all the freedom and openness as possible as that's the target demographic. Corporations though are dumb. What do I mean by that? Well, just forking a driver, re-branding and releasing it (not that this has happened before, huehuehue) is a horrible practice. Drivers need maintenance and the knowledge on how to maintain them properly. If they cause an issue on an end-users machine it needs to be diagnosed and addressed. Can they provide that? No, they'll conveniently send them to me who's expected to give free support and fix everything. Nope, get fucked, GPL and other restrictions it will be for you then. I haven't settled on the best solution there yet but I'm working on ideas that benefit all sides as much as possible.

Regarding WHQL... Binaries without it aren't really worth it nowadays so it's either just the sources and have fun or a properly signed binary. Could I sell it? Eh. Would it sell? Eh. Would I take a few Schekels if offered? Oh, absolutely, the bills haven't stopped 🤣 I need to figure this out. Bring back donations? Eh. Maybe they'll flourish again if a finished attractive product is released? So many questions, so little time. We'll figure it out 😀

I think I've got everything out I wanted to address, pardon the wall of text 😅


Meh, HCK tests on Windows 8.1 do a bit more in-depth Bluetooth testing it seems and it's complaining that I've left out some I/O handlers I thought I don't need. Well, can't argue against the machine so back to hacking and fixing 💀


There we go! 😃


Another night of wasting electricity done but the results at least are satisfying 😁


Next I'll test with children connected. Not sure how it'll react since the power and sleep tests will most certainly disconnect them from the Bluetooth host but we'll see!

I was bitching a lot about the Hardware Lab Kit and debugging utilities in general in the past but to perfectly honest; the more I become familiar with them and overcome their quirks the more I appreciate the feedback they provide.

Like for example; I've tried to connect a DS3 while the profile driver is running under Driver Verifier (which in short means that a strong rule-set applies triggering bugs that might have gone under) on Windows 8.1 and it crashed with:

This is a very common bugcheck.  Usually the exception address pinpoints
the driver/function that caused the problem.  Always note this address
as well as the link date of the driver/image that contains this address.
Arg1: ffffffff80000003, The exception code that was not handled
Arg2: fffff801f61cd3c0, The address that the exception occurred at
Arg3: 0000000000000000, Parameter 0 of the exception
Arg4: 7efefefefeff3252, Parameter 1 of the exception

Once in WinDbg with the symbols loaded we get a nice "stack trace":

... : nt!KeBugCheckEx
... : nt!KiFatalExceptionHandler+0x22
... : nt!RtlpExecuteHandlerForException+0xd
... : nt!RtlDispatchException+0x1a5
... : nt!KiDispatchException+0x18d
... : nt!KiExceptionDispatch+0xc2
... : nt!KiBreakpointTrap+0x2dc
... : nt!DbgBreakPoint+0x1
... : Wdf01000!FxIoTargetSendIoctl+0x282da
... : Wdf01000!imp_WdfIoTargetSendIoctlSynchronously+0x48
... : BthPS3!L2CAP_PS3_HandleRemoteConnect+0x16a [e:\development\gogs\bthps3\bthps3\l2cap.c @ 49] 
... : BthPS3!BthPS3_IndicationCallback+0x9c [e:\development\gogs\bthps3\bthps3\bluetooth.c @ 552] 
... : bthport!L2CapInt_ProcessL2capConnectReq+0x40b
... : bthport!L2CapInt_ProcessSignallingPacket+0x45f
... : bthport!L2CapInt_ProcessReadBip+0x13a
... : bthport!HCI_ProcessAclReadBip+0x661
... : bthport!HCI_ProcessAclRead+0x2a8
... : bthport!HCI_ProcessMpBip+0x92
... : bthport!BTHPORT_RecvMpBip+0x41
... : BTHUSB!BthUsb_ReadTransferComplete+0x18d
... : BTHUSB!UsbWrapWorkRoutine+0x18d
... : BTHUSB!UsbWrapInterruptReadComplete+0x1d3
... : nt!IovpLocalCompletionRoutine+0x174
... : nt!IopfCompleteRequest+0x2ee
... : nt!IovCompleteRequest+0x1d7
... : Wdf01000!FxRequest::CompleteInternal+0x23c
... : Wdf01000!imp_WdfRequestComplete+0x8c
... : BthPS3PSM!UrbFunctionBulkInTransferCompleted+0x107 [e:\development\gogs\bthps3\bthps3psm\filter.c @ 233] 
... : Wdf01000!FxRequestBase::CompleteSubmitted+0x459
... : Wdf01000!FxIoTarget::_RequestCompletionRoutine+0x162
... : nt!IopUnloadSafeCompletion+0x49
... : nt!IovpLocalCompletionRoutine+0x174
... : nt!IopfCompleteRequest+0x2ee
... : nt!IovCompleteRequest+0x1d7
... : USBPORT!USBPORT_Core_iCompleteDoneTransfer+0xa02
... : USBPORT!USBPORT_Core_iIrpCsqCompleteDoneTransfer+0x21c
... : USBPORT!USBPORT_Core_UsbIocDpc_Worker+0x238
... : USBPORT!USBPORT_Xdpc_Worker_IocDpc+0x1fe
... : nt!KiExecuteAllDpcs+0x1b0
... : nt!KiRetireDpcList+0xd7
... : nt!KiIdleLoop+0x5a

Alrighty, so line 49 in the function L2CAP_PS3_HandleRemoteConnect in l2cap.c of the driver BthPS3 is triggering the fault. Let's see what's there:


Ah, an inline function! What does it contain that might bomb? I have a suspicion (mostly because it's hinted in the stack trace 😉 )...


Let's have a look at the docs of WdfIoTargetSendIoctlSynchronously and check out the requirements section:


Hm, but our L2CAP callback is invoked at PASSIVE_LEVEL, right? Let's throw in some tracing and have another run:

2019/03/10-10:46:19.023	TRACE_LEVEL_VERBOSE	BthPS3_IndicationCallback Entry (Low (0x00))
2019/03/10-10:46:19.023	TRACE_LEVEL_VERBOSE	L2CAP_PS3_HandleRemoteConnect Entry (Low (0x00))
2019/03/10-10:46:19.038	TRACE_LEVEL_VERBOSE	BthPS3_IndicationCallback Entry (DPC (0x02))
2019/03/10-10:46:19.038	TRACE_LEVEL_VERBOSE	L2CAP_PS3_HandleRemoteConnect Entry (DPC (0x02))

Hey! 😠 The second call isn't at PASSIVE_LEVEL, that's DISPATCH_LEVEL! How dare you! Oh wait, the docs, check the docs:

IRQL Developers should code this function to operate at either IRQL = DISPATCH_LEVEL (if the callback function does not access paged memory), or IRQL = PASSIVE_LEVEL (if the callback function must access paged memory)

Ouch! How evil! 😅 So that can totally happen and !wdfkd.wdflogdump gives clear insights:

1: kd> !wdfkd.wdflogdump BthPS3
37: FxVerifierCheckIrqlLevel - Called at wrong IRQL; at level 2, should be at level 0
---- end of log ----

Right, so this would've fallen under the radar if it didn't bomb in the lab. Thanks, WHQL 😝 Onward with the fixes!

How future proof is this filter solution? Do you think it is possible for Windows to make such a change in a update that could break it or you think that is unlikely or just straight up impossible unless windows change in a fundamental way?

Sorry if this is no place to ask or if my question does not make much sense. I am no developer/coder in any meaningful way, but I know just enough about coding to understand your posts here and I find it really interesting to know how this actually works. I would really appreciate to hear your thoughts on this with some insight on why and how windows could or couldn't break this in the near future.
Also, thanks for your work on the ScpToolKit and on maintaining this posts on the development! I find myself refreshing this page many times a day just to see your explanations on how you are making your way through this.