Linux Device Drivers, 3. Auflage (LDD3)
Zusammenfassung
Standardwerk von Jonathan Corbet, Alessandro Rubini und Greg Kroah-Hartman (O’Reilly, 2005) zur Entwicklung von Gerätetreibern für Linux (Kernel 2.6). Die Auswertung stützt sich auf die Kapitel ch00–ch09 (Vorwort bis Hardware-Kommunikation) sowie ch11–ch17 (Datentypen bis Netzwerk-Treiber); Kern-APIs und Code-Muster sind hier verdichtet. Grundkonzepte sind stabil, konkrete APIs teils veraltet.
Inhalt
Grundlagen (Kap. 1–2)
- Leitprinzip: Treiber liefern mechanism not policy – Hardware-Fähigkeiten ohne Nutzungsvorgaben bereitstellen.
- Drei Geräteklassen: Char-Geräte (Bytestrom,
/dev-Node), Block-Geräte (Dateisystem-fähig, eigene Kernel-Schnittstelle), Netzwerk-Interfaces (Pakete, kein/dev-Eintrag). - Minimalmodul:
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void) { printk(KERN_ALERT "Hello, world\n"); return 0; }
static void hello_exit(void) { printk(KERN_ALERT "Goodbye\n"); }
module_init(hello_init);
module_exit(hello_exit);- Modul ≠ Applikation: event-getrieben, nur Kernel-Symbole nutzbar (keine libc), sehr kleiner Stack (ab 4096 Byte – große Variablen dynamisch allokieren), keine Fließkomma-Arithmetik;
currentliefertstruct task_structdes aufrufenden Prozesses (current->comm,current->pid). - Build:
obj-m := hello.oplusmake -C /lib/modules/$(uname -r)/build M=$(PWD) modules. Werkzeuge:insmod/rmmod(direkt),modprobe(löst Abhängigkeiten auf),lsmod(/proc/modules). Module sind an die exakte Kernel-Version gebunden (vermagic.o); Versionsprüfungen mitKERNEL_VERSION(a,b,c)vs.LINUX_VERSION_CODE.
Char-Treiber (Kap. 3)
scull= Speicherbereich als Gerät: scull0–3 (global/persistent), scullpipe (FIFO, blocking I/O ohne Interrupts), scullsingle/scullpriv/sculluid/scullwuid (Zugriffsbeschränkungen).- Device Numbers:
dev_t(12 Bit Major, 20 Bit Minor); MakrosMAJOR(dev),MINOR(dev),MKDEV(major, minor). - Nummern allozieren:
int alloc_chrdev_region(dev_t *dev, firstminor, count, name)(dynamisch, empfohlen) bzw.register_chrdev_region(first, count, name); Freigabe mitunregister_chrdev_region()im Cleanup. struct file_operationsverbindet Syscalls mit dem Treiber:.owner = THIS_MODULE,read(struct file *, char __user *, size_t, loff_t *),write,llseek,poll(Backend von select/epoll/poll),ioctl,mmap,open,release; NULL-Felder haben definierte Defaults (z. B. read →-EINVAL).__usermarkiert Zeiger, die nicht direkt dereferenziert werden dürfen.
Debugging und Nebenläufigkeit (Kap. 4–5)
- printk-Loglevels:
KERN_EMERG…KERN_DEBUG(ohne Komma vor dem Formatstring); Oops-Meldungen auswerten;/proc-Schnittstellen für Statusinformationen. - Semaphoren (
<asm/semaphore.h>, schlafend):DECLARE_MUTEX(name),init_MUTEX(sem), P/V viadown()/down_interruptible()(rückgibt-EINTR) /down_trylock()undup(). - Spinlocks (
<linux/spinlock.h>, nicht schlafend, für Interrupt-Kontext):spinlock_t lock = SPIN_LOCK_UNLOCKED; spin_lock_init(&lock); spin_lock()/spin_unlock(); Varianten mit IRQ-Sperre nötig, wenn der Lock auch von Handlern genutzt wird.
Blocking I/O, Zeit und Speicher (Kap. 6–8)
- User-Datenaustausch:
copy_to_user()/copy_from_user(), Einzelwerte viaget_user()/put_user(). - Warten:
wait_queue_head_t+wait_event_interruptible(queue, condition); Aufwecken von der anderen Seite (z. B. ISR) mitwake_up_interruptible(). - Zeit:
jiffies(read-only, Wrap-around-sichere Vergleichetime_after(a,b)); Umrechnungj + HZ*n/1000; Verzögerungenndelay/udelay/mdelay(busy-wait) vs.msleep(schläft); Kernel-Timer:struct timer_list,add_timer(),del_timer(); Wanduhrzeitdo_gettimeofday(struct timeval *). - Speicher:
void *kmalloc(size_t size, int flags)/kfree(); Flags:GFP_KERNEL(darf schlafen, Prozesskontext),GFP_ATOMIC(Interrupt-Kontext, kann fehlschlagen), dazuGFP_USER,__GFP_DMA,__GFP_HIGHMEM;vmalloc()für große, virtuell zusammenhängende Bereiche; Slab-Caches (kmem_cache_t) für gleichförmige Objekte.
Hardware-Zugriff (Kap. 9)
- I/O-Ports (x86): Reservierung
struct resource *request_region(unsigned long first, unsigned long n, char *name)/release_region(); Zugriffunsigned inb(unsigned port)/outb(byte, port)(je nach Breiteinw/outw,inl/outl;_p-Varianten pausieren; String-Varianten wieinsw). - I/O-Memory (portabel, auch ARM/Zynq): Mapping
void *ioremap(unsigned long phys_addr, unsigned long size)/iounmap(addr); Zugriff ausschließlich überreadb/readw/readlundwriteb/writew/writel– niemals den Zeiger direkt dereferenzieren; Reihenfolgeabsicherungen über Memory Barriers (wmb,rmb).
Kernel-Datentypen und Portabilität (Kap. 11)
- Drei Typklassen: Standard-C (
int), explizit dimensioniert (u8/u16/u32/u64,<asm/types.h>) und kernelspezifisch (pid_t,sector_t).longund Pointer sind architekturabhängig (64 Bit auf LP64); Speicheradressen daher alsunsigned long. - Verknüpfte Listen: eingebettetes
struct list_head;list_add()/list_add_tail()(FIFO-Warteschlangen), Traversal mitlist_for_each(), Containerobjekt viacontainer_of(ptr, type, member).
PCI-Treiber (Kap. 12)
- Identifikation: Tabelle
struct pci_device_idmit MakrosPCI_DEVICE(vendor, device)/PCI_DEVICE_CLASS(...), Export an User Space viaMODULE_DEVICE_TABLE(pci, ids)– Grundlage für Hotplug. - Rahmen:
struct pci_driver(.id_table,.probe,.remove, suspend/resume) mitpci_register_driver()/pci_unregister_driver(). Inprobe:pci_enable_device(), für Busmaster-DMApci_set_master(). - BARs nie hart kodieren:
pci_resource_start/end/flags(dev, bar)liefern die Regionen; danach wie üblichrequest_regionbzw.ioremap. Suche ohne Treiberbindung:pci_get_device().
USB-Treiber (Kap. 13)
- Hierarchie: Device → Konfiguration → Interface → Endpoint; Endpoints sind unidirektionale Pipes, vier Typen: control (Endpoint 0, immer vorhanden), interrupt (festes Intervall), bulk (große Mengen), isochronous (bandbreitengarantiert, verlusttolerant).
- Treiber:
struct usb_driver(.probe,.disconnect,.id_table) mitusb_register()/usb_deregister(). - Transfer primär über URBs:
usb_alloc_urb(),usb_submit_urb()(asynchron, Completion-Callback auch in Interrupt-Kontext); synchrone Kurzformenusb_control_msg()/usb_bulk_msg().
Device Model und sysfs (Kap. 14)
- Kern ist
struct kobject: Referenzverwaltung, Hierarchie, sysfs-Darstellung und uevents (Hotplug an udev). kobjects werden in Domänenobjekte eingebettet (z. B.struct cdev.kobj), Rückgewinnung percontainer_of(). - Gruppierung über
kset/subsystem; das Modell beschreibt Busse (bus_type) und die device↔driver-Zuordnung über den.probe-Mechanismus. - Kommunikation mit User Space über Attribute (sysfs-Dateien statt Debug-ioctls).
mmap und DMA (Kap. 15)
- mmap:
remap_pfn_range(vma, vm_start, pfn, size, prot)bildet physischen RAM/I/O-Speicher en bloc ab; alternativ seitenweise über dienopage-Methode vonvm_operations_struct. - DMA-Busadressen haben den opaken Typ
dma_addr_t; Adressierbarkeit prüfen mitdma_set_mask(dev, mask). - Kohärent:
dma_alloc_coherent(dev, size, &handle, flag)ohne Cache-Kohärenzaufwand. Streaming:dma_map_single()mit RichtungDMA_TO_DEVICE/DMA_FROM_DEVICE; Ownership-Wechsel nur viadma_sync_single_for_cpu()/_for_device(). Mehrere Puffer als Scatterliste mappen.
Block-Treiber (Kap. 16)
- Major optional via
register_blkdev(); eigentliche Registrierung überstruct gendiskplusblock_device_operations(open/release/ioctl/media_changed/revalidate_disk). - Requests landen in der
request_queue(blk_init_queue(fn, lock)); Abarbeitung im Loop mitelv_next_request(q), Abschluss je Request mitend_request(req, ok). - Vor
add_disk(gd)vollständig initialisieren – der Kernel liest sofort Partitionstabellen:set_capacity()(512-Byte-Sektoren), Sektorgröße viablk_queue_hardsect_size().
Netzwerk-Treiber (Kap. 17)
- Interface =
struct net_device:alloc_netdev(sizeof_priv, "sn%d", setup)bzw.alloc_etherdev()(+ether_setup-Defaults), aktivieren mitregister_netdev(). - Sendepfad:
hard_start_xmit(struct sk_buff *, dev); Paketdaten inskb->data(skb->lenBytes). Flusssteuerungnetif_stop_queue()/netif_wake_queue(). - Empfang: skb aufbauen und mit
netif_rx()dem Stack zuführen; unter Last polled Betrieb über NAPI (Interrupts deaktiviert).
Restliche Kapitel (Übersicht)
- Interrupt-Handling (Kap. 10): Top/Bottom Half. TTY-Treiber (Kap. 18).
Quellen
raw/ch01.pdf…raw/ch09.pdf,raw/ch11.pdf…raw/ch17.pdf– Kapitel 1–9 und 11–17 als Einzeldateien (ausgewertet)raw/ch00.pdf(Vorwort),raw/ch10.pdf,raw/ch18.pdf,raw/TITLE.pdf,raw/COPYRIGHT.pdf,raw/AUTHOR.COLO.pdf,raw/ldr3TOC.fm.pdf,raw/ldr3IX.fm.pdf– übrige Buchteile