Drivers para Linux embebido Martin Ribelotta www.emtech.com.ar Temario Drivers en Linux Hola System-land Mundo! Recursos del Kernel IO-MEM, Direcciones Virtuales vs Reales Comunicándose con el user-land Ejemplo practico: Chalten amba_dev Drivers en Linux User-Land App-0 App-1 Socket Video4Linux USB driver Driver Lib Daemon System-Land FireWire driver WebCam App-3 App-2 Driver Lib Kernel Trap Gates (Syscalls) Virtual File System SATA USB driver driver Disco Rígido GEM Kernel Modesetting Framebuffer Video Placa de Audio ALSA Hola 'system-land' Mundo! Codigo minimo de nuestro driver hola_kernel.c #include <linux/module.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/kernel.h> static int __init hola_init(void) { static int __init hola_init(void) { printk("Hola system-land!.\n"); printk("Hola system-land!.\n"); return 0; // 0 Ok, No 0 fallo return 0; // 0 Ok, No 0 fallo } } static void __exit hola_done(void) { static void __exit hola_done(void) { printk("Chau system-land!\n"); printk("Chau system-land!\n"); } } module_init(hola_init); module_init(hola_init); module_exit(hola_done); module_exit(hola_done); Hola 'system-land' Mundo! Makefile obj-m += hola_kernel.o obj-m += hola_kernel.o all: all: clean: clean: make -C /lib/modules/$(KDIR)/build M=$(PWD) modules make -C /lib/modules/$(KDIR)/build M=$(PWD) modules make -C /lib/modules/$(KDIR)/build M=$(PWD) clean make -C /lib/modules/$(KDIR)/build M=$(PWD) clean Hola 'system-land' Mundo! Así se compila: $~/: $~/: make make KDIR=/home/devel/kernel-2.6.37-chalten KDIR=/home/devel/kernel-2.6.37-chalten Hola 'system-land' Mundo! Así se compila: $~/: $~/: make make KDIR=/home/devel/kernel-2.6.37-chalten KDIR=/home/devel/kernel-2.6.37-chalten !Debemos tener los headers del nucleo a mano! O los fuentes directamente ;-) Hola 'system-land' Mundo! Copiamos al target (nfs o ssh) devel@host$~/: devel@host$~/: scp scp hola_kernel.ko hola_kernel.ko user@target:/root user@target:/root Hola 'system-land' Mundo! Copiamos al target (nfs o ssh) devel@host$~/: devel@host$~/: scp scp hola_kernel.ko hola_kernel.ko user@target:/root user@target:/root Y por fin ejecutamos (en el target) root@target root@target $~/: $~/: insmod insmod /root/hola_kernel.ko /root/hola_kernel.ko Hola 'system-land' Mundo! Copiamos al target (nfs o ssh) devel@host$~/: devel@host$~/: scp scp hola_kernel.ko hola_kernel.ko user@target:/root user@target:/root Y por fin ejecutamos (en el target) root@target root@target $~/: $~/: insmod insmod /root/hola_kernel.ko /root/hola_kernel.ko Ahora el log nos dice que el modulo está cargado root@target $~/: dmesg | tail -n 1 [66668.270161] Hola system-land!. Hola 'system-land' Mundo! Cuando queramos removerlo: root@target root@target $~/: $~/: rmmod rmmod hola_kernel hola_kernel Hola 'system-land' Mundo! Cuando queramos removerlo: root@target root@target $~/: $~/: rmmod rmmod hola_kernel hola_kernel Al revisar el log y vemos que se descargó correctamente root@target $~/: dmesg | tail -n 2 [66668.270161] Hola system-land!. [66679.457397] Chau system-land! Recursos del Kernel Interface para “char devices” Interface para “block devices” Memoria dinámica en el núcleo Threads del núcleo Manejo de direcciones virtuales/reales Acceso a buses (pci, usb, scsi, etc.) Abstracción de DMA Acceso a userland (/proc, /dev, /sysfs) Recursos del Kernel Interface para “char devices” Interface para “block devices” Memoria dinámica en el núcleo Threads del núcleo Manejo de direcciones virtuales/reales Acceso a buses (pci, usb, scsi, etc.) Abstracción de DMA Acceso a userland (/proc, /dev, /sysfs) IO-MEM, Direcciones Virtuales vs Reales Repaso a Memoria Virtual Bus del sistema MEM CPU Dirección logica TLB Page table I/O Periférico Dirección fisica IO-MEM, Direcciones Virtuales vs Reales Pasos para acceder a un area de memoria Adqurir la reguion para uso privado memreg memreg == request_mem_region(STARTADDR, request_mem_region(STARTADDR, SIZE, SIZE, "name"); "name"); IO-MEM, Direcciones Virtuales vs Reales Pasos para acceder a un area de memoria Adqurir la reguion para uso privado memreg memreg == request_mem_region(STARTADDR, request_mem_region(STARTADDR, SIZE, SIZE, "name"); "name"); Mapear las direcciónes fisicas al area virtual void void *memptr *memptr == ioremap(STARTADDR, ioremap(STARTADDR, SIZE); SIZE); IO-MEM, Direcciones Virtuales vs Reales Pasos para acceder a un area de memoria Adqurir la reguion para uso privado memreg memreg == request_mem_region(STARTADDR, request_mem_region(STARTADDR, SIZE, SIZE, "name"); "name"); Mapear las direcciónes fisicas al area virtual void void *memptr *memptr == ioremap(STARTADDR, ioremap(STARTADDR, SIZE); SIZE); STARTADDR debe estar alineado a 4K SIZE esta dado en bloques de 4K IO-MEM, Direcciones Virtuales vs Reales Ahora accedemos como si fuera un area adquirida com malloc o similar void void *memptr *memptr TLB Primitivas Primitivasde delectura lectura unsigned unsigned int int ioread8(void ioread8(void *addr); *addr); unsigned int ioread16(void unsigned int ioread16(void *addr); *addr); unsigned int ioread32(void *addr); unsigned int ioread32(void *addr); IO-MEM, Direcciones Virtuales vs Reales Ahora accedemos como si fuera un area adquirida com malloc o similar void void *memptr *memptr TLB Primitivas Primitivasde deescritura escritura void void iowrite8(u8 iowrite8(u8 value, value, void void *addr); *addr); void iowrite16(u16 value, void void iowrite16(u16 value, void *addr); *addr); void iowrite32(u32 value, void *addr); void iowrite32(u32 value, void *addr); IO-MEM, Direcciones Virtuales vs Reales Al terminar de usar las cosas: Limpiar! Desmapeamos la memoria fisica iounmap(memptr); iounmap(memptr); Desbloqueamos la región release_mem_region(STARTADDR, release_mem_region(STARTADDR, SIZE); SIZE); Comunicándose con el user-land Directorio /dev tipico dev_t device; // major/minor Comunicándose con el user-land Necesitamos: #include <linux/fs.h> #include <linux/uaccess.h> Comunicándose con el user-land Creamos un dev_t dev = register_chrdev(0, “nombre_sugerido”, &fops); Número mayor asignado automáticamente struct file_operations scull_fops = { .owner = THIS_MODULE, .llseek = func_llseek, .read = func_read, .write = func_write, .ioctl = func_ioctl, .open = func_open, .release = func_release, }; Comunicándose con el user-land Ejemplos de prototipos de callbacks ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *offp); ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp); Comunicándose con el user-land Tus aimgas son: unsigned long copy_to_user(void __user *to, const void *from, unsigned long count); unsigned long copy_from_user(void *to, const void __user *from, unsigned long count); Comunicándose con el user-land Comunicándose con el user-land Al remover el modulo: unregister_chrdev(MAJOR(device), “nombre”); Comunicándose con el user-land Referencias: Linux Device Drivers 3rd edition http://lwn.net/Kernel/LDD3/ Kernel API http://www.gnugeneration.com/mirrors/kernel-api/book1.html Contacto EmTech [email protected] www.emtech.com.ar