Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Linux Devices Drivers Desarrollo de un char device Alejandro Furfaro Noviembre 2010 Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Agenda 1 Prerequisitos. 2 Primer Ejemplo. 3 Registro y funcionalidades. 4 Script de Instalación, No mayor 5 Poniendo al proceso a dormir 6 Manejando Interrupciones Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones ¿Que tenemos que instalar antes de empezar? Instalando los headers del kernel ¿Que más? Linux Headers Dijimos que un driver es código de kernel. En consecuencia, un diver va a invocar funciones internas del kernel, y utilizará sus esctructuras de datos. Necesitamos contar con las definiciones de los prototipos de estas funciones, y estructuras. ¿Donde encontramos estas definiciones? Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones ¿Que tenemos que instalar antes de empezar? Instalando los headers del kernel ¿Que más? Linux headers En los headers del sistema operativo Los fuentes del Sistema Operativo proporcionan en sus archivos headers, las definiciones de prototipos de funciones y de estructuras de datos, que emplea el kernel. Y son las que empleará nuestro device driver. Por lo tanto hay que instalar los headers. Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones ¿Que tenemos que instalar antes de empezar? Instalando los headers del kernel ¿Que más? Versión exacta Para saber exactamente que versión de kernel tenemos instalada en el sistema, el comando uname es la respuesta: ˜ $ uname −r 2.6.26−1−686 ˜$ Usando la propiedad de encerrar un comando entre ‘ ‘ ˜ $ sudo apt−g e t i n s t a l l l i n u x −headers −‘uname −r ‘ Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones ¿Que tenemos que instalar antes de empezar? Instalando los headers del kernel ¿Que más? Ya tenemos los headers ¿y ahora? ˜ $ sudo apt−g e t i n s t a l l l i n u x −manual E instalamos las man pages del kernel, en donde econtraremos las funciones internas del kernel mas comunes. Mucas otras habrá que rebuscar en otros medios. ˜ $ sudo apt−g e t i n s t a l l k e r n e l −i n t e r n a l −guide Alguna doc, adicional del kernel. ˜ $ sudo apt−g e t i n s t a l l ‘ ‘ L i n u x Device D r i v e r s −3e r . E d i c i ó n −Alessandro Rubini− Ed . O’ r r e i l y ’ ’ :) Faltarı́a que lo incluyan en el repo. “El libro” para aprender a programar drivers. Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Tiny example Manos a la obra includes y macros # include <l i n u x / module . h> # include <l i n u x / i n i t . h> # include <l i n u x / k e r n e l . h> / ∗ p r i n t k ( ) ∗ / /∗∗ ∗Macro que d e c l a r a e l a u t o r d e l m ódulo ∗∗/ MODULE AUTHOR( ” Fulano−de−t a l ” ) ; /∗∗ ∗Macro que d e c l a r a e l t i p o de l i c e n c i a d e l m ódulo . ∗∗/ MODULE LICENSE( ” Dual BSD/ GPL” ) ; Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Tiny example Manos a la obra init y exit (muy básicos) i n t h e l l o i n i t ( void ) { p r i n t k ( KERN INFO ” Hola mundo\n ” ) ; return 0; } void h e l l o e x i t ( void ) { p r i n t k ( KERN INFO ” Chau mundo ! ! ! \ n ” ) ; } module init ( h e l l o i n i t ) ; module exit ( h e l l o e x i t ) ; Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Tiny example Manos a la obra Makefile obj−m : = h e l l o . o KERNELDIR ?= / l i b / modules / $ ( s h e l l uname −r ) / b u i l d PWD : = $ ( s h e l l pwd ) default : $ (MAKE) −C $ ( KERNELDIR ) M=$ (PWD) modules clean : rm h e l l o . mod . c h e l l o . o rm h e l l o . ko h e l l o . mod . o modules . o r d e r Module . symvers Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Tiny example Manos a la obra compilando... 1 Necesitamos solamente los archivos hello.c anterior y Makefile. ˜ / DeviceDriverK2 . 6 / h e l l o $ l s −l s total 8 4 −rw−r−−r−− 1 a l e j a n d r o a l e j a n d r o 589 o c t 19 2009 h e l l o . c 4 −rw−r−−r−− 1 a l e j a n d r o a l e j a n d r o 246 nov 2 15:33 M a k e f i l e ˜ / DeviceDriverK2 . 6 / h e l l o $ 2 ¿Compilamos? ˜ / DeviceDriverK2 . 6 / h e l l o $ sudo make [ sudo ] password f o r a l e j a n d r o : make −C / l i b / modules /2.6.26 −1 −686/ b u i l d M= /home / a l e j a n d r o / DeviceDriverK2 . 6 / h e l l o modules make [ 1 ] : se i n g r e s a a l d i r e c t o r i o ‘ / u s r / s r c / l i n u x−headers −2.6.26−1−686’ CC [M] / home / a l e j a n d r o / DeviceDriverK2 . 6 / h e l l o / h e l l o . o B u i l d i n g modules , stage 2 . MODPOST 1 modules CC / home / a l e j a n d r o / DeviceDriverK2 . 6 / h e l l o / h e l l o . mod . o LD [M] / home / a l e j a n d r o / DeviceDriverK2 . 6 / 2 0 0 9 / h e l l o / h e l l o . ko make [ 1 ] : se s a l e d e l d i r e c t o r i o ‘ / u s r / s r c / l i n u x−headers −2.6.26−1−686’ ˜ / DeviceDriverK2 . 6 / h e l l o $ 3 Listar el directorio y ver los archivos que se generaron. Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Registrando el dispositivo Capabilities Pasando parámetros al driver haciendo cosas útiles en init y exit i n t h e l l o i n i t ( void ) { int result ; r e s u l t = r e g i s t e r c h r d e v ( h e l l o m a j o r , ” h e l l o 2 ” ,& h e l l o f o p s ) ; p r i n t k ( KERN INFO ” h e l l o 2 : e l n úmero mayor es %d\n ” , r e s u l t ) ; hello major = result ; return 0; } void h e l l o e x i t ( void ) { unregister chrdev ( hello major , ” hello2 ” ) ; } module init ( h e l l o i n i t ) ; module exit ( h e l l o e x i t ) ; Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Registrando el dispositivo Capabilities Pasando parámetros al driver variables importantes /∗∗ ∗ v a r i a b l e donde se almacena e l Major number d e l m ódulo . ∗ Se i n i c i a l i z a a 0 para p e d i r un número a l sistema . ∗/ int hello major = 0; /∗∗ ∗ D e c l a r a c i ó n de l a s f u n c i o n e s que usar á e l d r i v e r ∗ cuando sea accedido . ∗/ struct f i l e o p e r a t i o n s hello fops = { . open = hello open , . release = hello release , . owner = THIS MODULE }; Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Registrando el dispositivo Capabilities Pasando parámetros al driver Manejando funciones... / ∗ Esta f u n c i ó n es l a que c o n t e s t a r á l o s llamados a l a f u n c i ó n ∗ open ( char ∗ , i n t ) cuyo p r i m e r par ámetro corresponda a l nodo ∗ en / dev cuyo major number corresponda a l de e s t e d r i v e r ∗ / i n t h e l l o o p e n ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p ) { p r i n t k ( KERN INFO ” H e l l o 2 : A p e r t u r a de A r c h i v o \n ” ) ; return 0; } / ∗ Esta f u n c i ó n es l a que c o n t e s t a r á l o s llamados a l a f u n c i ó n ∗ open ( char ∗ , i n t ) cuyo p r i m e r par ámetro corresponda a l nodo ∗ en / dev cuyo major number corresponda a l de e s t e d r i v e r ∗ / i n t h e l l o r e l e a s e ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p ) { return 0; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Registrando el dispositivo Capabilities Pasando parámetros al driver Como se hace... Se pasan por lı́nea de comandos al instalar el driver insmod . / h e l l o 2 . ko h e l l o m a j o r =355 Se reciben en el código del módulo en una variable /∗∗ ∗ D e c l a r a c i ó n de par ámetro d e l m ódulo , \ r e f t d 3 m a j o r . ∗ Puede s e r seteado por e l user a l hacer e l insmod . ∗ ∗/ module param ( h e l l o m a j o r , i n t , 0 ) ; module param tiene tres argumentos: nombre, tipo de dato, y la máscara de permisos con la que se creará la entrada en sysfs El tipo de dato es bool, asociado a una variable que debe ser de tipo int, (evaluada como TRUE o FALSE), o invbool que invierte el valor de la variable, o charp, para manejar strings. luego los tipos clásicos del C: int, long, short, uint, ulong, ushort Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Automatizando la instalación Como obtenemos en No Mayor Alojando dinámicamente el No Mayor Al fuente hello3.c y a Makefile, en este tercer ejemplo sumamos un shell script para obtener dinámicamente el Número Mayor, y con él crear automáticamente los nodos necesarios en el /dev. ˜ / DeviceDriverK2 . 6 / d r i v e r 3 $ cat h e l l o 3 d r i v e r . i n i t En particular importan las lı́neas 59 a 63 del script (activarlas en el editor que estén usando!!) i f $INSMOD $devpath $OPTIONS ; then MAJOR= ‘awk ” \\$2==\ ” $DEVICE\ ” { p r i n t \\$1} ” / proc / devices ‘ r e m o v e f i l e s $FILES c r e a t e f i l e s $FILES else Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Automatizando la instalación Como obtenemos en No Mayor Alojando dinámicamente el No Mayor Al fuente hello3.c y a Makefile, en este tercer ejemplo sumamos un shell script para obtener dinámicamente el Número Mayor, y con él crear automáticamente los nodos necesarios en el /dev. ˜ / DeviceDriverK2 . 6 / d r i v e r 3 $ cat h e l l o 3 d r i v e r . i n i t En particular importan las lı́neas 59 a 63 del script (activarlas en el editor que estén usando!!) i f $INSMOD $devpath $OPTIONS ; then MAJOR= ‘awk ” \\$2==\ ” $DEVICE\ ” { p r i n t \\$1} ” / proc / devices ‘ r e m o v e f i l e s $FILES c r e a t e f i l e s $FILES else Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Automatizando la instalación Como obtenemos en No Mayor Alojando dinámicamente el No Mayor Al fuente hello3.c y a Makefile, en este tercer ejemplo sumamos un shell script para obtener dinámicamente el Número Mayor, y con él crear automáticamente los nodos necesarios en el /dev. ˜ / DeviceDriverK2 . 6 / d r i v e r 3 $ cat h e l l o 3 d r i v e r . i n i t En particular importan las lı́neas 59 a 63 del script (activarlas en el editor que estén usando!!) i f $INSMOD $devpath $OPTIONS ; then MAJOR= ‘awk ” \\$2==\ ” $DEVICE\ ” { p r i n t \\$1} ” / proc / devices ‘ r e m o v e f i l e s $FILES c r e a t e f i l e s $FILES else Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Automatizando la instalación Como obtenemos en No Mayor awk... mucho mas que un comando La base es el potente awk (o gawk en Linux, por GNU awk). Veamos un ejemplo desde el shell: ˜ / $ awk ” \$2==\ ” sound\ ” { p r i n t \$1} ” / proc / d e v i c e s 14 ˜ / $ l s −l a s / dev / dsp 0 crw−rw−−−− 1 r o o t audio 14 , 3 nov 8 13:58 / dev / dsp ˜/$ Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo el método que pone el proceso a dormir es, tı́picamente, read Primer paso: agregamos los métodos en File Operations. struct f i l e o p e r a t i o n s hello fops = { . read = h e l l o r e a d , / / read ( ) es bloqueante . . open = hello open , . release = hello release , . owner = THIS MODULE }; Segundo paso: Escribimos la función read. s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char u s e r ∗ buf , s i z e t count , l o f f t ∗ f p o s ) { int result ; r e s u l t = c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) ; p r i n t k ( KERN INFO ” h e l l o 4 : d i s p o s i t i v o l e ı́ d o con número menor %d\n ” , i m i n o r ( f i l p −>p r i v a t e d a t a ) ) ; return s t r l e n ( k b u f f e r ) ; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo el método que pone el proceso a dormir es, tı́picamente, read Primer paso: agregamos los métodos en File Operations. struct f i l e o p e r a t i o n s hello fops = { . read = h e l l o r e a d , / / read ( ) es bloqueante . . open = hello open , . release = hello release , . owner = THIS MODULE }; Segundo paso: Escribimos la función read. s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char u s e r ∗ buf , s i z e t count , l o f f t ∗ f p o s ) { int result ; r e s u l t = c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) ; p r i n t k ( KERN INFO ” h e l l o 4 : d i s p o s i t i v o l e ı́ d o con número menor %d\n ” , i m i n o r ( f i l p −>p r i v a t e d a t a ) ) ; return s t r l e n ( k b u f f e r ) ; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo el método que pone el proceso a dormir es, tı́picamente, read Primer paso: agregamos los métodos en File Operations. struct f i l e o p e r a t i o n s hello fops = { . read = h e l l o r e a d , / / read ( ) es bloqueante . . open = hello open , . release = hello release , . owner = THIS MODULE }; Segundo paso: Escribimos la función read. s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char u s e r ∗ buf , s i z e t count , l o f f t ∗ f p o s ) { int result ; r e s u l t = c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) ; p r i n t k ( KERN INFO ” h e l l o 4 : d i s p o s i t i v o l e ı́ d o con número menor %d\n ” , i m i n o r ( f i l p −>p r i v a t e d a t a ) ) ; return s t r l e n ( k b u f f e r ) ; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo copy to user y copy from user A copy from user la utilizaremos cuando nos ocupemos del método write Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo Hace falta retocar el resto Tercer paso: en el método open (), conseguimos la referencia a filp->private data. /∗∗ ∗ p u n t e r o a b u f f e r de cada d e v i c e . ∗ ∗/ char ∗ k b u f f e r ; i n t h e l l o o p e n ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p ) { unsigned i n t minor number = i m i n o r ( inode ) ; p r i n t k ( KERN INFO ” h e l l o 4 : d e v i c e %d opened\n ” , minor number ) ; /∗∗ ∗ Para que l o s o t r o s metodos puedan o b t e n e r e l numero menor , ∗ l o guardo en e l campo p r i v a t e d a t a de l a e s t r u c t u r a f i l e . ∗ ∗/ f i l p −>p r i v a t e d a t a = inode ; k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) , GFP KERNEL ) ; memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1 ) ; return 0; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo Semáforos del kernel Primero instanciamos la estructura semaphore que define un semáforo del kernel, e inicializamos el semáforo en el método open(). /∗∗ ∗ p u n t e r o a b u f f e r de cada d e v i c e . ∗ ∗/ char ∗ k b u f f e r ; /∗∗ ∗ Sem áforos para s i n c r o n i z a r e l acceso a l b u f f e r . ∗ ∗/ s t r u c t semaphore sem ; i n t h e l l o o p e n ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p ) { unsigned i n t minor number = i m i n o r ( inode ) ; p r i n t k ( KERN INFO ” h e l l o 5 : d e v i c e %d opened\n ” , minor number ) ; k b u f f e r =NULL ; s e m a i n i t (&sem , 1 ) ; / / l i b e r a d o , para c o n t r o l de acceso a k b u f f e r return 0; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo Tomando el semáforo Una vez creado lo manejamos desde el método read (). Usamos la familia de funciones down () del kernel s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char u s e r ∗ buf , s i z e t count , l o f f t ∗ f p o s ) { i f ( ! d o w n i n t e r r u p t i b l e (&sem ) ) { k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) + 1 , GFP KERNEL ) ; memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1 ) ; /∗ copio e l contenido a l b u f f e r del usuario ∗/ i f ( c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) ) { up(&sem ) ; p r i n t k ( KERN INFO ” h e l l o 5 : E r r o r de L e c t u r a en c o p y t o u s e r r e t u r n −EFAULT ; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo liberando el semáforo La función de liberación de un semáforo es: void up(struct semaphore *sem); else { up(&sem ) ; p r i n t k ( KERN INFO ” h e l l o 5 : L e c t u r a e x i t o s a , %d b y t e s \n ” , s return s t r l e n ( k b u f f e r ) ; } } else { p r i n t k ( KERN INFO ” h e l l o 5 : L e c t u r a con e r r o r toma de sem áforo \n ” ) ; r e t u r n −EFAULT ; } } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo funciones down En el mundo Linux, la función P se llama down (o alguna variedad de ese nombre). ‘‘down’’ se refiere al hecho que su operación consiste en: decrementar el valor del semáforo, por lo general pone a “dormir” al proceso que invocó al método del módulo, hasta que el semáforo se libere, permitir el acceso a un recurso de manera segura Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo funciones down Hay tres variantes o versiones de down void down(struct semaphore *sem); int down interruptible(struct semaphore *sem); int down trylock(struct semaphore *sem); down decrementa el valor del semáforo y espera lo que resulte necesario. down interruptible hace lo mismo pero la operación es interrumpible. down trylock nunca duerme. Si el semáforo no está disponible al momento de la llamada, retorna immediatamente un valor no nulo. Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo Versiones Interrumpibles vs. No Interrumpibles Debemos siempre tratar de utilizar la versión interrumpible de cualquier llamada para dormir un proceso, ya que permite ..que desde el espacio de usuario, el proceso que está dormido esperando (por un semáforo en este caso), pueda ser interrumpido por el usuario. Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo Versiones Interrumpibles vs. No Interrumpibles Regla general No usar operaciones No interrumpibles, a menos que sea absolutamente necesario. Las operaciones No Interrumpibles son una excelente forma de crear procesos que no puedan ser terminados de ninguna manera posible (aparecen en estado D por Dreaded en la salida de ps). El uso de down interruptible requiere algunas precaucionea adicionales, sin embargo, al ser interrumpida la operación, la función retorna un valor no nulo, y el proceso llamante no mantiene el semaforo. Por lo tanto, el uso correcto de down interruptible requiere siempre chequear el valor retornado, y responder de manera acorde al proceso invocante. Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo Durmiendo y despertando al escribir Antes de mirar este tramo de código, conviene abrir hello5.c y mirar la definición de file operations ;) s s i z e t h e l l o w r i t e ( s t r u c t f i l e ∗ f i l p , const char u s e r ∗ buf , s i z e t count , l o f f t ∗ f p o s ) { i f ( ! d o w n i n t e r r u p t i b l e (&sem ) ) { kfree ( k b u f f e r ) ; /∗ destruyo e l b u f f e r actual ∗/ / / p i d o ( count ) bytes , e s p e c i f i c a d o s por e l u s u a r i o . k b u f f e r = k m a l l o c ( count , GFP KERNEL ) ; i f ( ! kbuffer ) { / ∗ s i hubo un e r r o r , devuelvo −ENONMEM∗ / p r i n t k ( KERN INFO ” h e l l o 5 : E s c r i t u r a con e r r o r acceso a memoria \ k b u f f e r = NULL ; up(&sem ) ; r e t u r n −ENOMEM; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Antes que nada... recursos del kernel Mandando a dormir al proceso desde el kernel Manejando eficientemente los bloqueos a procesos Escritura del dispositivo Durmiendo y despertando al escribir /∗ copio a k b u f f e r e l buf del usuario , ∗ s i hay e r r o r devuelvo −EFAULT ∗ / i f ( c o p y f r o m u s e r ( k b u f f e r , buf , count ) ) { p r i n t k ( KERN INFO ” h e l l o 5 : E s c r i t u r a con e r r o r en c o p y f r o m u s e r kfree ( kbuffer ) ; up(&sem ) ; r e t u r n −EFAULT ; } else { p r i n t k ( KERN INFO ” h e l l o 5 : E s c r i t u r a k m a l l o c para %d b y t e s \n ” , c p r i n t k ( KERN INFO ” h e l l o 5 : %s \n ” , k b u f f e r ) ; up(&sem ) ; r e t u r n count ; } } else { p r i n t k ( KERN INFO ” h e l l o 5 : E s c r i t u r a con e r r o r toma de semaforo \n ” r e t u r n −EFAULT ; } return 0; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Para usar una IRQ se la pedimos al kernel el handler bloquenado procesos ¿Donde se pide? Abrir holla6.c e ir al método open (). i n t h e l l o o p e n ( s t r u c t inode ∗ inode , s t r u c t f i l e ∗ f i l p ) { unsigned i n t minor number = i m i n o r ( inode ) ; p r i n t k ( KERN INFO ” h e l l o 6 : d e v i c e %d opened\n ” , minor number ) ; k b u f f e r =NULL ; s e m a i n i t (&sem , 0 ) ; / / tomado , para c o n t r o l de acceso a k b u f f e r t i c k c o u n t e r = TICK RELOAD ; p r i n t k ( KERN INFO ” h e l l o 6 : Opening i r q d e v i c e \n ” ) ; i f ( r e q u e s t i r q ( IRQ USED , &my handler , IRQF SHARED , ” h e l l o 6 ” , ” u f a ” r e t u r n −EBUSY; p r i n t k ( KERN INFO ” h e l l o 6 : i r q t i c k c o u n t e r = %d\n ” , t i c k c o u n t e r ) ; p r i n t k ( KERN INFO ” h e l l o 6 : Salgo d e l Opening i r q d e v i c e \n ” ) ; return 0; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Para usar una IRQ se la pedimos al kernel el handler bloquenado procesos que hacemos en el hadnler? Accedemos al hardware, y despertamos al proceso que ha sido bloqueado cuando solicitó los datos disponibles i r q r e t u r n t my handler ( i n t i r q , void ∗ d e v i d ) / / , s t r u c t p t r e g s ∗ re { t i c k c o u n t e r −−; p r i n t k ( KERN INFO ” h e l l o 6 : my handler con t i c k c o u n t e r= %d \n ” , t i c k c o if (! tick counter ) { p r i n t k ( KERN INFO ” h e l l o 6 : t i c k c o u n t e r =0 l i b e r o sem \n ” ) ; t i c k c o u n t e r = TICK RELOAD ; up(&sem ) ; w a k e u p i n t e r r u p t i b l e (& t d i g w a i t q u e u e ) ; } r e t u r n IRQ HANDLED ; } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Para usar una IRQ se la pedimos al kernel el handler bloquenado procesos No lo habı́amos hecho con semáforos? que mas? Utilizamos colas de mensaje /∗∗ ∗ Waitqueue que usaremos en e l modo de espera . ∗/ DECLARE WAIT QUEUE HEAD( t d i g w a i t q u e u e ) ; Cuando el proceso requiere los datos (invoca el método read () de nuestro módulo), lo bloqueamos hasta que los datos estén disponibles, utilizando otras funciones algo mas poderosas que los semáforos s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char u s e r ∗ buf , s i z e t cou p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a i n g r e s o \n ” ) ; i f ( ! d o w n i n t e r r u p t i b l e (&sem ) ) { k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) + 1 , GFP KERNEL ) ; memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1 Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Para usar una IRQ se la pedimos al kernel el handler bloquenado procesos No lo habı́amos hecho con semáforos? que mas? Utilizamos colas de mensaje /∗∗ ∗ Waitqueue que usaremos en e l modo de espera . ∗/ DECLARE WAIT QUEUE HEAD( t d i g w a i t q u e u e ) ; Cuando el proceso requiere los datos (invoca el método read () de nuestro módulo), lo bloqueamos hasta que los datos estén disponibles, utilizando otras funciones algo mas poderosas que los semáforos s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char u s e r ∗ buf , s i z e t cou p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a i n g r e s o \n ” ) ; i f ( ! d o w n i n t e r r u p t i b l e (&sem ) ) { k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) + 1 , GFP KERNEL ) ; memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1 Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Para usar una IRQ se la pedimos al kernel el handler bloquenado procesos No lo habı́amos hecho con semáforos? que mas? Utilizamos colas de mensaje /∗∗ ∗ Waitqueue que usaremos en e l modo de espera . ∗/ DECLARE WAIT QUEUE HEAD( t d i g w a i t q u e u e ) ; Cuando el proceso requiere los datos (invoca el método read () de nuestro módulo), lo bloqueamos hasta que los datos estén disponibles, utilizando otras funciones algo mas poderosas que los semáforos s s i z e t h e l l o r e a d ( s t r u c t f i l e ∗ f i l p , char u s e r ∗ buf , s i z e t cou p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a i n g r e s o \n ” ) ; i f ( ! d o w n i n t e r r u p t i b l e (&sem ) ) { k b u f f e r = k m a l l o c ( s t r l e n ( INIT STRING ) + 1 , GFP KERNEL ) ; memcpy ( ( char ∗ ) k b u f f e r , INIT STRING , s t r l e n ( INIT STRING ) + 1 Alejandro Furfaro Linux Devices DriversDesarrollo de un char device Prerequisitos Primer Ejemplo Registro y parámetros Script de Instalación, No mayor Poniendo al proceso a dormir Manejando Interrupciones Para usar una IRQ se la pedimos al kernel el handler bloquenado procesos No lo habı́amos hecho con semáforos? que mas? i f ( c o p y t o u s e r ( buf , k b u f f e r , s t r l e n ( k b u f f e r ) ) ) / ∗ c o p i o e l c o n t e { up(&sem ) ; p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a con e r r o r en c o p y t o u s e r \n ” r e t u r n −EFAULT ; } else { // up(&sem ) ; p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a e x i t o s a , %d b y t e s \n ” , s t r l e n return s t r l e n ( k b u f f e r ) ; } } else { p r i n t k ( KERN INFO ” h e l l o 6 : L e c t u r a con e r r o r toma de semaforo \n ” ) ; r e t u r n −EFAULT ; } } Alejandro Furfaro Linux Devices DriversDesarrollo de un char device