Professional Documents
Culture Documents
WritingdevicedriversinLinux:Abrief
tutorial
ShortURL:http://fsmsh.com/1238
Like
Share
581
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
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
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
#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
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 <1>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
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
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
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
<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
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
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
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
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
/*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
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
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
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
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
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
}
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
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
IwouldliketothanktheInstitutoPolitcnicodeBragana,theNcleoEstudantildeLinuxdelInstituto
PolitcnicodeBragana(NUX),theAsociacindeSoftwareLibredeLen(SLen)andtheNcleode
EstudantesdeEngenhariaInformticadaUniversidadedevoraformakingthisupdatepossible.
Category:Hacking
License:
GFDL
XavierCalbet'sarticles Loginorregistertopostcomments
http://www.freesoftwaremagazine.com/articles/drivers_linux
22/22