uhidd

USB HID daemon

Overview

uhidd is a userland driver/daemon for USB HID devices like mouse, keyboard etc. It is based on libusb20 and currently only works for FreeBSD 8.x and above.

The uhidd userland driver aims to support USB HID devices with multiple Top-Level application collections in one interface, i.e, HID devices with multiple logical device sharing one endpoint. This kind of devices currently can not be fully supported by the in-kernel drivers (ukbd/ums/uhid). See the examples section below for examples of such devices and how to setup the driver to support them.

uhidd also provide support for multimedia keys found on USB keyboard, a multimedia keymap needs to be created to make it function. See the examples section below.

News

[2012-10-15] uhidd 0.2.1 released:

Download & Install

The latest release tarball is uhidd-0.2.1.tar.gz

You could also fetch the latest source code from github:

% git clone https://github.com/kaiwang27/uhidd.git

The recommended way to install uhidd is through the ports system instead of compiling the source by yourself.

# pkg install uhidd

The port will also install rc(8) script and devd(8) config file.

How to use it

uhidd daemon is using libusb20 to access usb device, so it deals with /dev/ugenX.Y nodes. Note that before you can use uhidd with certain ugenX.Y device, you need to make sure there is no kernel HID device driver attached to that device. You could either remove 'device ukbd', 'device ums' and 'device uhid' from your kernel config file and recomplie the kernel, or if these drivers are loaded as kernel modules, kldunload(8) them. The relevant devd(8) rules for those kernel modules in /etc/devd/usb.conf also needs to be removed to prevent them from loading automatically by devd(8). However, uhidd could be also used together with kernel drivers, for example uhidd and a kernel driver can each attach to one interface of the same device. See the Examples section below for details.

The following example starts uhidd deamon on /dev/ugen2.2, attaching keyboard, mouse, consumer control and generic hid child devices: (see the uhidd(8) manual page for details about the command line args)

# /usr/local/sbin/uhidd -kmohs /dev/ugen2.2

Or, if you installed uhidd from ports you can use the uhidd rc(8) script to simplify things:

Frist add the following lines to /etc/rc.conf: (uhidd_flags corresponding to the command line args)

uhidd_flags="-kmohs"
uhidd_enable="YES"

Then you have several ways to invoke the rc(8) script:

To start uhidd on /dev/ugen2.2

# /usr/local/etc/rc.d/uhidd start /dev/ugen2.2

To stop uhidd on /dev/ugen2.2

# /usr/local/etc/rc.d/uhidd stop /dev/ugen2.2

To start uhidd on all /dev/ugenX.Y devices that do not have uhidd attached: (This command will run automatically at the startup, if uhidd_enable="YES" is present)

# /usr/local/etc/rc.d/uhidd start

To stop all uhidd instances:

# /usr/local/etc/rc.d/uhidd stop

To start uhidd on new attached ugenX.Y device automatically, you can use the following devd(8) config file: (It is already installed to /usr/local/etc/devd/ if you install uhidd from ports )

notify 0 {
        match "subsystem" "DEVICE";
        match "type" "ATTACH";
        match "cdev" "ugen[0-9]+.[0-9]+";
        action "env LD_PRELOAD=/usr/local/lib/libcuse4bsd.so /usr/local/etc/rc.d/uhidd start /dev/$cdev";
};

Note that uhidd will ignore ugenX.Y device that does not have USB HID interfaces, so it is safe to "try" attaching uhidd on any ugenX.Y device.

Coexists/Conflicts with kernel drivers

The recommanded way to use uhidd is:

Note that if you have USB kernel drivers(ukbd(4),ums(4) and uhid(4)) compiled as modules, it's possible that uhidd and USB kernel drivers attach to the device at the same time and cause strange behaviors like mouse cursor jumping, keyboard key repeating etc. It's better to remove the relevant devd(8) rules from /etc/devd/usb.conf in this case. To recover from such issues, you can restart uhidd by /usr/local/etc/rc.d uhidd restart.

How does it work

The uhidd daemon attaches interface driver to each interface of the device and attaches sub class drivers to the top-level application collections inside the interface.

The HID interface driver retrieve hid data from the shared interrupt endpoint using libusb20, then dispatch the data to one of the class drivers, based on the report ID found in the beginning of data. The HID class driver then process the data as appropriate:

Multimedia Keys

To support multimedia keys basically is to translate USB consumer usage to keycodes. A multimedia keymap that describes the usage and keycode binding is required in order to make it work. You can build the keymap manually, or better you can let uhidd generate it automatically.

When you start uhidd on a USB mutlimedia Keyboard without a pre-configured keymap, uhidd will prepare a in memory keymap. Each time you pressed a new multimedia keys, uhidd will remember the key(hid usage) you pressed and assign a free keycode to it, then it adds the usage<->keycode binding to the in memory keymap. The keymap will also be written down to the file /var/run/uhidd.ugen.X.Y/cc_keymap, what you need to do is copy the keymap to the configuration file /usr/local/etc/uhidd.conf, so next time uhidd will load the fixed keymap for this keyboard instead of generating another new keymap again. To use the translated keycodes under X window, you will need to write yet another keymap .Xmodmap that map the keycode to X window key event. See the Examples section below for an complete setup example for Mircosoft Wireless Desktop 1000.

To build the keymap manually, you need to know the consumer usage names and a bunch of unassigned keycodes. Consumer usage name list can be found in usage_consumer.c in the source code tarball. A list of free keycodes and the format of the keymap is documented in the uhidd.conf(5) manual page.

If you played with USB multimedia keyboard on FreeBSD before, you were probably using the usbhidaction(1) utility. The advantage of uhidd is that it does not translate hid usage directly to an action(command), instead it only translate them to keycodes and let other applications (e.g. KDE) to handle keycodes->action translation. So in my opinion uhidd is more suitable if you intent to let X window (KDE/Gnome) to handle multimedia key events, for example. Further more, uhidd support more USB multimedia keyboards than usbhidaction(1)

Examples

PlayStation 2 Hand Controller to USB converter

The convertor can support two PS2 hand controls. Below is the appearance of the device.

PlayStation 2 Hand Controller to USB converter

When connected to the PC under FreeBSD with in-kernel uhid(4) driver active, it will attach itself as a single /dev/uhid<X> device. However, from the report descriptor of the converter, it's actually one interface containing two joystick:

USAGE PAGE Generic Desktop(0x1)
USAGE Joystick(0x4)[Generic Desktop(0x1)]
COLLECTION Application(1)
  REPORT ID 1
  ......
END COLLECTION
USAGE Joystick(0x4)[Generic Desktop(0x1)]
COLLECTION Application(1)
  REPORT ID 2
  ......
END COLLECTION

There are two top-level application collections (joystick) corresponding to the two PS2 hand controller it supports. The first controller has report ID 1, and the second has report ID 2. This converter works just fine under M$ Windows, but it won't work under FreeBSD (or Linux the last time I tested, it attached as just one /dev/js0) The problem is that some applications/libraries (for example devel/sdl12) that deal with USB joysticks expect that there should be exactly one /dev/uhid<X> node for each USB joystick, but here we got one /dev/uhid<X> for two joysticks. So it just won't work.

uhidd daemon on the other hand can handle this device properly. It will attach those two joystick application collections as two generic hid child devices and creates two virtual HID device nodes. The use uhidd daemon with this device, first you need deactivate the in-kernel uhid(4) driver, then execute:

# uhidd -hs /dev/ugenX.Y

-h tells the daemon to attach generic hid child device. -s tells the daemon to strip the report ID for each report data, to mimic the behaviour of the uhid(4) driver. Note that you should unload uhid.ko kernel modules first, otherwise the uhid(4) kernel module and the uhidd daemon will each get a part of the hid data. (and there won't be any warning/error messages.)

At this point, you should find two newly created uvhid<X> (or any other name specified in the configuration option) nodes under /dev. If you use the defualt uvhid<X> names, you will probably need to create two symbolic links, in case the application/library has hard-coded /dev/uhidX in its source code:

# cd /dev
# ln -s uvhid0 uhid0
# ln -s uvhid1 uhid1

Then start the application and it should just work.

Microsoft Keyboard Mouse Combo

The M$ keyboard/mouse combos usually has two interfaces: one interface contains a single keyboard application collection, another one contains a mouse collection plus several consumer control collections. At the time of writing the kernel ums(4) driver still has problems supporting the mouse part, while the keyboard is well supported by the ukbd(4) (except that the multimedia keys won't work.)

Below is the report descriptor of the combos:

[interface:0]
USAGE Keyboard(0x6)[Generic Desktop(0x1)]
COLLECTION Application(1)
    ...... (omitted)
END COLLECTION

[interface:1]
USAGE Consumer Control(0x1)[Consumer(0xc)]
COLLECTION Application(1)
    ......
END COLLECTION
USAGE Mouse(0x2)[Generic Desktop(0x1)]
COLLECTION Application(1)
    ......
END COLLECTION
USAGE Consumer Control(0x1)[Consumer(0xc)]
COLLECTION Application(1)
    ......
END COLLECTION
USAGE System Control(0x80)[Generic Desktop(0x1)]
COLLECTION Application(1)
    ......
END COLLECTION

To use uhidd daemon with the combo, first you need to deactivate in-kernel uhid(4), ukbd(4) and ums(4) driver and then execute:

# uhidd -kmo /dev/ugenX.Y

-k tells the daemon to attach the keyboard, -m to attach the mouse and -o to attach the consumer control driver to handle multimedia keys.

Since the keyboard collection is on a separate interface and is the only child device of that interface, it is possible to let the kernel ukbd(4) driver handle the keyboard interface, while uhidd handles the mouse and multimedia keys.

# kldload ukbd.ko
# uhidd -mo /dev/ugenX.Y

Letting kernel ukbd(4) driver handle the keyboard interface makes it possible to use ddb(4) with this keyboard/mouse combo.

At this point, the mouse should work just fine, while additional work is needed to make the multimedia keys work. First you can just press the multimedia keys on the keyboard, at least once for each key. Then check if you can find a keymap file /var/run/uhidd.ugen.X.Y/cc_keymap created by the uhidd daemon. What you need to do is to copy paste the content of this file to your uhidd configuration file (usually /usr/local/etc/uhidd.conf, create a new one if you need) and restart uhidd daemon. The daemon will then load the provided keymap and translate keys according to the bindings defined in it. Note that if you do not copy the keymap to configuration file for this keyboard, uhidd will try to generate a new keymap (probably different bindings) again the next time it restarts.

Example multimedia keymap for Microsoft Wiress Desktop 1000:

# 0x045e is the vendor id and 0x009d is the product id.
# See `uhidd.conf(5)` manual page for details about configuration file.
0x045e:0x009d={
        cc_keymap={
                Help="0x7F"
                Scan_Next_Track="0x62"
                Scan_Previous_Track="0x60"
                Stop="0x70"
                Play/Pause="0x54"
                Mute="0x63"
                Volume_Increment="0x5A"
                Volume_Decrement="0x5F"
                AL_Consumer_Control_Configuration="0x74"
                AL_Email_Reader="0x75"
                AL_Calculator="0x76"
                AL_Network_Chat="0x7C"
                AL_Logoff="0x73"
                AL_Documents="0x6F"
                AL_Spell_Check="0x6C"
                AL_Image_Browser="0x71"
                AL_Audio_Browser="0x72"
                AC_New="0x66"
                AC_Open="0x67"
                AC_Close="0x68"
                AC_Save="0x6D"
                AC_Print="0x6E"
                AC_Undo="0x64"
                AC_Home="0x7A"
                AC_Redo/Repeat="0x65"
                AC_Reply="0x69"
                AC_Forward_Msg="0x6A"
                AC_Send="0x6B"
        }
}

Now you can confirm this multimedia key translation works by observing key events under xev(1), assuming you are using X. It is working if xev(1) generate something when you press the multimedia keys. To make the keycodes work under KDE/Gnome you need to create a Xmodmap that additionally translates the keycodes reported by xev(1) to X key events. Below is a example Xmodmap for Wireless Desktop 1000:

keycode 170 = XF86AudioRaiseVolume
keycode 184 = XF86AudioLowerVolume
keycode 190 = XF86AudioMute
......

It can be loaded by execute:

xmodmap .Xmodmap

You can put this line into your X initialization file.

For most multimedia keyboards, the basic keyboard part and the multimedia keys are on separate interfaces. So it's perfectly OK to let ukbd(4) handle the basic keyboard, and let uhidd deal with the multimedia keys.

The uhidd daemon is confirmed working with MICROSOFT WIRELESS LASER DESKTOP 5000 USB and MICROSOFT WIRELESS OPTICAL DESKTOP 1000 USB, and it should work with other models in the series as well.

Debugging and Report Bugs

Normally uhidd runs in daemon mode in background. Command line option -d makes it run in foreground. -v makes it output debugging information and multiple -v specified in the command line will increase the level of verbosity. -D makes the daemon dump the report descriptor.

If you are having problems using uhidd with your USB hid device, send the output of the following command to kaiw@FreeBSD.org

# uhidd -dD -vvv -kmohu /dev/ugenX.Y

uhidd (last edited 2019-09-18T02:39:21+0000 by KubilayKocak)