domingo, 11 de marzo de 2018

Jugando con las macros del Razer BlackWidow Ultimate 2012 para Linux(Parte 1)

Nota: Ya existen proyectos super eficientes como OpenRazer.

Sin embargo el presente articulo fue creado con el fin de experimentar un poco con libusb, udev.

Para quien es este articulo?
Esta pensando para programadores, y curiosos. No se explica como funciona Libusb, pero se da un ejemplo de vida real.

Contexto:
El teclado viene con 5 Macro Keys. Que necesitan ser inicial izadas en orden de que Linux detecte sus entradas. Desgraciadamente para ello se necesita, hacer un poco de ingeniería inversa de las entradas. El objetivo de  articulo no es para aprender a hacer ingeniería inversa del tema. Pero pueden ver un vídeo muy ilustrativo aqui.

En resumen; Lo que necesitamos hacer es mandar unos bits mágicos a través de LibUSB al teclado para que empiece a usar los Macros.
La forma mas sencilla de probar todo es con evtest.

sudo evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
#Blablabla en mi compu el input esta en el 4
/dev/input/event4:      Razer Razer BlackWidow Ultimate
/dev/input/event5:      Razer Razer BlackWidow Ultimate
/dev/input/event6:      Razer Razer BlackWidow Ultimate
#Blablabla
Select the device event number [0-20]: 4
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x1532 product 0x10d version 0x111
Input device name: "Razer Razer BlackWidow Ultimate"
Supported events:
  Event type 0 (EV_SYN)
    #Bla bla bla Y esta es la parte interesante... Note que el Key configura teclas F13 en adelante. Esas son las macros.
    Event code 183 (KEY_F13)
    Event code 184 (KEY_F14)
    Event code 185 (KEY_F15)
    Event code 186 (KEY_F16)
    Event code 187 (KEY_F17)
    Event code 188 (KEY_F18)
    Event code 189 (KEY_F19)
    Event code 190 (KEY_F20)
    Event code 191 (KEY_F21)
    Event code 192 (KEY_F22)
    Event code 193 (KEY_F23)
    Event code 194 (KEY_F24)
    Event code 240 (KEY_UNKNOWN)
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
  Event type 17 (EV_LED)
    Event code 0 (LED_NUML)
    Event code 1 (LED_CAPSL)
    Event code 2 (LED_SCROLLL)
Key repeat handling:
  Repeat type 20 (EV_REP)
    Repeat code 0 (REP_DELAY)
      Value    250
    Repeat code 1 (REP_PERIOD)
      Value     33
Properties:
Testing ... (interrupt to exit)
Event: time 1520787420.323002, type 17 (EV_LED), code 0 (LED_NUML), value 0
Event: time 1520787420.323002, -------------- EV_SYN ------------
Event: time 1520787420.376467, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70028
Event: time 1520787420.376467, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0
Event: time 1520787420.376467, -------------- EV_SYN ------------
Event: time 1520787422.226497, type 4 (EV_MSC), code 4 (MSC_SCAN), value 700e0
Event: time 1520787422.226497, type 1 (EV_KEY), code 29 (KEY_LEFTCTRL), value 1
Event: time 1520787422.226497, -------------- EV_SYN ------------
Event: time 1520787422.476487, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70006
Event: time 1520787422.476487, type 1 (EV_KEY), code 46 (KEY_C), value 1
Event: time 1520787422.476487, -------------- EV_SYN ------------


Cada vez que toquen una tecla convencional, verán el input de la tecla. Pero si se presiona alguna de las macros no pasa nada. Para solucionar cree un pequeño programa en C el cual le pasa los bits mágicos al teclado.

#include 
#include 
#include 
 
using namespace std;
 
#define VENDOR_ID 0x1532
#define PRODUCT_ID 0x10D
#define USB_REQUEST_TYPE  0x21
#define USB_VALUE  0x300
#define USB_REQUEST  9
#define USB_INDEX  0
#define USB_INTERFACE  0 //La interfaz de configuracion esta en el 0
 
unsigned char buffer[90] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00}; //Magic bits
 
int main(int argc, char** argv) {
    libusb_context *ctx = NULL; //a libusb session
    libusb_device_handle *dev_handle; //a device handle
    int result; //Result form the writting
    result = libusb_init(&ctx); //initialize a library session
    if (result < 0) {
        cout << "libusb_init init error " << result << endl; //there was an error
        return false;
    }
    dev_handle = libusb_open_device_with_vid_pid(ctx, VENDOR_ID, PRODUCT_ID); //these are vendorID and productID I found for my usb device
    if (dev_handle == NULL) {
        cout << "libusb_open_device_with_vid_pid cannot open Blackwidow keyboard" << endl;
        return false;
    }
    if (libusb_kernel_driver_active(dev_handle, USB_INTERFACE) == 1) {
        if (libusb_detach_kernel_driver(dev_handle, USB_INTERFACE) != 0) {
            cout << "libusb_detach_kernel_driver error" << endl;
            return false;
        }
    }
    result = libusb_claim_interface(dev_handle, USB_INTERFACE); //claim interface 0 (the first) of device (mine had jsut 1)
    if (result < 0) {
        cout << "Cannot Claim Interface" << endl;
        return false;
    }
    result = libusb_control_transfer(dev_handle, USB_REQUEST_TYPE, USB_REQUEST, USB_VALUE, USB_INDEX, buffer, 90, 0);
    if (result != 90) {
        return false;
    }
    result = libusb_release_interface(dev_handle, USB_INTERFACE); //release the claimed interface
    if (result != 0) {
        cout << "Cannot Release Interface" << endl;
        return false;
    }
    //Attached and reclain the driver
    libusb_attach_kernel_driver(dev_handle, USB_INTERFACE);
    libusb_close(dev_handle); //close the device we opened
    libusb_exit(ctx); //needs to be called to end the
    return 0;
}


Ahora bien debemos compilar el programa con libusb y ejecutar este programa con sudo

# Instalar la libreria necesaria
sudo apt-get install libusb-1.0-0-dev
# Instalar GCC
sudo apt-get install build-essential
# Compilar
g++ blackwidowkeyboardinit.cpp `pkg-config libusb-1.0 --libs --cflags` -o blackwidowkeyboardinit

# Ejecutar el programa
sudo ./blackwidowkeyboardinit 

#Ahora bien debemos moverlo a una ubicacion final
sudo mv blackwidowkeyboardinit /usr/bin/blackwidowkeyboardinit

Y por ultimo volvemos a probar con evtest.

Ahora si veremos las entradas del teclado cuando presionemos algún MacroKey...

Lo que queda es que el programa se ejecute con permisos de super usuario, cuando se inserta el teclado. Para eso recurrimos al udev.
#Dentro de la carpeta /etc/udev/rules.d
#Creamos un archivo 
sudo touch 40-razer_blackwidow_keyboard.rules

#Con el contenido
#ACTION=="add", ATTR{idVendor}=="1532", ATTR{idProduct}=="010d", RUN+="/bin/sh -c 'echo == >> /home/koza/udev-env.txt; env >> /home/koza/udev-env.txt'"
#ACTION=="add", ATTR{idVendor}=="1532", ATTR{idProduct}=="010d", RUN+="/usr/bin/blackwidowkeyboardinit"

#Refrescamos las reglas con udev
sudo udevadm control --reload-rules

#Reiniciasmos udev
sudo service udev restart

Tomar en cuenta que la primera linea del archivo se puede eliminar, Pero por testing la dejo allí.
Simplemente cuando insertar el teclado en un USB en home crear un archivo con los datos del ambiente.

Si quieres experimentar con las reglas debes simplemente ejecutar el comando siguiente después de cada cambio.

udevadm control --reload-rules && udevadm trigger


Nota: Probado en Debian 8 (Jessie)

AEM hablemos del arquetipo 11

Cuando creamos un proyecto con AEM. Siempre es importante saber que arquetipo estamos usando. Pues esto me determinara que source, herramien...