Device Drivers under Linux
C Hanish Menon
www.hanishkvc.com
Modified On:
Feb 2000, March 2001
Overview
- Linux
- Device Driver Skeletons - Char, Block, Net
- Accessing Resources
- Synchronisation
- Tasking Assistants
- Time
- Misc - PCI
- References
Computer Architecture
|-----| Bus (Control, Addr, Data) |---------|
| CPU |<----------------------------------->| Devices |
|-----| ^ |---------|
^ |
| v
| |-----|
| | DMA |
| |-----|
| ^
| |
| v |--------|
|--------------------------------------->| Memory |
|--------|
- IOPorts (Addr, Data) (Memory mapped, IO mapped)
- Interrupts (Or Polling)
- DMA (PIO)
Linux - Macro Kernel
- (Kernel core + SubSystems + Drivers) -Single
- Conventions for Resource Access
or Could lead to Disaster
- Modular, HAL (to some extent)
- Simple, Straight, Configurable
- Kernel mode code Non pre-emptive
However Interrupts occur, CoOperative calls
Linux - Modular
- Init_Module & CleanUp_Module
- MOD_[INC/DEC]_USE_COUNT
- MODULE_PARM(variable,var_type)
var_type = [min[-max]]{b,h,i,l,s}
- Symbols
EXPORT_NO_SYMBOLS
EXPORT_SYMTAB
EXPORT_SYMBOL[_NOVERS](name)
- User land: insmod, rmmod, modprobe
- Sources: module.h
Driver Interfaces
You decide depending on the
- Device characteristics
- What kernel provides for that class of drivers
- Your requirements
CharDev - Intro
- Files (VFS)
- Dev file
- Usual file interface
- (Type, Major No), Minor No
Major No - Device or Type of Device (i.e a driver)
Minor No - Physical or Logical entities of Device
- Allows
- Arbitrary length of data during i/o
- Sequential access
- No buffering
- register_chrdev, unregister_chrdev
(MajorNo,Name,FileOperations)
CharDev - Interface
- Init_Module
- Check Device & Resource availability
- Initialize Device
- Register Driver & FileOperations
- Open
Allocate resource
MOD_INC_USE_COUNT
- Read & Write
Usual i/o
Access_Ok(AccessMode,Area,Size)
__Put_User(Src,Dest)
__Get_User(Dest,Src)
CharDev - Interface2
- Ioctl (ioctl_nr,lparam)
Control i/o
- Close
DeAllocate Resources
MOD_DEC_USE_COUNT
- CleanUp_Modules
Unregister Driver
- __InitFunc(Common_Device_Initialize)
Incase driver supports Static & Dynamic Modular linking
Called from init_module(dyna) & subsystem_init_file(static)
- Fasync & Poll
Allow flexible i/o from User space
Poll - Polling from User land
- Source: select.c, poll.h
- poll_wait(file*,waitqueue*,poll_table*)
Called within poll
- wake_up_interruptible(&waitqueue*)
Called from event point (interrupt handler)
- User land: select/poll
Fasync -Interrupt mode for User land
- fs,siginfo files
- fasync_helper(fd,file*,on,&fasync_struct*)
Called from fasync
- kill_fasync(fasync_struct*,SIGIO,SIGPOLL_Code)
Called from where the event occurs
SIGPOLL_Code
- POLL_IN, POLL_OUT, POLL_ERR, POLL_HUP, ...
- User land: open(O_NONBLOCK), SIGIO, fcntl(O_ASYNC)
BlockDev - Intro (todo)
- Kernel provides
- Buffering
- Fixed block size
- Simple interface
- Define certain macros and values
blk_size, blksize_size, hardsect_size
- Predefined block_read, block_write, sync ...
- Implement
request_handler
interrupt_handler
generic_blkdev_ioctls_handler
BlockDev - Misc (todo)
- Request structure
rq_dev, cmd, sector, nr_sectors, cur_nr_sectors
buffer, buffer_head list
- buffer_head structure
b_dev, b_rdev, b_blocknr, b_size, b_rsector
b_data, b_next
- blk_init_queue(request_queue, request_handler)
BlockDev - You do (todo)
- Open & Close - Mount & UnMount
- request_handler
- Loop thro the list of CURRENT requests
- Merge clustered request (if you feel so)
- Initiate DMA or so if any wrt device & Return
- interrupt_handler
- end_request
- Call request_handler
- ioclt_handler
geo, size, dma, flushbuffer, partinfo, readahead, ...
- Source: xt.c
NetworkDev - Intro (OkOk)
- Fill net_device structure
- IFNames, base_addr, irq, dma, mtu, type, promiscuity
- tx_buffer, rx_buffer
- dev_addr(mac), broadcast_addr, multi_cast_addrs
- Init & Uninit
init_etherdev, ether_setup - Initialize with defaults
[register/unregister]_netdev(net_device)
- hard_header, rebuild_header ~ defaults
- open, stop, hard_start_xmit, get_stats, ...
- Works on packets -independent of protocol
- Source: netdevice.h, skeleton.c, net/core/dev.c, net_init.c
NetworkDev - You Do (OkOk)
- hardware_start_transmit, tx_timeout
- do_ioctl, set_multicast_list, get_stats
- interrupt_handler
Identify & Process Reasons
If receive_related then Trigger NET_BH
- Bottom_Half (part of Interrupt_handler if no BH)
Data in dev_rx_buffer = Trigger dma, return
recieve_dma_over = Pass to higher layers (netif_rx)
- Source: skeleton.c, loopback.c, via-rhine.c, rtl8139.c
Accessing Resources
- SubSystems help
Portability
Managing Resources
- Manage resources
[check| request| release| allocate]_resource
- I/O operations
- Sources: ioport.h, resource.c, IO_mapping.txt, DMA_mapping.txt
Resource - Memory
- kmalloc(size,flags)
GFP_KERNEL, GFP_NFS, GFP_USER - can sleep
GFP_ATOMIC - won't sleep(for Interrupt Handlers)
GFP_BUFFER, GFP_DMA
- kfree
- Sources: slab.c, mm.h
Resource - IOPort
- Request_region(start, size, name)
- free_region(start, size)
- I/O operations
[in/out][b/w/l][_p]
[ins/outs][b/w/l]
- Sources: ioport.h
Resource - IRQ
- request_irq(irq, handler, irqflags, devname, dev_id)
SA_SHIRQ - Sharable
SA_INTERRUPT - disable local interrupts during handler
SA_SAMPLE_RANDOM - part of entropy poll
- free_irq(irq,dev_id)
dev_id - during irq sharing
- Sources: irq.c
Resource - DMA
- request_dma(dmanr,devname), free_dma
- set_dma_mode, set_dma_addr, set_dma_count
virt_to_bus => address visible to io_devices
(same as Physical addr (in x86))
- enable_dma, disable_dma
- get_dma_residue
- Sources: dma.h, IO_mapping.txt
Resource - IOMemory
- ioremap(offset,size)
- map device memory to kernel segments (during initialization
page mapping is provided only till top of physical memory)
- X86 => io_memory part of system_memory space But NOT Necessarily
in others
- iounmap
- request_mem_region
- [read/write][b/w/l], memcopy_[from/to]io, memset_io
- Sources: io.h, IO_mapping.txt, ioport.h
Synchronisation - 1
- Uni processor system
- save_flags(flags) - cli() - CS - restore_flags(flags)
- cli() - CriticalSection -sti()
- SMP
- spin_lock[_irqsave](spinlock_t, flags)
- Spin_unlock[_irqrestore](spinlock_t, flags)
Irqsave version Disables local interrupts - required
if access in Interrupt context - Slower
- Sources: spinlock.txt, spinlock.h
Synchronisation -2
- Read Write locks
- Useful if many readers and few writers
- [read|write]_lock - CS - [read|write]_unlock
- irqsave & irqrestore version also available
- IrqSave Restore version Disables local interrupts -
required if access in Interrupt context - Slower
- Sources: spinlock.txt, spinlock.h
Synchronisation - 3
- wait queue
- [interruptible_]sleep_on[_timeout](wait_queue)
- wake_up[_interruptible](wait_queue)
- test_and_[set/clear]_bit, [test/clear]_bit
- Sources: sched.c
Tasking Assistants
- Reduce time spent in Interrupt context
- Low priority interrupts disabled
- Same priority interrupts in pending state
- Acknowledge the device
- Take care of device buffers.
- Sources: interrupt.h
TaskAssist - Software Interrupts
- New softirqs not to be defined - Use tasklets
- Run simultaneously on different Processors
- Require to be Re -Entrant
- open_softirq(nr, func, data), raise_softirq(nr)
- Sources: interrupt.h
TaskAssist - Tasklets
- SMP
- run simultaneously on different Processors
- A given Tasklet runs only on a single Processor
at a given instant.
- tasklet_init(tasklet*,func,data), tasklet_kill
- tasklet_enable,tasklet_disable, tasklet_schedule(tasklet*)
- Sources: interrupt.h
TaskAssist - Bottom halves
- SMP
Only one Bottom Half on a given system at a time
- Fixed 32 Bhs
- Old use tasklets instead
- init_bh(nr, func), mark_bh(nr), remove_bh
- Sources: interrupt.h
TaskAssist - TaskQueue
- Extensions to few existing BHs
- TIMER_BH, TQUEUE_BH, IMMEDIATE_BH
- Existing BH s check these lists and run them one after the other
- queue_task(tq_struct, bh_nr) ~ tq_struct.routine
- mark_bh(bh_nr) ???
- Sources: interrupt.h, sched.c
Time
- HZ, jiffies
- TSC -Cycle Count Register in Processor(new processors)
- Dynamic timers - timer_list
init_timer, mod_timer
- xtime & do_gettimeofday - Date & Time of day (Calendar Time)
- Sources: timer.h, timer.c
PCI - Start
- CONFIG_PCI, pcibios_present
- pci_find_[device/class] ~ Old way
- (Vender_id,Device_id,&till_now_pci_dev)
- (class_code,&till_now_pci_dev)
- pci_register_driver(pci_driver) ~ New way
id_table, Probe, Remove, Resume, Suspend
- Sources: pci.h, pci.txt
PCI - Misc
- pci_[read/write]_config_[byte/word/dword]
(pci_dev,where,[ptr/val])
- Misc
- pci_resource_start(pci_dev,BAR)
check_region
- pci_enable_device(pci_dev)
- pci_set_master(pci_dev)
- pci_find_slot(bus,devfn)
- PCI -DMA
pci_alloc_consistent (i.e sync) & pci_map_single,
pci_map_sg (scatter gather)
- Sources: pci.h, pci.txt, DMA-mapping.txt
Misc
- Kernel_Threads
Processes without user context
- Threads
Processes that share data
Clone
References
- LINUX Kernel Source - www.kernel.org
- LXR (cross referencing package) - www.kernel.org
- Grep and friends along with a Few sleepless nights :-)
- Linux Module Programming - www.linuxdocs.org
- Linux Device Drivers - Alexandro Rubini
- Understanding Linux kernel