HOWTO: Using multimedia keys from uhid(4) devices

Markand

Member

Reaction score: 11
Messages: 90

Hello,

People that have keyboard with multimedia keys such as "Play" "Volume up" and so on may encounter problems if these keys are not detected by xev(1).

In fact there are two ways to use these keys, a lot of manufacturers usually set these keys are simple keys with a special code that you can deal with xev(1). Some USB keyboards prefer using HID instead. It's better for some reasons like: you can use these keys without X and without setting anything in your window manager usbhidaction(1) is there for this.

1. Detect your USB (uhid) keyboard

First, check if you really have a uhid(4) device, do not forget to add it to your kernel config!

$ dmesg | grep uhid
Code:
uhid0: <BTC USB Multimedia Keyboard, class 0/0, rev 1.10/1.20, addr 3> on usbus0
Good news, uhid0 is my keyboard!

2. Get the usable keys

Now we need to see which keys are available. Let see on mine.

$ usbhidctl -f /dev/uhid0 -r
Code:
Report descriptor:
Collection page=Generic_Desktop usage=System_Control
Input   size=1 count=1 page=Generic_Desktop usage=System_Power_Down, logical range 0..1
Input   size=1 count=1 page=Generic_Desktop usage=System_Sleep, logical range 0..1
Input   size=1 count=1 page=Generic_Desktop usage=System_Wake_Up, logical range 0..1
End collection
Collection page=Consumer usage=Consumer_Control
Input   size=1 count=1 page=Consumer usage=Volume_Increment, logical range 0..1
Input   size=1 count=1 page=Consumer usage=Volume_Decrement, logical range 0..1
Input   size=1 count=1 page=Consumer usage=Mute, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AC_Search, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AC_Home, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AC_Bookmarks, logical range 0..1
Input   size=1 count=1 page=Consumer usage=Help, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AL_Email_Reader, logical range 0..1
Input   size=1 count=1 page=Consumer usage=Stop, logical range 0..1
Input   size=1 count=1 page=Consumer usage=Scan_Next_Track, logical range 0..1
Input   size=1 count=1 page=Consumer usage=Play/Pause, logical range 0..1
Input   size=1 count=1 page=Consumer usage=Scan_Previous_Track, logical range 0..1
Input   size=1 count=1 page=Consumer usage=Eject, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AC_Back, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AC_Forward, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AC_Stop, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AC_Copy, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AC_Cut, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AL_Consumer_Control_Configuration, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AL_Local_Machine_Browser, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AL_Calculator, logical range 0..1
Input   size=1 count=1 page=Consumer usage=AC_Refresh, logical range 0..1
End collection

... skipping useless microsoft usages ...

Total   input size 0 bytes
Total  output size 0 bytes
Total feature size 0 bytes
My keyboard only have the following buttons :
  • Volume up "Volume_Increment"
  • Volume down "Volume_Decrement"
  • Volume muting "Mute"
  • System sleep "System_Sleep"

The other keys described are not on the keyboard but I guess the manufacturer didn't want to remove then, but no worries it will work.

3. Write a usbhidaction(1) config file

Now it's time to setup a usbhidaction(1) file to associate a shell command to the specified button, the syntax is really easy.

Let's try to add an action for the Volume_Increment input:

Collection page=Consumer(1) usage=Consumer_Control(2)
Input size=1 count=1 page=Consumer(3) usage=Volume_Increment(4), logical range 0..1

There is two way to fill an action, by specifying everything like this:

Code:
# Syntax of usbhidaction file
# Collection page(1):Collection usage(2).input page(4):input usage(3) bounce debounce command

Consumer:Consumer_Control.Consumer:Volume_Increment 1 0 mixer vol +2
Or the simpliest method is to remove (2) and (4) and simply use the following:

Code:
# Simplified syntax

Consumer:Volume_Increment 1 0 mixer vol +2
You must admit, it's really better.

But what are these 1 0 ?

As you can see the input has two logical range logical range 0..1, because it's a button it can has two states: pressed and released. 1 means pressed and 0 means released (I guess).

Thus the usbhidaction(1) waits for the bounce value 1 and the debounce value 0 that simply means the key was pressed and released.

4. Passing arguments when calling the action

Whenever the command is called you can pass any arguments you like just like a shell command see :

Code:
# Syntax with arguments passed when calling the command
# $1 is the mixer device
# $2 is the increment value I want to use

Consumer:Volume_Increment 1 0 mixer -f $1 vol +$2
Now if I run the usbhidaction(1) and give the both arguments it will use them each time I press the key.

$ usbhidaction -d -f /dev/uhid0 -c usbaction.conf /dev/mixer1 2
Code:
Setting the mixer vol from 56:56 to 58:58.
Setting the mixer vol from 58:58 to 60:60.
Setting the mixer vol from 60:60 to 62:62.
Setting the mixer vol from 62:62 to 64:64.
As you can see in this example I passed the /dev/mixer device and the increment value I wanted, then I pressed my volume up button fourth times.

5. Automatically run usbhidaction when the device is found

Now you may guess, using the /dev/uhid0 device is not well, imagine you have a joypad detected as /dev/uhid0 before the keyboard you will need to check which one is the good one. So now we will add a devd(8) appropriate entry to match only our keyboard.

First we need to get the keyboard hid vendor Id and product Id with usbconfig(8)

I know that my keyboard hid is /dev/uhid0 but to know the vendor Id and product Id I need to deal with ugenx.y devices.

[cmd=]$ usbconfig show_ifdrv[/cmd]
Code:
ugen0.1: <EHCI root HUB Intel> at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE
ugen0.1.0: uhub0: <Intel EHCI root HUB, class 9/0, rev 2.00/1.00, addr 1>
ugen1.1: <EHCI root HUB Intel> at usbus1, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE
ugen1.1.0: uhub1: <Intel EHCI root HUB, class 9/0, rev 2.00/1.00, addr 1>
ugen1.2: <product 0x0020 vendor 0x8087> at usbus1, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON
ugen0.2: <product 0x0020 vendor 0x8087> at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE
ugen0.2.0: uhub3: <vendor 0x8087 product 0x0020, class 9/0, rev 2.00/0.00, addr 2>
ugen0.3: <USB Multimedia Keyboard BTC> at usbus0, cfg=0 md=HOST spd=LOW (1.5Mbps) pwr=ON
ugen0.3.0: ukbd0: <BTC USB Multimedia Keyboard, class 0/0, rev 1.10/1.20, addr 3>
ugen0.3.1: uhid0: <BTC USB Multimedia Keyboard, class 0/0, rev 1.10/1.20, addr 3>
ugen0.4: <USB Laser Mouse Logitech> at usbus0, cfg=0 md=HOST spd=LOW (1.5Mbps) pwr=ON
ugen0.4.0: ums0: <Logitech USB Laser Mouse, class 0/0, rev 2.00/31.00, addr 4>
ugen0.5: <EDRClassone vendor 0x0a12> at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON
ugen0.5.0: ubt0: <vendor 0x0a12 EDRClassone, class 224/1, rev 2.00/19.58, addr 5>
Now I know that my keyboard hid is located at ugen0.3, directly attached to the same ukbd(4) device.

We need to get the vendor Id and product Id

$ usbconfig -d ugen0.3 dump_device_desc
Code:
ugen0.3: <USB Multimedia Keyboard BTC> at usbus0, cfg=0 md=HOST spd=LOW (1.5Mbps) pwr=ON

  bLength = 0x0012 
  bDescriptorType = 0x0001 
  bcdUSB = 0x0110 
  bDeviceClass = 0x0000 
  bDeviceSubClass = 0x0000 
  bDeviceProtocol = 0x0000 
  bMaxPacketSize0 = 0x0008 
  [I][B]idVendor = 0x046e (1)[/B][/I]
  [I][B]idProduct = 0x55a5 (2)[/B][/I]
  bcdDevice = 0x0120 
  iManufacturer = 0x0001  <BTC>
  iProduct = 0x0002  <USB Multimedia Keyboard>
  iSerialNumber = 0x0000  <no string>
  bNumConfigurations = 0x0001
Now, we need to add the devd.conf(5) entry:

Code:
attach 100 {
        device-name "uhid[0-9]+";                                                           
        match "vendor"  "0x046e"; # (1)
        match "product" "0x55a5"; # (2)

	#
	# Do not add -d flag ! if you do it devd will never detach to background
	# because usbhidaction will run in foreground !
	#
	# Remember you can add arguments after /etc/usbhidaction.conf like
	# action "/usr/bin/usbhidaction -f $device-name -c /etc/usbhidaction.conf /dev/mixer1 2"
	#
        action "/usr/bin/usbhidaction -f $device-name -c /etc/usbhidaction.conf";
};
Not so hard, and of course the usbhidaction(1) config in /etc/usbhidaction.conf like this:

Code:
#
# For Emprex keyboard
#

Consumer:Volume_Increment	1 0 mixer vol +2
Consumer:Volume_Decrement	1 0 mixer vol -2
Consumer:Mute			1 0 mixer vol 0
That's it! Now each time you reboot it will automatically run usbhidaction(1) only for your keyboard hid and not any other uhid(4) devices.

I hope this helps!
 

Greg Fitzgerald

Member

Reaction score: 3
Messages: 54

When I configure usbhidaction on the command line it all works perfectly. Once I add it to devd.conf and reboot my uhid2 for my keyboard is none existent (even though the keyboard works fine), and I get a error when devd starts up that the uhid is missing Consumer:Volume_Increment. I double checked my vendor and product id's in my devd.conf. Does anyone know what could cause this?

Code:
Consumer:Volume_Increment 1 0 /usr/local/bin/vol up
Consumer:Volume_Decrement 1 0 /usr/local/bin/vol down
Consumer:Mute 1 0 /usr/local/bin/vol toggle
Code:
attach 100 {
        device-name "uhid[0-9]+";                                                          
        match "vendor"  "0x04d9";
        match "product" "0x0348";

    #
    # Do not add -d flag ! if you do it devd will never detach to background
    # because usbhidaction will run in foreground !
    #
    # Remember you can add arguments after /etc/usbhidaction.conf like
    # action "/usr/bin/usbhidaction -f $device-name -c /etc/usbhidaction.conf /dev/mixer1 2"
    #
        action "/usr/bin/usbhidaction -f $device-name -c /etc/usbhidaction.conf";
};
Code:
usbconfig -d ugen2.2 dump_device_desc
ugen2.2: <DuckyChannel International Co., Ltd. Ducky Keyboard> at usbus2, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)

  bLength = 0x0012 
  bDescriptorType = 0x0001 
  bcdUSB = 0x0200 
  bDeviceClass = 0x0000  <Probed by interface class>
  bDeviceSubClass = 0x0000 
  bDeviceProtocol = 0x0000 
  bMaxPacketSize0 = 0x0040 
  idVendor = 0x04d9 
  idProduct = 0x0348 
  bcdDevice = 0x0110 
  iManufacturer = 0x0003  <DuckyChannel International Co., Ltd.>
  iProduct = 0x0001  <Ducky Keyboard>
  iSerialNumber = 0x0000  <no string>
  bNumConfigurations = 0x0001
 
Top