You are on page 1of 22

29/5/2015

Writing device drivers in Linux: A brief tutorial

WritingdevicedriversinLinux:Abrief
tutorial
ShortURL:http://fsmsh.com/1238
Like

Share

581

+368 Re c om e nde isto no Google

Wed,2006042611:03XavierCalbet
DoyoupineforthenicedaysofMinix1.1,whenmenweremenandwrotetheirowndevice
drivers?LinusTorvalds

Prerequisites
InordertodevelopLinuxdevicedrivers,itisnecessarytohaveanunderstandingofthefollowing:
Cprogramming.SomeindepthknowledgeofCprogrammingisneeded,likepointerusage,bit
manipulatingfunctions,etc.
Microprocessorprogramming.Itisnecessarytoknowhowmicrocomputersworkinternally:
memoryaddressing,interrupts,etc.Alloftheseconceptsshouldbefamiliartoanassembler
programmer.
ThereareseveraldifferentdevicesinLinux.Forsimplicity,thisbrieftutorialwillonlycover
type chardevicesloadedasmodules.Kernel2.6.xwillbeused(inparticular,kernel2.6.8underDebian
Sarge,whichisnowDebianStable).

Userspaceandkernelspace
Whenyouwritedevicedrivers,itsimportanttomakethedistinctionbetweenuserspaceandkernel
space.
Kernelspace.Linux(whichisakernel)managesthemachine'shardwareinasimpleandefficient
manner,offeringtheuserasimpleanduniformprogramminginterface.Inthesameway,thekernel,
andinparticularitsdevicedrivers,formabridgeorinterfacebetweentheenduser/programmerand
thehardware.Anysubroutinesorfunctionsformingpartofthekernel(modulesanddevicedrivers,
forexample)areconsideredtobepartofkernelspace.
Userspace.Enduserprograms,liketheUNIX shellorotherGUIbasedapplications
( kpresenterforexample),arepartoftheuserspace.Obviously,theseapplicationsneedto
interactwiththesystem'shardware.However,theydontdosodirectly,butthroughthekernel
supportedfunctions.
Allofthisisshowninfigure1.

Interfacingfunctionsbetweenuserspaceandkernelspace
http://www.freesoftwaremagazine.com/articles/drivers_linux

1/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

Thekerneloffersseveralsubroutinesorfunctionsinuser
space,whichallowtheenduserapplication
programmertointeractwiththehardware.Usually,in
UNIXorLinuxsystems,thisdialogueisperformed
throughfunctionsorsubroutinesinordertoreadand
writefiles.ThereasonforthisisthatinUnixdevicesare
seen,fromthepointofviewoftheuser,asfiles.
Ontheotherhand,inkernelspaceLinuxalsooffers
severalfunctionsorsubroutinestoperformthelowlevel
interactionsdirectlywiththehardware,andallowthe
transferofinformationfromkerneltouserspace.
Usually,foreachfunctioninuserspace(allowingthe
useofdevicesorfiles),thereexistsanequivalentin
kernelspace(allowingthetransferofinformationfrom
thekerneltotheuserandviceversa).Thisisshownin
Table1,whichis,atthispoint,empty.Itwillbefilled
whenthedifferentdevicedriversconceptsare
introduced.

Events

Userfunctions

Figure1:Userspacewhereapplicationsreside,and
kernelspacewheremodulesordevicedriversreside

Kernelfunctions

Loadmodule
Opendevice
Readdevice
Writedevice
Closedevice
Removemodule
Table1.Devicedrivereventsandtheirassociatedinterfacingfunctionsinkernelspaceanduser
space.

Interfacingfunctionsbetweenkernelspaceandthe
hardwaredevice
Therearealsofunctionsinkernelspacewhichcontrolthedeviceorexchangeinformationbetweenthekernel
andthehardware.Table2illustratestheseconcepts.Thistablewillalsobefilledastheconceptsare
introduced.
Events

Kernelfunctions

Readdata
Writedata
http://www.freesoftwaremagazine.com/articles/drivers_linux

2/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

Table2.Devicedrivereventsandtheirassociatedfunctionsbetweenkernelspaceandthe
hardwaredevice.

Thefirstdriver:loadingandremovingthedriverinuser
space
IllnowshowyouhowtodevelopyourfirstLinuxdevicedriver,whichwillbeintroducedinthekernelasa
module.
ForthispurposeIllwritethefollowingprograminafilenamed nothing.c
<nothing.c>=
#include<linux/module.h>
MODULE_LICENSE("DualBSD/GPL")
Sincethereleaseofkernelversion2.6.x,compilingmoduleshasbecomeslightlymorecomplicated.First,you
needtohaveacomplete,compiledkernelsourcecodetree.IfyouhaveaDebianSargesystem,youcan
followthestepsinAppendixB(towardstheendofthisarticle).Inthefollowing,Illassumethatakernel
version2.6.8isbeingused.
Next,youneedtogenerateamakefile.Themakefileforthisexample,whichshouldbenamed Makefile,
willbe:
<Makefile1>=
objm:=nothing.o
Unlikewithpreviousversionsofthekernel,itsnowalsonecessarytocompilethemoduleusingthesame
kernelthatyouregoingtoloadandusethemodulewith.Tocompileit,youcantype:
$makeC/usr/src/kernelsource2.6.8M=`pwd`modules
Thisextremelysimplemodulebelongstokernelspaceandwillformpartofitonceitsloaded.
Inuserspace,youcanloadthemoduleasrootbytypingthefollowingintothecommandline:
#insmodnothing.ko
The insmodcommandallowstheinstallationofthemoduleinthekernel.However,thisparticularmodule
isntofmuchuse.
Itispossibletocheckthatthemodulehasbeeninstalledcorrectlybylookingatallinstalledmodules:
#lsmod
Finally,themodulecanberemovedfromthekernelusingthecommand:
http://www.freesoftwaremagazine.com/articles/drivers_linux

3/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

#rmmodnothing
Byissuingthe lsmodcommandagain,youcanverifythatthemoduleisnolongerinthekernel.
ThesummaryofallthisisshowninTable3.
Events

Userfunctions

Loadmodule

insmod

Kernelfunctions

Opendevice
Readdevice
Writedevice
Closedevice
Removemodule

rmmod

Table3.Devicedrivereventsandtheirassociatedinterfacingfunctionsbetweenkernelspaceand
userspace.

TheHelloworlddriver:loadingandremovingthedriver
inkernelspace
Whenamoduledevicedriverisloadedintothekernel,somepreliminarytasksareusuallyperformedlike
resettingthedevice,reservingRAM,reservinginterrupts,andreservinginput/outputports,etc.
Thesetasksareperformed,inkernelspace,bytwofunctionswhichneedtobepresent(andexplicitly
declared): module_initand module_exittheycorrespondtotheuserspace
commands insmodand rmmod,whichareusedwheninstallingorremovingamodule.Tosumup,the
usercommands insmodand rmmodusethekernelspace
functions module_initand module_exit.
Letsseeapracticalexamplewiththeclassicprogram Helloworld:
<hello.c>=
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
MODULE_LICENSE("DualBSD/GPL")
staticinthello_init(void){
printk("<1>Helloworld!\n")
return0
}
staticvoidhello_exit(void){
http://www.freesoftwaremagazine.com/articles/drivers_linux

4/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

printk("<1>Bye,cruelworld\n")
}
module_init(hello_init)
module_exit(hello_exit)
Theactualfunctions hello_initand hello_exitcanbegivenanynamedesired.However,in
orderforthemtobeidentifiedasthecorrespondingloadingandremovingfunctions,theyhavetobepassed
asparameterstothefunctions module_initand module_exit.
The printkfunctionhasalsobeenintroduced.Itisverysimilartothewellknown printfapartfrom
thefactthatitonlyworksinsidethekernel.The &lt1>symbolshowsthehighpriorityofthemessage
(lownumber).Inthisway,besidesgettingthemessageinthekernelsystemlogfiles,youshouldalsoreceive
thismessageinthesystemconsole.
Thismodulecanbecompiledusingthesamecommandasbefore,afteraddingitsnameintotheMakefile.
<Makefile2>=
objm:=nothing.ohello.o
Intherestofthearticle,IhavelefttheMakefilesasanexerciseforthereader.AcompleteMakefilethatwill
compileallofthemodulesofthistutorialisshowninAppendixA.
Whenthemoduleisloadedorremoved,themessagesthatwerewritteninthe printkstatementwillbe
displayedinthesystemconsole.Ifthesemessagesdonotappearintheconsole,youcanviewthembyissuing
the dmesgcommandorbylookingatthesystemlogfilewith cat/var/log/syslog.
Table4showsthesetwonewfunctions.
Events

Userfunctions

Kernelfunctions

Loadmodule

insmod

module_init()

rmmod

module_exit()

Opendevice
Readdevice
Writedevice
Closedevice
Removemodule

Table4.Devicedrivereventsandtheirassociatedinterfacingfunctionsbetweenkernelspaceand
userspace.

Thecompletedrivermemory:initialpartofthedriver
Illnowshowhowtobuildacompletedevicedriver: memory.c.Thisdevicewillallowacharactertobe
readfromorwrittenintoit.Thisdevice,whilenormallynotveryuseful,providesaveryillustrativeexample
http://www.freesoftwaremagazine.com/articles/drivers_linux

5/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

sinceitisacompletedriverit'salsoeasytoimplement,sinceitdoesntinterfacetoarealhardwaredevice
(besidesthecomputeritself).
Todevelopthisdriver,severalnew #includestatementswhichappearfrequentlyindevicedriversneed
tobeadded:
<memoryinitial>=
/*Necessaryincludesfordevicedrivers*/
#include<linux/init.h>
#include<linux/config.h>
#include<linux/module.h>
#include<linux/kernel.h>/*printk()*/
#include<linux/slab.h>/*kmalloc()*/
#include<linux/fs.h>/*everything...*/
#include<linux/errno.h>/*errorcodes*/
#include<linux/types.h>/*size_t*/
#include<linux/proc_fs.h>
#include<linux/fcntl.h>/*O_ACCMODE*/
#include<asm/system.h>/*cli(),*_flags*/
#include<asm/uaccess.h>/*copy_from/to_user*/
MODULE_LICENSE("DualBSD/GPL")

/*Declarationofmemory.cfunctions*/
intmemory_open(structinode*inode,structfile*filp)
intmemory_release(structinode*inode,structfile*filp)
ssize_tmemory_read(structfile*filp,char*buf,size_tcount,lof
ssize_tmemory_write(structfile*filp,char*buf,size_tcount,lo
voidmemory_exit(void)
intmemory_init(void)
/*Structurethatdeclarestheusualfile*/
/*accessfunctions*/
structfile_operationsmemory_fops={
read:memory_read,
write:memory_write,
open:memory_open,
release:memory_release
}
/*Declarationoftheinitandexitfunctions*/
module_init(memory_init)
module_exit(memory_exit)
/*Globalvariablesofthedriver*/
/*Majornumber*/
intmemory_major=60
/*Buffertostoredata*/
char*memory_buffer
Afterthe #includefiles,thefunctionsthatwillbedefinedlateraredeclared.Thecommonfunctions
http://www.freesoftwaremagazine.com/articles/drivers_linux

6/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

whicharetypicallyusedtomanipulatefilesaredeclaredinthedefinitionof
the file_operationsstructure.Thesewillalsobeexplainedindetaillater.Next,theinitializationand
exitfunctionsusedwhenloadingandremovingthemodulearedeclaredtothekernel.Finally,theglobal
variablesofthedriveraredeclared:oneofthemisthe majornumberofthedriver,theotherisa
pointertoaregioninmemory, memory_buffer,whichwillbeusedasstorageforthedriverdata.

Thememorydriver:connectionofthedevicewithitsfiles
InUNIXandLinux,devicesareaccessedfromuserspaceinexactlythesamewayasfilesareaccessed.
Thesedevicefilesarenormallysubdirectoriesofthe /devdirectory.
Tolinknormalfileswithakernelmoduletwonumbersareused: majornumberand minor
number.The majornumberistheonethekernelusestolinkafilewithitsdriver.The minor
numberisforinternaluseofthedeviceandforsimplicityitwontbecoveredinthisarticle.
Toachievethis,afile(whichwillbeusedtoaccessthedevicedriver)mustbecreated,bytypingthefollowing
commandasroot:
#mknod/dev/memoryc600
Intheabove, cmeansthata chardeviceistobecreated, 60isthe majornumberand 0is
the minornumber.
Withinthedriver,inordertolinkitwithitscorresponding /devfileinkernelspace,
the register_chrdevfunctionisused.Itiscalledwiththreearguments: majornumber,astring
ofcharactersshowingthemodulename,anda file_operationsstructurewhichlinksthecallwiththe
filefunctionsitdefines.Itisinvoked,wheninstallingthemodule,inthisway:
<memoryinitmodule>=
intmemory_init(void){
intresult
/*Registeringdevice*/
result=register_chrdev(memory_major,"memory",&memory_fops)
if(result<0){
printk(
"<1>memory:cannotobtainmajornumber%d\n",memory_major)
returnresult
}
/*Allocatingmemoryforthebuffer*/
memory_buffer=kmalloc(1,GFP_KERNEL)
if(!memory_buffer){
result=ENOMEM
gotofail
}
memset(memory_buffer,0,1)
printk("<1>Insertingmemorymodule\n")
http://www.freesoftwaremagazine.com/articles/drivers_linux

7/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

return0
fail:
memory_exit()
returnresult
}
Also,notetheuseofthe kmallocfunction.Thisfunctionisusedformemoryallocationofthebufferinthe
devicedriverwhichresidesinkernelspace.Itsuseisverysimilartothewellknown mallocfunction.
Finally,ifregisteringthe majornumberorallocatingthememoryfails,themoduleactsaccordingly.

Thememorydriver:removingthedriver
Inordertoremovethemoduleinsidethe memory_exitfunction,the
function unregsiter_chrdevneedstobepresent.Thiswillfreethe majornumberforthe
kernel.
<memoryexitmodule>=
voidmemory_exit(void){
/*Freeingthemajornumber*/
unregister_chrdev(memory_major,"memory")
/*Freeingbuffermemory*/
if(memory_buffer){
kfree(memory_buffer)
}
printk("<1>Removingmemorymodule\n")
}
Thebuffermemoryisalsofreedinthisfunction,inordertoleaveacleankernelwhenremovingthedevice
driver.

Thememorydriver:openingthedeviceasafile
Thekernelspacefunction,whichcorrespondstoopeningafileinuserspace( fopen),isthe
member open:ofthe file_operationsstructureinthecallto register_chrdev.Inthis
case,itisthe memory_openfunction.Ittakesasarguments:an inodestructure,whichsends
informationtothekernelregardingthe majornumberand minornumberand
a filestructurewithinformationrelativetothedifferentoperationsthatcanbeperformedonafile.
Neitherofthesefunctionswillbecoveredindepthwithinthisarticle.
Whenafileisopened,itsnormallynecessarytoinitializedrivervariablesorresetthedevice.Inthissimple
example,though,theseoperationsarenotperformed.
The memory_openfunctioncanbeseenbelow:
http://www.freesoftwaremagazine.com/articles/drivers_linux

8/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

<memoryopen>=
intmemory_open(structinode*inode,structfile*filp){
/*Success*/
return0
}
ThisnewfunctionisnowshowninTable5.
Events

Userfunctions

Kernelfunctions

Loadmodule

insmod

module_init()

Opendevice

fopen

file_operations:open

rmmod

module_exit()

Readdevice
Writedevice
Closedevice
Removemodule

Table5.Devicedrivereventsandtheirassociatedinterfacingfunctionsbetweenkernelspaceand
userspace.

Thememorydriver:closingthedeviceasafile
Thecorrespondingfunctionforclosingafileinuserspace( fclose)isthe release:memberof
the file_operationsstructureinthecallto register_chrdev.Inthisparticularcase,itisthe
function memory_release,whichhasasargumentsan inodestructureanda filestructure,just
likebefore.
Whenafileisclosed,itsusuallynecessarytofreetheusedmemoryandanyvariablesrelatedtotheopening
ofthedevice.But,onceagain,duetothesimplicityofthisexample,noneoftheseoperationsareperformed.
The memory_releasefunctionisshownbelow:
<memoryrelease>=
intmemory_release(structinode*inode,structfile*filp){

/*Success*/
return0
}
ThisnewfunctionisshowninTable6.
Events

Userfunctions

Kernelfunctions

Loadmodule

insmod

module_init()

http://www.freesoftwaremagazine.com/articles/drivers_linux

9/22

29/5/2015

Opendevice

Writing device drivers in Linux: A brief tutorial

fopen

file_operations:open

Closedevice

fclose

file_operations:release

Removemodule

rmmod

module_exit()

Readdevice
Writedevice

Table6.Devicedrivereventsandtheirassociatedinterfacingfunctionsbetweenkernelspaceand
userspace.

Thememorydriver:readingthedevice
Toreadadevicewiththeuserfunction freadorsimilar,themember read:of
the file_operationsstructureisusedinthecallto register_chrdev.Thistime,itisthe
function memory_read.Itsargumentsare:atypefilestructureabuffer( buf),fromwhichtheuser
spacefunction( fread)willreadacounterwiththenumberofbytestotransfer( count),whichhasthe
samevalueastheusualcounterintheuserspacefunction( fread)andfinally,thepositionofwhereto
startreadingthefile( f_pos).
Inthissimplecase,the memory_readfunctiontransfersasinglebytefromthedriverbuffer
( memory_buffer)touserspacewiththefunction copy_to_user:
<memoryread>=
ssize_tmemory_read(structfile*filp,char*buf,
size_tcount,loff_t*f_pos){

/*Transferingdatatouserspace*/
copy_to_user(buf,memory_buffer,1)
/*Changingreadingpositionasbestsuits*/
if(*f_pos==0){
*f_pos+=1
return1
}else{
return0
}
}
Thereadingpositioninthefile( f_pos)isalsochanged.Ifthepositionisatthebeginningofthefile,itis
increasedbyoneandthenumberofbytesthathavebeenproperlyreadisgivenasareturnvalue, 1.Ifnotat
thebeginningofthefile,anendoffile( 0)isreturnedsincethefileonlystoresonebyte.
InTable7thisnewfunctionhasbeenadded.
Events

Userfunctions

http://www.freesoftwaremagazine.com/articles/drivers_linux

Kernelfunctions
10/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

Loadmodule

insmod

module_init()

Opendevice

fopen

file_operations:open

Readdevice

fread

file_operations:read

Closedevice

fclose

file_operations:release

Removemodules

rmmod

module_exit()

Writedevice

Table7.Devicedrivereventsandtheirassociatedinterfacingfunctionsbetweenkernelspaceand
userspace.

Thememorydriver:writingtoadevice
Towritetoadevicewiththeuserfunction fwriteorsimilar,themember write:of
the file_operationsstructureisusedinthecallto register_chrdev.Itisthe
function memory_write,inthisparticularexample,whichhasthefollowingasarguments:atypefile
structure buf,abufferinwhichtheuserspacefunction( fwrite)willwrite count,acounterwith
thenumberofbytestotransfer,whichhasthesamevaluesastheusualcounterintheuserspacefunction
( fwrite)andfinally, f_pos,thepositionofwheretostartwritinginthefile.
<memorywrite>=
ssize_tmemory_write(structfile*filp,char*buf,
size_tcount,loff_t*f_pos){
char*tmp
tmp=buf+count1
copy_from_user(memory_buffer,tmp,1)
return1
}
Inthiscase,thefunction copy_from_usertransfersthedatafromuserspacetokernelspace.
InTable8thisnewfunctionisshown.
Events

Userfunctions

Kernelfunctions

Loadmodule

insmod

module_init()

Opendevice

fopen

file_operations:open

Closedevice

fread

file_operations:read

Writedevice

fwrite

file_operations:write

Closedevice

fclose

file_operations:release

Removemodule

rmmod

module_exit()

http://www.freesoftwaremagazine.com/articles/drivers_linux

11/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

Devicedrivereventsandtheirassociatedinterfacingfunctionsbetweenkernelspaceanduser
space.

Thecompletememorydriver
Byjoiningallofthepreviouslyshowncode,thecompletedriverisachieved:
<memory.c>=
<memoryinitial>
<memoryinitmodule>
<memoryexitmodule>
<memoryopen>
<memoryrelease>
<memoryread>
<memorywrite>
Beforethismodulecanbeused,youwillneedtocompileitinthesamewayaswithpreviousmodules.The
modulecanthenbeloadedwith:
#insmodmemory.ko
Itsalsoconvenienttounprotectthedevice:
#chmod666/dev/memory
Ifeverythingwentwell,youwillhaveadevice /dev/memorytowhichyoucanwriteastringof
charactersanditwillstorethelastoneofthem.Youcanperformtheoperationlikethis:
$echonabcdef>/dev/memory
Tocheckthecontentofthedeviceyoucanuseasimple cat:
$cat/dev/memory
Thestoredcharacterwillnotchangeuntilitisoverwrittenorthemoduleisremoved.

Therealparlelportdriver:descriptionoftheparallelport
IllnowproceedbymodifyingthedriverthatIjustcreatedtodeveloponethatdoesarealtaskonareal
device.Illusethesimpleandubiquitouscomputerparallelportandthedriverwillbe
called parlelport.
Theparallelportiseffectivelyadevicethatallowstheinputandoutputofdigitalinformation.Morespecifically
ithasafemaleD25connectorwithtwentyfivepins.Internally,fromthepointofviewoftheCPU,ituses
threebytesofmemory.InaPC,thebaseaddress(theonefromthefirstbyteofthedevice)is
usually 0x378.Inthisbasicexample,Illusejustthefirstbyte,whichconsistsentirelyofdigitaloutputs.

http://www.freesoftwaremagazine.com/articles/drivers_linux

12/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

Theconnectionoftheabovementionedbytewiththeexternalconnectorpinsisshowninfigure2.

Theparlelportdriver:
initializingthemodule
Theprevious memory_initfunctionneeds
modificationchangingtheRAMmemoryallocationfor
thereservationofthememoryaddressoftheparallel
port( 0x378).Toachievethis,usethefunctionfor
Figure2:Thefirstbyteoftheparallelportanditspin
checkingtheavailabilityofamemoryregion
connectionswiththeexternalfemaleD25connector
( check_region),andthefunctiontoreservethe
memoryregionforthisdevice
( request_region).Bothhaveasargumentsthebaseaddressofthememoryregionanditslength.
The request_regionfunctionalsoacceptsastringwhichdefinesthemodule.
<parlelportmodifiedinitmodule>=
/*Registeringport*/
port=check_region(0x378,1)
if(port){
printk("<1>parlelport:cannotreserve0x378\n")
result=port
gotofail
}
request_region(0x378,1,"parlelport")

Theparlelportdriver:removingthemodule
Itwillbeverysimilartothe memorymodulebutsubstitutingthefreeingofmemorywiththeremovalofthe
reservedmemoryoftheparallelport.Thisisdonebythe release_regionfunction,whichhasthe
sameargumentsas check_region.
<parlelportmodifiedexitmodule>=
/*Makeportfree!*/
if(!port){
release_region(0x378,1)
}

Theparlelportdriver:readingthedevice
Inthiscase,arealdevicereadingactionneedstobeaddedtoallowthetransferofthisinformationtouser
space.The inbfunctionachievesthisitsargumentsaretheaddressoftheparallelportanditreturnsthe
contentoftheport.
<parlelportinport>=
http://www.freesoftwaremagazine.com/articles/drivers_linux

13/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

/*Readingport*/
parlelport_buffer=inb(0x378)
Table9(theequivalentofTable2)showsthisnewfunction.
Events

Kernelfunctions

Readdata

inb

Writedata
Devicedrivereventsandtheirassociatedfunctionsbetweenkernelspaceandthehardwaredevice.

Theparlelportdriver:writingtothedevice
Again,youhavetoaddthewritingtothedevicefunctiontobeabletotransferlaterthisdatatouserspace.
Thefunction outbaccomplishesthisittakesasargumentsthecontenttowriteintheportanditsaddress.
<parlelportoutport>=
/*Writingtotheport*/
outb(parlelport_buffer,0x378)
Table10summarizesthisnewfunction.
Events

Kernelfunctions

Readdata

inb

Writedata

outb

Devicedrivereventsandtheirassociatedfunctionsbetweenkernelspaceandthehardwaredevice.

Thecompleteparlelportdriver
Illproceedbylookingatthewholecodeofthe parlelportmodule.Youhavetoreplacethe
word memoryfortheword parlelportthroughoutthecodeforthe memorymodule.Thefinal
resultisshownbelow:
<parlelport.c>=
<parlelportinitial>
<parlelportinitmodule>
<parlelportexitmodule>
<parlelportopen>
<parlelportrelease>
<parlelportread>
<parlelportwrite>
http://www.freesoftwaremagazine.com/articles/drivers_linux

14/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

Initialsection
Intheinitialsectionofthedriveradifferent majornumberisused( 61).Also,theglobal
variable memory_bufferischangedto portandtwomore #includelinesare
added: ioport.hand io.h.
<parlelportinitial>=
/*Necessaryincludesfordrivers*/
#include<linux/init.h>
#include<linux/config.h>
#include<linux/module.h>
#include<linux/kernel.h>/*printk()*/
#include<linux/slab.h>/*kmalloc()*/
#include<linux/fs.h>/*everything...*/
#include<linux/errno.h>/*errorcodes*/
#include<linux/types.h>/*size_t*/
#include<linux/proc_fs.h>
#include<linux/fcntl.h>/*O_ACCMODE*/
#include<linux/ioport.h>
#include<asm/system.h>/*cli(),*_flags*/
#include<asm/uaccess.h>/*copy_from/to_user*/
#include<asm/io.h>/*inb,outb*/
MODULE_LICENSE("DualBSD/GPL")
/*Functiondeclarationofparlelport.c*/
intparlelport_open(structinode*inode,structfile*filp)
intparlelport_release(structinode*inode,structfile*filp)
ssize_tparlelport_read(structfile*filp,char*buf,
size_tcount,loff_t*f_pos)
ssize_tparlelport_write(structfile*filp,char*buf,
size_tcount,loff_t*f_pos)
voidparlelport_exit(void)
intparlelport_init(void)
/*Structurethatdeclaresthecommon*/
/*fileaccessfcuntions*/
structfile_operationsparlelport_fops={
read:parlelport_read,
write:parlelport_write,
open:parlelport_open,
release:parlelport_release
}
/*Driverglobalvariables*/
/*Majornumber*/
intparlelport_major=61
/*Controlvariableformemory*/
/*reservationoftheparallelport*/
intport
http://www.freesoftwaremagazine.com/articles/drivers_linux

15/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

module_init(parlelport_init)
module_exit(parlelport_exit)

Moduleinit
InthismoduleinitializingroutineIllintroducethememoryreserveoftheparallelportaswasdescribed
before.
<parlelportinitmodule>=
intparlelport_init(void){
intresult
/*Registeringdevice*/
result=register_chrdev(parlelport_major,"parlelport",
&parlelport_fops)
if(result<0){
printk(
"<1>parlelport:cannotobtainmajornumber%d\n",
parlelport_major)
returnresult
}

<parlelportmodifiedinitmodule>
printk("<1>Insertingparlelportmodule\n")
return0
fail:
parlelport_exit()
returnresult
}

Removingthemodule
Thisroutinewillincludethemodificationspreviouslymentioned.
<parlelportexitmodule>=
voidparlelport_exit(void){
/*Makemajornumberfree!*/
unregister_chrdev(parlelport_major,"parlelport")
<parlelportmodifiedexitmodule>
printk("<1>Removingparlelportmodule\n")
}

Openingthedeviceasafile
http://www.freesoftwaremagazine.com/articles/drivers_linux

16/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

Thisroutineisidenticaltothe memorydriver.
<parlelportopen>=
intparlelport_open(structinode*inode,structfile*filp){
/*Success*/
return0
}

Closingthedeviceasafile
Again,thematchisperfect.
<parlelportrelease>=
intparlelport_release(structinode*inode,structfile*filp){
/*Success*/
return0
}

Readingthedevice
Thereadingfunctionissimilartothe memoryonewiththecorrespondingmodificationstoreadfromthe
portofadevice.
<parlelportread>=
ssize_tparlelport_read(structfile*filp,char*buf,
size_tcount,loff_t*f_pos){

/*Buffertoreadthedevice*/
charparlelport_buffer
<parlelportinport>
/*Wetransferdatatouserspace*/
copy_to_user(buf,&parlelport_buffer,1)

/*Wechangethereadingpositionasbestsuits*/
if(*f_pos==0){
*f_pos+=1
return1
}else{
return0
}
}

http://www.freesoftwaremagazine.com/articles/drivers_linux

17/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

Writingtothedevice
Itisanalogoustothe memoryoneexceptforwritingtoadevice.
<parlelportwrite>=
ssize_tparlelport_write(structfile*filp,char*buf,
size_tcount,loff_t*f_pos){
char*tmp
/*Bufferwritingtothedevice*/
charparlelport_buffer
tmp=buf+count1
copy_from_user(&parlelport_buffer,tmp,1)
<parlelportoutport>

return1
}

LEDstotesttheuseoftheparallelport
InthissectionIlldetailtheconstructionofapieceofhardwarethatcanbeusedtovisualizethestateofthe
parallelportwithsomesimpleLEDs.
WARNING:Connectingdevicestotheparallelportcanharmyourcomputer.Makesurethatyou
areproperlyearthedandyourcomputeristurnedoffwhenconnectingthedevice.Anyproblems
thatariseduetoundertakingtheseexperimentsisyoursoleresponsibility.
Thecircuittobuildisshowninfigure3YoucanalsoreadPC&Electronics:ConnectingYourPCtothe
OutsideWorldbyZollerasreference.
Inordertouseit,youmustfirstensurethatallhardwareiscorrectlyconnected.Next,switchoffthePCand
connectthedevicetotheparallelport.ThePCcanthenbeturnedonandalldevicedriversrelatedtothe
parallelportshouldberemoved(forexample, lp, parport, parport_pc,etc.).
The hotplugmoduleoftheDebianSargedistributionisparticularlyannoyingandshouldberemoved.If
thefile /dev/parlelportdoesnotexist,itmustbecreatedasrootwiththecommand:
#mknod/dev/parlelportc610
Thenitneedstobemadereadableandwritablebyanybodywith:
#chmod666/dev/parlelport
Themodulecannowbeinstalled, parlelport.Youcancheckthatitiseffectivelyreservingthe
input/outputportaddresses 0x378withthecommand:
$cat/proc/ioports
http://www.freesoftwaremagazine.com/articles/drivers_linux

18/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

ToturnontheLEDsandcheckthatthesystemisworking,executethecommand:
$echonA>/dev/parlelport
ThisshouldturnonLEDzeroandsix,leavingalloftheothersoff.
Youcancheckthestateoftheparallelportissuingthecommand:
$cat/dev/parlelport

Finalapplication:flashing
lights
Finally,Illdevelopaprettyapplicationwhichwillmake
theLEDsflashinsuccession.Toachievethis,a
programinuserspaceneedstobewrittenwithwhich
onlyonebitatatimewillbewrittento
the /dev/parlelportdevice.

Figure3:ElectronicdiagramoftheLEDmatrixto
monitortheparallelport

<lights.c>=
#include<stdio.h>
#include<unistd.h></p>
intmain(){
unsignedcharbyte,dummy
FILE*PARLELPORT
/*Openingthedeviceparlelport*/
PARLELPORT=fopen("/dev/parlelport","w")
/*Weremovethebufferfromthefilei/o*/
setvbuf(PARLELPORT,&dummy,_IONBF,1)
/*Initializingthevariabletoone*/
byte=1
/*Wemakeaninfiniteloop*/
while(1){
/*Writingtotheparallelport*/
/*toturnonaLED*/
printf("Bytevalueis%d\n",byte)
fwrite(&byte,1,1,PARLELPORT)
sleep(1)
/*Updatingthebytevalue*/
byte<<=1
if(byte==0)byte=1
}
fclose(PARLELPORT)
http://www.freesoftwaremagazine.com/articles/drivers_linux

19/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

}
Itcanbecompiledintheusualway:
$gccolightslights.c
andcanbeexecutedwiththecommand:
$lights
Thelightswillflashsuccessivelyoneaftertheother!TheflashingLEDsandtheLinuxcomputerrunningthis
programareshowninfigure4.

Conclusion
Havingfollowedthisbrieftutorialyoushouldnowbecapableofwritingyourowncompletedevicedriverfor
simplehardwarelikearelayboard(seeAppendixC),oraminimaldevicedriverforcomplexhardware.
LearningtounderstandsomeofthesesimpleconceptsbehindtheLinuxkernelallowsyou,inaquickand
easyway,togetuptospeedwithrespecttowritingdevicedrivers.And,thiswillbringyouanotherstep
closertobecomingatrueLinuxkerneldeveloper.

Bibliography
A.Rubini,J.Corbert.2001.Linuxdevicedrivers
(secondedition).Ed.OReilly.Thisbookisavailable
forfreeontheinternet.
JonathanCorbet.2003/2004.Portingdevicedriversto
the2.6kernel.Thisisaveryvaluableresourcefor
portingdriverstothenew2.6Linuxkernelandalsofor
learningaboutLinuxdevicedrivers.
B.Zoller.1998.PC&Electronics:ConnectingYour
PCtotheOutsideWorld(ProductivitySeries).
Nowadaysitisprobablyeasiertosurfthewebfor
hardwareprojectslikethisone.

Figure4:FlashingLEDsmountedonthecircuitboard
andthecomputerrunningLinux.Twoterminalsare
shown:onewheretheparlelportmoduleisloaded
andanotheronewherethelightsprogramisrun.
Tuxiscloselyfollowingwhatisgoingon

M.Waite,S.Prata.1990.CProgramming.Anyother
goodbookonCprogrammingwouldsuffice.

AppendixA.CompleteMakefile
<Makefile>=
objm:=nothing.ohello.omemory.oparlelport.o
http://www.freesoftwaremagazine.com/articles/drivers_linux

20/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

AppendixB.CompilingthekernelonaDebianSargesystem
Tocompilea2.6.xkernelonaDebianSargesystemyouneedtoperformthefollowingsteps,whichshould
berunasroot:
Installthekernelimage2.6.xpackage.
Rebootthemachinetomakethistherunningkernelimage.ThisisdonesemiautomaticallybyDebian.
Youmayneedtotweaktheliloconfigurationfile /etc/lilo.confandthenrun liloto
achievethis.
Installthekernelsource2.6.xpackage.
Changetothesourcecodedirectory, cd/usr/srcandunzipanduntarthesourcecode
with bunzip2kernelsource2.6.x.tar.bz2and tarxvfkernel
source2.6.x.tar.Changetothekernelsourcedirectorywith cd/usr/src/kernel
source2.6.x
CopythedefaultDebiankernelconfigurationfiletoyourlocalkernelsourcedirectory cp
/boot/config2.6.x.config.
Makethekernelandthemoduleswith makeandthen makemodules.

AppendixC.Exercises
Ifyouwouldliketotakeonsomebiggerchallenges,hereareacoupleofexercisesyoucando:
IoncewrotetwodevicedriversfortwoISAMeilhausboards,ananalogtodigitalconverter(ME26)
andarelaycontrolboard(ME53).ThesoftwareisavailablefromtheADQproject.GetthenewerPCI
versionsoftheseMeilhausboardsandupdatethesoftware.
TakeanydevicethatdoesntworkonLinux,buthasaverysimilarchipsettoanotherdevicewhichdoes
haveaprovendevicedriverforLinux.Trytomodifytheworkingdevicedrivertomakeitworkforthe
newdevice.Ifyouachievethis,submityourcodetothekernelandbecomeakerneldeveloperyourself!

Commentsandacknowledgements
Threeyearshaveelapsedsincethefirstversionofthisdocumentwaswritten.Itwasoriginallywrittenin
Spanishandintendedforversion2.2ofthekernel,butkernel2.4wasalreadymakingitsfirststepsatthat
time.Thereasonforthischoiceisthatgooddocumentationforwritingdevicedrivers,theLinuxdevice
driversbook(seebibliography),laggedthereleaseofthekernelinsomemonths.Thisnewversionisalso
comingoutsoonafterthereleaseofthenew2.6kernel,butuptodatedocumentationisnowreadilyavailable
inLinuxWeeklyNewsmakingitpossibletohavethisdocumentsynchronizedwiththenewestkernel.
Fortunatelyenough,PCsstillcomewithabuiltinparallelport,despitetheactualtrendofchangingeverything
insideaPCtorenderitobsoleteinnotime.LetushopethatPCsstillcontinuetohavebuiltinparallelports
forsometimeinthefuture,orthatatleast,parallelportPCIcardsarestillbeingsold.
Thistutorialhasbeenoriginallytypedusingatexteditor(i.e. emacs)in nowebformat.Thistextisthen
processedwiththe nowebtooltocreatea LaTeXfile( .tex)andthesourcecodefiles( .c).All
thiscanbedoneusingthesupplied makefile.documentwiththecommand makef
makefile.document.
http://www.freesoftwaremagazine.com/articles/drivers_linux

21/22

29/5/2015

Writing device drivers in Linux: A brief tutorial

IwouldliketothanktheInstitutoPolitcnicodeBragana,theNcleoEstudantildeLinuxdelInstituto
PolitcnicodeBragana(NUX),theAsociacindeSoftwareLibredeLen(SLen)andtheNcleode
EstudantesdeEngenhariaInformticadaUniversidadedevoraformakingthisupdatepossible.
Category:Hacking
License:
GFDL
XavierCalbet'sarticles Loginorregistertopostcomments

http://www.freesoftwaremagazine.com/articles/drivers_linux

22/22

You might also like