Linux Device Drivers Técnicas Digitales III Ing. Gustavo Nudelman 2011
←
→
Transcripción del contenido de la página
Si su navegador no muestra la página correctamente, lea el contenido de la página a continuación
Linux Device Drivers Técnicas Digitales III Ing. Gustavo Nudelman 2011 Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Conceptos Generales Subtitulo o nombre del Capitulo 1 capitulo Universidad Universidad Tecnológica Nacional - Facultad Tecnológica Regional Buenos Aires Nacional
Introducción – Que es un driver ? • Un kernel no puede contemplar estáticamente todos los dispositivos existentes ni menos los que se desarrollan a futuro. • El kernel debe permitir ser reestructurado en forma dinámica Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Introducción – Extendiendo el kernel • Una de las grandes prestaciones de Linux reside en la posibilidad de extender las funcionalidades del kernel extendiendo su estructura. • Recompilar el kernel • Obtenemos un nuevo kernel monolítico que incorporará nuevas funcionalidades (Built in) • Utilizar módulos insertables • Permite agregar y/o quitar funcionalidades con módulos compilados aparte, sin necesidad de reiniciar el sistema ni interrumpir la actividad del kernel. • Si los módulos interactúan con el hardware se dice que son “Device Drivers” • Los módulos son implementados con código objeto, (no ejecutable) que es enlazado dinámicamente mediante a herramientas que provee el sistema Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Características de un device driver • Provee mecanismo de acceso a dispositivos. No políticas • Se ejecutará en el contexto del proceso que lo invoque y será parte de dicho proceso • Cualquier error de programación que en aplicaciones pueda significar un “Segmentation fault”, producirá un “kernel panic”. • No se puede invocar a funciones que se encuentran en bibliotecas del sistema, ya que no se realiza un proceso de linking. (ej. No se puede usar printf()) • Se tiene accesos a estructuras del sistema operativo • Se debe disponer de los fuentes del kernel. • ESTAMOS MODIFICANDO KERNEL Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Tipos de drivers • Carácter: • Acceden a las direcciones I/O transfiriendo una sucesión (stream) de bytes • Pueden ser invocados por encuesta (pooling) o interrupción • Se accede a los bytes en forma secuencial • Bloque • Dispositivos que almacenan grandes cantidades de información • Las operaciones de I/O se realizan por bloques que poseen un tamaño mínimo de 512 bytes o potencias de 2 sucesivas. • Las operaciones de I/O las realizan con DMA • El descriptor de archivo que representa el punto de entrada al driver, permite “moverse” hacia adelante y hacia atrás. • Red • Interactúan con los dispositivos de red pero también con dispositivos virtuales y estructuras de kernel que comprende al protocolo de la capa superior siguiente (Campo type) Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Identificación de un driver Major Minor • Los inodos mostrados en la figura (directorio /dev) identifican los puntos de entrada a los drivers para realizar operaciones (Open, Read, Write y close con los dispositivos) • Numero Major: Identifica a una unidad funcional separada • Número Minor: Identifica subunidades funcionales que se pueden controlar con el mismo driver, o diferentes modos de uso de un mismo dispositivo. • El archivo /proc/devices muestra los dispositivos con su número major Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Estructura y diseño de un char device driver Subtitulo o nombre del Capitulo 2 capitulo Universidad Universidad Tecnológica Nacional - Facultad Tecnológica Regional Buenos Aires Nacional
Estructura de un device driver • Se trata de un conjunto de funciones normalizado, ya que un driver debe responder a una interface bien definida con el kernel que posea determinados puntos de entrada • Carece de función main() – No necesita un punto de entrada autónomo • Las funciones son llamadas desde quien invoca al driver, o desde eventos del sistema • Si bien un usuario normal puede programar y compilar un driver, la tarea de incorporar este al kernel solo puede ser efectuada por root. • Lo que agregamos es código objeto puro No debemos linkear • UN DRIVER NO ES UN PROCESO SINO PARTE DEL PROCESO Y CONTEXTO QUE LO INVOCA Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Estructura de un char device driver module_init([Funcion_Init]); Funciones básicas (constructor y module_exit([Funcion_cleanup]); destructor). Siempre deben estar struct file_operations [name] = { .read = [Funcion_read], Estructura con los puntos de .write = [Funcion_Write], entrada. (Los que no se utilicen se .open = [Funcion_Open], mapean a NULL) .release = [Funcion_Close], .owner = THIS_MODULE }; Funcion_Init( . . .) { ...... } Funcion_Cleanup( . . .) Desarrollo de las funciones { ...... } Funcion_ Read(. . .) Universidad Tecnológica Nacional - Facultad . Regional Buenos Aires
Descripción de los métodos - Inserción • module_init: • La función asociada con este evento será invocada al momento de insertar el módulo en el kernel (modprobe o insmod) • La función no recibe argumentos pero puede devolver un entero indicando si tuvo éxito la inserción • Se acostumbra a registrar en driver en este evento mediante register_chrdev • Podemos revisar el driver insertado con el comando lsmod Int register_chrdev (unsigned int major, const char *name, const struct file_operations) Devuelve el Nombre con Puntero a la estructura numero Numero major el que se requerido. (si es cero file_operations donde “major” registra el quedan definidas las asignado el sistema busca el módulo primero disponible) funciones Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Descripción de los métodos - Apertura • .open: • La función asociada con este método será invocada al momento que se efectue la llamada “open” sobre inodo que funciona como punto de entrada al driver. • Dado que esté es el evento donde se “pide” por un recurso, es en esta función donde se debe reservar todo lo inherente a dicho recurso Int [Funcion_Open] (struct inode *inode, struct file *filp) : struct file { Devolvemos el mode_t f_mode; éxito o fracaso Datos de inode por el loff_t pos; de la cual accede el usuario unsigned int f_flags; implementación (estado struct file_operations *f_op; del fichero, hora de void *private_data; creación y modificación, ... etc.) Universidad Tecnológica Nacional - Facultad Regional Buenos Aires }
Método .Open – Estructuras asociadas Definidas en : struct file { mode_t f_mode; Modo de lectura/escritura loff_t pos; posición actual para read/write unsigned int f_flags; Opciones del fichero usadas al abrir struct file_operations *f_op; Estructura f_op void *private_data; Memoria reservada para guardar datos ... (Muchas veces se guarda el minor para } que este disponible para otros métodos) struct inode { kdev_t i_rdev; Contiene la inf. del número major y minor ... } Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Método .Open – utilización • Chequear errores del dispositivo • Inicializar el dispositivo • Comprueba disponibilidad de recursos necesarios • Reserva recursos del sistema (ej., IRQ, semáforos, etc.) • Comprueba el numero minor y actualiza contadores Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Método .Open – Reserva de IRQ int request_irq (unsigned int irq, void (*handler) (int, void *, struct pt_regs *), unsigned long irqflags, const char *devname, void *dev_id) • Irq: Número de IRQ que estamos solicitando • Handler: Puntero al handler de la IRQ • Irq flag: Comportamiento de la IRQ (Ej. IRQF_SHARED si es compartida) • devname: Es el string que se muestra en /proc/interrupts • Dev_id: Es un puntero, especialmente utilizado en interrupciones de tipo compartidas, sirve para colocar allí información privada de manejador de interrupción Si la asignación puede realizarse, la función devuelve 0 También podemos observar /proc/interrupts Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Método .read - Introducción • La función asociada con este método será invocada al momento que se efectúe una operación de lectura (read) a través del descriptor de archivo del inodo asociado. • La información a entregar, se copia desde un buffer en modo Kernel al buffer correspondiente en modo usuario. • Se acostumbra a entregar la información requerida si esta está disponible, o a bloquear el proceso si no lo está. • No es de buena práctica no bloquear al proceso si la información no está disponible Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Método .read ssize_t [Func Read] (struct file *flip, char *buff, size_t count, loff_t *offp) • flip : Puntero a la estructura tipo FILE (Idem al método .open) • buff: Buffer de usuario donde debemos depositar los datos • count : Se le indica al driver la cantidad de bytes que debe leer • offp : Se actualiza la posición del puntero a archivo. Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Método .read (2) Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Semáforos del Kernel • La manera de poner a dormir un proceso en modo kernel es con semáforos • Se trata de semáforos del kernel – NO SYSTEM V • Debemos Instanciarlo struct semaphore [semName] • Debemos Inicializarlo Sema_init (&semName, valor ) • Se decrementa (toma) el semáforo previo a una operación. Si el semaforo estaba en 0, el proceso pasa a “sleeping” down_interruptible(&semName) El proceso puede ser interrumpido por señal down(&semName) El proceso no puede ser interrumpido Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Poniendo a dormir al proceso en .read ssize_t [Func Read] (struct file *flip, char *buff, size_t count, loff_t *offp) { char byte_read; down_interruptible (&sem); byte_read=inb(0x2f8); copy_to_user(buff,&byte_read,(unsigned long) 1); } • También podemos reservar memoria dinámica en modo kernel (según el tamaño especificado para leer) Kbuffer = kmalloc(TAMAÑO,GFP_KERNEL); (En modoo kernel, usamos kmalloc para reservar memoria) Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Despertando al proceso static irqreturn_t [intr_handler](int IRQ, void *dev_id, struct pt_regs *regs) N de IRQ Estructura (por si lo Misma IRQ_NONE estructura con los queremos registros de IRQ_HANDLED informar en que en la función la CPU antes algún log) de la request_irq() interrupción Uso práctico Irqreturn_t[handler] (int irq, void *devid, struct pt_regs *regs) { up (&sem); return IRQ HANDLED } Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Método .write - Introducción • La función asociada con este método será invocada al momento que se efectue una llamada “write” a través del descriptor de archivo del inodo asociado. • La información entregada por el proceso, se copia desde el buffer de usuario al buffer correspondiente en modo kernel. • El driver debe bloquear al proceso con semáforos en el caso de que el dispositivo no esté disponible. Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Método .write – función asociada ssize_t [Funcion Write](struct file *filp, const char *buff, size_t count, loff_t *offp) • flip : Puntero a la estructura tipo FILE (Idem al método .open) • buff: Buffer de usuario donde recibimos los datos • count : Es usuario especifica la cantidad de bytes a escribir • offp : Se actualiza la posición del puntero a archivo. • Como ya sabemos, debemos retornar la cantidad de bytes realmente escritos • Los datos recibidos pasan al kernel utilizando la función unsigned long copy_from_user(void *to, const void *from, unsigned long count) Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Método .release • La función asociada con este evento será invocada al momento que se invoque a la llamada close a través del descriptor de archivo del inodo asociado. void [Funcion Close] (struct inode *inodep, struct file *filp) Utiliza los mismos argumentos que el método .open • Suele decrementarse el contador de uso en el caso de que tengamos diferentes dispositivos actuando con el mismo driver • Se liberan recursos como memoria alocada, interrupciones reservadas, etc. void free_irq (unsigned int irq, void *dev_id); • Se apaga el dispositivo si se trata del último cierre Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Método .ioctl • Utilizado para configurar dispositivos una vez abiertos • La configuración se hace por medio de comandos • El comando de configuración es propietario el dispositivo y para quien diseñe el driver (Este debe proveer el set) int ptr_ioctl (struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg) Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
module_exit - Remoción del módulo • La función asociada con este método será invocada al momento de remover el módulo del kernel (rmmod) • La función no recibe argumentos pero puede devolver un entero indicando si tuvo éxito la remoción • Se acostumbra a quitar el driver del registro del sistema int unregister_chrdev (unsigned int major, const char *name); Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Compilación y gestión del módulo Subtitulo o nombre del Capitulo 3 capitulo Universidad Universidad Tecnológica Nacional - Facultad Tecnológica Regional Buenos Aires Nacional
Requerimientos previos Fuentes del kernel • Redhat/fedora # yum install kernel-devel # yum install doc • Debian /Ubuntu # apt-get install linux-headers-$(uname -r) Los fuentes se instalan en /usr/src Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Compilación - Makefile file=[nombre del archivo fuente] obj-m := fent.o KERNELDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules • obj-m es el nombre del módulo a crear(se creará un .o y un .ko) • KERNELDIR es el directorio donde se encuentran las fuentes del kernel que se necesitan para compilar los módulos • PWD es el directorio actual • default la orden con la que crearemos los módulos Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Inserción y remoción del módulo • Inserción • modprobe (Controla dependencias) • Insmod (no controla dependencias) • Remoción • rmmod remueve el módulo • modprobe -r Podemos ver la salida de estos comandos en /var/log/messages o en la consola si esta es de texto Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Inodo de acceso en /dev • El inodo de acceso o punto de entrada al driver, se crea y vincula con el módulo mediante el comando mknod # mknod /dev/ c Especifica Es el parámetro que vincula al que es un inodo con el driver char device Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Utilidades de consola • lsmod muestra los módulos instalados en el kernel • El archivo /proc/devices muestra los dispositivos con su número major asignado • El archivo /proc/interrupts permite ver las interrupciones asociadas al driver (útil para chequear si la asignación fue exitosa) • Se recomienda monitorear la salida de cada método mediante # tail –f /var/log/messages Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Resumen • Un driver no deja de ser un módulo compuesto por un conjunto de funciones que responden a un standard de la arquitectura del S.O. • Las interacciones con el sistema operativo son pocas, simples y bien definidas • El arte consiste en el manejo del dispositivo en si mismo Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
Ref: http://lwn.net/Kernel/LDD3/ Universidad Tecnológica Nacional - Facultad Regional Buenos Aires
También puede leer