Sunteți pe pagina 1din 181

EmbeddedLinuxdriverdevelopment

EmbeddedLinux
kernelanddriver
development
MichaelOpdenacker
ThomasPetazzoni
FreeElectrons
Copyright20042009,FreeElectrons.
CreativeCommonsBYSA3.0license
Latestupdate:May26,2010,
Documentsources,updatesandtranslations:
http://freeelectrons.com/docs/kernel
Corrections,suggestions,contributionsandtranslationsarewelcome!

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Contents
Driverdevelopment
Loadablekernelmodules

Sleeping,Interruptmanagement

Memorymanagement

Handlingconcurrency

I/Omemoryandports

Debugging

Characterdrivers

mmap

Processesandscheduling

Devicemodel,sysfs

2
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
Loadablekernelmodules

3
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

hellomodule
/*hello.c*/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
staticint__inithello_init(void)
{
printk(KERN_ALERT"Goodmorrow");
printk(KERN_ALERT"tothisfairassembly.\n");
return0;
}
staticvoid__exithello_exit(void)
{
printk(KERN_ALERT"Alas,poorworld,whattreasure");
printk(KERN_ALERT"hastthoulost!\n");
}

__init:
removedafterinitialization
(statickernelormodule).
__exit:discardedwhen
modulecompiledstatically
intothekernel.

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Greetingmodule");
MODULE_AUTHOR("WilliamShakespeare");

Exampleavailableonhttp://freeelectrons.com/doc/c/hello.c

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Modulelicense
Severalusages
Usedtorestrictthekernelfunctionsthatthemodulecanuseifit
isn'taGPLlicensedmodule
DifferencebetweenEXPORT_SYMBOL()and
EXPORT_SYMBOL_GPL()

Usedbykerneldeveloperstoidentifyissuescomingfrom
proprietarydrivers,whichtheycan'tdoanythingabout
(Taintedkernelnoticeinkernelcrashesandoopses).
Usefulforuserstocheckthattheirsystemis100%free
(check/proc/sys/kernel/tainted)

Values
GPL,GPLv2,GPLandadditionalrights,DualMIT/GPL,Dual
BSD/GPL,DualMPL/GPL,Proprietary
5
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Compilingaoutoftreemodule
ThebelowMakefileshouldbereusableforanyoutoftree
Linux2.6module
Intreemodulesarecoveredlater
Justrunmaketobuildthehello.kofile
Caution:makesurethereisa[Tab]characteratthe
beginningofthe$(MAKE)line(makesyntax)
Either
#Makefileforthehellomodule

[Tab]!
(nospaces)

objm:=hello.o
KDIR:=/lib/modules/$(shellunamer)/build
PWD:=$(shellpwd)
default:
$(MAKE)C$(KDIR)SUBDIRS=$(PWD)modules

fullkernel
sourcedirectory
(configuredand
compiled)
orjustkernel
headersdirectory
(minimum
needed)

Exampleavailableonhttp://freeelectrons.com/doc/c/Makefile
6
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Modulesandkernelversion
Tobecompiled,akernelmoduleneedsaccesstothekernel
headers,containingthefunctions,typesandconstantsdefinitions
Twosolutions
Fullkernelsources
Onlykernelheaders(linuxheaders*packagesinDebian/Ubuntu
distributions)

Thesourcesorheadersmustbeconfigured
Manymacrosorfunctionsdependontheconfiguration

AkernelmodulecompiledagainstversionXofkernelheaders
willnotloadinkernelversionY
modprobe/insmodwillsayInvalidmoduleformat
7
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Symbolsexportedtomodules
Fromakernelmodule,
onlyalimitednumberofkernelfunctionscanbecalled
Functionsandvariableshavetobeexplicitlyexported
bythekerneltobevisiblefromakernelmodule
Twomacrosareusedinthekernel
toexportfunctionsandvariables:
EXPORT_SYMBOL(symbolname),whichexportsa
functionorvariabletoallmodules
EXPORT_SYMBOL_GPL(symbolname),whichexportsa
functionorvariableonlytoGPLmodules
Anormaldrivershouldnotneedanynonexportedfunction.

8
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

hellomodulewithparameters
/*hello_param.c*/
#include<linux/init.h>
#include<linux/module.h>
#include<linux/moduleparam.h>
MODULE_LICENSE("GPL");

Thanksto
JonathanCorbet
fortheexample!

/*Acoupleofparametersthatcanbepassedin:howmanytimeswesay
hello,andtowhom*/
staticchar*whom="world";
module_param(whom,charp,0);
staticinthowmany=1;
module_param(howmany,int,0);
staticint__inithello_init(void)
{
inti;
for(i=0;i<howmany;i++)
printk(KERN_ALERT"(%d)Hello,%s\n",i,whom);
return0;
}
staticvoid__exithello_exit(void)
{
printk(KERN_ALERT"Goodbye,cruel%s\n",whom);
}
module_init(hello_init);
module_exit(hello_exit);

Exampleavailableonhttp://freeelectrons.com/doc/c/hello_param.c

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Declaringamoduleparameter
#include<linux/moduleparam.h>
module_param(
name,
/*nameofanalreadydefinedvariable*/
type,
/*eitherbyte,short,ushort,int,uint,long,
ulong,charp,orbool.
(checkedatcompiletime!)*/
perm
/*for/sys/module/<module_name>/parameters/<param>
0:nosuchmoduleparametervaluefile*/
);
Example
intirq=5;
module_param(irq,int,S_IRUGO);

10
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Declaringamoduleparameterarray
#include<linux/moduleparam.h>
module_param_array(
name,
/*nameofanalreadydefinedarray*/
type,
/*sameasinmodule_param*/
num,
/*numberofelementsinthearray,orNULL(nocheck?)*/
perm
/*sameasinmodule_param*/
);
Example
staticintbase[MAX_DEVICES]={0x820,0x840};
module_param_array(base,int,NULL,0);

11
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
Addingsourcestothekerneltree

12
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Newdriverinkernelsources(1)
Toaddanewdrivertothekernelsources:
Addyournewsourcefiletotheappropriatesourcedirectory.
Example:drivers/usb/serial/navman.c
Describetheconfigurationinterfaceforyournewdriver
byaddingthefollowinglinestotheKconfigfileinthisdirectory:
configUSB_SERIAL_NAVMAN
tristate"USBNavmanGPSdevice"
dependsonUSB_SERIAL
help
Tocompilethisdriverasamodule,chooseMhere:the
modulewillbecallednavman.

13
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Newdriverinkernelsources(2)
AddalineintheMakefilefilebasedontheKconfigsetting:
obj$(CONFIG_USB_SERIAL_NAVMAN)+=navman.o
Runmakexconfigandseeyournewoptions!
Runmakeandyournewfilesarecompiled!
SeeDocumentation/kbuild/fordetails

14
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

HowtocreateLinuxpatches
Downloadthelatestkernelsources
Makeacopyofthesesources:
rsyncalinux2.6.9rc2/linux2.6.9rc2patch/
Applyyourchangestothecopiedsources,andtestthem.
Runmakedistcleantokeeponlysourcefiles.
Createapatchfile:
diffNurlinux2.6.9rc2/\
linux2.6.9rc2patch/>patchfile
Alwayscomparethewholesourcestructures
(suitableforpatchp1)
Patchfilename:shouldrecallwhatthepatchisabout.
Ifyouneedtomanagealotofpatches,usegitorquiltinstead
ThankstoNicolasRougier(Copyright2003,
http://webloria.loria.fr/~rougier/)fortheTuximage

15

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

PracticallabWritingmodules
Writeakernelmodulewithseveral
capabilities,includingmodule
parameters.
Accesskernelinternalsfromyour
module.
Setuptheenvironmenttocompileit

16
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
Memorymanagement

17
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Physicalandvirtualmemory
Physicaladdressspace

Virtualaddressspaces

0xFFFFFFFF

0xFFFFFFFFF

I/Omemory3
I/Omemory2
I/Omemory1

Flash

0xFFFFFFFF
0xC0000000

Memory
Management
Unit

MMU

Process1

0x00000000

0x00000000

CPU
0xFFFFFFFF

RAM1
RAM0

Kernel

Kernel

0xC0000000

Alltheprocesseshavetheir
ownvirtualaddressspace,and
runasiftheyhadaccesstothe
wholeaddressspace.

Process2

0x00000000

0x00000000

18
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

kmallocandkfree
Basicallocators,kernelequivalentsofglibc'smallocand
free.
#include<linux/slab.h>
staticinlinevoid*kmalloc(size_tsize,intflags);

size:numberofbytestoallocate
flags:priority(explainedinafewpages)
voidkfree(constvoid*objp);
Example:(drivers/infiniband/core/cache.c)
structib_update_work*work;
work=kmalloc(sizeof*work,GFP_ATOMIC);
...
kfree(work);

19
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

kmallocfeatures
Quick(unlessit'sblockedwaitingformemorytobefreed).
Doesn'tinitializetheallocatedarea.
TheallocatedareaiscontiguousinphysicalRAM.
Allocatesby2nsizes,andusesafewmanagementbytes.
So,don'taskfor1024whenyouneed1000!You'dget2048!
Caution:driversshouldn'ttrytokmalloc
morethan128KB(upperlimitinsomearchitectures).
Minimummemoryconsumption:
32or64bytes(pagesizedependent).

20
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Mainkmallocflags(1)
Definedininclude/linux/gfp.h(GFP:__get_free_pages)
GFP_KERNEL
Standardkernelmemoryallocation.Mayblock.Fineformost
needs.
GFP_ATOMIC
RAMallocatedfromcodewhichisnotallowedtoblock(interrupt
handlers)orwhichdoesn'twanttoblock(criticalsections).Never
blocks.
GFP_USER
Allocatesmemoryforuserprocesses.Mayblock.Lowestpriority.

21
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Mainkmallocflags(2)
Extraflags(canbeaddedwith|)
__GFP_DMAorGFP_DMA
AllocateinDMAzone
__GFP_ZERO
Returnsazeroedpage.
__GFP_NOFAIL
Mustnotfail.Nevergives
up.Caution:useonlywhen
mandatory!

__GFP_NORETRY
Ifallocationfails,doesn'ttry
togetfreepages.
Example:
GFP_KERNEL|__GFP_DMA
Note:almostonly
__GFP_DMAorGFP_DMA
usedindevicedrivers.

22
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Relatedallocationfunctions
Again,namessimilartothoseofClibraryfunctions
staticinlinevoid*kzalloc(
size_tsize,gfp_tflags);
Zeroestheallocatedbuffer.
staticinlinevoid*kcalloc(
size_tn,size_tsize,gfp_tflags);
Allocatesmemoryforanarrayofnelementsofsizesize,
andzeroesitscontents.
void*__must_checkkrealloc(
constvoid*,size_t,gfp_t);
Changesthesizeofthegivenbuffer.

23
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Availableallocators
Memoryisallocatedusingslabs(groupsofoneormorecontinuouspages
fromwhichobjectsareallocated).Severalcompatibleslaballocatorsare
available:
SLAB:original,wellprovenallocatorinLinux2.6.
SLOB:muchsimpler.Morespaceefficientbutdoesn'tscalewell.Saves
afewhundredsofKBinsmallsystems(dependson
CONFIG_EMBEDDED)
SLUB:thenewdefaultallocatorsince2.6.23,simplerthanSLAB,
scalingmuchbetter(inparticularforhugesystems)andcreatingless
fragmentation.

24
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Slabcachesandmemorypools
Slabcaches:makeitpossibletoallocatemultiple
objectsofthesamesize,withoutwastingRAM.
Sofar,mainlyusedincoresubsystems,
butnotmuchindevicedrivers
(exceptUSBandSCSIdrivers)
Memorypools:poolsofpreallocatedobjects,
toincreasethechancesofallocationstosucceed.
Oftenusedwithfilecaches.

25
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Allocatingbypages
MoreappropriatewhenyouneedbigslicesofRAM:
Apageisusually4K,butcanbemadegreaterinsomearchitectures
(sh,mips:4,8,16or64K,butnotconfigurableini386orarm).
unsignedlongget_zeroed_page(intflags);
Returnsapointertoafreepageandfillsitupwithzeros
unsignedlong__get_free_page(intflags);
Same,butdoesn'tinitializethecontents
unsignedlong__get_free_pages(intflags,
unsignedintorder);
ReturnsapointeronanareaofseveralcontiguouspagesinphysicalRAM.
order:log2(<number_of_pages>)

Ifvariable,canbecomputedfromthesizewiththeget_orderfunction.
Maximum:8192KB(MAX_ORDER=11ininclude/linux/mmzone.h),
exceptinafewarchitectureswhenoverwrittenwithCONFIG_FORCE_MAX_ZONEORDER.

26
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Freeingpages
voidfree_page(unsignedlongaddr);
voidfree_pages(unsignedlongaddr,
unsignedintorder);
Needtousethesameorderasinallocation.

27
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

vmalloc
vmalloccanbeusedtoobtaincontiguousmemoryzones
invirtualaddressspace(evenifpagesmaynotbe
contiguousinphysicalmemory).
void*vmalloc(unsignedlongsize);
voidvfree(void*addr);

28
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Memoryutilities
void*memset(void*s,intc,size_tcount);
Fillsaregionofmemorywiththegivenvalue.
void*memcpy(void*dest,
constvoid*src,
size_tcount);
Copiesoneareaofmemorytoanother.
Usememmovewithoverlappingareas.
LotsoffunctionsequivalenttostandardClibraryonesdefined
ininclude/linux/string.h
andininclude/linux/kernel.h(sprintf,etc.)

29
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Kernelmemorydebugging
Debuggingfeaturesavailablesince2.6.31
Kmemcheck
Dynamiccheckerforaccesstouninitializedmemory.
Onlyavailableonx86sofar,butwillhelptoimprovearchitecture
independentcodeanyway.
SeeDocumentation/kmemcheck.txtfordetails.
Kmemleak
Dynamiccheckerformemoryleaks
Thisfeatureisavailableforallarchitectures.
SeeDocumentation/kmemleak.txtfordetails.
Bothhaveasignificantoverhead.Onlyusethemindevelopment!

30
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
I/Omemoryandports

31
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

PortI/Ovs.MemoryMappedI/O
MMIO

PIO

Sameaddressbustoaddress
memoryandI/Odevices

Differentaddressspacesfor
memoryandI/Odevices

AccesstotheI/Odevices
usingregularinstructions

UsesaspecialclassofCPU
instructionstoaccessI/O
devices

MostwidelyusedI/Omethod
acrossthedifferent
architecturessupportedby
Linux

Exampleonx86:INandOUT
instructions

32
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

RequestingI/Oports
/proc/ioportsexample(x86)
0000001f:dma1
00200021:pic1
00400043:timer0
00500053:timer1
0060006f:keyboard
00700077:rtc
0080008f:dmapagereg
00a000a1:pic2
00c000df:dma2
00f000ff:fpu
0100013f:pcmcia_socket0
01700177:ide1
01f001f7:ide0
03760376:ide1
0378037a:parport0
03c003df:vga+
03f603f6:ide0
03f803ff:serial
0800087f:0000:00:1f.0
08000803:PM1a_EVT_BLK
08040805:PM1a_CNT_BLK
0808080b:PM_TMR
08200820:PM2_CNT_BLK
0828082f:GPE0_BLK
...

structresource*request_region(
unsignedlongstart,
unsignedlonglen,
char*name);
Triestoreservethegivenregionandreturns
NULLifunsuccessful.Example:
request_region(0x0170,8,"ide1");
voidrelease_region(
unsignedlongstart,
unsignedlonglen);
Seeinclude/linux/ioport.hand
kernel/resource.c

33
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

MappingI/Oportsinvirtualmemory
SinceLinux2.6.9,itispossibletogetavirtualaddress
correspondingtoanI/Oportsrange.
AllowsfortransparentuseofPIOandMMIOatthesame
time,asthismemorycanbeaccessedinthesamewayas
MMIOmemory.
Themappingisdoneusingtheioport_mapfunction:
#include<asm/io.h>;
void*ioport_map(unsignedlongport,
unsignedlongnr);
voidioport_unmap(void*address);

34
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

RequestingI/Omemory
/proc/iomemexample

Equivalentfunctionswiththesame

000000000009efff:SystemRAM
0009f0000009ffff:reserved
000a0000000bffff:VideoRAMarea
000c0000000cffff:VideoROM
000f0000000fffff:SystemROM
001000003ffadfff:SystemRAM
001000000030afff:Kernelcode
0030b000003b4bff:Kerneldata
3ffae0003fffffff:reserved
40000000400003ff:0000:00:1f.1
4000100040001fff:0000:02:01.0
4000100040001fff:yenta_socket
4000200040002fff:0000:02:01.1
4000200040002fff:yenta_socket
40400000407fffff:PCICardBus#03
4080000040bfffff:PCICardBus#03
40c0000040ffffff:PCICardBus#07
41000000413fffff:PCICardBus#07
a0000000a0000fff:pcmcia_socket0
a0001000a0001fff:pcmcia_socket1
e0000000e7ffffff:0000:00:00.0
e8000000efffffff:PCIBus#01
e8000000efffffff:0000:01:00.0
...

interface
structresource*request_mem_region(
unsignedlongstart,
unsignedlonglen,
char*name);
voidrelease_mem_region(
unsignedlongstart,
unsignedlonglen);

35
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

MappingI/Omemoryinvirtualmemory
ToaccessI/Omemory,driversneedtohaveavirtual
addressthattheprocessorcanhandle.
Theioremapfunctionssatisfythisneed:
#include<asm/io.h>;
void*ioremap(unsignedlongphys_addr,
unsignedlongsize);
voidiounmap(void*address);
Caution:checkthatioremapdoesn'treturnaNULL
address!
Notethatanioremap_nocachefunctionexists.
ThisdisablestheCPUcacheatthegivenaddressrange.
36
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Differenceswithstandardmemory
Readsandwritesonmemorycanbecached
Thecompilermaychoosetowritethevalueinacpu
register,andmayneverwriteitinmainmemory.
Thecompilermaydecidetooptimizeorreorderreadand
writeinstructions.

37
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

AvoidingI/Oaccessissues
CachingonI/Oportsormemoryalreadydisabled,eitherbythe
hardwareorbyLinuxinitcode.
UsethevolatilestatementinyourCcodetopreventthe
compilerfromusingregistersinsteadofwritingtomemory.
Memorybarriersaresuppliedtoavoidreordering
Hardwareindependent
#include<asm/kernel.h>
voidbarrier(void);

Hardwaredependent

#include<asm/system.h>
voidrmb(void);
Onlyimpactsthebehaviorofthe
voidwmb(void);
compiler.Doesn'tpreventreordering voidmb(void);
intheprocessor:maynotworkon
Safeonallarchitectures!
SMPoroutoforderarchitectures!
38
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

AccessingI/OportsTheoldstyle
Functionstoread/writebytes,wordandlongstoI/Oports:
unsignedin[bwl](unsignedlong*addr);
voidout[bwl](unsignedport,unsignedlong*addr);

Andthestringsvariants:oftenmoreefficientthanthecorresponding
Cloop,iftheprocessorsupportssuchoperations!
voidins[bwl](unsignedport,void*addr,unsignedlong
count);
voidouts[bwl](unsignedport,void*addr,unsignedlong
count);

Notusablewithioport_map():
thosefunctionsneedanI/Oportnumber,notapointertomemory.
PerfectlyfineifdoingonlyPIO

39
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

AccessingMMIOdevicesTheoldstyle
Directlyreadingfromorwritingtoaddressesreturnedby
ioremap(pointerdereferencing)maynotworkonsome
architectures.
TodoPCIstyle,littleendianaccesses:
unsignedread[bwl](void*addr);
voidwrite[bwl](unsigneda,void*addr);

PerfectlyfineifdoingonlyMMIO.WhendoingmixedPIOand
MMIO,theioread/iowritefamilyoffunctionscanbeused
everywhereinsteadofread/write

40
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

AccessingI/OmemoryThenewstyle
Thankstoioport_map(),itispossibletomixPIOandMMIOin
atransparentway.Thefollowingfunctionscanbeusedtoaccess
memoryareasreturnedbyioport_map()orioremap():
unsignedintioread8(void*addr);(samefor16and32)
voidiowrite8(u8value,void*addr);(samefor16and32)

Toreadorwriteaseriesofvalues:
voidioread8_rep(void*addr,void*buf,unsignedlongcount);
voidiowrite8_rep(void*addr,constvoid*buf,unsignedlongcount);

Otherusefulfunctions:
voidmemset_io(void*addr,u8value,unsignedintcount);
voidmemcpy_fromio(void*dest,void*source,unsignedintcount);
voidmemcpy_toio(void*dest,void*source,unsignedintcount);

Note:manydriversstilluseoldfunctionsinstead:
readb,readl,readw,writeb,writel,writew,outb,inb,...

41
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

/dev/mem
Usedtoprovideuserspaceapplications
withdirectaccesstophysicaladdresses.
Usage:open/dev/memandreadorwriteatgivenoffset.
Whatyoureadorwriteisthevalue
atthecorrespondingphysicaladdress.
UsedbyapplicationssuchastheXserver
towritedirectlytodevicememory.
Since2.6.26(x86only,2.6.32status):onlynonRAMcan
beaccessedforsecurityreasons,unlessexplicitly
configuredotherwise(CONFIG_STRICT_DEVMEM).

42
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

PracticallabI/Omemoryandports
Makearemoteconnectiontoyour
boardthroughssh.
Accessthesystemconsolethrough
thenetwork.
ReservetheI/Omemoryaddresses
usedbytheserialport.
Readdeviceregistersandwritedata
tothem,tosendcharactersonthe
serialport.

43
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
Characterdrivers

44
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Usefulnessofcharacterdrivers
Exceptforstoragedevicedrivers,mostdriversfordeviceswith
inputandoutputflowsareimplementedascharacterdrivers.
So,mostdriversyouwillfacewillbecharacterdrivers
Youwillregretifyousleepduringthispart!

45
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Creatingacharacterdriver
Userspace

Userspaceneeds
Thenameofadevicefilein/devto
interactwiththedevicedriverthrough
regularfileoperations(open,read,write,
close...)

Read
buffer
read

write

Thekernelneeds
Copytouser

major/minor

Read
handler

Write
handler

Copyfromuser

/dev/foo

Toknowwhichdriverisinchargeofdevice
fileswithagivenmajor/minornumberpair
Foragivendriver,tohavehandlers(file
operations)toexecutewhenuserspace
opens,reads,writesorclosesthedevice
file.

Write
string

Devicedriver
Kernelspace
46

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Declaringacharacterdriver
Devicenumberregistration
Needtoregisteroneormoredevicenumbers(major/minor
pairs),dependingonthenumberofdevicesmanagedbythe
driver.
Needtofindfreeones!
Fileoperationsregistration
Needtoregisterhandlerfunctionscalledwhenuserspace
programsaccessthedevicefiles:open,read,write,
ioctl,close...

47
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Informationonregistereddevices
Registereddevicesarevisiblein/proc/devices:
Characterdevices:
1mem
4/dev/vc/0
4tty
4ttyS
5/dev/tty
5/dev/console
5/dev/ptmx
6lp
10misc
13input
14sound
...

Blockdevices:
1ramdisk
3ide0
8sd
9md
22ide1
65sd
66sd
67sd
68sd

Major
number

Registered
name
48

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

dev_tdatatype
Kerneldatatypetorepresentamajor/minornumberpair
Alsocalledadevicenumber.
Definedin<linux/kdev_t.h>
Linux2.6:32bitsize(major:12bits,minor:20bits)
Macrotocomposethedevicenumber:
MKDEV(intmajor,intminor);
Macrotoextracttheminorandmajornumbers:
MAJOR(dev_tdev);
MINOR(dev_tdev);

49
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Registeringdevicenumbers(1)
#include<linux/fs.h>
intregister_chrdev_region(
dev_tfrom,
/*Startingdevicenumber*/
unsignedcount,
/*Numberofdevicenumbers*/
constchar*name); /*Registeredname*/
Returns0iftheallocationwassuccessful.
Example
staticdev_tacme_dev=MKDEV(202,128);
if(register_chrdev_region(acme_dev,acme_count,acme)){
printk(KERN_ERRFailedtoallocatedevicenumber\n);
...

50
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Registeringdevicenumbers(2)
Ifyoudon'thavefixeddevicenumbersassignedtoyourdriver
Betternottochoosearbitraryones.
Therecouldbeconflictswithotherdrivers.
ThekernelAPIoffersaalloc_chrdev_regionfunction
tohavethekernelallocatefreeonesforyou.Youcanfindthe
allocatedmajornumberin/proc/devices.txt.

51
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Fileoperations(1)
Beforeregisteringcharacterdevices,youhavetodefine
file_operations(calledfops)forthedevicefiles.
Herearethemainones:
int(*open)(
structinode*,/*Correspondstothedevicefile*/
structfile*);/*Correspondstotheopenfiledescriptor*/
Calledwhenuserspaceopensthedevicefile.
int(*release)(
structinode*,
structfile*);
Calledwhenuserspaceclosesthefile.

52
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Thefilestructure
Iscreatedbythekernelduringtheopencall.
Representsopenfiles.
mode_tf_mode;
Thefileopeningmode(FMODE_READand/orFMODE_WRITE)
loff_tf_pos;
Currentoffsetinthefile.
structfile_operations*f_op;
Allowstochangefileoperationsfordifferentopenfiles!
structdentry*f_dentry
Usefultogetaccesstotheinode:f_dentry>d_inode.

53
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Fileoperations(2)
ssize_t(*read)(
structfile*,
/*Openfiledescriptor*/
__userchar*,
/*Userspacebuffertofillup*/
size_t,
/*Sizeoftheuserspacebuffer*/
loff_t*);
/*Offsetintheopenfile*/
Calledwhenuserspacereadsfromthedevicefile.
ssize_t(*write)(
structfile*,
__userconstchar*,

/*Openfiledescriptor*/
/*Userspacebuffertowrite
tothedevice*/
size_t,
/*Sizeoftheuserspacebuffer*/
loff_t*);
/*Offsetintheopenfile*/
Calledwhenuserspacewritestothedevicefile.

54
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Exchangingdatawithuserspace(1)
Indrivercode,youcan'tjustmemcpybetween
anaddresssuppliedbyuserspaceand
theaddressofabufferinkernelspace!
Correspondtocompletelydifferent
addressspaces(thankstovirtualmemory)
Theuserspaceaddressmaybeswappedouttodisk
Theuserspaceaddressmaybeinvalid
(userspaceprocesstryingtoaccessunauthorizeddata)

55
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Exchangingdatawithuserspace(2)
Youmustusededicatedfunctionssuchasthefollowingones
inyourreadandwritefileoperationscode:
include<asm/uaccess.h>
unsignedlongcopy_to_user(void__user*to,
constvoid*from,
unsignedlongn);
unsignedlongcopy_from_user(void*to,
constvoid__user*from,
unsignedlongn);
Makesurethatthesefunctionsreturn0!
Anotherreturnvaluewouldmeanthattheyfailed.

56
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Fileoperations(3)
int(*ioctl)(structinode*,structfile*,
unsignedint,unsignedlong);
Canbeusedtosendspecificcommandstothedevice,whichare
neitherreadingnorwriting(e.g.changingthespeedofaserial
port,settingvideooutputformat,queryingadeviceserial
number...).
int(*mmap)(structfile*,
structvm_area_struct*);
Askingfordevicememorytobemapped
intotheaddressspaceofauserprocess.
Moreinourmmapsection.
Thesewerejustthemainones:
about25fileoperationscanbeset,correspondingtoall
thesystemcallsthatcanbeperformedonopenfiles.
57
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

readoperationexample
staticssize_t
acme_read(structfile*file,char__user*buf,size_tcount,loff_t*ppos)
{
/*Theacme_bufaddresscorrespondstoadeviceI/Omemoryarea*/
/*ofsizeacme_bufsize,obtainedwithioremap()*/
intremaining_size,transfer_size;
remaining_size=acme_bufsize(int)(*ppos);//byteslefttotransfer
if(remaining_size==0){/*Allread,returning0(EndOfFile)*/
return0;
}
/*Sizeofthistransfer*/
transfer_size=min(remaining_size,(int)count);

if(copy_to_user(buf/*to*/,acme_buf+*ppos/*from*/,transfer_size)){
returnEFAULT;
}else{/*Increasethepositionintheopenfile*/
*ppos+=transfer_size;
returntransfer_size;
}
}

Readmethod

Pieceofcodeavailablein
http://freeelectrons.com/doc/c/acme.c

58
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

writeoperationexample
staticssize_t
acme_write(structfile*file,constchar__user*buf,size_tcount,loff_t*ppos)
{
intremaining_bytes;
/*Numberofbytesnotwrittenyetinthedevice*/
remaining_bytes=acme_bufsize(*ppos);

if(count>remaining_bytes){
/*Can'twritebeyondtheendofthedevice*/
returnEIO;
}
if(copy_from_user(acme_buf+*ppos/*to*/,buf/*from*/,count)){
returnEFAULT;
}else{
/*Increasethepositionintheopenfile*/
*ppos+=count;
returncount;
}
}

Writemethod

Pieceofcodeavailablein
http://freeelectrons.com/doc/c/acme.c

59
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

fileoperationsdefinitionexample(3)
Definingafile_operationsstructure:
#include<linux/fs.h>
staticstructfile_operationsacme_fops=
{
.owner=THIS_MODULE,
.read=acme_read,
.write=acme_write,
};
Youjustneedtosupplythefunctionsyouimplemented!Defaultsfor
otherfunctions(suchasopen,release...)arefineifyoudonot
implementanythingspecial.

60
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Characterdeviceregistration(1)
Thekernelrepresentscharacterdriverswithacdevstructure
Declarethisstructureglobally(withinyourmodule):
#include<linux/cdev.h>
staticstructcdevacme_cdev;
Intheinitfunction,initializethestructure:
cdev_init(&acme_cdev,&acme_fops);

61
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Characterdeviceregistration(2)
Then,nowthatyourstructureisready,addittothesystem:
intcdev_add(
structcdev*p,
/*Characterdevicestructure*/
dev_tdev,
/*Startingdevicemajor/minornumber
*/
unsignedcount); /*Numberofdevices*/
Example(continued):
if(cdev_add(&acme_cdev,acme_dev,acme_count)){
printk(KERN_ERRChardriverregistrationfailed\n);

...

62
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Characterdeviceunregistration
Firstdeleteyourcharacterdevice:
voidcdev_del(structcdev*p);
Then,andonlythen,freethedevicenumber:
voidunregister_chrdev_region(dev_tfrom,
unsignedcount);
Example(continued):
cdev_del(&acme_cdev);
unregister_chrdev_region(acme_dev,acme_count);

63
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Linuxerrorcodes
Trytoreporterrorswitherrornumbersasaccurateas
possible!Fortunately,macronamesareexplicitandyou
canrememberthemquickly.
Genericerrorcodes:
include/asmgeneric/errnobase.h
Platformspecificerrorcodes:
include/asm/errno.h

64
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Chardriverexamplesummary(1)
staticvoid*acme_buf;
staticintacme_bufsize=8192;
staticintacme_count=1;
staticdev_tacme_dev=MKDEV(202,128);
staticstructcdevacme_cdev;
staticssize_tacme_write(...){...}
staticssize_tacme_read(...){...}
staticstructfile_operationsacme_fops=
{
.owner=THIS_MODULE,
.read=acme_read,
.write=acme_write
};

65
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Chardriverexamplesummary(2)
Showshowtohandleerrorsanddeallocateresourcesintherightorder!
staticint__initacme_init(void)
{
interr;
acme_buf=ioremap(ACME_PHYS,
acme_bufsize);
if(!acme_buf){
err=ENOMEM;
gotoerr_exit;
}
if(register_chrdev_region(acme_dev,
acme_count,acme)){
err=ENODEV;
gotoerr_free_buf;
}
cdev_init(&acme_cdev,&acme_fops);
if(cdev_add(&acme_cdev,acme_dev,
acme_count)){
err=ENODEV;
gotoerr_dev_unregister;
}

return0;
err_dev_unregister:
unregister_chrdev_region(
acme_dev,acme_count);
err_free_buf:
iounmap(acme_buf);
err_exit:
returnerr;
}
staticvoid__exitacme_exit(void)
{
cdev_del(&acme_cdev);
unregister_chrdev_region(acme_dev,
acme_count);
iounmap(acme_buf);
}

Completeexamplecodeavailableonhttp://freeelectrons.com/doc/c/acme.c
66
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Definethefileoperationscallbacksforthedevicefile:read,write,ioctl...
Inthemoduleinitfunction,reservemajorandminornumberswith
register_chrdev_region(),initacdevstructurewithyourfileoperationsandadditto
thesystemwithcdev_add().
Inthemoduleexitfunction,callcdev_del()andunregister_chrdev_region()

Systemadministration

Loadthecharacterdrivermodule
Createdevicefileswithmatchingmajorandminornumbersifneeded
Thedevicefileisreadytouse!

Systemuser

Userspace

Characterdriverwriter

Kernel

Characterdriversummary

Kernel

Executesthecorrespondingfileoperations

Kernel

Openthedevicefile,read,write,orsendioctl'stoit.

67
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

PracticallabCharacterdrivers
Writingasimplecharacterdriver,to
writedatatotheserialport.
Onyourworkstation,checkingthat
transmitteddataisreceivedcorrectly.
Exchangingdatabetweenuserspace
andkernelspace.
Practicingwiththecharacterdevice
driverAPI.
Usingkernelstandarderrorcodes.

68
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxDriverDevelopment

Driverdevelopment
Processesandscheduling

69
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Processes
Aprocessisaninstanceofarunningprogram
Multipleinstancesofthesameprogramcanberunning.
Programcode(textsection)memoryisshared.
Eachprocesshasitsowndatasection,addressspace,processor
state,openfilesandpendingsignals.
Thekernelhasaseparatedatastructureforeachprocess.

70
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Threads
InLinux,threadsarejustimplementedasprocesses!
Newthreadsareimplementedasregularprocesses,
withtheparticularitythattheyarecreatedwiththesameaddress
space,filesystemresources,filedescriptorsandsignalhandlers
astheirparentprocess.

71
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Aprocesslife
EXIT_ZOMBIE

Parentprocess
Callsfork()
andcreates
anewprocess

Theprocessiselected
bythescheduler

TASK_RUNNING
Readybut
notrunning

Theeventoccurs
ortheprocessreceives
asignal.Processbecomes
runnableagain

Theprocessispreempted
bytheschedulertorun
ahigherprioritytask

TASK_INTERRUPTIBLE
TASK_UNINTERRUPTIBLE
orTASK_KILLABLE

Taskterminatedbutits
resourcesarenotfreedyet.
Waitingforitsparent
toacknowledgeitsdeath.

TASK_RUNNING
Actuallyrunning

Decidestosleep
onawaitqueue
foraspecificevent

Waiting

72
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Processcontext
Userspaceprogramsandsystemcallsarescheduledtogether

Processcontinuinginuserspace...
(orreplacedbyahigherpriorityprocess)
(canbepreempted)

Processexecutinginuserspace...
(canbepreempted)
Systemcall
orexception

Kernelcodeexecuted
onbehalfofuserspace
(canbepreemptedtoo!)

Stillhasaccesstoprocess
data(openfiles...)

73
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Kernelthreads
Thekerneldoesnotonlyreactfromuserspace(systemcalls,
exceptions)orhardwareevents(interrupts).Italsorunsitsown
processes.
Kernelthreadsarestandardprocessesscheduledandpreemptedin
thesameway(youcanviewthemwithtoporps!)Theyjusthave
nospecialaddressspaceandusuallyrunforever.
Kernelthreadexamples:
pdflush:regularlyflushesdirtymemorypagestodisk
(filechangesnotcommittedtodiskyet).
migration/<n>:PerCPUthreadstomigrateprocesses
betweenprocessors,tobalanceCPUloadbetweenprocessors.

74
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Processpriorities
Regularprocesses
Prioritiesfrom20(maximum)to19(minimum)
Onlyrootcansetnegativepriorities
(rootcangiveanegativeprioritytoaregularuserprocess)
Usethenicecommandtorunajobwithagivenpriority:
nicen<priority><command>
Usetherenicecommandtochangeaprocesspriority:
renice<priority>p<pid>

75
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Timeslices
Theschedulerprioritizeshighpriorityprocesses
bygivingthemabiggertimeslice.
Initialprocesstimeslice:parent'stimeslicesplitin2
(otherwiseprocesswouldcheatbyforking).
Minimumpriority:5msor1jiffy(whicheverislarger)
Defaultpriorityinjiffies:100ms
Maximumpriority:800ms
Note:actuallydependsonHZ.
Seekernel/sched.cfordetails.

76
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Realtimepriorities
Processeswithrealtimeprioritycanbestartedbyrootusingthe
POSIXAPI
Availablethrough<sched.h>(seemansched.hfordetails)
100realtimeprioritiesavailable
SCHED_FIFOschedulingclass:
TheprocessrunsuntilcompletionunlessitisblockedbyanI/O,
voluntarilyrelinquishestheCPU,orispreemptedbyahigherpriority
process.
SCHED_RRschedulingclass:
Difference:theprocessesarescheduledinaRoundRobinway.
Eachprocessisrununtilitexhaustsamaxtimequantum.Thenother
processeswiththesamepriorityarerun,andsoandso...

77
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Whenisschedulingrun?
Eachprocesshasaneed_reschedflagwhichisset:
Afteraprocessexhausteditstimeslice.
Afteraprocesswithahigherpriorityisawakened.
Thisflagischecked(possiblycausingtheexecutionofthe
scheduler)
Whenreturningtouserspacefromasystemcall
Whenreturningfrominterrupts(includingthecputimer),
whenkernelpreemptionisenabled.
Schedulingalsohappenswhenkernelcodeexplicitlyruns
schedule()orexecutesanactionthatsleeps.

78
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
Sleeping

79
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Sleeping
Sleepingisneededwhenaprocess(userspaceorkernelspace)
iswaitingfordata.

Userspaceprocess...

...Userspace

Other
processes
are
scheduled

readdevicefile

return

Systemcall...
askfor
data

sleep

...Systemcall
wakeup
Interrupt
handler

datareadynotification

80
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Howtosleep(1)
Mustdeclareawaitqueue
Staticqueuedeclaration
DECLARE_WAIT_QUEUE_HEAD(module_queue);
Ordynamicqueuedeclaration
wait_queue_head_tqueue;
init_waitqueue_head(&queue);

81
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Howtosleep(2)
Severalwaystomakeakernelprocesssleep
wait_event(queue,condition);
SleepsuntilthetaskiswokenupandthegivenCexpressionistrue.
Caution:can'tbeinterrupted(can'tkilltheuserspaceprocess!)
wait_event_killable(queue,condition);(SinceLinux2.6.25)
Canbeinterrupted,butonlybyafatalsignal(SIGKILL)
wait_event_interruptible(queue,condition);
Canbeinterruptedbyanysignal
wait_event_timeout(queue,condition,timeout);
Alsostopssleepingwhenthetaskiswokenupandthetimeoutexpired.
wait_event_interruptible_timeout(queue,condition,timeout);
Sameasabove,interruptible.

82
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

HowtosleepExample
Fromdrivers/ieee1394/video1394.c
wait_event_interruptible(
d>waitq,
(d>buffer_status[v.buffer]
==VIDEO1394_BUFFER_READY)
);
if(signal_pending(current))
returnEINTR;
Currentlyrunningprocess

83
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Wakingup!
Typicallydonebyinterrupthandlerswhendatasleeping
processesarewaitingforareavailable.

wake_up(&queue);
Wakesupallthewaitingprocessesonthegivenqueue
wake_up_interruptible(&queue);
Wakesuponlytheprocesseswaitinginaninterruptible
sleeponthegivenqueue

84
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Sleepingandwakingupimplementation
Theschedulerdoesn'tkeepevaluatingthesleepingcondition!
wait_event_interruptible(&queue,condition);
TheprocessisputintheTASK_INTERRUPTIBLEstate.
wake_up_interruptible(&queue);
Forallprocesseswaitinginqueue,conditionisevaluated.
Whenitevaluatestotrue,theprocessisputback
totheTASK_RUNNINGstate,andtheneed_reschedflagforthe
currentprocessisset.
Thisway,severalprocessescanbewokenupatthesametime.

85
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
Interruptmanagement

86
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Interrupthandlerconstraints
Notrunfromausercontext:
Can'ttransferdatatoandfromuserspace
(needtobedonebysystemcallhandlers)
InterrupthandlerexecutionismanagedbytheCPU,not
bythescheduler.Handlerscan'trunactionsthatmay
sleep,becausethereisnothingtoresumetheir
execution.Inparticular,needtoallocatememorywith
GFP_ATOMIC.
Havetocompletetheirjobquicklyenough:
theyshouldn'tblocktheirinterruptlinefortoolong.

87
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Registeringaninterrupthandler(1)
Definedininclude/linux/interrupt.h
intrequest_irq(
Returns0ifsuccessful
unsignedintirq,
Requestedirqchannel
irq_handler_thandler,
Interrupthandler
unsignedlongirq_flags,
Optionmask(seenextpage)
constchar*devname,
Registeredname
void*dev_id);
Pointertosomehandlerdata

CannotbeNULLandmustbeuniqueforsharedirqs!
voidfree_irq(unsignedintirq,void*dev_id);
dev_idcannotbeNULLandmustbeuniqueforsharedirqs.
Otherwise,onasharedinterruptline,
free_irqwouldn'tknowwhichhandlertofree.

88
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Registeringaninterrupthandler(2)
irq_flagsbitvalues(canbecombined,noneisfinetoo)
IRQF_DISABLED
"Quick"interrupthandler.Runwithallinterruptsdisabledonthecurrentcpu
(insteadofjustthecurrentline).Forlatencyreasons,shouldonlybeused
whenneeded!
IRQF_SHARED
Runwithinterruptsdisabledonlyonthecurrentirqlineandonthelocalcpu.
Theinterruptchannelcanbesharedbyseveraldevices.Requiresa
hardwarestatusregistertellingwhetheranIRQwasraisedornot.

89
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Whentoregisterthehandler
Eitheratdriverinitializationtime:
consumeslotsofIRQchannels!
Oratdeviceopentime(firstcalltotheopenfileoperation):
betterforsavingfreeIRQchannels.
Needtocountthenumberoftimesthedeviceisopened,to
beabletofreetheIRQchannelwhenthedeviceisno
longerinuse.

90
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Informationoninstalledhandlers
/proc/interrupts
CPU0
0:5616905XTPICtimer#Registeredname
1:9828XTPICi8042
2:0XTPICcascade
3:1014243XTPICorinoco_cs
7:184XTPICIntel82801DBICH4
8:1XTPICrtc
9:2XTPICacpi
11:566583XTPICehci_hcd,uhci_hcd,
uhci_hcd,uhci_hcd,yenta,yenta,radeon@PCI:1:0:0
12:5466XTPICi8042
14:121043XTPICide0
15:200888XTPICide1
NMI:0
NonMaskableInterrupts
ERR:0
Spuriousinterruptcount

91
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Totalnumberofinterrupts
cat/proc/stat|grepintr
intr819076760929671037701102775520196...
Totalnumber
ofinterrupts

IRQ1
total

IRQ2 IRQ3
total
...

92
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Theinterrupthandler'sjob
Acknowledgetheinterrupttothedevice
(otherwisenomoreinterruptswillbegenerated)
Read/writedatafrom/tothedevice
Wakeupanywaitingprocesswaitingforthecompletion
ofthisread/writeoperation:
wake_up_interruptible(&module_queue);

93
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Interrupthandlerprototype
irqreturn_t(*handler)(
int, //irqnumberofthecurrentinterrupt
void*dev_id
//Pointerusedtokeeptrack
//ofthecorrespondingdevice.
//Usefulwhenseveraldevices
//aremanagedbythesamemodule
);
Returnvalue:
IRQ_HANDLED:recognizedandhandledinterrupt
IRQ_NONE:notonadevicemanagedbythemodule.Usefulto
shareinterruptchannelsand/orreportspuriousinterruptsto
thekernel.
94
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Tophalfandbottomhalfprocessing(1)
Splittingtheexecutionofinterrupthandlersin2parts
Tophalf:theinterrupthandlermustcompleteasquickly
aspossible.Onceitacknowledgedtheinterrupt,itjust
schedulesthelengthyrestofthejobtakingcareofthe
data,foralaterexecution.
Bottomhalf:completingtherestoftheinterrupthandler
job.Handlesdata,andthenwakesupanywaitinguser
process.
Bestimplementedbytasklets(alsocalledsoftirqs).

95
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Tophalfandbottomhalfprocessing(2)
Declarethetaskletinthemodulesourcefile:
DECLARE_TASKLET(module_tasklet,/*name*/
module_do_tasklet,/*function*/
data/*params*/
);
Schedulethetaskletinthetophalfpart(interrupthandler):
tasklet_schedule(&module_tasklet);
Notethatatasklet_hi_schedulefunctionisavailableto
definehighprioritytaskletstorunbeforeordinaryones.
Bydefault,taskletsareexecutedrightafteralltophalves
(hardirqs)

96
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Interruptmanagementfun
Inatraininglab,somebodyforgottounregisterahandleron
asharedinterruptlineinthemoduleexitfunction.

Whydidhiskerneloopswithasegmentationfault
atmoduleunload?
Answer...

Inatraininglab,somebodyfreedthetimerinterrupthandler
bymistake(usingthewrongirqnumber).Thesystemfroze.
Rememberthekernelisnotprotectedagainstitself!

97
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Interruptmanagementsummary
Devicedriver
Whenthedevicefileisfirst
open,registeraninterrupt
handlerforthedevice's
interruptchannel.
Interrupthandler
Calledwhenaninterruptis
raised.
Acknowledgetheinterrupt

Tasklet
Processthedata
Wakeupprocesseswaiting
forthedata
Devicedriver
Whenthedeviceisnolonger
openedbyanyprocess,
unregistertheinterrupt
handler.

Ifneeded,scheduleatasklet
takingcareofhandlingdata.
Otherwise,wakeupprocesses
waitingforthedata.
98
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

PracticallabInterrupts
Addingreadcapabilitytothe
characterdriverdevelopedearlier.
Registeraninterrupthandler.
Waitingfordatatobeavailableinthe
readfileoperation.
Wakingupthecodewhendatais
availablefromthedevice.

99
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
Concurrentaccesstoresources

100
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Sourcesofconcurrencyissues
Thesameresourcescanbeaccessedbyseveralkernel
processesinparallel,causingpotentialconcurrencyissues
Severaluserspaceprogramsaccessingthesamedevicedata
orhardware.Severalkernelprocessescouldexecutethesame
codeonbehalfofuserprocessesrunninginparallel.
Multiprocessing:thesamedrivercodecanberunningon
anotherprocessor.ThiscanalsohappenwithsingleCPUswith
hyperthreading.
Kernelpreemption,interrupts:kernelcodecanbeinterruptedat
anytime(justafewexceptions),andthesamedatamaybe
accessbyanotherprocessbeforetheexecutioncontinues.

101
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Avoidingconcurrencyissues
Avoidusingglobalvariablesandshareddatawhenever
possible
(cannotbedonewithhardwareresources).
Usetechniquestomanageconcurrentaccessto
resources.
SeeRustyRussell'sUnreliableGuideToLocking
Documentation/DocBook/kernellocking/
inthekernelsources.

102
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Concurrencyprotectionwithlocks
Process1

Failed
Acquirelock
Success

Criticalcodesection

Tryagain

Process2
Waitlockrelease

Success

Sharedresource

Releaselock

103
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Linuxmutexes
ThemainlockingprimitivesinceLinux2.6.16.
Betterthancountingsemaphoreswhenbinaryonesare
enough.
Mutexdefinition:
#include<linux/mutex.h>
Initializingamutexstatically:
DEFINE_MUTEX(name);
Orinitializingamutexdynamically:
voidmutex_init(structmutex*lock);

104
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

lockingandunlockingmutexes
voidmutex_lock(structmutex*lock);
Triestolockthemutex,sleepsotherwise.
Caution:can'tbeinterrupted,resultinginprocessesyoucannotkill!
intmutex_lock_killable(structmutex*lock);
Same,butcanbeinterruptedbyafatal(SIGKILL)signal.Ifinterrupted,returns
anonzerovalueanddoesn'tholdthelock.Testthereturnvalue!!!
intmutex_lock_interruptible(structmutex*lock);
Same,butcanbeinterruptedbyanysignal.
intmutex_trylock(structmutex*lock);
Neverwaits.Returnsanonzerovalueifthemutexisnotavailable.
intmutex_is_locked(structmutex*lock);
Justtellswhetherthemutexislockedornot.
voidmutex_unlock(structmutex*lock);
Releasesthelock.Doitassoonasyouleavethecriticalsection.

105
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Reader/writersemaphores
Allowsharedaccessbyunlimitedreaders,orbyonly1writer.Writersget
priority.
voidinit_rwsem(structrw_semaphore*sem);
voiddown_read(structrw_semaphore*sem);
intdown_read_trylock(structrw_semaphore*sem);
intup_read(structrw_semaphore*sem);
voiddown_write(structrw_semaphore*sem);
intdown_write_trylock(structrw_semaphore*sem);
intup_write(structrw_semaphore*sem);
Wellsuitedforrarewrites,holdingthesemaphorebriefly.Otherwise,
readersgetstarved,waitingtoolongforthesemaphoretobereleased.

106
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Spinlocks
Lockstobeusedforcodethatisnotallowedtosleep
(interrupthandlers),orthatdoesn'twanttosleep(critical
sections).Beverycarefulnottocallfunctionswhichcan
sleep!
Originallyintendedformultiprocessorsystems
Spinlocksneversleepandkeepspinning
inaloopuntilthelockisavailable.

Stilllocked?

Spinlock

Spinlockscausekernelpreemptiontobedisabled
ontheCPUexecutingthem.

107
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Initializingspinlocks
Static
spinlock_tmy_lock=SPIN_LOCK_UNLOCKED;
Dynamic
voidspin_lock_init(spinlock_t*lock);

108
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Usingspinlocks
Severalvariants,dependingonwherethespinlockiscalled:
voidspin_[un]lock(spinlock_t*lock);
Doesn'tdisableinterrupts.Usedforlockinginprocesscontext
(criticalsectionsinwhichyoudonotwanttosleep).
voidspin_lock_irqsave/spin_unlock_irqrestore
(spinlock_t*lock,unsignedlongflags);
Disables/restoresIRQsonthelocalCPU.
Typicallyusedwhenthelockcanbeaccessedinbothprocess
andinterruptcontext,topreventpreemptionbyinterrupts.
voidspin_[un]lock_bh(spinlock_t*lock);
Disablessoftwareinterrupts,butnothardwareones.
Usefultoprotectshareddataaccessedinprocesscontext
andinasoftinterrupt(bottomhalf).Noneedtodisable
hardwareinterruptsinthiscase.
Notethatreader/writerspinlocksalsoexist.

109

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Deadlocksituations
Theycanlockupyoursystem.Makesuretheyneverhappen!
Don'tcallafunctionthatcan
trytogetaccesstothesame
lock

Holdingmultiplelocksisrisky!

Getlock1
Getlock1

Getlock2

call
Getlock2
Waitforlock1

Dead
Lock!

Getlock1

Dead
Lock!

110
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Kernellockvalidator
FromIngoMolnarandArjanvandeVen
Addsinstrumentationtokernellockingcode
Detectviolationsoflockingrulesduringsystemlife,suchas:
Locksacquiredindifferentorder
(keepstrackoflockingsequencesandcomparesthem).
Spinlocksacquiredininterrupthandlersandalsoinprocess
contextwheninterruptsareenabled.
Notsuitableforproductionsystemsbutacceptableoverheadin
development.
SeeDocumentation/lockdepdesign.txtfordetails

111
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Alternativestolocking
Aswehavejustseen,lockingcanhaveastrongnegative
impactonsystemperformance.Insomesituations,youcould
dowithoutit.
ByusinglockfreealgorithmslikeReadCopyUpdate(RCU).
RCUAPIavailableinthekernel
(Seehttp://en.wikipedia.org/wiki/RCU).
Whenavailable,useatomicoperations.

112
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Atomicvariables
Usefulwhenthesharedresourceisan
integervalue
Evenaninstructionliken++isnot
guaranteedtobeatomiconall
processors!
Header
#include<asm/atomic.h>
Type
atomic_t
containsasignedinteger(atleast24
bits)

Operationswithoutreturnvalue:
voidatomic_inc(atomic_t*v);
voidatomic_dec(atomic_t*v);
voidatomic_add(inti,atomic_t*v);
voidatomic_sub(inti,atomic_t*v);

Simularfunctionstestingtheresult:
intatomic_inc_and_test(...);
intatomic_dec_and_test(...);
intatomic_sub_and_test(...);

Functionsreturningthenewvalue:
intatomic_inc_and_return(...);
intatomic_dec_and_return(...);
intatomic_add_and_return(...);
intatomic_sub_and_return(...);

Atomicoperations(mainones)
Setorreadthecounter:
atomic_set(atomic_t*v,inti);
intatomic_read(atomic_t*v);

113
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Atomicbitoperations
Supplyveryfast,atomicoperations
Onmostplatforms,applytoanunsignedlongtype.
Applytoavoidtypeonafewothers.
Set,clear,toggleagivenbit:
voidset_bit(intnr,unsignedlong*addr);
voidclear_bit(intnr,unsignedlong*addr);
voidchange_bit(intnr,unsignedlong*addr);
Testbitvalue:
inttest_bit(intnr,unsignedlong*addr);
Testandmodify(returnthepreviousvalue):
inttest_and_set_bit(...);
inttest_and_clear_bit(...);
inttest_and_change_bit(...);

114
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

PracticallabLocking
Addlockingtothedrivertoprevent
concurrentaccessestoshared
ressources

115
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
Debuggingandtracing

116
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Debuggingwithprintk
Universaldebuggingtechniqueusedsincethebeginningof
programming(firstfoundincavemendrawings)
Printedornotintheconsoleor/var/log/messages
accordingtothepriority.Thisiscontrolledbytheloglevel
kernelparameter,orthrough/proc/sys/kernel/printk
(seeDocumentation/sysctl/kernel.txt)
Availablepriorities(include/linux/kernel.h):
#defineKERN_EMERG"<0>"/*systemisunusable*/
#defineKERN_ALERT"<1>"/*actionmustbetakenimmediately*/
#defineKERN_CRIT"<2>"/*criticalconditions*/
#defineKERN_ERR"<3>"/*errorconditions*/
#defineKERN_WARNING"<4>"/*warningconditions*/
#defineKERN_NOTICE"<5>"/*normalbutsignificantcondition*/
#defineKERN_INFO"<6>"/*informational*/
#defineKERN_DEBUG"<7>"/*debuglevelmessages*/

117
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Debuggingwith/procor/sys
Insteadofdumpingmessagesinthekernellog,youcanhaveyour
driversmakeinformationavailabletouserspace
Throughafilein/procor/sys,whichcontentsarehandledby
callbacksdefinedandregisteredbyyourdriver.
Canbeusedtoshowanypieceofinformation
aboutyourdeviceordriver.
Canalsobeusedtosenddatatothedriverortocontrolit.
Caution:anybodycanusethesefiles.
Youshouldremoveyourdebugginginterfaceinproduction!
Sincethearrivalofdebugfs,nolongerthepreferreddebugging
mechanism

118
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Debugfs
Avirtualfilesystemtoexportdebugginginformationtouserspace.
Kernelconfiguration:DEBUG_FS
Kernelhacking>DebugFilesystem
Muchsimplertocodethananinterfacein/procor/sys.
ThedebugginginterfacedisappearswhenDebugfsisconfigured
out.
Youcanmountitasfollows:
sudomounttdebugfsnone/mnt/debugfs
Firstdescribedonhttp://lwn.net/Articles/115405/
APIdocumentedintheLinuxKernelFilesystemAPI:
http://freeelectrons.com/kerneldoc/latest/DocBook/filesystems/index.html

119
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Simpledebugfsexample
#include<linux/debugfs.h>
staticchar*acme_buf;
staticunsignedlongacme_bufsize;
staticstructdebugfs_blob_wrapperacme_blob;
staticstructdentry*acme_buf_dentry;

//modulebuffer

staticu32acme_state;
staticstructdentry*acme_state_dentry;

//modulevariable

/*Moduleinit*/
acme_blob.data=acme_buf;
acme_blob.size=acme_bufsize;
acme_buf_dentry=debugfs_create_blob("acme_buf",S_IRUGO,
NULL,&acme_blob);
acme_state_dentry=debugfs_create_bool("acme_state",S_IRUGO,
NULL,&acme_state);
/*Moduleexit*/
debugfs_remove(acme_buf_dentry);
debugfs_remove(acme_state_dentry);

//Create
//newfiles
//indebugfs

//removingthefilesfromdebugfs

120
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Debuggingwithioctl
Canusetheioctl()systemcalltoqueryinformation
aboutyourdriver(ordevice)orsendcommandstoit.
Thiscallstheioctlfileoperationthatyoucanregisterin
yourdriver.
Advantage:yourdebugginginterfaceisnotpublic.
Youcouldevenleaveitwhenyoursystem(oritsdriver)isin
thehandsofitsusers.

121
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

UsingMagicSysRq
Linuxalsohas3fingerkeystosaveyourwork;)
Allowstorunmultipledebug/rescuecommandsevenwhenthe
kernelseemstobeindeeptrouble.Examplecommands:
[ALT][SysRq][d]:killsallprocesses,exceptinit.
[ALT][SysRq][n]:makesRTprocessesniceable.
[ALT][SysRq][s]:attemptstosyncallmountedfilesystems.
[ALT][SysRq][b]:immediatelyrebootwithoutsyncingand
unmounting.
Typicalcombination:[ALT][SysRq][s]
andthen[ALT][SysRq][b]
DetailedinDocumentation/sysrq.txt

122
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Debuggingwithgdb
Ifyouexecutethekernelfromadebuggeronthesame
machine,thiswillinterferewiththekernelbehavior.
However,youcanaccessthecurrentkernelstatewithgdb:
gdb/usr/src/linux/vmlinux/proc/kcore
uncompressedkernelkerneladdressspace
Youcanaccesskernelstructures,followpointers...
(readonly!)
Requiresthekerneltobecompiledwith
CONFIG_DEBUG_INFO(Kernelhackingsection)

123
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

kgdbAkerneldebugger
Theexecutionofthekernelisfullycontrolledbygdbfrom
anothermachine,connectedthroughaserialline.
Candoalmosteverything,includinginsertingbreakpointsin
interrupthandlers.
FeatureincludedinstandardLinuxsince2.6.26(x86and
sparc).arm,mipsandppcsupportmergedin2.6.27.

124
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Usingkgdb
Detailsavailableinthekerneldocumentation:
http://freeelectrons.com/kerneldoc/latest/DocBook/kgdb/
RecommendedtoturnonCONFIG_FRAME_POINTERtoaid
inproducingmorereliablestackbacktracesingdb.
YoumustincludeakgdbI/Odriver.Oneofthemiskgdb
overserialconsole(kgdboc:kgdboverconsole,enabled
byCONFIG_KGDB_SERIAL_CONSOLE)
Configurekgdbocatboottimebypassingtothekernel:
kgdboc=<ttydevice>,[baud].Forexample:
kgdboc=ttyS0,115200

125
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Usingkgdb(2)
Thenalsopasskgdbwaittothekernel:
itmakeskgdbwaitforadebuggerconnection.
Bootyourkernel,andwhentheconsoleisinitialized,interruptthe
kernelwith[Alt][SyrRq][g].
Onyourworkstation,startgdbasfollows:
%gdb./vmlinux
(gdb)setremotebaud115200
(gdb)targetremote/dev/ttyS0
Onceconnected,youcandebugakernelthewayyouwould
debuganapplicationprogram.

126
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

DebuggingwithaJTAGinterface
TwotypesofJTAGdongles
Thoseofferingagdbcompatibleinterface,overaserialportoranEthernet
connexion.Gdbcandirectlyconnecttothem.
Thosenotofferingagdbcompatibleinterfacearegenerallysupportedby
OpenOCD(OpenOnChipDebugger)
OpenOCDisthebridgebetweenthegdbdebugginglanguageandthe
JTAGdonglespecificlanguage
http://openocd.berlios.de/web/
Seetheverycompletedocumentation:http://openocd.berlios.de/doc/
Foreachboard,you'llneedanOpenOCDconfigurationfile(askyour
supplier)
SeeveryusefuldetailsonusingEclipse/gcc/gdb/OpenOCDonWindows:
http://www2.amontec.com/sdk4arm/ext/jlynchtutorial20061124.pdfand
http://www.yagarto.de/howto/yagarto2/
127
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Morekerneldebuggingtips
EnableCONFIG_KALLSYMS_ALL
(GeneralSetup>Configurestandardkernelfeatures)
togetoopsmessageswithsymbolnamesinsteadofrawaddresses
(thisobsoletestheksymoopstool).
Ifyourkerneldoesn'tbootyetorhangswithoutanymessage,youcan
activateLowLeveldebugging(KernelHackingsection,onlyavailableon
arm):
CONFIG_DEBUG_LL=y
TechniquestolocatetheCinstructionwhichcausedanoops:
http://kerneltrap.org/node/3648
MoreaboutkerneldebugginginthefreeLinuxDeviceDriversbook:
http://lwn.net/images/pdf/LDD3/ch04.pdf

128
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

TracingwithSystemTap
http://sourceware.org/systemtap/
Infrastructuretoaddinstrumentationtoarunningkernel:
tracefunctions,readandwritevariables,followpointers,gatherstatistics...
Eliminatestheneedtomodifythekernelsourcestoaddone'sown
instrumentationtoinvestigatedafunctionalorperformanceproblem.
Usesasimplescriptinglanguage.
Severalexamplescriptsandprobepointsareavailable.
BasedontheKprobesinstrumentationinfrastructure.
SeeDocumentation/kprobes.txtinkernelsources.
Linux2.6.26:supportedonmostpopularCPUs(armincludedin2.6.25).
However,lackofrecentsupportformips(2.6.16only!).

129
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

SystemTapscriptexample(1)
#!/usr/bin/envstap
#Usingstatisticsandmapstoexaminekernelmemoryallocations
globalkmalloc
probekernel.function("__kmalloc"){
kmalloc[execname()]<<<$size
}
#Exitafter10seconds
probetimer.ms(10000){exit()}
probeend{
foreach([name]inkmalloc){
printf("Allocationsfor%s\n",name)
printf("Count:%dallocations\n",@count(kmalloc[name]))
printf("Sum:%dKbytes\n",@sum(kmalloc[name])/1000)
printf("Average:%dbytes\n",@avg(kmalloc[name]))
printf("Min:%dbytes\n",@min(kmalloc[name]))
printf("Max:%dbytes\n",@max(kmalloc[name]))
print("\nAllocationsbysizeinbytes\n")
print(@hist_log(kmalloc[name]))
printf("\n\n");
}
}

130
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

SystemTapscriptexample(2)
#!/usr/bin/envstap
#Logseachfilereadperformedbyeachprocess
probekernel.function("vfs_read")
{
dev_nr=$file>f_dentry>d_inode>i_sb>s_dev
inode_nr=$file>f_dentry>d_inode>i_ino
printf("%s(%d)%s0x%x/%d\n",
execname(),pid(),probefunc(),dev_nr,inode_nr)
}

Nicetutorialonhttp://sources.redhat.com/systemtap/tutorial.pdf

131
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Kernelcrashanalysiswithkexec/kdump
kexecsystemcall:makesitpossibleto
callanewkernel,withoutrebootingand
goingthroughtheBIOS/firmware.
Idea:afterakernelpanic,makethe
kernelautomaticallyexecuteanew,
cleankernelfromareservedlocationin
RAM,toperformpostmortemanalysis
ofthememoryofthecrashedkernel.

1.Copydebug
kernelto
reserved
RAM
3.Analyze
crashed
kernelRAM

Standardkernel
2.kernel
panic,kexec
debugkernel
Debugkernel

SeeDocumentation/kdump/kdump.txt
inthekernelsourcesfordetails.
RegularRAM

132
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Kernelmarkers
Capabilitytoaddstaticmarkerstokernelcode,
mergedinLinux2.6.24byMatthieuDesnoyers.
Almostnoimpactonperformance,untilthemarkerisdynamically
enabled,byinsertingaprobekernelmodule.
Usefultoinserttracepointsthatwon'tbeimpactedbychangesin
theLinuxkernelsources.
Seemarkerandprobeexample
insamples/markersinthekernelsources.
Seehttp://en.wikipedia.org/wiki/Kernel_marker

133
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

LTTng
http://lttng.org
ThesuccessoroftheLinuxTraceToolkit(LTT)
Toolkitallowingtocollectandanalyzetracinginformationfrom
thekernel,basedonkernelmarkersandkerneltracepoints.
Sofar,basedonkernelpatches,butdoingitsbesttouseintree
solutions,andtobemergedinthefuture.
Veryprecisetimestamps,verylittleoverhead.
Usefuldocumentationonhttp://lttng.org/?q=node/2#manuals

134
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

LTTV
ViewerforLTTngtraces
Supportforhugetraces(testedwith15GBones)
Cancombinemultipletracefilesinasingleview.
Graphicalortextinterface
Seehttp://lttng.org/files/lttvdoc/user_guide/

135
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

PracticallabKerneldebugging
Loadabrokendriverandseeitcrash
Analyzetheerrorinformation
dumpedbythekernel.
Disassemblethecodeandlocate
theexactCinstructionwhichcaused
thefailure.
UsetheJTAGandOpenOCDto
remotelycontrolthekernelexecution

136
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
mmap

137
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

mmap(1)
Possibilitytohavepartsofthevirtualaddressspaceofaprogram
mappedtothecontentsofafile!
>cat/proc/1/maps(initprocess)
startend
permoffsetmajor:minorinodemappedfilename
007710000077f000rxp0000000003:051165839/lib/libselinux.so.1
0077f00000781000rwp0000d00003:051165839/lib/libselinux.so.1
0097d00000992000rxp0000000003:051158767/lib/ld2.3.3.so
0099200000993000rp0001400003:051158767/lib/ld2.3.3.so
0099300000994000rwp0001500003:051158767/lib/ld2.3.3.so
0099600000aac000rxp0000000003:051158770/lib/tls/libc2.3.3.so
00aac00000aad000rp0011600003:051158770/lib/tls/libc2.3.3.so
00aad00000ab0000rwp0011700003:051158770/lib/tls/libc2.3.3.so
00ab000000ab2000rwp00ab000000:000
0804800008050000rxp0000000003:05571452/sbin/init(text)
0805000008051000rwp0000800003:05571452/sbin/init(data,stack)
08b4300008b64000rwp08b4300000:000
f6fdf000f6fe0000rwpf6fdf00000:000
fefd4000ff000000rwpfefd400000:000
ffffe000fffff000p0000000000:000

138
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

mmap(2)
Particularlyusefulwhenthefileisadevicefile!
AllowstoaccessdeviceI/Omemoryandportswithouthavingto
gothrough(expensive)read,writeorioctlcalls!
Xserverexample(mapsexcerpt)
startend
permoffsetmajor:minorinodemappedfilename
08047000081be000rxp0000000003:05310295/usr/X11R6/bin/Xorg
081be000081f0000rwp0017600003:05310295/usr/X11R6/bin/Xorg
...
f4e08000f4f09000rwse000000003:05655295/dev/dri/card0
f4f09000f4f0b000rws4281a00003:05655295/dev/dri/card0
f4f0b000f6f0b000rwse800000003:05652822/dev/mem
f6f0b000f6f8b000rwsfcff000003:05652822/dev/mem

Amoreuserfriendlywaytogetsuchinformation:pmap<pid>

139
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

mmapoverview

Process

mmap
system
call(once)

access
virtual
address

Processvirtualaddressspace

MMU

Devicedriver
mmapfopcalled
initializesthemapping

access
physical
address

Physicaladdressspace

140
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

HowtoimplementmmapUserspace
Openthedevicefile
Callthemmapsystemcall(seemanmmapfordetails):
void*mmap(
void*start,
/*Often0,preferredstartingaddress*/
size_tlength, /*Lengthofthemappedarea*/
intprot,
/*Permissions:read,write,execute*/
intflags,
/*Options:sharedmapping,privatecopy...
*/
intfd,
/*Openfiledescriptor*/
off_toffset
/*Offsetinthefile*/
);
Yougetavirtualaddressyoucanwritetoorreadfrom.

141
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

HowtoimplementmmapKernelspace
Characterdriver:implementammapfileoperation
andaddittothedriverfileoperations:
int(*mmap)(
structfile*,
/*Openfilestructure*/
structvm_area_struct* /*KernelVMAstructure*/
);
Initializethemapping.
Canbedoneinmostcaseswiththeremap_pfn_range()
function,whichtakescareofmostofthejob.

142
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

remap_pfn_range()
pfn:pageframenumber
Themostsignificantbitsofthepageaddress
(withoutthebitscorrespondingtothepagesize).
#include<linux/mm.h>
intremap_pfn_range(
structvm_area_struct*,
/*VMAstruct*/
unsignedlongvirt_addr, /*Startinguservirtualaddress*/
unsignedlongpfn, /*pfnofthestartingphysicaladdress*/
unsignedlongsize,
/*Mappingsize*/
pgprot_t
/*Pagepermissions*/
);

143
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Simplemmapimplementation
staticintacme_mmap(
structfile*file,structvm_area_struct*vma)
{
size=vma>vm_endvma>vm_start;
if(size>ACME_SIZE)
returnEINVAL;
if(remap_pfn_range(vma,
vma>vm_start,
ACME_PHYS>>PAGE_SHIFT,
size,
vma>vm_page_prot))
returnEAGAIN;
return0;
}
144
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

devmem2
http://freeelectrons.com/pub/mirror/devmem2.c,byJanDerkBakker
Veryusefultooltodirectlypeek(read)orpoke(write)I/Oaddresses
mappedinphysicaladdressspacefromashellcommandline!
Veryusefulforearlyinteractionexperimentswithadevice,without
havingtocodeandcompileadriver.
Usesmmapto/dev/mem.
Examples(b:byte,h:half,w:word)
devmem20x000c0004h(reading)
devmem20x000c0008w0xffffffff(writing)
devmemisnowavailableinBusyBox,makingiteveneasiertouse.

145
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

mmapsummary
Thedevicedriverisloaded.
Itdefinesanmmapfileoperation.
Auserspaceprocesscallsthemmapsystemcall.
Themmapfileoperationiscalled.
Itinitializesthemappingusingthedevicephysicaladdress.
Theprocessgetsastartingaddresstoreadfromandwriteto
(dependingonpermissions).
TheMMUautomaticallytakescareofconvertingtheprocess
virtualaddressesintophysicalones.
Directaccesstothehardware!
Noexpensivereadorwritesystemcalls!

146
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Driverdevelopment
Kernelarchitecturefordevicedrivers

147
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Kernelanddevicedrivers
Userspace

Application

Systemcallinterface

Framework
Kernel
Driver

Businfrastructure

Hardware

148

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Kernelanddevicedrivers
Manydevicedriversarenotimplementeddirectlyascharacter
drivers
Theyareimplementedunderaframework,specifictoagiven
devicetype(framebuffer,V4L,serial,etc.)
Theframeworkallowstofactorizethecommonpartsofdriversfor
thesametypeofdevices
Fromuserspace,theyarestillseenascharacterdevicesbythe
applications
Theframeworkallowstoprovideacoherentuserspaceinterface
(ioctl,etc.)foreverytypeofdevice,regardlessofthedriver

Thedevicedriversrelyonthebusinfrastructuretoenumerate
thedevicesandcommunicatewiththem.
149
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Kernelframeworks

150
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Frameworks
Application

Application

Application

Systemcallinterface
Character
driver

Framebuffer
core

V4L
core

TTY
core

Framebuffer
driver

V4L
driver

Serial
core

IDE
core

SCSI
core

Serial
driver

IDE
driver

USBstorage
driver

Businfrastructure

Block
core

151

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Example:framebufferframework
KerneloptionCONFIG_FB
menuconfigFB
tristate"Supportforframebufferdevices"

Implementedindrivers/video/
fb.c,fbmem.c,fbmon.c,fbcmap.c,fbsysfs.c,
modedb.c,fbcvt.c

Implementsasinglecharacterdriveranddefinestheuser/kernel
API
Firstpartofinclude/linux/fb.h

Definesthesetofoperationsaframebufferdrivermustimplement
andhelperfunctionsforthedrivers
structfb_ops
Secondpartofinclude/linux/fb.h
(in#ifdef__KERNEL__)

152

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Framebufferdriverskeleton
Skeletondriverindrivers/video/skeletonfb.c
Implementsthesetofframebufferspecificoperationsdefined
bythestructfb_opsstructure
xxxfb_open()

xxxfb_fillrect()

xxxfb_read()

xxxfb_copyarea()

xxxfb_write()

xxxfb_imageblit()

xxxfb_release()

xxxfb_cursor()

xxxfb_checkvar()

xxxfb_rotate()

xxxfb_setpar()

xxxfb_sync()

xxxfb_setcolreg()

xxxfb_ioctl()

xxxfb_blank()

xxxfb_mmap()

xxxfb_pan_display()
153
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Framebufferdriverskeleton
Aftertheimplementationoftheoperations,definitionofastruct
fb_opsstructure
staticstructfb_opsxxxfb_ops={
.owner=THIS_MODULE,
.fb_open=xxxfb_open,
.fb_read=xxxfb_read,
.fb_write=xxxfb_write,
.fb_release=xxxfb_release,
.fb_check_var=xxxfb_check_var,
.fb_set_par=xxxfb_set_par,
.fb_setcolreg=xxxfb_setcolreg,
.fb_blank=xxxfb_blank,
.fb_pan_display=xxxfb_pan_display,
.fb_fillrect=xxxfb_fillrect,/*Needed!!!*/
.fb_copyarea=xxxfb_copyarea,/*Needed!!!*/
.fb_imageblit=xxxfb_imageblit,/*Needed!!!*/
.fb_cursor=xxxfb_cursor,/*Optional!!!*/
.fb_rotate=xxxfb_rotate,
.fb_sync=xxxfb_sync,
.fb_ioctl=xxxfb_ioctl,
.fb_mmap=xxxfb_mmap,
};

154
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Framebufferdriverskeleton
Intheprobe()function,registrationoftheframebufferdevice
andoperations
staticint__devinitxxxfb_probe
(structpci_dev*dev,
conststructpci_device_id*ent)
{
structfb_info*info;
[...]
info=framebuffer_alloc(sizeof(structxxx_par),device);
[...]
info>fbops=&xxxfb_ops;
[...]
if(register_framebuffer(info)<0)
returnEINVAL;
[...]
}

register_framebuffer()willcreatethecharacterdevice
thatcanbeusedbyuserspaceapplicationwiththegeneric
framebufferAPI
155
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

DeviceModelandBusInfrastructure

156
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Unifieddevicemodel
The2.6kernelincludedasignificantnewfeature:
aunifieddevicemodel
Insteadofhavingdifferentadhocmechanismsinthevarious
subsystems,thedevicemodelunifiesthedescriptionofthedevices
andtheirtopology
Minimizingcodeduplication
Commonfacilities(referencecounting,eventnotification,power
management,etc.)
Enumeratethedevices,viewtheirinterconnections,linkthedevicesto
theirbusesanddrivers,categorizethembyclasses.

157
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Busdrivers
Thefirstcomponentofthedevicemodelisthebusdriver
Onebusdriverforeachtypeofbus:USB,PCI,SPI,MMC,ISA,etc.

Itisresponsiblefor
Registeringthebustype
Allowingtheregistrationofadapterdrivers(USBcontrollers,I2C
adapters,etc.),ableofdetectingtheconnecteddevices
Allowingtheregistrationofdevicedrivers(USBdevices,I2C
devices,PCIdevices,etc.),managingthedevices
Matchingthedevicedriversagainstthedevicesdetectedbythe
adapterdrivers.

158
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Listofdeviceidentifiers
Dependingonthebustype,themethodforbindingadevicetoa
driverisdifferent.Formanybuses,itisbasedonuniqueidentifiers
Thedevicedriverdefinesatablewiththelistofdeviceidentifiersit
isabletomanage:

staticconststructpci_device_idrhine_pci_tbl[]={
{0x1106,0x3043,PCI_ANY_ID,PCI_ANY_ID,},/*VT86C100A*/
{0x1106,0x3053,PCI_ANY_ID,PCI_ANY_ID,},/*VT6105M*/
{}/*terminatelist*/
};
MODULE_DEVICE_TABLE(pci,rhine_pci_tbl);

Codeonthisslideandonthenextslidesaretaken
fromtheviarhinedriverindrivers/net/viarhine.c
159
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Definingthedriver
Thedevicedriverdefinesadriverstructure,usuallyspecializedby
thebusinfrastructure(pci_driver,usb_driver,etc.)
Thestructurepointsto:thedevicetable,aprobefunction,called
whenadeviceisdetectedandvariousothercallbacks

staticstructpci_driverrhine_driver={
.name=DRV_NAME,
.id_table=rhine_pci_tbl,
.probe=rhine_init_one,
.remove=__devexit_p(rhine_remove_one),
#ifdefCONFIG_PM
.suspend=rhine_suspend,
.resume=rhine_resume,
#endif/*CONFIG_PM*/
.shutdown=rhine_shutdown,
};

160
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Registeringthedriver
Inthemoduleinitializationfunction,thedriverisregisteredtothe
businfrastructure,inordertoletthebusknowthatthedriveris
availabletohandledevices.
staticint__initrhine_init(void)
{
[...]
returnpci_register_driver(&rhine_driver);
}
staticvoid__exitrhine_cleanup(void)
{
pci_unregister_driver(&rhine_driver);
}

IfanewPCIdevicematchesoneoftheidentifiersofthetable,the
probe()methodofthePCIdriverwillgetcalled.

161
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Probemethod
Theprobe()methodreceivesasargumentastructure
describingthedevice,usuallyspecializedbythebus
infrastructure(pci_dev,usb_device,etc.)
Thisfunctionisresponsiblefor
Initializingthedevice,mappingI/Omemory,registeringthe
interrupthandlers.Thebusinfrastructureprovidesmethodsto
gettheaddresses,interruptsnumbersandotherdevice
specificinformation.
Registeringthedevicetotheproperkernelframework,for
examplethenetworkinfrastructure.

162
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Devicedriver(5)
staticint__devinitrhine_init_one(structpci_dev*pdev,
conststructpci_device_id*ent)
{
structnet_device*dev;
[...]
rc=pci_enable_device(pdev);
[...]
pioaddr=pci_resource_start(pdev,0);
memaddr=pci_resource_start(pdev,1);
[...]
dev=alloc_etherdev(sizeof(structrhine_private));
[...]
SET_NETDEV_DEV(dev,&pdev>dev);
[...]
rc=pci_request_regions(pdev,DRV_NAME);
[...]
ioaddr=pci_iomap(pdev,bar,io_size);
[...]
rc=register_netdev(dev);
[...]
}

163
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Globalarchitecture

PCIbusdriver

Thedevice
driver
registers
itselfandthe
device
identifiersto
thebus
driver

Thebusdriver
detectsa
matchingdevice,
andcallsthe
probe()method
ofthedevice
driver.

viarhine
devicedriver

Network
device
framework
3
Theprobe()
methodofthe
devicedriver
initializesthe
deviceand
registersanew
networkinterface

164
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

sysfs
Thebus,device,drivers,etc.structuresareinternaltothe
kernel
Thesysfsvirtualfilesystemoffersamechanismtoexportsuch
informationtouserspace
Usedforexamplebyudevtoprovideautomaticmoduleloading,
firmwareloading,devicefilecreation,etc.
sysfsisusuallymountedin/sys
/sys/bus/containsthelistofbuses
/sys/devices/containsthelistofdevices
/sys/classenumeratesdevicesbyclass(net,input,
block...),whateverthebustheyareconnectedto.Veryuseful!

Takeyourtimetoexplore/sysonyourworkstation.
165
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Platformdevices
Onembeddedsystems,devicesareoftennotconnected
throughabusallowingenumeration,hotplugging,andproviding
uniqueidentifiersfordevices.
However,westillwantthedevicestobepartofthedevice
model.
Thesolutiontothisistheplatformdriver/platformdevice
infrastructure.
Theplatformdevicesarethedevicesthataredirectlyconnected
totheCPU,withoutanykindofbus.

166
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Implementationoftheplatformdriver
Thedriverimplementsaplatform_driverstructure
(exampletakenfromdrivers/serial/imx.c)
staticstructplatform_driverserial_imx_driver={
.probe=serial_imx_probe,
.remove=serial_imx_remove,
.driver={
.name="imxuart",
.owner=THIS_MODULE,
},
};

Andregistersitsdrivertotheplatformdriverinfrastructure
staticint__initimx_serial_init(void)
{
[...]
ret=platform_driver_register(&serial_imx_driver);
[...]
}
167
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Platformdeviceinstantiation(1)
Intheboardspecificcode,theplatformdevicesare
instantiated(arch/arm/machimx/mx1ads.c):
staticstructplatform_deviceimx_uart1_device={
.name="imxuart",
.id=0,
.num_resources=ARRAY_SIZE(imx_uart1_resources),
.resource=imx_uart1_resources,
.dev={
.platform_data=&uart_pdata,
}
};

Thematchbetweenthedeviceandthedriverismadeusing
thename.Itmustbeuniqueamongstdrivers!

168
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Platformdeviceinstantiation(2)
Thedeviceispartofalist
staticstructplatform_device*devices[]__initdata={
&cs89x0_device,
&imx_uart1_device,
&imx_uart2_device,
};

Andthelistofdevicesisaddedtothesystem
duringboardinitialization
staticvoid__initmx1ads_init(void)
{
[...]
platform_add_devices(devices,ARRAY_SIZE(devices));
}

169
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

I/Oresources
EachplatformdeviceisassociatedwithasetofI/Oresources,
referencedintheplatform_devicestructure
staticstructresourceimx_uart1_resources[]={
[0]={
.start=0x00206000,
.end=0x002060FF,
.flags=IORESOURCE_MEM,
},
[1]={
.start=(UART1_MINT_RX),
.end=(UART1_MINT_RX),
.flags=IORESOURCE_IRQ,
},
};

ItallowsthedrivertobeindependentoftheI/Oaddresses,IRQ
numbers!Seeimx_uart2_deviceforanotherdeviceusingthe
sameplatformdriver.
170
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Insidetheplatformdriver
Whenaplatform_deviceisaddedtothesystemusing
platform_add_device(),theprobe()methodoftheplatform
drivergetscalled
Thismethodisresponsibleforinitializingthehardware,
registeringthedevicetotheproperframework(inourcase,the
serialdriverframework)
TheplatformdriverhasaccesstotheI/Oresources:
res=platform_get_resource(pdev,IORESOURCE_MEM,0);
base=ioremap(res>start,PAGE_SIZE);
sport>rxirq=platform_get_irq(pdev,0);

171
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Frameworkandbusinfrastructure
Atypicaldriverwill
Beregisteredinsideaframework
Relyonabusinfrastructureandthedevicemodel

ExamplewiththeiMXserialdriver,drivers/serial/imx.c
Atmoduleinitializationtime,thedriverregistersitselfbothtothe
UARTframeworkandtotheplatformbusinfrastructure
staticint__initimx_serial_init(void)
{
ret=uart_register_driver(&imx_reg);
[...]
ret=platform_driver_register(&serial_imx_driver);
[...]
return0;
}

172
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

iMXserialdriver
DefinitionoftheiMXUARTdriver
staticstructuart_driverimx_reg={
.owner=THIS_MODULE,
.driver_name=DRIVER_NAME,
.dev_name=DEV_NAME,
.major=SERIAL_IMX_MAJOR,
.minor=MINOR_START,
.nr=ARRAY_SIZE(imx_ports),
.cons=IMX_CONSOLE,
};

DefinitionoftheiMXplatformdriver
staticstructplatform_driverserial_imx_driver={
.probe=serial_imx_probe,
.remove=serial_imx_remove,
[...]
.driver={
.name="imxuart",
.owner=THIS_MODULE,
},
};
173
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

iMXserialdriver
Whentheplatformdeviceisinstantiated,theprobe()
methodiscalled
staticintserial_imx_probe(structplatform_device*pdev)
{
structimx_port*sport;
sport=kzalloc(sizeof(*sport),GFP_KERNEL);
res=platform_get_resource(pdev,IORESOURCE_MEM,0);
base=ioremap(res>start,PAGE_SIZE);
/*sportinitialization*/
sport>port.irq=platform_get_irq(pdev,0);
sport>port.ops=&imx_pops;
sport>clk=clk_get(&pdev>dev,"uart_clk");
clk_enable(sport>clk);
uart_add_one_port(&imx_reg,&sport>port);
}

174
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

iMXserialdriver
Theoperationstructureuart_opsisassociatedtoeach
port.TheoperationsareimplementedintheiMXdriver
staticstructuart_opsimx_pops={
.tx_empty=imx_tx_empty,
.set_mctrl=imx_set_mctrl,
.get_mctrl=imx_get_mctrl,
.stop_tx=imx_stop_tx,
.start_tx=imx_start_tx,
.stop_rx=imx_stop_rx,
.enable_ms=imx_enable_ms,
.break_ctl=imx_break_ctl,
.startup=imx_startup,
.shutdown=imx_shutdown,
.set_termios=imx_set_termios,
.type=imx_type,
.release_port=imx_release_port,
.request_port=imx_request_port,
.config_port=imx_config_port,
.verify_port=imx_verify_port,
};
175
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

References
Kerneldocumentation
Documentation/drivermodel/
Documentation/filesystems/sysfs.txt
Linux2.6DeviceModel
http://www.bravegnu.org/devicemodel/devicemodel.html
LinuxDeviceDrivers,chapter14TheLinuxDeviceModel
http://lwn.net/images/pdf/LDD3/ch14.pdf
Thekernelsourcecode
Fullofexamplesofotherdrivers!

176
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

EmbeddedLinuxdriverdevelopment

Annexes
Quizanswers

177
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Quizanswers
Interrupthandling
Q:Whydidthekernelsegfaultatmoduleunload(forgettingto
unregisterahandlerinasharedinterruptline)?
A:Kernelmemoryisallocatedatmoduleloadtime,tohostmodule
code.Thismemoryisfreedatmoduleunloadtime.Ifyouforgetto
unregisterahandlerandaninterruptcomes,thecpuwilltrytojump
totheaddressofthehandler,whichisinafreedmemoryarea.
Crash!

178
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Relateddocuments

Allourtechnicalpresentations
onhttp://freeelectrons.com/docs
Linuxkernel
Devicedrivers
Architecturespecifics
EmbeddedLinuxsystemdevelopment
FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Howtohelp
Youcanhelpustoimproveandmaintainthisdocument...
Bysendingcorrections,suggestions,contributionsand
translations
Byaskingyourorganizationtoorderdevelopment,consulting
andtrainingservicesperformedbytheauthorsofthese
documents(seehttp://freeelectrons.com/).
Bysharingthisdocumentwithyourfriends,colleagues
andwiththelocalFreeSoftwarecommunity.
Byaddinglinksonyourwebsitetoouronlinematerials,
toincreasetheirvisibilityinsearchengineresults.

FreeElectrons.Kernel,driversandembeddedLinuxdevelopment,consulting,trainingandsupport.http//freeelectrons.com

Linuxkernel
Linuxdevicedrivers
Boardsupportcode
Mainstreamingkernelcode
Kerneldebugging
EmbeddedLinuxTraining
Allmaterialsreleasedwithafreelicense!
UnixandGNU/Linuxbasics
Linuxkernelanddriversdevelopment
RealtimeLinux,uClinux
Developmentandprofilingtools
Lightweighttoolsforembeddedsystems
Rootfilesystemcreation
Audioandmultimedia
Systemoptimization

FreeElectrons
Ourservices
CustomDevelopment
Systemintegration
EmbeddedLinuxdemosandprototypes
Systemoptimization
Applicationandinterfacedevelopment
Consultingandtechnicalsupport
Helpindecisionmaking
Systemarchitecture
Systemdesignandperformancereview
Developmenttoolandapplicationsupport
Investigatingissuesandfixingtoolbugs

S-ar putea să vă placă și