Fundamentos de drivers para dispositivos en Haiku

Traducción desde: https://www.haiku-os.org/documents/dev/device_driver_basics/

Cuerpo

Los controladores de dispositivos son difíciles de escribir. Comprender el hardware puede ser la parte más difícil. A menudo, la documentación es difícil de leer y entender porque está escrita desde la perspectiva de un diseñador de hardware (es decir, si puede obtener la documentación, muchos fabricantes son muy reacios a divulgarla). Los controladores trabajan directamente con el kernel: un indicador incorrecto puede bloquear todo el sistema operativo. Y, finalmente, no puede usar herramientas de depuración cálidas y cómodas, ya que está trabajando en el kernel.
¿Todavía seguro que quieres escribir uno? DE ACUERDO. Vamos a hablar un poco sobre la API.
Hay 5 (!) Funciones y una global que debe exportar. Son:
  • status_t init_hardware (void) 
    Llamado cuando el sistema arranca. Debe devolver B_OK si el hardware existe. 
  • status_t init_driver (void) 
    Llamado para inicializar tu driver. Debe devolver B_OK, o el intento de abrir el controlador fallará. 
  • void uninit_driver (void) 
    se llama cuando el controlador se descarga de la memoria. 
  • const char ** publish_devices (void) 
    devuelve una matriz de caracteres * que nombran los dispositivos que este controlador exportará. Los nombres son relativos a / dev. 
  • int api_version 
    este global le dice al sistema de controladores con qué versión de la API compiló. Rellénelo con B_CUR_DRIVER_API_VERSION. 
y finalmente:
  • device_hooks * find_device (const char * name) 
    devuelve una serie de punteros de función que proporcionan las llamadas del sistema de archivos dev a: abrir, cerrar, liberar, controlar, leer, escribir, seleccionar, deseleccionar, readv, writev (en ese orden)
Entonces, para ver esto desde un nivel muy alto, implemente las 5 funciones llamadas directamente, la exportación global y algunos / todos los enlaces de dispositivos, y ya está todo listo. La API aquí es muy fácil de entender.
Vamos a hablar un poco sobre cómo construir y probar estos. Una forma sencilla de construir es usar el motor de archivos make de Be. Estas tres líneas de ejemplo demuestran lo que se necesita configurar:
  NOMBRE = usbrawpci 
  TIPO = DRIVER 
  SRCS = usbraw.c 
El controlador debe copiarse en ~ / config / add-ons / kernel / drivers / bin. Se debe hacer un enlace desde allí a la ubicación en ~ / config / add-ons / kernel / drivers donde desea que aparezca el controlador en devfs (/ dev). Por ejemplo:
  ln -s ~ / config / add-ons / kernel / drivers / bin / usbrawpci 
  ~ / config / add-ons / kernel / drivers / dev / bus / usb / usbrawpci 
La depuración es difícil. No hay dos maneras de eso. La mejor manera es con un terminal conectado a su puerto serie. Suponiendo que no tenga tal cosa (yo no), puede habilitar el registro en un archivo. Busque en ~ / config / settings / kernel / drivers / sample. Hay un archivo llamado "kernel". Es una muestra de una configuración del kernel. Cópielo un nivel (en el directorio de controladores) y descomente la línea "syslog_debug_output". Esto hace que el kernel escriba el registro en / var / log / syslog. Ahora puede usar dprintf para imprimir datos al syslog; funciona de manera muy parecida a printf.
La sincronización es importante porque podría (potencialmente) tener dos o más usuarios usando el controlador al mismo tiempo. Imagine 2 aplicaciones ejecutándose en una caja de doble procesador, perfectamente sincronizada para que ambas comiencen a escribir en su dispositivo al mismo tiempo. Ambos estarían en su código de conductor al mismo tiempo *. Eso significa que todo el trabajo de configuración que A está haciendo para prepararse para escribir, B está cambiando casi al mismo tiempo. Muy mal. Hay un par de maneras de proteger su código que se usan comúnmente en los controladores BeOS. Uno es spinlocks. Esto es conceptualmente:
  static volátile myLock; 
  init_hardware () {myLock = 0;} 
  lock () {while (myLock);  myLock = 1;} 
Digo conceptualmente, porque esto no funcionaría en una situación de kernel preprocesador o multiprocesador, por lo que se hace una magia especial para hacer que esto funcione correctamente. En cualquier caso, estoy seguro de que es obvio que esto desperdicia muchos ciclos de CPU.Por esa razón, debe usarse solo para proteger pequeños bits de código rápido que suceden dentro de una interrupción. Afortunadamente para nosotros, no tenemos que escribir spinlocks. Puedes crearlos simplemente con:
  spinlock foo;  // Crear un spinlock 
  cpu_staus old;  // Para mantener toda la información de la CPU 
  old = disable_interrupts();  // Shhh - no interrumpas 
  acquire_spinlock (&foo);  // obtener el bloqueo 
  .. hacer cosas ... // Terminar rápido 
  release_spinlock (& ​foo);  // Desbloquear 
  restore_interrupts(old);  // OK - interrumpeme ahora. 
Nota final sobre los cierres giratorios: consulte con el BeBook sobre lo que puede y no puede hacer dentro de los cierres giratorios: sus opciones son muy limitadas. Pero, de nuevo, no deberías hacer mucho en tu código de interrupción de todos modos.
Así que los spinlocks están bien para el código de interrupción. ¿Qué pasa con el código de no interrupción (que debería ser la mayoría del controlador)? Los semáforos son tu respuesta. Al igual que en la tierra del usuario.
Esto es básicamente todo lo que hay para escribir controladores de dispositivos. La parte difícil es obtener (y comprender) las especificaciones para el hardware. BeOS hace que sea muy fácil crear controladores una vez que sepa qué necesita el hardware.
Sin embargo, solo para hacer esto un poco más fácil, inventé una "plantilla". Está completamente sin probar, pero puede resultarle útil; siéntase libre de descargarlo desde aquí .
Hora de Libertad

Post a Comment

Previous Post Next Post