12ba:0200

(Esta entrada es la séptima parte de una serie que cubre un proyecto personal que realicé en el verano de 2014; pueden ver todas las partes aquí).

El dispositivo USB-HID con identificador de vendedor 0x12ba e identificador de producto 0x0200 (y que por lo tanto aparece como 12ba:0200 al invocar lsusb) tiene el siguiente reporte descriptivo en C:

0x05, 0x01,       /* Usage Page (Generic Desktop Controls) */
0x09, 0x05,       /* Usage (Gamepad) */
0xa1, 0x01,       /* Collection (Application) */
0x15, 0x00,       /*   Logical Minimum (0) */
0x25, 0x01,       /*   Logical Maximum (1) */
0x35, 0x00,       /*   Physical Minimum (0) */
0x45, 0x01,       /*   Physical Maximum (1) */
0x75, 0x01,       /*   Report Size (1) */
0x95, 0x0d,       /*   Report Count (13) */
0x05, 0x09,       /*   Usage Page (Button) */
0x19, 0x01,       /*   Usage Minimum (Button 1) */
0x29, 0x0d,       /*   Usage Maximum (Button 13) */
0x81, 0x02,       /*   Input 2 (Data, Variable, Abs) */
0x95, 0x03,       /*   Report Count (3) */
0x81, 0x01,       /*   Input 1 (Data, Var, Abs) */
0x05, 0x01,       /*   Usage Page (Generic Desktop) */
0x25, 0x07,       /*   Logical Minimum (7) */
0x46, 0x3b, 0x01, /*   Physical Maximum 315 */
0x75, 0x04,       /*   Report Size (4) */
0x95, 0x01,       /*   Report Count (1) */
0x65, 0x14,       /*   Unit 20 (English rotation, degrees) */
0x09, 0x39,       /*   Usage (Hat switch) */
0x81, 0x42,       /*   Input 66 (Data, Var, Abs,Null) */
0x65, 0x00,       /*   Unit (None) */
0x95, 0x01,       /*   Report Count 1 */
0x81, 0x01,       /*   Input (Const, Array, Abs) */
0x26, 0xff, 0x00, /*   Logical Maximum 255 */
0x46, 0xff, 0x00, /*   Physical Maximum 255 */
0x09, 0x30,       /*   Direction-X 48 */
0x09, 0x31,       /*   Direction-Y 49 */
0x09, 0x32,       /*   Direction-Z 50 */
0x09, 0x35,       /*   Rotate-Z 53 */
0x75, 0x08,       /*   Report Size 8 */
0x95, 0x04,       /*   Report Count 4 */
0x81, 0x02,       /*   Input 2 (Data, Var, Abs) */
0x06, 0x00, 0xff, /*   Usage Page 65280 (null) */
0x09, 0x20,       /*   Usage 32 (null) */
0x09, 0x21,       /*   Usage 33 (null) */
0x09, 0x22,       /*   Usage 34 (null) */
0x09, 0x23,       /*   Usage 35 (null) */
0x09, 0x24,       /*   Usage 36 (null) */
0x09, 0x25,       /*   Usage 37 (null) */
0x09, 0x26,       /*   Usage 38 (null) */
0x09, 0x27,       /*   Usage 39 (null) */
0x09, 0x28,       /*   Usage 40 (null) */
0x09, 0x29,       /*   Usage 41 (null) */
0x09, 0x2a,       /*   Usage 42 (null) */
0x09, 0x2b,       /*   Usage 43 (null) */
0x95, 0x0c,       /*   Report Count 12 */
0x81, 0x02,       /*   Input 2 (Data, Var, Abs) */
0x0a, 0x21, 0x26, /*   Usage 9761 (null) */
0x95, 0x08,       /*   Report Count 8 */
0xb1, 0x02,       /*   Feature 2 (Data, Var, Abs) */
0x0a, 0x21, 0x26, /*   Usage 9761 (null) */
0x91, 0x02,       /*   Output 2 (Data, Var, Abs) */
0x26, 0xff, 0x03, /*   Logical Maximum 1023 */
0x46, 0xff, 0x03, /*   Physical Maximum 1023 */
0x09, 0x2c,       /*   Usage 44 (null) */
0x09, 0x2d,       /*   Usage 45 (null) */
0x09, 0x2e,       /*   Usage 46 (null) */
0x09, 0x2f,       /*   Usage 47 (null) */
0x75, 0x10,       /*   Report Size 16 */
0x95, 0x04,       /*   Report Count 4 */
0x81, 0x02,       /*   Input 2 (Data, Var, Abs) */
0xc0              /* End_Collection */

Como lo hice con el DualShock 3, veámoslo por partes.

0x05, 0x01,       /* Usage Page (Generic Desktop Controls) */
0x09, 0x05,       /* Usage (Gamepad) */
0xa1, 0x01,       /* Collection (Application) */

Comienza igual que el DualShock 3, la única diferencia es que se identifica como gamepad, no como joystick. La verdad en estos días, para una computadora (incluyendo al PlayStation 3), no existe realmente diferencia entre joystick y gamepad; ambos tienen 2 o más ejes, y un montón de botones.

0x15, 0x00,       /* Logical Minimum (0) */
0x25, 0x01,       /* Logical Maximum (1) */
0x35, 0x00,       /* Physical Minimum (0) */
0x45, 0x01,       /* Physical Maximum (1) */
0x75, 0x01,       /* Report Size (1) */
0x95, 0x0d,       /* Report Count (13) */
0x05, 0x09,       /* Usage Page (Button) */
0x19, 0x01,       /* Usage Minimum (Button 1) */
0x29, 0x0d,       /* Usage Maximum (Button 13) */
0x81, 0x02,       /* Input 2 (Data, Variable, Abs) */

Esta parte codifica 13 botones (“Report Count (13)”) binarios (“Report Size (1)”), no “analógicos”; o sea, el dispositivo sólo avisa si uno de estos botones está o no apachurrado, no da información acerca de qué tanto lo está apretando el usuario.

0x95, 0x03,       /* Report Count (3) */
0x81, 0x01,       /* Input 1 (Data, Var, Abs) */

Luego el dispositivo mete tres bits de relleno (padding); de esta manera, los primeros dos bytes que envía el dispositivo (13 + 3 = 16 bits = 2 bytes), llevan la información de qué botones están o no presionados. Obviamente, 1 (bit prendido) significa que el botón está apretado, y 0 (bit apagado) que no lo está.

0x05, 0x01,       /* Usage Page (Generic Desktop) */
0x25, 0x07,       /* Logical Minimum (7) */
0x46, 0x3b, 0x01, /* Physical Maximum 315 */
0x75, 0x04,       /* Report Size (4) */
0x95, 0x01,       /* Report Count (1) */
0x65, 0x14,       /* Unit 20 (English rotation, degrees) */
0x09, 0x39,       /* Usage (Hat switch) */
0x81, 0x42,       /* Input 66 (Data, Var, Abs,Null) */
0x65, 0x00,       /* Unit (None) */
0x95, 0x01,       /* Report Count 1 */
0x81, 0x01,       /* Input (Const, Array, Abs) */
0x26, 0xff, 0x00, /* Logical Maximum 255 */
0x46, 0xff, 0x00, /* Physical Maximum 255 */

Luego vienen 4 bits (“Report Size (4)”, “Report Count (1)”) que codifican el “hat switch“, la crucecita que tienen casi todos los gamepads que sirven para mover a Mario a la derecha, izquierda, que se agache, o que se meta al tubo del techo después de brincar.

Siendo yo programador, yo hubiera esperado que cada bit codificara una de las cuatro direcciones; algo como 0001 = norte, 0010 = sur, 0100 = este, 1000 = oeste, y 0000 que el usuario no está tocando ninguna… o algo del estilo. Esto también permitiría las combinaciones pertinentes: 0101 sería noreste, etc.

Los ingenieros que diseñaron esto, sin embargo, se les ocurrió que lo que tenía sentido es que 0000 fuera norte, 0001 noreste, 0010 este, 0011 sureste, 0100 sur, 0101 suroeste, 0110 este, 0111 noreste, y 1000 nada presionado. Siendo honesto, la verdad esto puede venir desde el estándar USB-HID, pero como no lo leí completo, no tengo idea. De cualquier forma, me suena a algo que se le ocurriría a un ingeniero.

Al definir el máximo como 255, se le asignan 8 bits al hat switch, así que 4 de ellos quedan también como relleno.

0x09, 0x30,       /* Direction-X 48 */
0x09, 0x31,       /* Direction-Y 49 */
0x09, 0x32,       /* Direction-Z 50 */
0x09, 0x35,       /* Rotate-Z 53 */
0x75, 0x08,       /* Report Size 8 */
0x95, 0x04,       /* Report Count 4 */
0x81, 0x02,       /* Input 2 (Data, Var, Abs) */

Parecido al DualShock 3, este gamepad define dos joysticks con dos ejes cada uno, usando 4 bytes para enviar su estado. Hasta donde he podido ver, el dispositivo 12ba:0200 no hace uso del primer joystick, enviando siempre la información correspondiente a como si estuviera centrado. El segundo joystick sí es usado; diré cómo más adelante.

0x06, 0x00, 0xff, /* Usage Page 65280 (null) */
0x09, 0x20,       /* Usage 32 (null) */
0x09, 0x21,       /* Usage 33 (null) */
0x09, 0x22,       /* Usage 34 (null) */
0x09, 0x23,       /* Usage 35 (null) */
0x09, 0x24,       /* Usage 36 (null) */
0x09, 0x25,       /* Usage 37 (null) */
0x09, 0x26,       /* Usage 38 (null) */
0x09, 0x27,       /* Usage 39 (null) */
0x09, 0x28,       /* Usage 40 (null) */
0x09, 0x29,       /* Usage 41 (null) */
0x09, 0x2a,       /* Usage 42 (null) */
0x09, 0x2b,       /* Usage 43 (null) */
0x95, 0x0c,       /* Report Count 12 */
0x81, 0x02,       /* Input 2 (Data, Var, Abs) */

Aquí es donde esto se pone interesante; todo ese relajo (que lsusb en Linux no puede reconocer, y por lo tanto yo le puse “null”), codifica 12 bytes que, hasta donde yo adivino, deberían codificar qué tanto están presionados 12 de los 13 botones que reportan los primeros 2 bytes. Deberían siendo la palabra clave.

0x0a, 0x21, 0x26, /* Usage 9761 (null) */
0x95, 0x08,       /* Report Count 8 */
0xb1, 0x02,       /* Feature 2 (Data, Var, Abs) */
0x0a, 0x21, 0x26, /* Usage 9761 (null) */
0x91, 0x02,       /* Output 2 (Data, Var, Abs) */
0x26, 0xff, 0x03, /* Logical Maximum 1023 */
0x46, 0xff, 0x03, /* Physical Maximum 1023 */
0x09, 0x2c,       /* Usage 44 (null) */
0x09, 0x2d,       /* Usage 45 (null) */
0x09, 0x2e,       /* Usage 46 (null) */
0x09, 0x2f,       /* Usage 47 (null) */
0x75, 0x10,       /* Report Size 16 */
0x95, 0x04,       /* Report Count 4 */
0x81, 0x02,       /* Input 2 (Data, Var, Abs) */

Por último, se definen 4 doble bytes, que son parecidos a los que usa el DualShock 3 para codificar los acelerómetros X, Y y Z, y el giroscopio.

0xc0      /* End_Collection */

Ese byte sólo cierra la colección y con ello el descriptor.

Este descriptor entonces codifica 13 botones, dos joysticks, presiones de los botones, acelerómetros, y giroscopio, utilizando 27 bytes: 2 bytes para los 13 botones (contando 3 bits de relleno), 1 byte para el hat switch, 4 bytes para los dos joysticks, 12 bytes para (supongo) la presión de 12 de los 13 botones, y 8 bytes para los acelerómetros y el giroscopio.

¿Cuál es entonces el dispositivo 12ba:0200, este famoso “gamepad”? Es este:

RockBand Stratocaster

RockBand Stratocaster

El bit 0 del primer byte es el botón azul, el bit 1 es el verde, el bit 2 es el rojo, etc. El primer joystick es ignorado; pero el segundo toma un eje para el whammy bar, y el otro para el “estilo del solo”… lo que sea que es eso.

La información básica del controlador (especialmente los primeros 7 bytes que envía el dispositivo) los encontré en esta página, pero casi todo lo demás lo tuve que averiguar (o adivinar) yo solo. Y no estoy 100% seguro de que tenga todo correctamente.

Sin embargo, lo que he sí tengo correcto sirvió perfectamente para que llevara a cabo mi proyecto… que esperaría que en este punto ya todo mundo pudiera adivinar cuál fue.

7 comentarios sobre “12ba:0200

  1. Hola Canek!…
    leí con mucho interes tus ultimos artículos… no entendí muchas cosas… pero, si entendí bien… lo que quieres hacer es que una Guitarra (de Jugete (disculpame por no sé nada de video juegos)) suene como una guitarra de verdad??? buena idea? estoy en lo correcto?
    saludos
    oscar

    1. Eso se puede hacer, pero obviamente lo interesante es hacerlo con la batería (la guitarra tiene 5 botones; hacer que eso suene como una guitarra de verdad está cabrón).

      Sí se puede con la batería; uno de los muchos proyectos que lo hace es Ps360ProDrummer, y de hecho el código que tienen sí me sirvió.

      Pero de eso no es mi proyecto.

  2. A mi me suena más que quieres mandar la correcta info a los mandos para que manden la info al juego y sacar el 100% en todas las canciones, para ello requieres el midi, para temporizarlo adecuadamente, supongo es difícil sacar esos trofeos. xD

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *