Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I picked up a Razer BlackWidow Ultimate that has additional keys meant for macros that are set using a tool that's installed on Windows. I'm assuming that these aren't some fancypants joojoo keys and should emit scancodes like any other keys.

Firstly, is there a standard way to check these scancodes in Linux? Secondly, how do I set these keys to do things in command line and X-based Linux setups? My current Linux install is Xubuntu 10.10, but I'll be switching to Kubuntu once I have a few things fixed up. Ideally the answer should be generic and system-wide.

Things I have tried so far:

Things I need to try

  • snoopy pro + reverse engineering (oh dear)

  • Wireshark - preliminary futzing around seems to indicate no scancodes emitted when what I seem to think is the keyboard is monitored and keys pressed. Might indicate additional keys are a separate device or need to be initialised somehow.

  • Need to cross reference that with lsusb output from Linux, in three scenarios: standalone, passed through to a Windows VM without the drivers installed, and the same with.

  • LSUSB only detects one device on a standalone Linux install

  • It might be useful to check if the mice use the same Razer Synapse driver , since that means some variation of razercfg might work (not detected, only seems to work for mice)

Things I have worked out:

  • In a Windows system with the driver, the keyboard is seen as a keyboard and a pointing device. The pointing device uses - in addition to your bog standard mouse drivers - a driver for something called a Razer Synapse.

  • Mouse driver seen in Linux under evdev and lsusb as well

  • Single device under OS X apparently, though I have yet to try lsusb equivalent on that

  • Keyboard goes into pulsing backlight mode in OS X upon initialisation with the driver. This should probably indicate that there's some initialisation sequence sent to the keyboard on activation.

  • They are, in fact, fancypants joojoo keys.

Extending this question a little:

I have access to a Windows system so if I need to use any tools on that to help answer the question, it's fine. I can also try it on systems with and without the config utility. The expected end result is still to make those keys usable on Linux however.

I also realise this is a very specific family of hardware. I would be willing to test anything that makes sense on a Linux system if I have detailed instructions - this should open up the question to people who have Linux skills, but no access to this keyboard.

The minimum end result I require:

I need these keys detected, and usable in any fashion on any of the current graphical mainstream Ubuntu variants, and naturally have to work with my keyboard. Virtual cookie and mad props if it's something nicely packaged and usable by the average user.

I will require compiled code that will work on my system, or a source that I can compile (with instructions if it's more complex than ./configure , make, make install) if additional software not on the Ubuntu repositories for the current LTS or standard desktop release at the time of the answer. I will also require sufficient information to replicate, and successfully use the keys on my own system.

share|improve this question
    
please update with any solutions, I'd love to achieve the same! –  Jonathan Day Oct 30 '11 at 22:39
    
I've really not had time to work on it lately, but i will be updating this question with whatever i've done, until i get an answer. –  Journeyman Geek Oct 30 '11 at 23:45
    
Maybe edit to mention the 500 rep in the large font? Otherwise that proclamation doesn't make much sense (given the 50 rep bounty registered with the system) –  Daniel Beck Nov 23 '11 at 18:02
    
Does this still work for you? (I'm talking about the latest firmware mentioned below by Sergey) –  Adonis K. Apr 9 '13 at 23:58
    
I've not tested it, I'm afraid, i've been meaning to try sergey's solution to see if it works, but I've been running windows mostly the past few months for schoolwork. If it does, I'll post a comment to that effect –  Journeyman Geek Apr 10 '13 at 3:02

5 Answers 5

up vote 33 down vote accepted
+500

M1-M5 are in fact regular keys - they just need to be specifically enabled before pressing them will generate a scancode. tux_mark_5 developed a small Haskell program which sends the correct SET_REPORT message to Razer keyboards to enable these keys, and ex-parrot ported the same code to Python.

On Arch linux systems the Python port has been packaged, and is available from https://aur.archlinux.org/packages.php?ID=60518.

On Debian or Ubuntu systems setting up the Python port of the code is relatively easy. You need to install PyUSB and libusb (as root):

    aptitude install python-usb

Then grab the blackwidow_enable.py file from http://finch.am/projects/blackwidow/ and execute it (also as root):

    chmod +x blackwidow_enable.py
    ./blackwidow_enable.py

This will enable the keys until the keyboard is unplugged or the machine is rebooted. To make this permanent call the script from whatever style of startup script you most prefer. For instructions on how to set this up in Debian have a look at the Debian documentation.

To use tux_mark_5's Haskell code you'll need to install Haskell and compile the code yourself. These instructions are for a Debian-like system (including Ubuntu).

  1. Install GHC, libusb-1.0-0-dev and cabal (as root):

    aptitude install ghc libusb-1.0-0-dev cabal-install git pkg-config
    
  2. Fetch the list of packages:

    cabal update
    
  3. Install USB bindings for haskell (no need for root):

    cabal install usb
    
  4. Download the utility:

    git clone git://github.com/tuxmark5/EnableRazer.git
    
  5. Build the utility:

    cabal configure
    cabal build
    
  6. Run the utility (also as root):

    ./dist/build/EnableRazer/EnableRazer
    

After this you can copy EnableRazer binary anywhere you want and run it at startup.

Immediately after execution, X server should see M1 as XF86Tools, M2 as XF86Launch5, M3 as XF86Launch6, M4 as XF86Launch7 and M5 as XF86Launch8. Events for FN are emitted as well. These keys can be bound within xbindkeys or KDE's system settings to arbitrary actions.

Since your keyboard might be different, you might need to change the product ID in Main.hs line 64:

withDevice 0x1532 0x<HERE GOES YOUR KEYBOARD's PRODUCT ID> $ \dev -> do
share|improve this answer
    
This works, good going. You'll get 500 rep in a day or two, when the system allows me to. I've taken the liberty of adding pkg-config, which was needed to install a prerequisite of the haskell usb package to the list of prerequisites. The software works with no modifications whatsoever on my standard blackwidow ultimate, with a product ID of 1532:010d according to lsusb. Keys are detected on evtest and appear to be usable, so this has perfectly met the requirements of the question. –  Journeyman Geek Feb 1 '12 at 5:28
    
in addition, kde dosen't have a key binding option it seems, xbindkeys seems to work tho –  Journeyman Geek Feb 1 '12 at 5:36
    
with ID 10e it works with my standard blackwidow (non-ultimate). I recommend to add a udev rule, so it gets started automaticly, without problems, e.g. make a file 99-enable-razer-keyboard.rules int /etc/udev/rules.d with SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="1532", ATTR{idProduct}=="010e", RUN+="/root/EnableRazer" in it (here you may have to modify the path and id again) –  flolo Mar 3 '12 at 11:47
    
Apparently the utility I wrote works with Razer Anansi as well. Here is the blog post of someone who has pulled this off: norgelinux.blogspot.com/2012/02/razer-anasi-on-arch-linux.html –  tux_mark_5 Mar 3 '12 at 17:08
    
I have the Razer BlackWidow 2013 (not the ultimate one), when I execute the blackwidow_enable.py script (with and without root), I get the message "Blackwidow not found.". Could you please help me debug this? I'm on UbuntuGnome 13.04. It seems like the Product_ID is different and after changing the product ID, I now get this error: Could not select configuration endpoint. –  Adonis K. May 19 '13 at 3:15

My solution is for Razer BlackWidow 2013 Mechanical Gaming Keyboard (Model Number: RZ03-0039), and was tested on openSUSE 12.3.

I used Google translate on this link

Basically it uses the modified version of @Sergey's answer for this question, but with simple modifications:

  1. My PRODUCT_ID = 0x011b

  2. On my openSUSE 12.3, python-usb is not available for Python 3, so I converted this script to work with Python 2 by removing the bwcmd method, and defined the USB_BUFFER = ... as in the link from @tux_mark_5's answer.


For convenience here is the entire code:

#!/usr/bin/python

"""This is a patched version of Sergey's code form
http://superuser.com/a/474595/8647

It worked for my Razer BlackWidow 2013 Mechanical Gaming Keyboard
(Model Number: RZ03-0039).

"""
import usb
import sys

VENDOR_ID = 0x1532       # Razer
PRODUCT_ID = 0x011b      # BlackWidow 2013 Mecanical Gaming Keyboard

USB_REQUEST_TYPE = 0x21  # Host To Device | Class | Interface
USB_REQUEST = 0x09       # SET_REPORT

USB_VALUE = 0x0300
USB_INDEX = 0x2
USB_INTERFACE = 2

USB_BUFFER = b"\x00\x00\x00\x00\x00\x02\x00\x04\x02\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00"

LOG = sys.stderr.write


class blackwidow(object):
    kernel_driver_detached = False

    def __init__(self):
        self.device = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)

        if self.device is None:
            raise ValueError("Device {}:{} not found\n".format(VENDOR_ID, PRODUCT_ID))
        else:
            LOG("Found device {}:{}\n".format(VENDOR_ID, PRODUCT_ID))

        if self.device.is_kernel_driver_active(USB_INTERFACE):
            LOG("Kernel driver active. Detaching it.\n")
            self.device.detach_kernel_driver(USB_INTERFACE)
            self.kernel_driver_detached = True

        LOG("Claiming interface\n")
        usb.util.claim_interface(self.device, USB_INTERFACE)

    def __del__(self):
        LOG("Releasing claimed interface\n")
        usb.util.release_interface(self.device, USB_INTERFACE)

        if self.kernel_driver_detached:
            LOG("Reattaching the kernel driver\n")
            self.device.attach_kernel_driver(USB_INTERFACE)

        LOG("Done.\n")

    def send(self, c):
        def _send(msg):
            result = 0
            try:
                result = self.device.ctrl_transfer(USB_REQUEST_TYPE, USB_REQUEST, wValue=USB_VALUE, wIndex=USB_INDEX, data_or_wLength=USB_BUFFER)
            except:
                sys.stderr.write("Could not send data.\n")

            if result == len(USB_BUFFER):
                LOG("Data sent successfully.\n")

            return result

        if isinstance(c, list):
            for i in c:
                print(' >> {}\n'.format(i))
                _send(i)
        elif isinstance(c, str):
            _send(c)


def main():
    init_new = '0200 0403'
    init_old = '0200 0402'
    pulsate  = '0303 0201 0402'
    bright   = '0303 0301 04ff'
    normal   = '0303 0301 04a8'
    dim      = '0303 0301 0454'
    off      = '0303 0301 0400'

    bw = blackwidow()
    bw.send(init_old)


if __name__ == '__main__':
    main()
share|improve this answer
    
Where did you find a copy of pyusb that has usb.util? –  KayEss Dec 6 at 9:23
    
@KayEss, I am using python-usb-1.0.0-21.1.noarch.rpm. IIRC it is part of the the stadard repositories of 0penSUSE 12.3. According to rpm -qi, the source is at sourceforge.net/projects/pyusb and the package source is at obs://build.opensuse.org/devel:languages:python –  Chen Levy Dec 7 at 5:30
    
I'm on Ubuntu, and there's no packaged version for python3 on that. I was then having a bit of trouble getting all of sudo, python3, pip and virtualenv in a line, but it's sorted now and I'm seeing codes for the macro keys. –  KayEss Dec 7 at 7:45

Razer seems to be forcing their cloud-based Synapse 2 configurator on all users nowadays, with accompanying firmware upgrade to version 2.*. Once you have upgraded the firmware, you cannot go back (keyboard is completely bricked if you try to flash it with older firmware).

The ‘magic bytes’ from the haskell program above won't work with the latest firmware. Instead, the driver sends these bytes during the initialization sequence: ‘0200 0403’. These enable the Macro keys, but the keyboard enters a peculiar mode in which instead of the standard HID protocol it sends 16-byte packets (presumably to increase the number of keys that can be pressed simultaneously). Linux HID system cannot quite cope with this, and while most keys work as expected, the Macro keys stay unrecognized: HID driver feeds no data to the input layer when they are pressed.

To make your keyboard enter the legacy mode (in which the Macro keys send XF86Launch* keycodes, and the FN key sends keycode 202), send these bytes: 0200 0402.

The full packet will be:

00000000 00020004 02000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 
00000000 00000000 0400

Here's a very rough and dirty program I wrote in less esotheric Python3 to perform the task. Note the code to generate the Razer control packets in blackwidow.bwcmd() and the Razer logo LED commands as a bonus :)

#!/usr/bin/python3

import usb
import sys

VENDOR_ID = 0x1532  # Razer
PRODUCT_ID = 0x010e  # BlackWidow / BlackWidow Ultimate

USB_REQUEST_TYPE = 0x21  # Host To Device | Class | Interface
USB_REQUEST = 0x09  # SET_REPORT

USB_VALUE = 0x0300
USB_INDEX = 0x2
USB_INTERFACE = 2

LOG=sys.stderr.write

class blackwidow(object):
  kernel_driver_detached = False

  def __init__(self):
    self.device = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)

    if self.device is None:
      raise ValueError("Device {}:{} not found\n".format(VENDOR_ID, PRODUCT_ID))
    else:
      LOG("Found device {}:{}\n".format(VENDOR_ID, PRODUCT_ID))

    if self.device.is_kernel_driver_active(USB_INTERFACE):
      LOG("Kernel driver active. Detaching it.\n")
      self.device.detach_kernel_driver(USB_INTERFACE)
      self.kernel_driver_detached = True

    LOG("Claiming interface\n")
    usb.util.claim_interface(self.device, USB_INTERFACE)

  def __del__(self):
    LOG("Releasing claimed interface\n")
    usb.util.release_interface(self.device, USB_INTERFACE)

    if self.kernel_driver_detached:
      LOG("Reattaching the kernel driver\n")
      self.device.attach_kernel_driver(USB_INTERFACE)

    LOG("Done.\n")

  def bwcmd(self, c):
    from functools import reduce
    c1 = bytes.fromhex(c)
    c2 = [ reduce(int.__xor__, c1) ]
    b = [0] * 90
    b[5:5+len(c1)] = c1
    b[-2:-1] = c2
    return bytes(b)

  def send(self, c):
    def _send(msg):
      USB_BUFFER = self.bwcmd(msg)
      result = 0
      try:
        result = self.device.ctrl_transfer(USB_REQUEST_TYPE, USB_REQUEST, wValue=USB_VALUE, wIndex=USB_INDEX, data_or_wLength=USB_BUFFER)
      except:
        sys.stderr.write("Could not send data.\n")

      if result == len(USB_BUFFER):
        LOG("Data sent successfully.\n")

      return result

    if isinstance(c, list):
#      import time
      for i in c:
        print(' >> {}\n'.format(i))
        _send(i)
#        time.sleep(.05)
    elif isinstance(c, str):
        _send(c)

###############################################################################

def main():
    init_new  = '0200 0403'
    init_old  = '0200 0402'
    pulsate = '0303 0201 0402'
    bright  = '0303 0301 04ff'
    normal  = '0303 0301 04a8'
    dim     = '0303 0301 0454'
    off     = '0303 0301 0400'

    bw = blackwidow()
    bw.send(init_old)

if __name__ == '__main__':
    main()
share|improve this answer
    
my keyboard got upgraded and I haven't tried it yet. I'll give it a shot when I have a proper linux system again. Unfortunately I can't give another bounty for this - in part due to how the bounty system works. I will probably switch over the correct answer to yours if it does tho. –  Journeyman Geek Sep 16 '12 at 10:50
    
apparently the module usb is missing on my system so this does not work :/ –  Journeyman Geek Feb 25 '13 at 8:55
    
The usb module is probably in pyusb package (or python-usb or something similar, depending on the distro). –  Sergey Feb 27 '13 at 17:34
    
Sergey thank you very much, it works even with the newer Razer BlackWidow 2013 with PRODUCT_ID = 0x011b. I've not tried to set any macro yet but I see the events coming from /dev/input/by-id/usb-Razer_Razer_BlackWidow_2013-event-kbd and also in xev :). –  binary_runner Jan 8 at 23:43

Perhaps this might shed some light on the issue (from the showkey manpage):

In 2.6 kernels raw mode, or scancode mode, is not very raw at all. Scan codes are first translated to key codes, and when scancodes are desired, the key codes are translated back. Various transformations are involved, and there is no guarantee at all that the final result corresponds to what the keyboard hardware did send. So, if you want to know the scan codes sent by various keys it is better to boot a 2.4 kernel. Since 2.6.9 there also is the boot option atkbd.softraw=0 that tells the 2.6 kernel to return the actual scan codes.

The raw scan codes are available only on AT and PS/2 keyboards, and even then they are disabled unless the atkbd.softraw=0 kernel parameter is used. When the raw scan codes are not available, the kernel uses a fixed built-in table to produce scan codes from keycodes. Thus, setkeycodes(8) can affect the output of showkey in scan code dump mode.

I'm about to see if showkey will dump anything with the macro keys after this boot option is set.

EDIT: After the reboot, no success, but I was looking into capturing raw input from the USB devices themselves. I noted the following, interestingly (I have a Razer Diamondback as well as BlackWidow):

[root@kestrel by-id]# pwd
/dev/input/by-id
[root@kestrel by-id]# ls
usb-Razer_Razer_BlackWidow_Ultimate-event-kbd    usb-Razer_Razer_Diamondback_Optical_Mouse-event-mouse
usb-Razer_Razer_BlackWidow_Ultimate-event-mouse  usb-Razer_Razer_Diamondback_Optical_Mouse-mouse
usb-Razer_Razer_BlackWidow_Ultimate-mouse
[root@kestrel by-id]#

However, using dd to capture raw input works on both diamondback mice, on the event-kbd device, but not on the BlackWidow mouse devices.

I'm guessing perhaps they do not generate any output until somehow activated by the drivers that are installed. I don't know much about Linux USB however, so I don't even know if this makes sense. Perhaps they need to be bound first?

Well, all three black widow devices are noted in /proc/bus/input/devices, however they don't appear to be enumerated in lsusb or /proc/bus/usb/devices. I'm not sure how to access these devices to attempt to bind them or interface with them in any way.

event4 seems to correspond to the actual keyboard, event6 with the macro keys, but I still can't capture any input from them. Hope that all helped.

   [root@kestrel input]# ls
devices  handlers
[root@kestrel input]# cat handlers
N: Number=0 Name=kbd
N: Number=1 Name=mousedev Minor=32
N: Number=2 Name=evdev Minor=64
N: Number=3 Name=rfkill
[root@kestrel input]# pwd
/proc/bus/input
[root@kestrel input]# cat devices
I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=PNP0C0C/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0C:00/input/input0
U: Uniq=
H: Handlers=kbd event0 
B: EV=3
B: KEY=10000000000000 0

I: Bus=0019 Vendor=0000 Product=0001 Version=0000
N: Name="Power Button"
P: Phys=LNXPWRBN/button/input0
S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input1
U: Uniq=
H: Handlers=kbd event1 
B: EV=3
B: KEY=10000000000000 0

I: Bus=0017 Vendor=0001 Product=0001 Version=0100
N: Name="Macintosh mouse button emulation"
P: Phys=
S: Sysfs=/devices/virtual/input/input2
U: Uniq=
H: Handlers=mouse0 event2 
B: EV=7
B: KEY=70000 0 0 0 0
B: REL=3

I: Bus=0003 Vendor=1532 Product=010d Version=0111
N: Name="Razer Razer BlackWidow Ultimate"
P: Phys=usb-0000:00:12.1-3/input0
S: Sysfs=/devices/pci0000:00/0000:00:12.1/usb4/4-3/4-3:1.0/input/input4
U: Uniq=
H: Handlers=kbd event4 
B: EV=120013
B: KEY=1000000000007 ff9f207ac14057ff febeffdfffefffff fffffffffffffffe
B: MSC=10
B: LED=7

I: Bus=0003 Vendor=1532 Product=010d Version=0111
N: Name="Razer Razer BlackWidow Ultimate"
P: Phys=usb-0000:00:12.1-3/input1
S: Sysfs=/devices/pci0000:00/0000:00:12.1/usb4/4-3/4-3:1.1/input/input5
U: Uniq=
H: Handlers=kbd event5 
B: EV=1f
B: KEY=837fff002c3027 bf00444400000000 1 c040a27c000 267bfad941dfed 9e000000000000 0
B: REL=40
B: ABS=100000000
B: MSC=10

I: Bus=0003 Vendor=1532 Product=010d Version=0111
N: Name="Razer Razer BlackWidow Ultimate"
P: Phys=usb-0000:00:12.1-3/input2
S: Sysfs=/devices/pci0000:00/0000:00:12.1/usb4/4-3/4-3:1.2/input/input6
U: Uniq=
H: Handlers=mouse2 event6 
B: EV=17
B: KEY=70000 0 0 0 0
B: REL=103
B: MSC=10

I: Bus=0003 Vendor=1532 Product=0002 Version=0110
N: Name="Razer Razer Diamondback Optical Mouse"
P: Phys=usb-0000:00:12.1-2/input0
S: Sysfs=/devices/pci0000:00/0000:00:12.1/usb4/4-2/4-2:1.0/input/input9
U: Uniq=
H: Handlers=mouse1 event3 
B: EV=17
B: KEY=7f0000 0 0 0 0
B: REL=103
B: MSC=10

[root@kestrel input]# 
share|improve this answer
    
I'm guessing that the system has to communicate with the keyboard to initialise them - with OS X, my keyboard is thrown into the 'pulsing' light mode. There's probably some initialisation required before the keyboard macro keys work –  Journeyman Geek Nov 23 '11 at 0:34
    
What I'm wondering is how to send that information to the mouse and event-mouse devices. event-mouse seems to be responsible, since its bound to a keyboard event handler. I mean, theoretically, all one would have to to is initialize the device and setup the proper scancode->keycode map with setkeycode and ittl pickup the events as normal keystrokes. –  srmaddox Nov 23 '11 at 1:05
    
Perhaps I can just open up eclipse and write a program to allow raw in/out to/from the device. Then trial and error can prevail. –  srmaddox Nov 23 '11 at 1:08
    
jespersaur.com/drupal/book/export/html/21 this should be of interest as far as the process goes. Its far beyond my skillset tho. –  Journeyman Geek Nov 23 '11 at 1:39

maybe this document will help you:

http://www.faqs.org/docs/Linux-HOWTO/Keyboard-and-Console-HOWTO.html#s1

share|improve this answer
    
its a start, and worth an upvote. Interestingly the macro keys do not show up in showkeys at all –  Journeyman Geek Oct 2 '11 at 7:28
    
maybe this macro key only emits scancode in combination with another key. Like a scancode for macro-1, etc ? –  ascobol Oct 2 '11 at 7:34
1  
They seem to work independantly once configured in windows. I may need to consider a lower level approach. My guess is since there's support for mice made by the same company with razercfg its using non standard scancodes. heh. If it was easy, i suppose i would have found it ;) –  Journeyman Geek Oct 2 '11 at 7:42

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.