For some time now, I have been partial to cordless mice; they’re much less of a hastle to use than “conventional” mice, especially if you use a laptop primarily. Several months ago, I decided to upgrade from a Logitech MX700 cordless optical mouse to an MX900 Bluetooth optical mouse, so that with my new Bluetooth-enabled laptop, I would not need to bring the bulky charger/base station it to plug into my computer at work every day.
As it would happen, the MX900 base station has a Bluetooth receiver that you can use (in conjunction with the WIDCOMM – now Broadcom – Bluetooth stack) to connect to other Bluetooth devices out there. At the time when I first got the mouse, I didn’t really see this as all that useful, as my laptop already had an integrated Bluetooth receiver that was supposed by the Microsoft Bluetooth stack included with Windows XP SP2. Recently, however, I got a second Bluetooth enabled device – a new cell phone – and decided that I might as well see what I could do with getting one of my other computers at my apartment talking to it.
 Now, a bit of background about the MX900 base station. It’s actually a pretty neat thing – during boot (and even in normal operating system use, if you don’t have the right software installed), the MX900 will act as if it were a standard HID USB mouse even though it is actually connected through Bluetooth – “HID emulation mode”, as I call it. This is a cool feature because it allows you to use your standard USB mouse drivers with the MX900 without having to go install all of Logitech’s drivers and the like before the mouse will work. Additionally, if your BIOS supports USB input devices (most modern ones do), you can use the MX900 there even though it functions over Bluetooth.
As a result of the handy HID emulation mode feature of the MX900, I can already use it as a mouse on my other, non-Bluetooth computers as if it were a plain USB mouse, with the operating system none the wiser. Therein is the rub, however; in order for me to be able to connect the MX900 base station to non-keyboard/mouse devices, I need to be able to convince Windows that it is actually a full fludged Bluetooth receiver and not just a USB mouse. Normally, Logitech’s SetPoint software installs a program that runs when you log in to Windows and magically turns the MX900 base station into Bluetooth HCI mode, that is, native Bluetooth receiver mode – assuming you had installed the WIDCOMM bluetooth stack, that is.
 So, I set out to install SetPoint on my test computer. Unfortunately, this didn’t really work out as planned. The computer I had available to work with was running Windows Server 2003 and it seems that the SetPoint installer for the version I needed wasn’t exactly well tested on Windows Server 2003. The installer would tend to blow up with heap corruption right away, making it impossible to do anything. I then tried running the installer under the Windows XP SP2 compatibility layer (right click the .exe, there is a compatibility option in the propsheet if you an administrator). This got me a bit further, but the Logitech installer inevitibly crashed.
Looking around a bit, there was actually a more recent version of SetPoint available (Logitech supports 2.22 with the MX900, the latesting being 2.60 which is designed for Logitech’s Bluetooth keyboard and mouse suite). I figured that it was worth a try to install 2.60 and see if that worked. Sure enough, the installer actually didn’t crash this time, but unfortunately, it would not accept that I had a Bluetooth device that was compatible with it; I got stuck at a dialog that instructed me to connect my Logitech Bluetooth device and hit OK, or skip the installation of the Bluetooth support and install “just plain” SetPoint. Well, that sucks – the whole point of this excercise was to get Bluetooth working on the test computer, not Logitech’s middleware.
Poking around in my temp directory, I noticed that while the installer was running, one of the temporary directories it created seemed to have a second installer for the WIDCOMM Bluetooth stack (WIDCOMM – now Broadcom - does not make their software directly available for download to end users, and instead requires them to get it bundled with hardware from an equipment manufacturer). A-ha – maybe there was light at the end of the tunnel, after all. While the Logitech installer was waiting for me to hit Next in one of the wizard steps, I manually launched the WIDCOMM installer from the temp directory that the Logitech installer had created. The installer actually worked fine, except that it too complained that it could not detect an active Bluetooth device (fortunately, though, it allowed me the option of continuing the install anyway).
After the WIDCOMM installer finished, I canceled out of the Logitech install and went to see if I could convince the WIDCOMM stack that I really did have a Bluetooth device. After not getting anywhere on my own, I turned to Google, where I found a number of people complaining about the same problem (about not being able to turn their MX900 receivers to native HCI mode), but no quick solution for Windows. I did, however, find something for Linux – a program called “hid2hci” that knew how to turn an MX900 Bluetooth receiver to a HCI mode. Fortunately, source code was included, so it was easy enough to see what it was doing. Unfortunately, I don’t really have a whole lot of experience with USB, on Windows or other platforms, and what I needed to do was port hid2hci to Windows.
The linux program is fairly simple. Even with my limited knowledge of USB, what it was doing appeared to be straightforward enough. The program sends three vendor-specific HID output reports (a HID report is the basic way to either report information from a device to the computer or change a setting on the device for HID devices) to the MX900 receiver. After receiving the special three HID reports, the MX900 changes its PnP ID and appears to the operating system as a different piece of hardware, an HCI Bluetooth receiver.
 So, I got started working on a Windows version of hid2hci. The first step was to crack open the Windows DDK documentation (you can download the DDK with the free KMDF 1.1 ISO distribution) and start looking around for ways to talk to USB devices. It turns out that there is already a rather full featured API to do this, from both user mode and kernel mode. Because all I really needed to do here was to send three custom commands to the MX900, I picked the user mode HID API to start with.
The user mode HID APIs live in hid.dll and come in two flavors: HID Parser routines (prefixed HidP_), and HID Device/Driver routines (prefixed HidD_). The former provide a high level interface for formatting, preparing, and parsing the actual USB HID reports, while the latter deal with actually sending and receiving USB HID reports. The API is a bit cumbersome, but it turns out to not be too hard to use. The basic idea is:
- Open a handle to the HID device that you want to talk to with CreateFile.
- Call HidD_GetPreparsedData to load the preparsed data for the collection. This basically retrieves all sorts of information about the HID device ahead of time in one blob that is later used by the HID Parser routines to ensure that you are matching the report formats used by the device.
- Call HidD_GetAttributes and HidD_GetCaps to make sure that the device is the one you meant to connect to and supports the HID usages that you are going to use. Here, I wanted to verify that the vendor specific usage page 0xFF00, usage 0x0001 is present (as this is where I wanted to send the magic 3 reports to turn the receiver to HCI mode).
- Build the HID report. I originally attempted to do this using the high level HID Parser APIs, but I couldn’t get it to work right – the HID Parser APIs kept complaining that the usage I requested didn’t exist. I assume that this is because Logitech never bothered to completely describe the format of the HID reports for this vendor specific usage, resulting in the high level parser becoming unhappy if you asked it to format a report for that usage. As a result, I just built the report manually by just writing the raw data into the report buffer and prepending the HID report ID (0x10) to the report data.
- Send the completed report to the device. There are two ways to do this – WriteFile and HidD_SetOutputReport. I attempted to use WriteFile first, but it always failed with ERROR_ACCESS_DENIED. Not being an expert on HID devices, I tried the other documented routine (HidD_SetOutputReport) send the report, which worked fine. HidD_SetOutputReport internally just sends a special IOCTL to the driver for the device you open, so the code paths are in fact different.
Steps 4 and 5 will basically need to be repeated for each of the three HID reports that we need to send to the Bluetooth receiver.
 There are a couple of other things that you need to do in order to get this to work that I glossed over. In particular, you need to actually find the device that you want to open with CreateFile. The best way to do this is by using the SetupDi family of APIs to enumerate all HID devices. We can then verify that each device has the expected vendor ID, product ID, and HID usages before we try to send it the magical commands to convert the device to native HCI mode.
After putting all of these steps together, I had something that appeared to do what the Linux hid2hci program did. Sure enough, when I ran my prototype hid2hci port on my test box, a new device appeared in Device Manager, which was detected by the WIDCOMM Bluetooth stack as a Cambridge Silicon Radio Bluetooth Receiver. Success!
The device itself stays in native HCI mode until it is reset (i.e. rebooting the computer or unplugging the receiver itself), so the HCI conversion program needs to either periodically scan for devices to switch to HCI mode, or register for a device change notification in order to enable full Bluetooth functionality if you reboot or disconnect the Bluetooth receiver.
The source code for my Logitech HID-to-HCI convertor program is available for download if you are interested in it. You will need Windows DDK installed in order to build it. Alternatively, you can download the binary if you just want the program and don’t want to install the development environment to build it. It takes two command line arguments: the hexadecimal vendor ID and hexadecimal product ID of the device that it should switch from HID emulation mode to native HCI mode. You can find these under Device Manager if you are using Windows XP SP2 or Windows Server 2003 SP1 by going to your device’s property sheet and going to the details tab, then selecting the Hardware Ids listbox item. The device you want is probably going to be named “USB Composite Device”. If you are using an MX900, then you can use 046d for the vendor ID and c705 for the product ID. There is no harm in running the program repeatedly after it has already switched your device(s) to HCI mode.