Control LED con libusb

LedController for GNU/Linux

LedController for GNU/Linux

Hoy me ha llegado un chinagadget comprado en DealExtreme.

Se trata de un notificador de nuevos emails o eventos de mensajería.

Como era de esperar el driver/software sólo funciona para sistemas Windows pero me gustaría poder utilizar este “cachibache” en mi Ubuntu.

He supuesto que encender una bombilla vía USB no debía ser muy complicado y me he puesto manos a la obra.

Lo primero que tenia que averiguar era la configuración y el protocolo del driver para encender/apagar el LED a voluntad.

Para hacer esta mini ingeniería inversa he utilizado VirtualBox y Wireshark.

Wireshark es ampliamente usado como sistema de captura y análisis de paquetes de red. Ahora además nos permite “sniffar” el tráfico USB.

Con VirtualBox se crea una maquina virtual Windows donde instalamos los drivers oficiales del notificador.

Con nuestra máquina virtual corriendo, ponemos a escuchar a nuestro sniffer.

Es necesario saber en que bus se encuentra nuestro dispositivo. Ésto se soluciona con un simple lsusb.

Ahora le indicamos a WireShark que escuche en dicho bus y ejecutamos un escenario donde se encienda/apague el led.

El resultado es un gran fichero de captura con mucha morralla.

Analizando el tráfico se observa un incontable número de paquetes enviados por interrupción pero cuyo contenido es siempre el mismo. Desconozco su propósito (¿quizás un sistema rudimentario de heartbeat?).

Filtrando por endpoint, se ve que hay dos endpoints, 1 y 2 (además del EP de control 0).

El EP 1 es el que usa para transferencias por interrupción del mensaje repetitivo.

El EP 2 es el que se usa para el envio de comandos de encendido.

En concreto, después de jugar con la aplicación de notificación, detecte la siguiente trama para encender el led rojo:


0x02 0x04 0x04 0x04 0x04

Para apagarlo, se usa ésta otra:


0x00 0x04 0x04 0x04 0x04

Con el protocolo (a priori) detectado, decidí hacer el driver en espacio de usuario para GNU/Linux.

Para acometer ésto, opté por usar la librería de acceso a USB, libusb.

Esta librería permite el desarrollo rápido de drivers/aplicaciones USB en GNU/Linux (también está disponible para Windows). Para dispositivos sencillos como este se ajusta perfectamente. Si por el contrario queremos controlar un dispositivo con alta intesidad de E/S, es recomendable implementarlo mediante un módulo kernel.

La implementación es sencilla. Se pasa como parámetro el color del LED a encender (en la secuencia, 2 = rojo, 1 = azul, 2 = verde).

Lo relevante está en la línea 55. Aquí es donde se envía por interrupción, el comando por el EP 2.

También es interesante la línea 41. Aquí se descarga el manejador kernel de dispositivo en caso de que algún driver lo haya reclamado (en este caso el driver HID).


#include <stdio.h>
#include <libusb.h>
#include <errno.h>

#define VID 0x1294
#define PID 0x1320

static struct libusb_device_handle *devh = NULL;

int main(int argc,char** argv)
{
 int ret;
 unsigned char code = 0;
 if (argc != 2 )
 {
    printf("syntax: %s red | green | blue | off\n",argv[0]);
    return -1;
 }
 if ( strcmp(argv[1],"red") == 0 )
 {
     code = 2;
 }
 else if ( strcmp(argv[1],"green") == 0 )
 {
     code = 3;
 }
 else if ( strcmp(argv[1],"blue") == 0 )
 {
     code = 1;
 }
 libusb_init(NULL);
 devh = libusb_open_device_with_vid_pid(NULL, VID, PID);
 if (devh == NULL )
 {
	printf("not found\n");
        return -1;
 }
 if ( libusb_kernel_driver_active(devh,0) )
 {
	printf("detach from kernel\n");
        ret = libusb_detach_kernel_driver(devh,0);
        if (ret < 0 )
        {
		printf("can't detach\n");
                return -1;
        }
 }
 char data[5];
 data[0] = code;
 data[1] = 0x4;
 data[2] = 0x4;
 data[3] = 0x4;
 data[4] = 0x4;
 int dummy;
 ret =  libusb_interrupt_transfer(devh,0x2,data,5,&dummy,0);
 if ( ret < 0 )
 {
  perror("error");
 }
return 0;
}

Os dejo el fuente de programa, una versión compilada dinámicamente y otra estáticamente.
Fuente: ledcontroller.tar.gz

20 Responses to Control LED con libusb

  1. Davide dice:

    Great work!

    I’m using it on my ubuntu machine!🙂

    is there a way to make led blinking?

    Thanks a lot!

    D.

    • Chema dice:

      Hi Davide,

      Thanks!

      Yes, you can use the following bash-script (with Perl’s sleep) to make red led blinking with a sleep of 300 milliseconds.

      x=”red” ; while [ 1 ]; do ledcontroller $x ; perl -e “use Time::HiRes; Time::HiRes::sleep(0.3);”; if [ $x = “red” ]; then x=”off”; else x=”red”; fi done

      I hope this works for you.

      Best regards

      • Davide dice:

        I think I’ve made a mistake.. I can’t stop blinking!🙂

        I have a script that call “ledblink”, that is the last script you wrote up if there’s unread mail, and the old script “ledcontroller off” if there’s not unread mail.

        Is it possible to integrate the blinking system on the original ledcontroller script so that it results one process at all?

        I was thinking adding another parameter, like “ledcontroller red -blink”.

        What do you think about?

      • Chema dice:

        Well, this program is very simple, only send a simple packet to turn-on the LED.
        I don’t know if the USB gadget controller has a native blinking mode.
        So, you need set up on and off manually.

        Your email notification, need launch a script like above, and when the user catches the alarm, kill the script and invoke ledcontroller off to force the lights out.
        All you need is get the PID of script, and kill it when you want.

        I hope this help you.

      • Davide dice:

        perfect I’ll solve in this way🙂

        thank you so much!

  2. Davide dice:

    great🙂

    it works!

  3. Gef dice:

    Hola, primero decirte que me ha parecido un post muy interesante, estoy intentando hacer algo similar y me gustaría saber si me puedes ayudar.

    El caso es que he comprado un botón rojo (http://dreamcheeky.com/big-red-button) y me gustaría que al apretarlo hiciera lo que yo quiero. Ya he conseguido reconocer y abrir el dispositivo con libusb y tengo las capturas de wireshark para los diferentes estados en los que puede estar (Tapa cerrada, Tapa abierta y sin apretar el botón, Tapa abierta con el botón apretado) pero no me aclaro a enviar los datos.

    ¿De dónde sacaste los datos enviados? ¿Como puedo leer el estado?

    Dime si necesitas algo más y lo subo

    Muchas gracias

    • Chema dice:

      Hola Gef,

      Lo normal es hacer ingeniera inversa para averiguar que protocolo e inicialización usa.
      Según veo, los drivers estan para Windows. Instala Wireshark en Windows (o USB Monitor, versión evaluación) y captura el trafico
      cuando conectas el dispositivo (para ver si hay algún envío de datos de inicialización) y luego por cada acción que hagas (abrir tapa, cerrar tapa, pulsar, botón, etc).

      Tendrás que usar la materia gris para buscar un patrón común y deducir el protocolo (esa es la parte bonita).

      Un saludo y suerte!

  4. Gef dice:

    Pues nada, lo probaré con USB Monitor ya que con las capturas de Wireshark no me aclaro. Ya si eso te comento.

    Muchas gracias

  5. Gef dice:

    Ya tengo los protocolos, ahora el problema que tengo es que enviando:

    char data[8];
    data[0] = 0x0;
    data[1] = 0x0;
    data[2] = 0x0;
    data[3] = 0x0;
    data[4] = 0x0;
    data[5] = 0x0;
    data[6] = 0x0;
    data[7] = 0x2;
    ret = libusb_interrupt_transfer(devh,0x81,data,8,&dev,3000);

    Y capturando el tráfico con Wireshark me dice que envío:

    01 02 00 00 00 00 00 00

    Y me he dado cuenta de que da igual que valores le envíe, siempre aparecen los mismos.

    La única diferencia entre mi paquete y uno válido es ese 01 del principio…

    ¿Alguna idea? Esto es muy frustrante

    • Chema dice:

      ¿Tiene alguna inicialización específica? Captura el tráfico cuando lo conectas y buscas paquetes de control privativos.

      • Gef dice:

        Ya lo he conseguido, en efecto estaba pasando por alto un paquete de control -.-

        Muchísimas gracias por tu ayuda🙂

      • Chema dice:

        De nada. Me alegro que “rule”🙂

  6. Douglas dice:

    Buen trabajo, tengo una duda, con la función libusb_interrupt_tranfer puedo recibir los datos enviados por el bus 4 usb, ya lo detecta sólo que aún no encuentro como recibir los datos, de antemano, muchas gracias!

    • Chema dice:

      Hola Douglas, gracias a ti.

      Si entiendo bien tu pregunta, quieres recibir datos de un endpoint via interrupción, ¿no?
      La llamada en el código, es para enviar datos hacia el dispositivo. Esto se hace, en el caso concreto de este aparato, enviando un paquete al endpoint 0x2.
      No sé si lo sabes, pero para distinguir entre endpoints de entrada y de salida, se usa el bit más significativo. Si esta a “1” es de entrada y no salida.

      Si suponemos que tu dispositivo tiene como endpoint de entrada el 0x81 (suele ser habitual), tendras que llamar a la función con dicho endpoint. El código de libusb sabrá analizando el bit más significativo
      si tiene que hacer un envio o esperar una recepción.

      Espero que te sirva.

      Un saludo.

  7. freedoh dice:

    Hi, excuse English.

    ‘code’ can be 0-7 giving: red, green, blue, cyan, purple, lime and white.

    • Chema dice:

      Hi freedoh, thanks you for the info.
      I already know something about it that I read in the DealExtreme forum. I hope to have time to modify the program.

      Kind regards.

  8. freedoh dice:

    Here you go:
    http://openpaste.org/1d1862A4

    • Chema dice:

      Thanks so much freedoh🙂

  9. Estimado Chema:

    Te felicito por el post, me ha parecido muy interesante. Te comento lo siguiente. Hace dos meses compré este mouse:

    http://www.voragolive.com/mouse-404.php

    Acá un video para que lo conozcas:

    Como te podrás dar cuenta el mouse tiene leds que prenden y apagan como si estuviera respirando. En Windows, mediante software, los leds pueden apagarse totalmente y cambiar la velocidad del parpadeo. Sus velocidades son: lento, medio y rápido.

    En Debian Wheezy, he cargado el módulo usbmon y he instalado Wireshark para realizar las capturas del mouse.

    Me he dado cuenta que en usbmon3, todos los botones (derecho, izquierdo, botón central (scroll) scroll -arriba, abajo-, forward y back) tienen los mismos valores.

    En usbmon1 es donde se produce el efecto de los leds. He realizado las capturas pero tengo problemas para encontrar un patrón que me ayude a programar los leds (Lenguaje C y libusb) como lo haría en Windows.

    ¿Serías tan amable de echarle un ojo a mi captura y orientarme al respecto? Te dejo el link de la captura:

    https://onedrive.live.com/redir?resid=74A96CD9AF2A6A94!746&authkey=!AIIg0pzeiD2GNig&ithint=file%2cgz

    De antemano, muchas gracias.

    Atentamente

    Noel Merino Hernández

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: