Sei sulla pagina 1di 23

Diseño de computadores

Creación de drivers
para dispositivos USB

Ingeniería Técnica de Informática de Gestión


Universidad de Jaén

Profesor: Luis Miguel Nieto Nieto

Rafa Muñoz Cárdenas


Contenidos

1. USB
1.1. Descriptores de un dispositivo
1.2. Comunicación con dispositivo
1.3. Modos de transferencia de información
2. Kernel vs. Libusb
3. Introducción a Usbfs
3.1. Libusb
4. Creación de un driver de ratón
5. Otros ejemplos de drivers
5.1. Ingeniería inversa
5.2. Lanzamisiles
6. Bibliografía
Enero 2009 Rafa Muñoz Cárdenas 2
1. USB

El Universal Serial Bus es un puerto que sirve para conectar periféricos a una
computadora.
Fue creado en 1996 por siete empresas: IBM, Intel, Northern Telecom, Compaq,
Microsoft, Digital Equipment Corporation y NEC.
Objetivos:
Permitir la conexión de periféricos en una sola interfaz de socket
estandarizada.
Mejorar la capacidad “plug-and-play” (permitiendo así el “hot swapping”).
Suministrar a través del mismo puerto energía a dispositivos de bajo
consumo.
4 versiones:
1.0: Tasa de transferencia de hasta 1'5 Mbps (192 KB/s). Utilizado en
teclados, ratones y joysticks.
1.1: Tasa de transferencia de hasta 12 Mbps (1'5 MB/s).
2.0: Tasa de transferencia de hasta 480 Mbps (60 MB/s).
3.0: Tasa de transferencia de hasta 4.8 Gbps (600 MB/s).

Enero 2009 Rafa Muñoz Cárdenas 3


1.1. Descriptores de un dispositivo

Son bloques de información que permiten al host aprender características del


periférico. Los descriptores más importantes son los siguientes:
Descriptor de dispositivo:
Contiene información básica del dispositivo.
Descriptor de configuración:
Representan el estado del dispositivo (activo, standby, inicialización) y su
configuración básica (potencia, nº de interfaces, etc).
Descriptor de interfaz:
Contiene identificador de interfaz, número de endpoints de interfaz, etc.
Un dispositivo USB puede consistir en varios sub-dispositivos lógicos (cada uno
tendrá una interfaz). Por ejemplo: una webcam con micrófono incorporado (interfaz
vídeo+interfaz audio). Es necesario un driver por cada interfaz.
Descriptor de endpoint:
Describe endpoints distintos del 0.

Enero 2009 Rafa Muñoz Cárdenas 4


1.2. Comunicación con dispositivo

Esta comunicación se realiza a través de pipes (canales lógicos).


Los pipes son conexiones del anfitrión al endpoint.
El endpoint es el buffer desde el cual los datos entran o salen del dispositivo.
Sólo será posible acceder a los datos de los descriptores anteriormente
explicados a través de endpoints.
Por ejemplo: para acceder a la configuración accedemos al endpoint 0.
Dos tipos de transferencia unidireccional: IN (dispositivo a PC) o OUT (PC a
dispositivo).

Enero 2009 Rafa Muñoz Cárdenas 5


1.3. Modos de transferencia de información

También denominados como tipos de endpoints. Tipos:


De control:
Los endpoints de control son usados para controlar el dispositivo USB
asíncronamente. P.e.: enviar comandos o pedir información de estado del periférico.
Todo dispositivo debe tener este “endpoint 0”, usado para inicializar el periférico al
conectarlo.
De interrupción:
El host solicita periódicamente pequeños paquetes de datos de tamaño fijo al
dispositivo. El dispositivo siempre tiene que esperar a ser atendido.
Se usa para ratones y teclados normalmente.
Bulk:
Transfieren grandes cantidades de datos. Con ella se asegura que los datos llegan
siempre a su destino. Se usa para almacenamiento de datos o impresoras.
Isochronous:
Transfieren grandes cantidades de datos. No se garantiza la llegada de los datos a su
destino. Se usa para dispositivos en tiempo real de vídeo o audio.

Enero 2009 Rafa Muñoz Cárdenas 6


2. Kernel vs. Libusb

Nuestro sistema no es capaz de manejar un periférico USB => Escribir un driver.

Alternativas al escribir drivers:


Kernel driver
Drivers centralizados en el núcleo.
Buena velocidad y rendimiento.
Soporta cualquier dispositivo complejo.
Flexibilidad
Pequeños cambios para otras arquitecturas (p.e: ARM).
No portable a otros SO.
Programación muy compleja.

Enero 2009 Rafa Muñoz Cárdenas 7


2. Kernel vs. Libusb

Libusb driver
Fácil de programar.
Esconde detalles innecesarios al programador.
Reduce el tamaño de los programas de forma notable.
Código portable a otras plataformas.
Drivers dispersos por internet (escasos grandes repositorios).
La comunicación con periféricos es más lenta respecto a drivers en kernel.
No válida para periféricos complejos.

Enero 2009 Rafa Muñoz Cárdenas 8


3. Introducción a Usbfs

Usbfs es un sistema de archivos diseñado específicamente para periféricos USB y


está incluido en el kernel Linux.
Recopila información de todos los dispositivos conectados al PC.
Evita escribir drivers como módulos en kernel.
Los drivers son ejecutados en espacio de usuario (no root).
Identifica los dispositivos y crea sus archivos asociados aunque su driver no haya
sido creado aún.

Para simplificar aún más la programación sobre Usbfs, podemos hacer uso de las
librerías Libusb (C/C++) y jUSB (Java). Ambas corren sobre esta capa software en
sistemas GNU/Linux.

Enero 2009 Rafa Muñoz Cárdenas 9


3.1. Libusb

Ideal para dispositivos sencillos: cámaras, teclados, ratones, scanners,


impresoras...
Multiplataforma: GNU/Linux, *BSD, Mac OS X y Win32.
Funciona en espacio de usuario => Fácil de depurar.

A continuación se muestra un ejemplo de información de un ratón recopilada por


Usbfs sobre un ratón:

Enero 2009 Rafa Muñoz Cárdenas 10


3.1. Libusb

$lsusb -v

Bus 002 Device 004: ID 046d:c001 Logitech, Inc. N48/M-BB48 [FirstMouse Plus]
Device Descriptor:
......
idVendor 0x046d Logitech, Inc.
idProduct 0xc001 N48/M-BB48 [FirstMouse Plus]
......
bNumConfigurations 1
Configuration Descriptor:
......
bNumInterfaces 1
......
(Bus Powered)
Remote Wakeup
MaxPower 50mA
Interface Descriptor:
......
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
BinterfaceSubClass 1 Boot Interface Subclass
bInterfaceProtocol 2 Mouse
iInterface 0
......
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0008 1x 8 bytes
BInterval 10

Enero 2009 Rafa Muñoz Cárdenas 11


4. Creación de un driver de ratón

Observando la salida de “lsusb -v” podemos empezar a crear nuestro driver usando la
librería Libusb.

raton.c:

// Estos datos dependeran del dispositivo


#define VENDOR 0x046d
#define PRODUCT 0xc001

Recorremos los buses buscando el dispositivo anteriormente definido:

static struct usb_device *find_device(const uint16_t &vendor, const uint16_t &product) {


struct usb_bus *bus;
struct usb_device *dev;
struct usb_bus *busses;

usb_init(); usb_find_busses(); usb_find_devices(); busses = usb_get_busses();

// Recorremos todos los buses


for (bus = busses; bus; bus = bus->next) {
for (dev = bus->devices; dev; dev = dev->next) {
if ((dev->descriptor.idVendor == vendor) &&
(dev->descriptor.idProduct == product)) {
return dev;
}
}
}

return NULL;
}

Enero 2009 Rafa Muñoz Cárdenas 12


4. Creación de un driver de ratón

Ahora declaramos las estructuras del dispositivo y lo buscamos usando la función anteriormente declarada:
int main() {
struct usb_device *dev;
struct usb_dev_handle *udev;
dev = find_device(VENDOR, PRODUCT);

Abrimos dispositivo y deshabilitamos driver si es que estaba funcionando antes:

udev = usb_open(dev);
usb_detach_kernel_driver_np(udev, dev->config->interface->altsetting->bInterfaceNumber);

Reservamos interfaz para nuestra aplicación:


reserva = usb_claim_interface(udev, 0);

Vamos a leer las interrupciones del ratón:


// Monitorizamos hasta que no pulsemos boton derecho e izquierdo a la vez (3 0 0 0)
while (data[0] != 3) {
data[0] = data[1] = data[2] = data[3] = 0; // (0 0 0 0)
intr = usb_interrupt_read(udev, 0x81, data, 4, 0); // Leemos 4 bytes del dispositivo usando interrupt_read

for (i = 0; i < 4 && intr > 0; i++) {


printf("%d ", data[i]);
}
printf("\n");

usb_clear_halt(udev, 0x81); // Reseteamos dispositivo para evitar que se sigan enviando


// los mismos bytes despues de hacer click
}

Enero 2009 Rafa Muñoz Cárdenas 13


4. Creación de un driver de ratón

Terminamos y cerramos la comunicación con el periférico:


usb_release_interface(udev, 0);
usb_close(udev);

Enero 2009 Rafa Muñoz Cárdenas 14


4. Creación de un driver de ratón

Probamos el programa:
$ g++ -Wall -W raton.c -o driver_raton -L/usr/lib/ -lusb
$ sudo ./driver_raton
Buscando raton USB...
¡Encontrado!
Deshabilitando driver de raton...

Estado de solicitud de interfaz: 0


Protocolo de dispositivo: 0
Longitud de descriptor: 18
Tipo de descriptor: 1
Numero de endpoints: 1
Clase de interfaz: 3
Protocolo: 2
Numero de interfaz: 0
Nombre de fichero de dispositivo: 006

Fabricante: Logitech
Nombre del producto: USB Mouse
Numero de serie de dispositivo: ?
Direccion de endpoint: 0x81

0 -1 0 0
0 -2 0 0
0 -1 0 0
0 -1 -1 0
0 -1 0 0
0 -1 0 0
1000
3000
Cerrando dispositivo.

Enero 2009 Rafa Muñoz Cárdenas 15


5. Otros ejemplos de drivers

Necesitamos crear drivers de dispositivos complejos a los que hay que enviar señales
de control para poder interactuar con ellos o URB's.

No disponemos de documentación sobre su funcionamiento, por lo que


necesitaremos realizar ingeniería inversa sobre drivers existentes (de Windows la
mayoría).

Enero 2009 Rafa Muñoz Cárdenas 16


5.1. Ingeniería inversa

Un programa conocido para “espiar” la información que circula por los buses USB es
SnoopyPro.
El procedimiento para capturar información revelante es el siguiente:
1. Iniciamos SnoopyPro y señalamos la interfaz a monitorizar.
2. Realizamos una acción simple con el dispositivo.
3. Guardamos la salida obtenida de realizar la acción simple.
4. Volvemos a realizar la acción simple con el periférico reiniciado para
asegurarnos y guardamos de nuevo el log con la salida.

Enero 2009 Rafa Muñoz Cárdenas 17


5.1. Ingeniería inversa

Ejemplo de i. inversa de dispositivo


Cada comando enviado al dispositivo requiere
3 mensajes de control:
El primero son todo ceros, probablemente
como señal de que una orden va a llegar.
En el segundo mensaje vemos que el buffer
tiene el valor 0x00000008, y
el endpoint para escribir el buffer es el 0 (0x00).
Finalmente un mensaje con todo ceros para no
repetir infinitamente el segundo mensaje.

Esta secuencia de mensajes viene de mover


hacia la derecha un dispositivo a través de un
programa para Windows proporcionado por el
fabricante.

Enero 2009 Rafa Muñoz Cárdenas 18


5.2. Lanzamisiles

El ejemplo anterior correspondía a un lanzamisiles... de juguete :)


Si continuamos monitorizando los movimientos del lanzamisiles vemos que:

Moviéndolo hacia arriba el contenido del buffer es 0x00000001.


A menos que se envíe un comando stop (0x00000000), se mantiene ejecutando
continuamente el último comando enviado.
Continuamos la monitorización y ya podemos escribir:
#define ML_STOP 0x00
#define ML_ARR 0x01
#define ML_ABA 0x02
#define ML_IZQ 0x04
#define ML_DER 0x08
#define ML_FUEGO 0x10

Si continuamos moviendo el lanzamisiles hacia un lado y llega a su ángulo máximo de


giro, detectamos que aparecen los siguientes bytes en el buffer del endpoint de
interrupciones (de tipo IN):
#define ML_MAX_ARR 0x80 /* 80 00 00 00 00 00 00 00 */
#define ML_MAX_ABA 0x40 /* 40 00 00 00 00 00 00 00 */
#define ML_MAX_IZQ 0x04 /* 00 04 00 00 00 00 00 00 */
#define ML_MAX_DER 0x08 /* 00 08 00 00 00 00 00 00 */

Enero 2009 Rafa Muñoz Cárdenas 19


5.2. Lanzamisiles

Otros datos interesantes extraídos:


Buffer de transferencia de 8 bytes.
Request type=0x21.
Request=0x9.
Value=0x200.
Index: “1” para el primer y tercer mensaje y “0” para el mensaje segundo.

Enero 2009 Rafa Muñoz Cárdenas 20


5.2. Lanzamisiles

Ahora podríamos ser capaces de crear nuestro driver para controlar el lanzamisiles.
int send_message(char* msg, int index) {
int j = usb_control_msg(launcher,
0x21,
0x9,
0x200,
index,
msg, // movimiento arriba/derecha/etc.
8, //bytes
1000); // timeout
return j;
}

void movement_handler(char mov) {


char msg[8];
msg[0] = 0x0; msg[1] = 0x0; msg[2] = 0x0; msg[3] = 0x0;
msg[4] = 0x0; msg[5] = 0x0; msg[6] = 0x0; msg[7] = 0x0;

int deally = send_message(msg, 1); // enviamos ceros


msg[0] = mov;
deally = send_message(msg, 0); // enviamos movimiento
deally = send_message(msg, 1); // enviamos ceros
}

Para cualquier movimiento excepto disparar, sería tan fácil como:


movement_handler(ML_ARR);

Aquí podemos ver una demostración del funcionamiento de este juguete.

Enero 2009 Rafa Muñoz Cárdenas 21


6. Bibliografía

1. USB
http://en.wikipedia.org/wiki/USB
http://free-electrons.com/doc/linux-usb.pdf
http://www.tech-pro.net/intro_usb.html
http://www.fujitsu.com/downloads/EU/es/soporte/discosduros/UnpaseoporUSBMSD.pdf
http://catarina.udlap.mx/u_dl_a/tales/documentos/lep/ordaz_g_r/capitulo2.pdf
Linux Device Drivers, Third Edition (O'Reilly): http://lwn.net/Kernel/LDD3/
http://www.lrr.in.tum.de/Par/arch/usb/download/usbdoc/usbdoc-1.32.pdf
2. Kernel vs. Libusb
http://www.freesoftwaremagazine.com/articles/drivers_linux?page=0%2C0
http://www.linuxquestions.org/questions/linux-kernel-70/kernel-driver-vs-libusb-662647/
http://www.nabble.com/Write-Linux-USB-Driver-td14913265.html
3. Introducción a Usbfs
http://www.linuxforums.org/forum/linux-tutorials-howtos-reference-material/10865-develop
http://libusb.wiki.sourceforge.net/
http://en.wikipedia.org/wiki/Libusb
http://www.linux-usb.org/USB-guide/x173.html

Enero 2009 Rafa Muñoz Cárdenas 22


6. Bibliografía

4. Creación de un driver de ratón


http://www.linuxjournal.com/article/7466
http://www.cs.indiana.edu/~bpisupat/work/usb.html
http://www.linuxjournal.com/article/6396
http://xianfengdesign.blogspot.com/2007/02/linux-usb-input-subsystem.html
5. Otros ejemplos de drivers:
http://www.linuxquestions.org/questions/linux-hardware-18/driver-help-with-libusb-512040/
http://matthias.vallentin.cc/2007/04/writing-a-linux-kernel-driver-for-an-unknown-usb-devic
http://scott.weston.id.au/software/pymissile/
http://www.jespersaur.com/drupal/book/export/html/21
http://www.amctrl.com/rocketlauncher.html

Enero 2009 Rafa Muñoz Cárdenas 23

Potrebbero piacerti anche