Vous êtes sur la page 1sur 163

LinuxDeviceDriversforyourGirlFriend

12Replies

ThisisthefirstarticleoftheseriesonLinuxdevicedrivers,whichaimstopresenttheusuallytechnical
topicinawaythatismoreinterestingtoawidercrosssectionofreaders.

Afteraweekofhardwork,wefinallygotourdriverworking,wasthefirstlineasPugsmethisgirl
friendShweta.

Why?Whatwasyourdriverupto?Washesick?Andwhathardworkdidyoudo?,cameaseriesof
questionfromShwetawithanaughtysmile.

PugswasconfusedaswhatwasShwetatalkingabout.Whichdriverareyoutalkingabout?,he
exclaimed.

Whyareyouaskingme?Youshouldtellme,whichofyourdrivers,youaretalkingabout?,replied
Shweta.

Pugsclicked,AhCmon!Notmycardrivers.Iamtalkingaboutmydevicedriverwrittenonmy
computer.

Iknowacardriver,abusdriver,apilot,ascrewdriver.Butwhatisthisdevicedriver?,queried
Shweta.

AndthatwasallneededtotriggerPugspassiontoexplaintheconceptofdevicedriversforanewbie.
Inparticular,theLinuxdevicedrivers,whichhehadbeenworkingonsincemanyyears.

Ofdriversandbuses
Adriverisonewhodrivesmanages,controls,directs,monitorstheentityunderhiscommand.Soa
busdriverdoesthatwithabus.Similarly,adevicedriverdoesthatwithadevice.Adevicecouldbeany
peripheralconnectedtoacomputer,forexamplemouse,keyboard,screen/monitor,harddisk,
camera,clock,younameit.

Apilotcouldbeapersonorautomaticsystems,possiblymonitoredbyaperson.Similarly,devicedriver
couldbeapieceofsoftwareoranotherperipheral/device,possiblydrivenbyasoftware.However,ifit
isananotherperipheral/device,itisreferredasdevicecontrollerinthecommonparlance.Andby

driver,weonlymeanthesoftwaredriver.Adevicecontrollerisadeviceitselfandhencemanyatimesit
alsoneedsadriver,commonlyreferredasabusdriver.

Generalexamplesofdevicecontrollersincludeharddiskcontrollers,displaycontrollers,audiocontroller
forthecorrespondingdevices.Moretechnicalexampleswouldbethecontrollersforthehardware
protocols,suchasanIDEcontroller,PCIcontroller,USBcontroller,SPIcontroller,I2Ccontroller,etc.
Pictorially,thiswholeconceptcanbedepictedasinfigure1.

Figure1:Device&driverinteraction

DevicecontrollersaretypicallyconnectedtotheCPUthroughtheirrespectivelynamedbuses(collection
ofphysicallines),forexamplepcibus,idebus,etc.Intodaysembeddedworld,wemoreoftencome
acrossmicrocontrollersthanCPUs,whicharenothingbutCPU+variousdevicecontrollersbuiltontoa
singlechip.Thiseffectiveembeddingofdevicecontrollersprimarilyreducescost&space,makingit
suitableforembeddedsystems.Insuchcases,thebusesareintegratedintothechipitself.Doesthis
changeanythingonthedriversormoregenericallysoftwarefront?

Notmuchexceptthatthebusdriverscorrespondingtotheembeddeddevicecontrollers,arenow
developedunderthearchitecturespecificumbrella.

Drivershavetwoparts
Busdriversprovideshardwarespecificinterfaceforthecorrespondinghardwareprotocols,andarethe
bottommosthorizontalsoftwarelayersofanoperatingsystem(OS).Overthesesittheactualdevice
drivers.Theseoperateontheunderlyingdevicesusingthehorizontallayerinterfaces,andhenceare
devicespecific.However,thewholeideaofwritingthesedriversistoprovideanabstractiontotheuser.
Andsoontheotherend,thesedoprovideinterfacetotheuser.ThisinterfacevariesfromOStoOS.In

short,adevicedriverhastwoparts:i)Devicespecific,andii)OSspecific.Refertofigure2.

Figure2:Linuxdevicedriverpartition

Thedevicespecificportionofadevicedriverremainssameacrossalloperatingsystems,andismore
ofunderstandinganddecodingofthedevicedatasheets,thanofsoftwareprogramming.Adatasheet
foradeviceisadocumentwithtechnicaldetailsofthedevice,includingitsoperation,performance,
programming,etc.Later,Ishallshowsomeexamplesofdecodingdatasheetsaswell.However,the
OSspecificportionistheonewhichistightlycoupledwiththeOSmechanismsofuserinterfaces.This
istheonewhichdifferentiatesaLinuxdevicedriverfromaWindowsdevicedriverfromaMACdevice
driver.

Verticals
InLinux,adevicedriverprovidesasystemcallinterfacetotheuser.And,thisistheboundaryline
betweenthesocalledkernelspaceanduserspaceofLinux,asshowninfigure2.Figure3elaborates
onfurtherclassification.

BasedontheOSspecificinterfaceofadriver,inLinuxadriverisbroadlyclassifiedinto3verticals:

PacketorientedorNetworkvertical
BlockorientedorStoragevertical
ByteorientedorCharactervertical

Figure3:Linuxkerneloverview

Theothertwoverticals,looselytheCPUverticalandmemoryverticalputtogetherwiththeotherthree
verticalsgivethecompleteoverviewoftheLinuxkernel,likeanytextbookdefinitionofanOS:AnOS
does5managementsnamely:CPU/process,memory,network,storage,device/io.Thoughthese2
couldbeclassifiedasdevicedrivers,whereCPU&memoryaretherespectivedevices,thesetwoare
treateddifferentlyformanyreasons.

ThesearethecorefunctionalitiesofanyOS,beitmicroormonolithickernel.Moreoftenthannot,
addingcodeintheseareasismainlyaLinuxportingeffort,typicallyforanewCPUorarchitecture.
Moreover,thecodeinthesetwoverticalscannotbeloadedorunloadedonthefly,unliketheother
threeverticals.AndhenceforthtotalkaboutLinuxdevicedrivers,wewouldmeantotalkonlyonthe
laterthreeverticalsinfigure3.

Letsgetalittledeeperintothesethreeverticals.Networkconsistsof2parts:i)Networkprotocolstack,
andii)Networkinterfacecard(NIC)orsimplynetworkdevicedrivers,whichcouldbeforethernet,wifi,
oranyothernetworkhorizontals.Storageagainconsistsof2parts:i)Filesystemdriversfordecoding
thevariousformatsonvariouspartitions,andii)Blockdevicedriversforvariousstorage(hardware)
protocols,thatisthehorizontalslikeIDE,SCSI,MTD,etc.

Withthisyoumaywonder,isthattheonlysetofdevicesforwhichyouneeddrivers,orLinuxhas
driversfor.Justholdon.Youdefinitelyneeddriversforthewholelotofdevicesinterfacingwitha
system,andLinuxdohavedriversforthem.However,theirbyteorientedaccessibilityputsallofthem
underthecharacterverticalyesImeanititisthemajoritybucket.Infact,becauseofthisvastness,
characterdrivershavegotfurthersubclassified.So,youhavettydrivers,inputdrivers,consoledrivers,

framebufferdrivers,sounddrivers,etc.AndthetypicalhorizontalsherewouldbeRS232,PS/2,VGA,
I2C,I2S,SPI,etc.

Multipleverticalinterfacingdrivers
OnafinalnoteonthecompletepictureofplacementofallthedriversintheLinuxdriverecosystem,the
horizontalslikeUSB,PCI,etcspanbelowmultipleverticals.Why?AswehaveaUSBwifidongle,aUSB
pendrive,aswellasaUSBtoserialconverterallUSBbutthreedifferentverticals.

InLinux,busdriversorthehorizontals,areoftensplitintotwoparts,oreventwodrivers:i)Device
controllerspecific,andii)Anabstractionlayeroverthatfortheverticalstointerface,commonlycalled
cores.Aclassicalexamplewouldbetheusbcontrollerdriversohci,ehci,etcandtheUSB
abstractionusbcore.

Summingup
So,toconcludeadevicedriverisapieceofsoftwarewhichdrivesadevice,thoughtherearesomany
classifications.Andincaseitdrivesonlyanotherpieceofsoftware,wecallitjustadriver.Examplesare
filesystemdrivers,usbcore,etc.Hence,alldevicedriversaredriversbutalldriversarenotdevice
drivers.

HeyPugs!Justholdon.Wearegettinglateforourclass.Andyouknowwhatkindoftrouble,wecan
getinto.Letscontinuefromhere,tomorrow.,exclaimedShweta.

WiththatPugswrappedupsaying,Okay.Thisismajorlywhatthedevicedrivertheoryis.Ifyouare
interested,laterIshallshowyouthecode&whatallhavewebeendoingforallthevariouskindsof
drivers.Andtheyhurriedtowardstheirclassroom.

SecondArticle>>

WritingyourFirstLinuxdriverintheClassroom
3Replies

Thissecondarticle,whichispartoftheseriesonLinuxdevicedrivers,dealswiththeconceptof
dynamicallyloadingdrivers,firstwritingaLinuxdriver,beforebuildingandthenloadingit.

<<FirstArticle

AsShwetaandPugsreachedtheirclassroom,theywerealreadylate.Theirprofessorwasalreadyin
there.TheylookedateachotherandShwetasheepishlyasked,Maywecomein,sir.Cmon!!!you
guysarelateagain,calledoutprofessorGopi.Andwhatisyourexcuse,today?.Sir,wewere
discussingyourtopiconly.IwasexplainingheraboutdevicedriversinLinux,wasahurriedreplyfrom
Pugs.Goodone!!So,thenexplainmeaboutdynamicloadinginLinux.Yougetitrightandyoutwoare
excused,professoremphasized.Pugswasmorethanhappy.Andheverywellknew,howtomakehis
professorhappycriticizeWindows.So,thisiswhathesaid.

Asweknow,atypicaldriverinstallationonWindowsneedsarebootforittogetactivated.Thatisreally
notacceptable,ifweneedtodoit,sayonaserver.ThatswhereLinuxwinstherace.InLinux,wecan
load(/install)orunload(/uninstall)adriveronthefly.Anditisactiveforuseinstantlyafterload.Also,it
isdisabledwithunload,instantly.Thisisreferredasdynamicloading&unloadingofdriversinLinux.

Asexpectedheimpressedtheprofessor.Okay!takeyourseats.Butmakesureyouarenotlate
again.Withthis,theprofessorcontinuedtotheclass,So,asyounowalreadyknow,whatisdynamic
loading&unloadingofdriversinto&outof(Linux)kernel.Ishallteachyouhowtodoit.Andthen,we
wouldgetintowritingourfirstLinuxdrivertoday.

Dynamicallyloadingdrivers
Thesedynamicallyloadabledriversaremorecommonlyreferredasmodulesandbuiltintoindividual
fileswith.ko(kernelobject)extension.EveryLinuxsystemhasastandardplaceundertherootfile
system(/)foralltheprebuiltmodules.Theyareorganizedsimilartothekernelsourcetreestructure
under/lib/modules/<kernel_version>/kernel,where<kernel_version>wouldbetheoutputofthe
commandunameronthesystem.ProfessordemonstratestotheclassasshowninFigure4.

Figure4:Linuxprebuiltmodules

Now,letustakeoneoftheprebuiltmodulesandunderstandthevariousoperationswithit.

Heresalistofthevarious(shell)commandsrelevanttothedynamicoperations:

lsmodListthecurrentlyloadedmodules
insmod<module_file>Insert/Loadthemodulespecifiedby<module_file>
modprobe<module>Insert/Loadthe<module>alongwithitsdependencies
rmmod<module>Remove/Unloadthe<module>

Theseresideunderthe/sbindirectoryandaretobeexecutedwithrootprivileges.LetustaketheFAT
filesystemrelateddriversforourexperimentation.Thevariousmodulefileswouldbefat.ko,vfat.ko,
etc.underdirectoriesfat(&vfatforolderkernels)under/lib/modules/`unamer`/kernel/fs.Incase,they
areincompressed.gzformat,theyneedtobeuncompressedusinggunzip,forusing
withinsmod.vfatmoduledependsonfatmodule.So,fat.koneedstobeloadedbeforevfat.ko.Todoall
thesesteps(decompression&dependencyloading)automatically,modprobecanbeusedinstead.
Observethatthereisno.koforthemodulenametomodprobe.rmmodisusedtounloadthemodules.
Figure5demonstratesthiscompleteexperimentation.

Figure5:Linuxmoduleoperations

OurfirstLinuxdriver
Withthatunderstood,nowletswriteourfirstdriver.Yes,justbeforethat,someconceptstobesetright.
Adriverneverrunsbyitself.Itissimilartoalibrarythatgetsloadedforitsfunctionstobeinvokedbythe
runningapplications.Andhence,thoughwritteninC,itlacksthemain()function.Moreover,itwould
getloaded/linkedwiththekernel.Hence,itneedstobecompiledinsimilarwaysasthekernel.Even
theheaderfilestobeusedcanbepickedonlyfromthekernelsources,notfromthe
standard/usr/include.

OneinterestingfactaboutthekernelisthatitisanobjectorientedimplementationinC.Anditisso
profoundthatwewouldobservethesameevenwithourfirstdriver.AnyLinuxdriverconsistsofa
constructorandadestructor.Theconstructorofamodulegetscalledwheneverinsmodsucceedsin
loadingthemoduleintothekernel.Andthedestructorofthemodulegetscalled
wheneverrmmodsucceedsinunloadingthemoduleoutofthekernel.Thesetwoarelikenormal
functionsinthedriver,exceptthattheyarespecifiedastheinit&exitfunctions,respectivelybythe
macrosmodule_init()&module_exit()includedthroughthekernelheadermodule.h

/*ofd.cOurFirstDrivercode*/
#include<linux/module.h>
#include<linux/version.h>
#include<linux/kernel.h>
staticint__initofd_init(void)/*Constructor*/
{

printk(KERN_INFO"Namaskar:ofdregistered");

return0;

}
staticvoid__exitofd_exit(void)/*Destructor*/
{

printk(KERN_INFO"Alvida:ofdunregistered");

}
module_init(ofd_init);
module_exit(ofd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("OurFirstDriver");

Aboveisthecompletecodeforourfirstdriver,sayofd.c.Notethatthereisnostdio.h(auserspace
header),insteadananalogouskernel.h(akernelspaceheader).printk()beingtheprintf()analogous.
Additionally,version.hisincludedforversioncompatibilityofthemodulewiththekernelintowhichitis
goingtogetloaded.Also,theMODULE_*macrospopulatethemodulerelatedinformation,whichacts
likethemodulessignature.

BuildingourfirstLinuxdriver
OncewehavetheCcode,itistimetocompileitandcreatethemodulefileofd.ko.Andforthatweneed
tobuilditinthesimilarway,asthekernel.So,weshallusethekernelbuildsystemtodothesame.
HerefollowsourfirstdriversMakefile,whichwouldinvokethekernelsbuildsystemfromthekernel
source.ThekernelsMakefilewouldinturninvokeourfirstdriversMakefiletobuildourfirstdriver.The
kernelsourceisassumedtobeinstalledat/usr/src/linux.Incaseofittobeatanyotherlocation,the
KERNEL_SOURCEvariablehastobeappropriatelyupdated.

#Makefilemakefileofourfirstdriver
#ifKERNELRELEASEisnotdefined,we'vebeencalleddirectlyfromthecommandline.
#Invokethekernelbuildsystem.
ifeq(${KERNELRELEASE},)

KERNEL_SOURCE:=/usr/src/linux

PWD:=$(shellpwd)

default:

${MAKE}C${KERNEL_SOURCE}SUBDIRS=${PWD}modules

clean:

${MAKE}C${KERNEL_SOURCE}SUBDIRS=${PWD}clean

#OtherwiseKERNELRELEASEisdefined;we'vebeeninvokedfromthe
#kernelbuildsystemandcanuseitslanguage.
else

objm:=ofd.o

endif

Note1:Makefilesareveryspacesensitive.Thelinesnotstartingatthefirstcolumnhaveatabandnot
spaces.

Note2:ForbuildingaLinuxdriver,youneedtohavethekernelsource(orattheleastthekernel
headers)installedonyoursystem.

WiththeCcode(ofd.c)andMakefileready,allweneedtodoisputthemina(new)directoryofitsown,
andtheninvokemakeinthatdirectorytobuildourfirstdriver(ofd.ko).

$make
makeC/usr/src/linuxSUBDIRS=...modules
make[1]:Enteringdirectory`/usr/src/linux'
CC[M].../ofd.o
Buildingmodules,stage2.
MODPOST1modules
CC.../ofd.mod.o
LD[M].../ofd.ko
make[1]:Leavingdirectory`/usr/src/linux'

Summingup

Oncewehavetheofd.kofile,dotheusualstepsasroot,orwithsudo.

#su
#insmodofd.ko
#lsmod|head10

lsmodshouldshowyoutheofddriverloaded.

Whilethestudentsweretryingtheirfirstmodule,thebellrang,markingtheendforthissessionofthe
class.AndprofessorGopiconcluded,sayingCurrently,youmaynotbeabletoobserveanything,other
thanlsmodlistingshowingourfirstdriverloaded.Wherestheprintkoutputgone?Findthatoutfor
yourselfinthelabsessionandupdatemewithyourfindings.Moreover,todaysfirstdriverwouldbethe
templatetoanydriveryouwriteinLinux.Writinganyspecializedadvanceddriverisjustamatterof
whatgetsfilledintoitsconstructor&destructor.So,hereonwards,ourlearningsshallbeinenhancing
thisdrivertoachieveourspecificdriverfunctionalities.

Notes
1. Inmostoftodaysdistros,onemaysafelyhaveKERNEL_SOURCEsetto/lib/modules/$(shell
unamer)/build,insteadof/usr/src/linuxi.e.KERNEL_SOURCE:=/lib/modules/$(shelluname
r)/buildintheMakefile.

ThirdArticle>>

KernelCExtrasinaLinuxDriver
2Replies

Thisthirdarticle,intheseriesonLinuxdevicedriversdealswiththekernelsmessagelogging,
andkernelspecificGCCextensions.

<<SecondArticle

EnthusedbyhowPugsimpressedprofessorGopi,inthelastclass,Shwetadecidedtodosomething
similar.Andtherewasalreadyanopportunityfindingoutwherehastheoutputofprintkgone.So,as
soonassheenteredthelab,shegotholdofthebestlocatedsystem,loggedintoit,andtookcharge.
Knowingherprofessorprettywell,sheknewthattherewouldbeahintforthefinding,fromtheclass
itself.So,sheflashedbackwhatalltheprofessortaught,andsuddenlyrememberedtheerroroutput
demonstrationfrominsmodvfat.kodmesg|tail.Sheimmediatelytriedthatandforsurefoundout
theprintkoutput,there.Buthowdiditcomehere?Ataponhershoulderbroughtheroutofthethought.
Shallwegoforacoffee?,proposedPugs.ButIneedto.Iknowwhatyouarethinkingabout.,
interruptedPugs.Letsgo,yaar.Illexplainyouallaboutdmesg.

Kernelsmessagelogging
Onthecoffeetable,Pugsbegan:

Asfarasparametersareconcerned,printf&printkaresame,exceptthatwhenprogrammingforthe
kernelwedontbotheraboutthefloatformatsof%f,%lf&theirlikes.Howeverunlikeprintf,printkisnot
destinedtodumpitsoutputonsomeconsole.Infact,itcannotdoso,asitissomethingwhichisinthe
background,andexecuteslikealibrary,onlywhentriggeredeitherfromthehardwarespaceortheuser
space.So,thenwheredoesprintkprint?Alltheprintkcalls,justputtheircontentsintothe(log)ring
bufferofthekernel.Then,thesyslogdaemonrunningintheuserspacepicksthemforfinalprocessing
&redirectiontovariousdevices,asconfiguredinitsconfigurationfile/etc/syslog.conf.

YoumusthaveobservedtheoutofplacemacroKERN_INFO,intheprintkcalls,inthepreviousarticle.
Thatactuallyisaconstantstring,whichgetsconcatenatedwiththeformatstringafterit,makingita
singlestring.Notethatthereisnocomma(,)betweenthemtheyarenotwoseparatearguments.
Thereareeightsuchmacrosdefinedin<linux/kernel.h>underthekernelsource,namely:

#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

*/

Dependingontheseloglevels(i.e.thefirst3charactersintheformatstring),thesyslogdaemoninthe
userspaceredirectsthecorrespondingmessagestotheirconfiguredlocationsatypicalonebeingthe
logfile/var/log/messagesforalltheloglevels.Hence,alltheprintkoutputsarebydefaultinthatfile.
Though,theycanbeconfigureddifferentlytosayserialport(/dev/ttyS0)orsayallconsoles,likewhat
happenstypicallyforKERN_EMERG.Now,/var/log/messagesisbuffered&containmessagesnotonly
fromthekernelbutalsofromvariousdaemonsrunningintheuserspace.Moreover,
the/var/log/messagesmostoftenisnotreadablebyanormaluser,andhenceauserspaceutility
dmesgisprovidedtodirectlyparsethekernelringbufferanddumpitonthestandardoutput.Figure6
showsthesnippetsfromthetwo.

Figure6:Kernelsmessagelogging

KernelspecificGCCextensions
WithalltheseShwetagotfrustrated,asshewantedtofindallthesebyherown,andthendoa
impressioninthenextclassbutallflop.Pissedoff,shesaid,Soasyouhaveexplainedallabout
printinginkernel,whydontyoutellabouttheweirdCinthedriveraswellthespecial
keywords__init,__exit,etc.

Thesearenotanyspecialkeywords.KernelCisnotanyweirdCbutjustthestandardCwithsome
additionalextensionsfromtheCcompilergcc.Macros__initand__exitarejusttwoofthese
extensions.However,thesedonothaveanyrelevanceincaseweareusingthemfordynamically
loadabledriver,butonlywhenthesamecodegetsbuiltintothekernel.Allthefunctionsmarked
with__initgetplacedinsidetheinitsectionofthekernelimageandallfunctionsmarkedwith__exitare
placedinsidetheexitsectionofthekernelimage,automaticallybygcc,duringkernelcompilation.What
isthebenefit?Allfunctionswith__initaresupposedtobeexecutedonlyonceduringbootup,tillthe
nextbootup.So,oncetheyareexecutedduringbootup,kernelfreesupRAMbyremovingthemby
freeinguptheinitsection.Similarly,allfunctionsinexitsectionaresupposedtobecalledduringsystem
shutdown.Now,ifsystemisshuttingdownanyway,whydoyouneedtodoanycleanups.Hence,
theexitsectionisnotevenbuiltintothekernelanothercooloptimization.

Thisisabeautifulexampleofhowkernel&gccgoeshandinhandtoachievelotofoptimizationsand
manyothertrickswecouldseeothers,aswegoalong.AndthatiswhyLinuxkernelcanbecompiled
onlyusinggccbasedcompilersacloseknitbond.

Kernelfunctionsreturnguidelines
Whilereturningfromcoffee,PugsstartedallpraisesfortheOSS&itscommunity.Doyouknowwhy
differentindividualsareabletocometogetherandcontributeexcellentlywithoutanyconflicts
moreoverinaprojectashugeasLinux?Therearemanyreasons.Butdefinitely,oneofthestrong
reasonsis,following&abidingbytheinherentcodingguidelines.Takeforexampletheguidelinefor
returningvaluesfromafunctioninkernelprogramming.

Anykernelfunctionneedingerrorhandling,typicallyreturnsanintegerliketypeandthereturnvalue
againfollowsaguideline.Foranerror,wereturnanegativenumberaminussignappendedwitha
macroincludedthroughthekernelheader<linux/errno.h>,thatincludesthevariouserrornumber
headersunderthekernelsources,namely<asm/errno.h>,<asmgeneric/errno.h>,<asmgeneric/errno
base.h>.Forsuccess,zeroisthemostcommonreturnvalue,unlessthereissomeadditional
informationtobeprovided.Inthatcase,apositivevalueisreturned,thevalueindicatingtheinformation
likenumberofbytestransferred.

KernelC=PureC
Oncebackintothelab,Shwetarememberedtheirprofessormentioningthatno/usr/includeheaders
canbeusedforkernelprogramming.ButPugssaidthatkernelCisjuststandardCwith
somegccextensions.Whythisconflict?Actuallythisisnotaconflict.StandardCisjustpureCjustthe
language.Theheadersarenotpartofit.ThosearepartofthestandardlibrariesbuiltinCforC
programmers,basedontheconceptofreusingcode.Doesthatmean,allstandardlibrariesandhence
allANSIstandardfunctionsarenotpartofpureC?Yes.Then,hadntitbeenreallytoughcodingthe
kernel.Notforthisreason.Inreality,kerneldevelopershavedevelopedtheirownneededsetof
functions,andtheyareallpartofthekernelcode.printkisjustoneofthem.Similarly,manystring
functions,memoryfunctions,areallpartofthekernelsourceundervariousdirectories
likekernel,ipc,lib,andthecorrespondingheadersunderinclude/linuxdirectory.

Oya!Thatiswhyweneedtohavekernelsourceforbuildingadriver,affirmedShweta.Ifnotthe
completesource,atleasttheheadersareamust.Andthatiswhywehaveseparatepackagestoinstall
completekernelsourceorjustthekernelheaders,addedPugs.Inthelab,allthesourcesaresetup.
ButifIwanttotryoutdriversonmyLinuxsystematmyhostelroom,howdoIgoaboutit?asked
Shweta.OurlabhaveFedora,wherethekernelsourcestypicallygetinstalled
under/usr/src/kernels/<kernel_version>unlikethestandardplace/usr/src/linux.Labadministratorsmust
haveinstalleditusingcommandlineyuminstallkerneldevel.IuseMandrivaandinstalledthekernel
sourcesusingurpmikernelsource,repliedPugs.But,IhaveUbuntu.Okay!!Forthatjustuseapt
getinstallpossibly,aptgetinstalllinuxsource.

Summingup
Labtimingswerejustgettingover.Suddenly,ShwetaputouthercuriosityHeyPugs!Whatisthenext
topicwearegoingtolearninourLinuxdevicedriversclass?.Hmmm!!Mostprobablycharacter
drivers.Withthisinformation,Shwetahurriedlypackedupherbag&headedtowardsherroomto
setupthekernelsourcesandtryoutthenextdriveronherown.Incaseyouarestuckup,justgiveme
acall.Illbethere,calledupPugsfromthebehindwithasmile.

FourthArticle>>

Notes:
1. Thedefaultsyslogfile/var/log/messagesmayvaryfromdistrotodistro.Forexample,inthe
latestUbuntudistros,itis/var/log/syslog.

OtherReferences:
1. Anotherpossiblepointertothemissing/var/log/messagesinUbuntu

LinuxCharacterDrivers
7Replies

Thisfourtharticle,whichispartoftheseriesonLinuxdevicedrivers,dealswiththevariousconceptsof
characterdriversandtheirimplementation.

<<ThirdArticle

ShwetaatherhostelroominfrontofherPC,allsettoexplorethecharactersofLinuxcharacterdrivers,
beforeitisbeingtaughtintheclass.SherecalledthefollowinglinesfromprofessorGopisclass:
todaysfirstdriverwouldbethetemplatetoanydriveryouwriteinLinux.Writinganyspecialized
advanceddriverisjustamatterofwhatgetsfilledintoitsconstructor&destructor..Withthat,she
tookoutthefirstdrivercode,andpoppedoutvariousreferencebookstostartwritingacharacterdriver
onherown.ShealsodownloadedtheonlineLinuxDeviceDriversbookbyJonathanCorbet,
AlessandroRubini,GregKroahHartmanfromhttp://lwn.net/Kernel/LDD3/.Herefollowsthesummary
fromhervariouscollations.

Wsofcharacterdrivers
Wealreadyknowwhataredriversandwhyweneedthem.Then,whatissospecialaboutcharacter
drivers?IfwewritedriversforbyteorientedoperationsorintheClingothecharacteroriented
operations,werefertothemascharacterdrivers.Andasthemajorityofdevicesarebyteoriented,the
majorityofdevicedriversarecharacterdevicedrivers.Takeforexample,serialdrivers,audiodrivers,
videodrivers,cameradrivers,basicI/Odrivers,.Infact,alldevicedriverswhichareneitherstorage
nornetworkdevicedriversareoneformortheotherformofcharacterdrivers.Letslookintothe
commonalitiesofthesecharacterdriversandhowShwetawroteoneofthem.

Figure7:Characterdriveroverview

Thecompleteconnection
AsshowninFigure7,foranyapplication(userspace)tooperateonabyteorienteddevice(hardware
space),itshouldusethecorrespondingcharacterdevicedriver(kernelspace).Andthecharacterdriver
usageisdonethroughthecorrespondingcharacterdevicefile(s),linkedtoitthroughthevirtualfile
system(VFS).Whatitmeansisthatanapplicationdoestheusualfileoperationsonthecharacter
devicefilethoseoperationsaretranslatedtothecorrespondingfunctionsintothelinkedcharacter
devicedriverbytheVFSthosefunctionsthendoesthefinallowlevelaccesstotheactualdevicesto
achievethedesiredresults.Notethatthoughtheapplicationdoestheusualfileoperations,their
outcomemaynotbetheusualones.Rather,theywouldbeasdrivenbythecorrespondingfunctionsin
thedevicedriver.Forexample,areadfollowedbyawritemaynotfetchwhathasbeenwritteninto,
unlikeinthecaseofregularfiles.Notethatthisistheusualexpectedbehaviourfordevicefiles.Lets
takeanaudiodevicefileasanexample.Whatwewriteintoitistheaudiodatawewanttoplayback,say
throughaspeaker.However,thereadwouldgetustheaudiodatawearerecording,saythrougha
microphone.Andtherecordeddataneednotbetheplayedbackdata.

Inthiscompleteconnectionfromapplicationtothedevice,therearefourmajorentitiesinvolved:

1. Application
2. Characterdevicefile
3. Characterdevicedriver
4. Characterdevice

Andtheinterestingthingisthat,allofthesecanexistindependentlyonasystem,withouttheother
beingthere.So,mereexistenceoftheseonasystemdoesntmeantheyarelinkedtoformthe
completeconnection.Rather,theyneedtobeexplicitlyconnected.Applicationgetsconnectedtoa
devicefilebyinvokingopensystemcallonthedevicefile.Devicefile(s)arelinkedtothedevicedriver
byspecificregistrationsbythedriver.Andthedevicedriverislinkedtoadevicebyitsdevicespecific
lowleveloperations.Thus,formingthecompleteconnection.Withthis,notethatthecharacterdevice
fileisnottheactualdevicebutjustaplaceholderfortheactualdevice.

Major&minornumber
Connectionbetweentheapplicationandthedevicefileisbasedonthenameofthedevicefile.
However,theconnectionbetweenthedevicefileandthedevicedriverisbasedonthenumberofthe
devicefile,notthename.Thisallowsauserspaceapplicationtohaveanynameforthedevicefile,and
enablesthekernelspacetohavetrivialindexbasedlinkagebetweenthedevicefile&thedevicedriver.
Thisdevicefilenumberismorecommonlyreferredasthe<major,minor>pair,orthemajor&minor
numbersofthedevicefile.Earlier(tillkernel2.4),onemajornumberwasforonedriver,andtheminor
numberusedtorepresentthesubfunctionalitiesofthedriver.Withkernel2.6,thisdistinctionisno
longermandatorytherecouldbemultipledriversundersamemajornumberbutobviouslywith
differentminornumberranges.However,thisismorecommonwiththenonreservedmajornumbers
andstandardmajornumbersaretypicallypreservedforsingledrivers.Forexample,4forserial
interfaces,13formice,14foraudiodevices,.Thefollowingcommandwouldlistthevarious
characterdevicefilesonyoursystem:

$lsl/dev/|grep^c

<major,minor>relatedsupportinkernel2.6
Type:(definedinkernelheader<linux/types.h>)

dev_t//containsbothmajor&minornumbers

Macros:(definedinkernelheader<linux/kdev_t.h>)

MAJOR(dev_tdev)//extractsthemajornumberfromdev
MINOR(dev_tdev)//extractstheminornumberfromdev
MKDEV(intmajor,intminor)//createsthedevfrommajor&minor

Connectingthedevicefilewiththedevicedriverinvolvestwosteps:

1. Registeringforthe<major,minor>rangeofdevicefiles
2. Linkingthedevicefileoperationstothedevicedriverfunctions

FirststepisachievedusingeitherofthefollowingtwoAPIs:(definedinkernelheader<linux/fs.h>)

intregister_chrdev_region(dev_tfirst,unsignedintcnt,char*name);
intalloc_chrdev_region(

dev_t*first,unsignedintfirstminor,unsignedintcnt,char*name);

FirstAPIregistersthecntnumberofdevicefilenumbersstartingfromfirst,withthename.SecondAPI
dynamicallyfiguresoutafreemajornumberandregistersthecntnumberofdevicefilenumbers
startingfrom<thefreemajor,firstminor>,withthename.Ineithercase,the/proc/deviceskernelwindow
liststhenamewiththeregisteredmajornumber.Withthisinformation,Shwetaaddedthefollowinginto
thefirstdrivercode.

#include<linux/types.h>
#include<linux/kdev_t.h>
#include<linux/fs.h>
staticdev_tfirst;//Globalvariableforthefirstdevicenumber

Intheconstructor,sheadded:

intret;
if((ret=alloc_chrdev_region(&first,0,3,"Shweta"))<0)
{

returnret;

}
printk(KERN_INFO"<Major,Minor>:<%d,%d>\n",MAJOR(first),MINOR(first));

Inthedestructor,sheadded:

unregister_chrdev_region(first,3);

Puttingitalltogether,itbecomes:

#include<linux/module.h>
#include<linux/version.h>
#include<linux/kernel.h>
#include<linux/types.h>
#include<linux/kdev_t.h>
#include<linux/fs.h>
staticdev_tfirst;//Globalvariableforthefirstdevicenumber
staticint__initofcd_init(void)/*Constructor*/
{

intret;

printk(KERN_INFO"Namaskar:ofcdregistered");

if((ret=alloc_chrdev_region(&first,0,3,"Shweta"))<0)

printk(KERN_INFO"<Major,Minor>:<%d,%d>\n",MAJOR(first),MINOR(first));

return0;

returnret;

}
staticvoid__exitofcd_exit(void)/*Destructor*/
{

unregister_chrdev_region(first,3);

printk(KERN_INFO"Alvida:ofcdunregistered");

}
module_init(ofcd_init);
module_exit(ofcd_exit);
MODULE_LICENSE("GPL");

MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("OurFirstCharacterDriver");

Then,Shwetarepeatedtheusualsteps,shelearntforthefirstdriver

Buildthedriver(.kofile)bytypingmake
Loadthedriverusinginsmod
Listtheloadedmodulesusinglsmod
Unloadthedriverusingrmmod

Summingup
Additionally,beforeunloadingthedriver,shepeepedintothekernelwindow/proc/devicestolookfor
theregisteredmajornumberwiththenameShwetausingcat/proc/devices.Itwasrightthere.Butshe
couldntfindanydevicefilecreatedunder/devwiththesamemajornumber.So,shecreatedthemby
handusingmknod,andthentriedreading&writingthose.Figure8showsallthese.Pleasenotethat
themajornumber250mayvaryfromsystemtosystembasedontheavailability.Figure8alsoshows
theresults,Shwetagotfromreading&writingoneofthedevicefiles.Thatremindedherthatthe
secondstepforconnectingthedevicefilewiththedevicedriverLinkingthedevicefileoperationsto
thedevicedriverfunctionsisnotyetdone.Sherealizedthatsheneedstodigfurtherinformationto
completethisstepandalsotofigureoutthereasonforthemissingdevicefilesunder/dev.Weshall
continuefurtherinournextarticle,tofigureoutwhatmoreisShwetalearningandhowisshegoing
aheadwithherfirstcharacterdriver.

Figure8:Characterdevicefileexperiments

FifthArticle>>

Characterdevicefiles:Creation&Operations
7Replies

Thisfiftharticle,whichispartoftheseriesonLinuxdevicedrivers,iscontinuationofthevarious
conceptsofcharacterdriversandtheirimplementation,dealtwithinthepreviousarticle.

<<FourthArticle

Inourpreviousarticle,wenotedthatevenwiththeregistrationfor<major,minor>devicerange,the
devicefileswerenotcreatedunderthe/dev,ratherShwetahadtocreatethembyhandusingmknod.
However,onfurtherstudy,Shwetafiguredoutawayfortheautomaticcreationofthedevicefilesusing
theudevdaemon.Shealsolearntthesecondstepforconnectingthedevicefilewiththedevicedriver
Linkingthedevicefileoperationstothedevicedriverfunctions.Hereareherlearnings.

Automaticcreationofdevicefiles
Earlierinkernel2.4,automaticcreationofdevicefileswasdonebythekernelitself,bycallingthe
appropriateAPIsofdevfs.However,askernelevolved,kerneldevelopersrealizedthatdevicefilesare
moreofauserspacethingandhenceasapolicyonlytheusersshoulddealwithit,notthekernel.With
thisidea,nowkernelonlypopulatestheappropriatedeviceclass&deviceinfointothe/syswindowfor
thedeviceunderconsideration.Andthen,theuserspaceneedtointerpretitandtakeanappropriate
action.InmostLinuxdesktopsystems,theudevdaemonpicksupthatinformationandaccordingly
createsthedevicefiles.

udevcanbefurtherconfiguredusingitsconfigurationfilestotunethedevicefilenames,their
permissions,theirtypes,etc.So,asfarasdriverisconcerned,theappropriate/sysentriesneedtobe
populatedusingtheLinuxdevicemodelAPIsdeclaredin<linux/device.h>andtherestwouldbe
handledbyudev.Deviceclassiscreatedasfollows:

structclass*cl=class_create(THIS_MODULE,"<deviceclassname>");

andthenthedeviceinfo(<major,minor>)underthisclassispopulatedby:

device_create(cl,NULL,first,NULL,"<devicenameformat>",);

wherefirstisthedev_twiththecorresponding<major,minor>.

Thecorrespondingcomplementaryortheinversecalls,whichshouldbecalledinchronologically
reverseorder,areasfollows:

device_destroy(cl,first);
class_destroy(cl);

RefertoFigure9,forthe/sysentriescreatedusingchardrvasthe<deviceclassname>andmynull
asthe<devicenameformat>.Thatalsoshowsthedevicefile,createdbyudev,basedonthe<major>:
<minor>entryinthedevfile.

Figure9:Automaticdevicefilecreation

Incaseofmultipleminors,device_create()anddevice_destroy()APIsmaybeputinforloop,and

the<devicenameformat>stringcouldbeuseful.Forexample,thedevice_create()callinaforloop
indexedbyicouldbeasfollows:

device_create(cl,NULL,MKDEV(MAJOR(first),MINOR(first)+i),NULL,"mynull%d",i);

Fileoperations
Whateversystemcallsormorecommonlyfileoperationswetalkofoveraregularfile,areapplicableto
thedevicefilesaswell.Thatswhatwesayafileisafile,andinLinuxalmosteverythingisafilefrom
userspaceperspective.Thedifferenceliesinthekernelspace,wherevirtualfilesystem(VFS)decodes
thefiletypeandtransfersthefileoperationstotheappropriatechannel,likefilesystemmoduleincase
ofaregularfileordirectory,correspondingdevicedriverincaseofadevicefile.Ourdiscussionof
interestisthesecondcase.

Now,forVFStopassthedevicefileoperationsontothedriver,itshouldhavebeentoldaboutthat.And
yes,thatiswhatiscalledregisteringthefileoperationsbythedriverwiththeVFS.Thisinvolvestwo
steps.(Theparenthesisedtextbelowreferstothenulldrivercodefollowingit.)First,istofillinafile
operationsstructure(structfile_operationspugs_fops)withthedesiredfileoperations
(my_open,my_close,my_read,my_write,)andtoinitializethecharacterdevicestructure(structcdev
c_dev)withthat,usingcdev_init().ThesecondstepistohandthisstructuretotheVFSusingthe
callcdev_add().Bothcdev_init()andcdev_add()aredeclaredin<linux/cdev.h>.Obviously,theactual
fileoperations(my_open,my_close,my_read,my_write)alsohadtobecodedbyShweta.So,tostart
with,Shwetakeptthemassimpleaspossible,soastosay,aseasyasthenulldriver.

Thenulldriver
Followingthesesteps,Shwetaputallthepiecestogethertoattemptherfirstcharacterdevicedriver.
Letsseewhatwastheoutcome.Heresthecompletecode:

#include<linux/module.h>
#include<linux/version.h>
#include<linux/kernel.h>
#include<linux/types.h>
#include<linux/kdev_t.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/cdev.h>

staticdev_tfirst;//Globalvariableforthefirstdevicenumber
staticstructcdevc_dev;//Globalvariableforthecharacterdevicestructure
staticstructclass*cl;//Globalvariableforthedeviceclass
staticintmy_open(structinode*i,structfile*f)
{

printk(KERN_INFO"Driver:open()\n");

return0;

}
staticintmy_close(structinode*i,structfile*f)
{

printk(KERN_INFO"Driver:close()\n");

return0;

}
staticssize_tmy_read(structfile*f,char__user*buf,size_tlen,loff_t*off)
{

printk(KERN_INFO"Driver:read()\n");

return0;

}
staticssize_tmy_write(structfile*f,constchar__user*buf,size_tlen,

loff_t*off)

printk(KERN_INFO"Driver:write()\n");

returnlen;

}
staticstructfile_operationspugs_fops=
{

.owner=THIS_MODULE,

.open=my_open,

.release=my_close,

.read=my_read,

.write=my_write

};
staticint__initofcd_init(void)/*Constructor*/
{

intret;

structdevice*dev_ret;

printk(KERN_INFO"Namaskar:ofcdregistered");

if((ret=alloc_chrdev_region(&first,0,1,"Shweta"))<0)

if(IS_ERR(cl=class_create(THIS_MODULE,"chardrv")))

returnret;

unregister_chrdev_region(first,1);

returnPTR_ERR(cl);

if(IS_ERR(dev_ret=device_create(cl,NULL,first,NULL,"mynull")))

class_destroy(cl);

unregister_chrdev_region(first,1);

returnPTR_ERR(dev_ret);

cdev_init(&c_dev,&pugs_fops);

if((ret=cdev_add(&c_dev,first,1))<0)

device_destroy(cl,first);

class_destroy(cl);

unregister_chrdev_region(first,1);

returnret;

return0;

}
staticvoid__exitofcd_exit(void)/*Destructor*/
{

cdev_del(&c_dev);

device_destroy(cl,first);

class_destroy(cl);

unregister_chrdev_region(first,1);

printk(KERN_INFO"Alvida:ofcdunregistered");

}
module_init(ofcd_init);
module_exit(ofcd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("OurFirstCharacterDriver");

Then,Shwetarepeatedtheusualbuildwithnewteststepsasfollows:

Buildthedriver(.kofile)byrunningmake.
Loadthedriverusinginsmod.
Listtheloadedmodulesusinglsmod.
Listthemajornumberallocatedusingcat/proc/devices.
nulldriverspecificexperiments(RefertoFigure10fordetails).
Unloadthedriverusingrmmod.

Figure10:nulldriverexperiments

Summingup
Shwetawassurelyhappyasallonherownshegotacharacterdriverwritten,whichworkssameasthe
driverforthestandarddevicefile/dev/null.Tounderstandwhatitmeans,checkforyourselfthe<major,
minor>tuplefor/dev/null,andsimilarlyalsotryouttheechoandcatcommandswithit.

ButonethingstartedbotheringShweta.Shehadgotherowncalls
(my_open,my_close,my_read,my_write)inherdriver,buthowaretheyworkingsounusuallyunlike
anyregularfilesystemcalls.Whatssounusual?WhateverIwrite,Igetnothingwhenreadisntthat
unusual,atleastfromregularfileoperationsperspective.Anyguessesonhowwouldshecrackthis
nut?Watchoutforthenextarticle.

SixthArticle>>

Notes:

1. Forusingafixedmajornumber,youmayuseregister_chrdev_region()instead
ofalloc_chrdev_region().
2. Usekernelversion>=2.6.3xfortheclass_create()andthedevice_create()APIstocompile
properlyworkasexplained.As,beforethatversiontheyhavebeenrapidlyevolvingand
changing.
3. KernelAPIs(likeclass_create(),device_create())whichreturnspointers,shouldbechecked
usingIS_ERRmacroinsteadofcomparingwithNULL,asNULLiszero(i.e.successandnotan
error).TheseAPIsreturnnegativepointersonerrorerrorcodefromwhichcouldbeextracted
usingPTR_ERR.Seetheusageintheaboveexample.

OtherReferences:
1. Workingofudevdaemon

Decodingthecharacterdevicefileoperations
2Replies

Thissixtharticle,whichispartoftheseriesonLinuxdevicedrivers,iscontinuationofthevarious
conceptsofcharacterdriversandtheirimplementation,dealtwithintheprevioustwoarticles.

<<FifthArticle

So,whatwasyourguessonhowwouldShwetacrackthenut?Obviously,usingthenutcrackernamed
Pugs.Wasntitobvious?<Smile>Inourpreviousarticle,wesawhowShwetawaspuzzledwithreading
nodata,evenafterwritingintothe/dev/mynullcharacterdevicefile.Suddenly,abellrangnotinside
herhead,arealoneatthedoor.Andforsure,therewastheavatarofPugs.

Howcomeyourehere?,exclaimedShweta.Afterreadingyourtweet,whatelse?Coolthatyou
crackedyourfirstcharacterdriverallonyourown.Thatsamazing.So,whatareyouuptonow?,said
Pugs.Illtellyouontheconditionthatyoudonotbecomeaspoilsport,repliedShweta.Okayyaar,Ill
onlygiveyoupointers.Andthatalso,onlyifIaskfor.Okie.Iamtryingtodecodetheworkingof
characterdevicefileoperations.Ihaveanidea.Whydontyoudecodeandexplainmeyour
understanding?.Notabadidea.Withthat,Shwetatailedthedmesglogtoobservetheprintksoutput
fromherdriver.Alongside,sheopenedhernulldrivercodeonherconsole,specificallyobservingthe
devicefileoperationsmy_open,my_close,my_read,andmy_write.

staticintmy_open(structinode*i,structfile*f)
{

printk(KERN_INFO"Driver:open()\n");

return0;

}
staticintmy_close(structinode*i,structfile*f)
{

printk(KERN_INFO"Driver:close()\n");

return0;

}
staticssize_tmy_read(structfile*f,char__user*buf,size_tlen,loff_t*off)
{

printk(KERN_INFO"Driver:read()\n");

return0;

}
staticssize_tmy_write(

structfile*f,constchar__user*buf,size_tlen,loff_t*off)

printk(KERN_INFO"Driver:write()\n");

returnlen;

Basedontheearlierunderstandingofreturnvalueofthefunctionsin
kernel,my_open()andmy_close()aretrivial.Theirreturntypesbeingintandbothofthemreturning
zero,meaningsuccess.However,thereturntypesofbothmy_read()andmy_write()arenotint,
butssize_t.Onfurtherdiggingthroughkernelheaders,thatturnsouttobesignedword.So,returninga
negativenumberwouldbeausualerror.Butanonnegativereturnvaluewouldhaveanadditional
meaning.Forreaditwouldbenumberofbytesread,andforwriteitwouldbenumberofbyteswritten.

Readingthedevicefile
Forunderstandingthisindetail,thecompleteflowhastoberelookedat.Letstakereadfirst.So,when
theuserdoesareadontothedevicefile/dev/mynull,thatsystemcallcomestothevirtualfilesystem
(VFS)layerinthekernel.VFSdecodesthe<major,minor>tuple&figuresoutthatitneedtoredirectit
tothedriversfunctionmy_read(),registeredwithit.Sofromthatangle,my_read()isinvokedasa
requesttoread,fromusthedevicedriverwriters.Andhence,itsreturnvaluewouldindicatetothe
requestertheuser,astohowmanybytesishegettingfromthereadrequest.Inournulldriver
example,wereturnedzeromeaningnobytesavailableorinotherwordsendoffile.Andhence,when
thedevicefileisbeingread,theresultisalwaysnothing,independentofwhatiswrittenintoit.

Hmmm!!!So,ifIchangeitto1,woulditstartgivingmesomedata?,Pugsaskedinhisverifyingstyle.
Shwetapausedforawhilelookedattheparametersofthefunctionmy_read()andconfirmedwitha
butdatawouldbesentbutitwouldbesomejunkdata,asthemy_read()functionisnotreally
populatingthedataintothebuf(secondparameterofmy_read()),providedbytheuser.In
fact,my_read()shouldwritedataintobuf,accordingtolen(thirdparameterofmy_read()),thecountin
bytesrequestedbytheuser.

Tobemorespecific,writelessthanorequaltolenbytesofdataintobuf,andthesamenumberbe
usedasthereturnvalue.Itisnotatypoinread,wewriteintobufthatscorrect.Wereadthedata
from(possibly)anunderlyingdeviceandthenwritethatdataintotheuserbuffer,sothattheusergets
it,i.e.readsit.Thatsreallysmartofyou,expressedPugswithsarcasm.

Writingintothedevicefile
Similarly,thewriteisjustthereverseprocedure.Userprovideslen(thirdparameterofmy_write())bytes
ofdatatobewritten,intobuf(secondparameterofmy_write()).my_write()wouldreadthatdataand

possiblywriteintoanunderlyingdevice,andaccordinglyreturnthenumberofbytes,ithasbeenableto
writesuccessfully.Aha!!Thatswhyallmywritesinto/dev/mynullhavebeensuccessful,withoutbeing
actuallydoinganyreadorwrite,exclaimedShwetafilledwithhappinessofunderstandingthecomplete
flowofdevicefileoperations.

Preservingthelastcharacter
ThatwasenoughShwetanotgivinganychancetoPugstoadd,correctorevenspeak.So,Pugs
cameupwithachallenge.Okay.Seemslikeyouarethoroughlyclearwiththeread/writefunda.Then,
heresaquestionforyou.Canyoumodifythesemy_read()andmy_write()functionssuchthat
wheneverIread/dev/mynull,Igetthelastcharacterwritteninto/dev/mynull?

Confidentenough,Shwetatookthechallengeandmodifiedthemy_read()andmy_write()functionsas
follows,alongwithanadditionofastaticglobalcharacter:

staticcharc;
staticssize_tmy_read(structfile*f,char__user*buf,size_tlen,loff_t*off)
{

printk(KERN_INFO"Driver:read()\n");

buf[0]=c;

return1;

}
staticssize_tmy_write(

structfile*f,constchar__user*buf,size_tlen,loff_t*off)

printk(KERN_INFO"Driver:write()\n");

c=buf[len1];

returnlen;

Almostthere,butwhatiftheuserhasprovidedaninvalidbuffer,orwhatiftheuserbufferisswapped
out.Wouldntthisdirectaccessofuserspacebufjustcrashandoopsthekernel,pouncedPugs.
Shwetanotgivingupthechallenge,divesintohercollatedmaterialandfiguresoutthattherearetwo
APIsjusttoensurethattheuserspacebuffersaresafetoaccessandthenupdatethem,aswell.With
thecompleteunderstandingoftheAPIs,sherewrotetheabovecodesnippetalongwithincludingthe
correspondingheader<asm/uaccess.h>,asfollows,leavingnochanceforPugstocomment:

#include<asm/uaccess.h>
staticcharc;
staticssize_tmy_read(structfile*f,char__user*buf,size_tlen,loff_t*off)
{

printk(KERN_INFO"Driver:read()\n");

if(copy_to_user(buf,&c,1)!=0)

else

returnEFAULT;
return1;

}
staticssize_tmy_write(

structfile*f,constchar__user*buf,size_tlen,loff_t*off)

printk(KERN_INFO"Driver:write()\n");

if(copy_from_user(&c,buf+len1,1)!=0)

else

returnEFAULT;
returnlen;

Then,Shwetarepeatedtheusualbuildandteststepsasfollows:

Buildthemodifiednulldriver(.kofile)byrunningmake.
Loadthedriverusinginsmod.
Writeinto/dev/mynull,sayusingechonPugs>/dev/mynull
Readfrom/dev/mynullusingcat/dev/mynull(StopusingCtrl+C)
Unloadthedriverusingrmmod.

Summingup
Oncating/dev/mynull,theoutputwasanonstopinfinitesequenceofs,asmy_read()givesthelast
onecharacterforever.So,PugsintervenesandpressesCtrl+Ctostoptheinfiniteread,andtriesto
explain,Ifthisistobechangedtothelastcharacteronlyonce,my_read()needstoreturn1thefirst
timeandzerofromsecondtimeonwards.Thiscanbeachievedusingtheoff(fourthparameter
ofmy_read()).ShwetanodsherheadtosupportPugsego.

SeventhArticle>>

Addon
Andheresthemodifiedreadusingtheoff:

staticssize_tmy_read(structfile*f,char__user*buf,size_tlen,loff_t*off)
{

printk(KERN_INFO"Driver:read()\n");

if(*off==0)

if(copy_to_user(buf,&c,1)!=0)

else

(*off)++;

return1;

else

returnEFAULT;

return0;

GenericHardwareAccessinLinux
2Replies

Thisseventharticle,whichispartoftheseriesonLinuxdevicedrivers,talksaboutaccessinghardware
inLinux.

<<SixthArticle

Shwetawasalljubilantabouthercharacterdriverachievements,assheenteredtheLinuxdevice
driverslaboratoryonthesecondfloorofhercollege.Whynot?Manyofherclassmateshadalready
readherblog&commentedonherexpertise.Andtodaywasachanceforshowoffatananotherlevel.
Tillnow,itwasallsoftware.TodayslabwasonaccessinghardwareinLinux.Studentsareexpectedto
learnbyexperimentationtoaccessvariouskindsofhardwareinLinuxonvariousarchitecturesover
multiplelabsessionshere.

Asusual,thelabstaffareabitskepticaltoletthestudentsdirectlygetontothehardware,withoutany
background.Sotobuildtheirbackground,theyhavepreparedsomeslidepresentations,whichcanbe
accessedfromSysPlayswebsite.

Generichardwareinterfacing
Aseveryonesettledinthelaboratory,labexpertPritistartedwiththeintroductiontohardware
interfacinginLinux.Skippingthetheoreticaldetails,thefirstinterestingslidewasaboutthegeneric
architecturetransparenthardwareinterfacing.SeeFigure11.

Figure11:Hardwaremapping

Thebasicassumptionbeingthatthearchitectureis32bit.Forothers,thememorymapwouldchange
accordingly.For32bitaddressbus,theaddress/memorymaprangesfrom0(0x00000000)to2321
(0xFFFFFFFF).Andanarchitectureindependentlayoutofthismemorymapwouldbeasshowninthe
Figure11memory(RAM)anddeviceregions(registers&memoriesofdevices)mappedinan
interleavedfashion.Thearchitecturedependentthingwouldbewhattheseaddressesareactually
there.Forexample,inanx86architecture,theinitial3GB(0x00000000to0xBFFFFFFF)istypicallyfor
RAMandthelater1GB(0xC0000000to0xFFFFFFFF)fordevicemaps.However,iftheRAMisless,
say2GB,devicemapscouldstartfrom2GB(0x80000000).

Typeincat/proc/iomemtolistthememorymaponyoursystem.cat/proc/meminfowouldgiveyouan
approximateRAMsizeonyoursystem.RefertoFigure12forasnapshot.

Figure12:Physical&busaddressesonanx86system

Irrespectiveoftheactualvalues,theaddressesreferringtoRAMaretermedasphysicaladdresses.
Andtheaddressesreferringtodevicemapsaretermedasbusaddresses,asthesedevicesarealways
mappedthroughsomearchitecturespecificbus.Forexample,PCIbusinx86architecture,AMBAbus

inARMarchitectures,SuperHywaybusinSuperH(orSH)architectures,GXbusonPowerPC(orPPC),
etc.

Allthearchitecturedependentvaluesofthesephysicalandbusaddressesareeitherdynamically
configurableoraretobeobtainedfromthedatasheets(i.e.hardwaremanuals)ofthecorresponding
architectureprocessors/controllers.Buttheinterestingpartisthat,inLinuxnoneofthesearedirectly
accessiblebutaretobemappedtovirtualaddressesandthenaccessedthroughthat.Thus,making
theRAManddeviceaccessesgenericenough,exceptjustmappingthemtovirtualaddresses.Andthe
correspondingAPIsformapping&unmappingthedevicebusaddressestovirtualaddressesare:

#include<asm/io.h>
void*ioremap(unsignedlongdevice_bus_address,unsignedlongdevice_region_size);
voidiounmap(void*virt_addr);

Theseareprototypedin<asm/io.h>.Oncemappedtovirtualaddresses,itboilsdowntothedevice
datasheet,astowhichsetofdeviceregistersand/ordevicememorytoreadfromorwriteinto,by
addingtheiroffsetstothevirtualaddressreturnedbyioremap().Forthat,thefollowingaretheAPIs
(prototypedinthesameheaderfile<asm/io.h>):

#include<asm/io.h>
unsignedintioread8(void*virt_addr);
unsignedintioread16(void*virt_addr);
unsignedintioread32(void*virt_addr);
unsignedintiowrite8(u8value,void*virt_addr);
unsignedintiowrite16(u16value,void*virt_addr);
unsignedintiowrite32(u32value,void*virt_addr);

AccessingthevideoRAMofDOSdays
Afterthisfirstsetofinformation,studentsweredirectedfortheliveexperiments.Theyweresuggested
todoaninitialexperimentwiththevideoRAMofDOSdaystounderstandtheusageoftheabove
APIs.Shwetagotontothesystemdisplayedthe/proc/iomemwindowoneverysimilartoasshown
inFigure12.Fromthere,shegotthevideoRAMaddressrangingfrom0x000A0000to0x000BFFFF.
AndwiththatsheaddedtheaboveAPIswithappropriateparametersintotheconstructorand

destructorofheralreadywrittennulldrivertoconvertitintoavramdriver.Then,sheaddedtheuser
accesstothevideoRAMthroughread&writecallsofthevramdriver.Hereswhatshecodedinthe
newfilevideo_ram.c:

#include<linux/module.h>
#include<linux/version.h>
#include<linux/kernel.h>
#include<linux/types.h>
#include<linux/kdev_t.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/cdev.h>
#include<linux/uaccess.h>
#include<asm/io.h>
#defineVRAM_BASE0x000A0000
#defineVRAM_SIZE0x00020000
staticvoid__iomem*vram;
staticdev_tfirst;
staticstructcdevc_dev;
staticstructclass*cl;
staticintmy_open(structinode*i,structfile*f)
{

return0;

}
staticintmy_close(structinode*i,structfile*f)
{

return0;

}
staticssize_tmy_read(structfile*f,char__user*buf,size_tlen,loff_t*off)
{

inti;

u8byte;

if(*off>=VRAM_SIZE)

if(*off+len>VRAM_SIZE)

for(i=0;i<len;i++)

return0;

len=VRAM_SIZE*off;

byte=ioread8((u8*)vram+*off+i);

if(copy_to_user(buf+i,&byte,1))

*off+=len;

returnlen;

returnEFAULT;

}
staticssize_tmy_write(

structfile*f,constchar__user*buf,size_tlen,loff_t*off)

inti;

u8byte;

if(*off>=VRAM_SIZE)

if(*off+len>VRAM_SIZE)

for(i=0;i<len;i++)

if(copy_from_user(&byte,buf+i,1))

iowrite8(byte,(u8*)vram+*off+i);

*off+=len;

returnlen;

return0;

len=VRAM_SIZE*off;

returnEFAULT;

}
staticstructfile_operationsvram_fops=
{

.owner=THIS_MODULE,

.open=my_open,

.release=my_close,

.read=my_read,

.write=my_write

};
staticint__initvram_init(void)/*Constructor*/
{

intret;

structdevice*dev_ret;

if((vram=ioremap(VRAM_BASE,VRAM_SIZE))==NULL)

printk(KERN_ERR"MappingvideoRAMfailed\n");

returnENOMEM;

if((ret=alloc_chrdev_region(&first,0,1,"vram"))<0)

if(IS_ERR(cl=class_create(THIS_MODULE,"chardrv")))

unregister_chrdev_region(first,1);

returnPTR_ERR(cl);

if(IS_ERR(dev_ret=device_create(cl,NULL,first,NULL,"vram")))

class_destroy(cl);

unregister_chrdev_region(first,1);

returnPTR_ERR(dev_ret);

cdev_init(&c_dev,&vram_fops);

if((ret=cdev_add(&c_dev,first,1))<0)

device_destroy(cl,first);

class_destroy(cl);

unregister_chrdev_region(first,1);

returnret;

return0;

returnret;

}
staticvoid__exitvram_exit(void)/*Destructor*/
{

cdev_del(&c_dev);

device_destroy(cl,first);

class_destroy(cl);

unregister_chrdev_region(first,1);

iounmap(vram);

}
module_init(vram_init);
module_exit(vram_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("VideoRAMDriver");

Summingup
Then,Shwetarepeatedthefollowingsteps:

Buildthevramdriver(video_ram.kofile)byrunningmakewiththesameMakefilechangedto
buildthisdriver.
Usualloadofthedriverusinginsmodvideo_ram.ko.
Usualwriteinto/dev/vram,sayusingechon0123456789>/dev/vram.
Readthe/dev/vramcontentsusingxxd/dev/vram|less.Theusualcat/dev/vramalsocanbe
usedbutthatwouldgiveallbinarycontent.xxdshowsthemupashexadecimalincentrewiththe
correspondingASCIIalongtherightside.
Usualunloadthedriverusingrmmodvideo_ram.

Note1:TodayssystemstypicallyuseseparatevideocardshavingtheirownvideoRAM.So,thevideo
RAMusedintheDOSdays,i.e.theonementionedinthisarticleisunusedandmanyatimesnoteven
present.Hence,playingaroundwithit,issafe,withoutanyeffectonthesystem,orthedisplay.

Note2:Moreover,ifthevideoRAMisabsent,theread/writemaynotbeactuallyreading/writing,but
justsending/receivingsignalsintheair.Insuchacase,writeswouldnotdoanychange,andreads
wouldkeeponreadingthesamevaluethusxxdshowingthesamevalues.

Itwasyethalfanhourleftforthepracticalclasstobeoverandalunchbreak.So,Shwetadecidedto
walkaroundandpossiblyhelpsomebodyintheirexperiments.

EighthArticle>>

Notes:
1. Whenapointeristaggedwith__iomem,itenablesthatpointerforcompilerchecks&/or
optimizations,relevantforI/Omappedmemory.

OtherReferences:
1. TranslatingaddressesinKernelSpaceondifferentarchitectures
2. AddressingConceptsinLinux

3. LinuxMemoryManagementOverview

Accessingx86specificI/OmappedhardwareinLinux
2Replies

Thiseightharticle,whichispartoftheseriesonLinuxdevicedrivers,continuesontalkingabout
accessinghardwareinLinux.

<<SeventhArticle

SeconddayintheLinuxdevicedriverslaboratorywasexpectedtobequitedifferentfromthetypical
softwareorientedclasses.Apartfromaccessing&programmingthearchitecturespecificI/Omapped
hardwareinx86,ithadlottoofferforfirsttimersinreadinghardwaredevicemanuals(commonly
referredasdatasheets)andtounderstandthemforwritingdevicedrivers.

Contrastthiswiththepreviouslaboratorysession,whichtaughtaboutthegenericarchitecture
transparenthardwareinterfacing.Itwasallaboutmappingandaccessingmemorymappeddevicesin
Linux,withoutanydevicespecificdetail.

x86specifichardwareinterfacing
Unlikemostotherarchitectures,x86hasanadditionalhardwareaccessingmechanismthroughadirect
I/Omapping.Itisadirect16bitaddressingschemeanddoesntneedamappingtovirtualaddressfor
itsaccessing.Theseaddressesarereferredtoasportaddresses,orinshortports.Asx86hasthisas
anadditionalaccessingmechanism,itcallsforadditionalsetofx86(assembly/machinecode)
instructions.Andyes,therearetheinputinstructionsinb,inw,inlforreadingan8bitbyte,a16bitword,
anda32bitlongwordrespectively,fromtheI/Omappeddevicesthroughtheports.Andthe
correspondingoutputinstructionsareoutb,outw,outl,respectively.AndtheequivalentC
functions/macrosareasfollows(availablethroughtheheader<asm/io.h>):

u8inb(unsignedlongport);
u16inw(unsignedlongport);
u32inl(unsignedlongport);
voidoutb(u8value,unsignedlongport);
voidoutw(u16value,unsignedlongport);
voidoutl(u32value,unsignedlongport);

Thebasicquestionmayarise,astowhichalldevicesareI/Omappedandwhataretheportaddresses

ofthesedevices.Theanswerisprettysimple.Asperx86specific,allthesedevices&theirmappings
arex86standardandhencepredefined.Figure13showsasnippetofthesemappingsthroughthe
kernelwindow/proc/ioports.ThelistingincludespredefinedDMA,timer,RTC,serial,parallel,PCIbus
interfacestonameafew.

Figure13:x86specificI/Oports

Simplesttheserialonx86platform
Forexample,thefirstserialportisalwaysI/Omappedfrom0x3F8to0x3FF.Butwhatdoesthis
mappingmean?Whatdowedowiththis?Howdoesithelpustousetheserialport?
Thatiswhereadatasheetofthedevicecontrollingthecorrespondingportneedstobelookedup.
Serialportiscontrolledbytheserialcontrollerdevice,commonlyknownasanUART(Universal
AsynchronousReceiver/Transmitter)orattimesaUSART(UniversalSynchronous/Asynchronous
Receiver/Transmitter).OnPCs,thetypicalUARTusedisPC16550D.Thedatasheet
(uart_pc16550d.pdf)forthesamehasalsobeenincludedintheselfextractingLDDKPackage.sh,used
fortheLinuxdevicedriverkit.Figure14showstherelevantportionofit.

Ingeneral,fromwhere&howdowegetthesedevicedatasheets?Typically,anonlinesearchwiththe
correspondingdevicenumbershouldyieldtheirdatasheetlinks.Andhowdoesonegetthedevice
number?Simple,byhavingalookatthedevice.Ifitisinsideadesktop,openitupandcheckitout.
Yes,thisistheleastyoumayhavetodotogetgoingwiththehardwareforwritingdevicedrivers.
Assumingallthishackinghasbeendone,itistimetopeepintothedatasheetofUARTPC16550D.

Foradevicedriverwriter,theusualsectionsofinterestinadatasheetaretheonesrelatedtoregisters
ofthedevice.Why?As,itistheseregisters,whichadevicedriverwriterneedtoreadfromand/orwrite
intofinallyusethedevice.Page14ofthedatasheet(alsoshowninFigure14)showsthecomplete
tableofallthetwelve8bitregisterspresentintheUARTPC16550D.Eachofthe8rowscorrespondsto
therespectivebitoftheregisters.Also,notethattheregisteraddressesstartfrom0andgoestill7.The
interestingthingtonoteaboutthisisthatadatasheetalwaysgivestheregisteroffsets,whichthen
needtobeaddedtothebaseaddressofthedevice,togettheactualregisteraddresses.Whodecides
thebaseaddressandwhereisitobtainedfrom?Baseaddressesaretypicallyboard/platformspecific,
unlesstheyaredynamicallyconfigurablelikeinthecaseofPCIdevices.Inthecasehere,i.e.serial
deviceonx86,itisdictatedbythex86architectureandthatiswhatpreciselywasthestartingserial
portaddressmentionedabove0x3F8.Andtheeightregisteroffsets0to7aretheonesexactly
mappingtotheeightportaddresses0x3F8to0x3FF.So,thesearetheactualaddressestobereador
writtenforreadingorwritingthecorrespondingserialregisters,toachievethedesiredserialoperations,
aspertheregisterdescriptions.

Figure14:RegistersofUARTPC16550D

Alltheserialregisteroffsetsandtheregisterbitmasksaredefinedintheheader<linux/serial_reg.h>.
So,ratherthanhardcodingthesevaluesfromthedatasheet,thecorrespondingmacroscouldbeused
instead.Allthefollowingcodeusesthesemacrosalongwiththefollowing:

#defineSERIAL_PORT_BASE0x3F8

Operatingonthedeviceregisters
TosummarizeallthesedecodingofUARTPC16550Ddatasheet,hereareafewexamplesofhowto
doreadandwriteoperationsoftheserialregistersandtheirbits.

ReadingandwritingtheLineControlRegister(LCR):

u8val;
val=inb(SERIAL_PORT_BASE+UART_LCR/*3*/);
outb(val,SERIAL_PORT_BASE+UART_LCR/*3*/);

SettingandclearingtheDivisorLatchAccessBit(DLAB)inLCR:

u8val;
val=inb(SERIAL_PORT_BASE+UART_LCR/*3*/);
/*SettingDLAB*/
val|=UART_LCR_DLAB/*0x80*/;
outb(val,SERIAL_PORT_BASE+UART_LCR/*3*/);
/*ClearingDLAB*/
val&=~UART_LCR_DLAB/*0x80*/;
outb(val,SERIAL_PORT_BASE+UART_LCR/*3*/);

ReadingandwritingtheDivisorLatch:

u8dlab;
u16val;
dlab=inb(SERIAL_PORT_BASE+UART_LCR);
dlab|=UART_LCR_DLAB;//SettingDLABtoaccessDivisorLatch
outb(dlab,SERIAL_PORT_BASE+UART_LCR);
val=inw(SERIAL_PORT_BASE+UART_DLL/*0*/);
outw(val,SERIAL_PORT_BASE+UART_DLL/*0*/);

BlinkinganLED
TogetarealexperienceofthelowlevelhardwareaccessandLinuxdevicedrivers,thebestwaywould
betoplaywiththeLinuxdevicedriverkit(LDDK).However,justforthefeeloflowlevelhardware
access,ablinkinglightemittingdiode(LED)maybetriedasfollows:

Connectalightemittingdiode(LED)witha330ohmresistorinseriesacrossthepin3(Tx)&pin
5(Gnd)oftheDB9connectorofyourPC.
Pullup&downthetransmit(Tx)linewitha500msdelay,byloadingtheblink_leddriver
usinginsmodblink_led.ko,andthenunloadingthedriverusingrmmodblink_led,before
reloading.

Belowistheblink_led.c,tobecompiledintotheblink_led.kodriver,byrunningmakeusingtheusual
driverMakefile:

#include<linux/module.h>
#include<linux/version.h>
#include<linux/types.h>
#include<linux/delay.h>
#include<asm/io.h>
#include<linux/serial_reg.h>
#defineSERIAL_PORT_BASE0x3F8
int__initinit_module()
{

inti;

u8data;

data=inb(SERIAL_PORT_BASE+UART_LCR);

for(i=0;i<5;i++)

/*PullingtheTxlinelow*/

data|=UART_LCR_SBC;

outb(data,SERIAL_PORT_BASE+UART_LCR);

msleep(500);

/*DefaultingtheTxlinehigh*/

data&=~UART_LCR_SBC;

outb(data,SERIAL_PORT_BASE+UART_LCR);

msleep(500);

return0;

}
void__exitcleanup_module()
{
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");

MODULE_DESCRIPTION("BlinkingLEDHack");

Summingup
AreyouwonderingaswherehasShwetagonetoday?Shehasbunkedalltheclasses.Watchoutfor
thenextarticletofindoutwhy.

NinthArticle>>

Notes
1. Theaboveexampleistodemonstratehowbareboneeasythelowlevelaccesscouldget.
However,tomakeitmoreperfect,oneshouldusethe
APIsrequest_region()andrelease_region(),respectivelybeforeandaftertheaccessesoftheI/O
portaddresses,respectivelytoacquireandreleasetherangeofI/Oportaddressestoaccess.
2. Also,youmighthaveobservedthatthereisnomodule_init()&module_exit()intheabovedriver.
Butnonetheless,insmod&rmmoddowork.Howisthat?Thatis
becauseinit_module()&cleanup_module()arethepredefinednamesfortheconstructor&the
destructor,respectively.Hence,youdonotneedmodule_init()&module_exit()totranslateyour
otherfunctionnamestothesepredefinedones.Caution:Sincekernel2.6onwards,ifyouare
buildingthedriverintothekernel,youshoulddefineyourownfunctionnames&
usemodule_init()&module_exit().

I/OControlinLinux
2Replies

Thisnintharticle,whichispartoftheseriesonLinuxdevicedrivers,talksaboutthetypicalioctl()
implementationandusageinLinux.

<<EighthArticle

Getmealaptopandtellmeabouttheexperimentsonthex86specifichardwareinterfacingconducted
inyesterdaysLinuxdevicedriverslaboratorysession,andalsoaboutwhatsplannedforthenext
session,criedShweta,exasperatedatbeingconfinedtobedandnotbeingabletoattendtheclasses.
Calmdown!!!Dontworryaboutthat.Wellhelpyoumakeupforyourclasses.Butfirsttelluswhat
happenedtoyou,sosuddenly,askedoneofherfriends,whohadcometovisitherinthehospital.Its
allthefaultofthosechaats,IhadinRohansbirthdayparty.Ihadsuchapainfulfoodpoisoningthatled
mehere,blamedShweta.Howareyoufeelingnow?,askedRohansheepishly.Illbeallfinejust
tellmeallaboutthefunwithhardware,youguyshad.Ihadbeenwaitingtoattendthatsessionandall
thishadtohappen,rightthen.

RohansatdownbesidesShwetaandsummarizedthesessiontoher,hopingtosootheher.That
excitedhermoreandshestartingforcingthemtotellherabouttheupcomingsessions,aswell.They
knewthatthosewouldbetodosomethingwithhardware,butwereunawareofthedetails.Meanwhile,
thedoctorcomesinandrequestseverybodytowaitoutside.Thatwasanopportunitytoplanand
prepare.Andtheydecidedtotalkaboutthemostcommonhardwarecontrollingoperation:theioctl().
Hereishowitwent.

Introducinganioctl()
Inputoutputcontrol(ioctl,inshort)isacommonoperationorsystemcallavailablewithmostofthe
drivercategories.Itisaonebillfitsallkindofsystemcall.Ifthereisnoothersystemcall,whichmeets
therequirement,thendefinitelyioctl()istheonetouse.Practicalexamplesincludevolumecontrolfor
anaudiodevice,displayconfigurationforavideodevice,readingdeviceregisters,basically
anythingtodowithanydeviceinput/output,orforthatmatteranydevicespecificoperations.Infact,it
isevenmoreversatileneednotbetiedtoanydevicespecificthingsbutanykindofoperation.An
exampleincludesdebuggingadriver,saybyqueryingofdriverdatastructures.

Questionishowcouldallthesevarietybeachievedbyasinglefunctionprototype.Thetrickisusing
itstwokeyparameters:thecommandandthecommandsargument.Thecommandisjustsome

number,representingsomeoperation,definedaspertherequirement.Theargumentisthe
correspondingparameterfortheoperation.Andthentheioctl()functionimplementationdoesaswitch
caseoverthecommandimplementingthecorrespondingfunctionalities.Thefollowinghadbeenits
prototypeinLinuxkernel,forquitesometime:

intioctl(structinode*i,structfile*f,unsignedintcmd,unsignedlongarg);

Though,recentlyfromkernel2.6.35,ithaschangedtothefollowing:

longioctl(structfile*f,unsignedintcmd,unsignedlongarg);

Ifthereisaneedformorearguments,allofthemareputinastructureandapointertothestructure
becomestheonecommandargument.Whetherintegerorpointer,theargumentistakenupasalong
integerinkernelspaceandaccordinglytypecastandprocessed.

ioctl()istypicallyimplementedaspartofthecorrespondingdriverandthenanappropriatefunction
pointerinitializedwithit,exactlyaswithothersystemcallsopen(),read(),Forexample,incharacter
drivers,itistheioctlorunlocked_ioctl(sincekernel2.6.35)functionpointerfieldinthestruct
file_operations,whichistobeinitialized.

Againlikeothersystemcalls,itcanbeequivalentlyinvokedfromtheuserspaceusingtheioctl()system
call,prototypedin<sys/ioctl.h>as:

intioctl(intfd,intcmd,...);

Here,cmdisthesameasimplementedinthedriversioctl()andthevariableargumentconstruct()is
ahacktobeabletopassanytypeofargument(thoughonlyone)tothedriversioctl().Other
parameterswillbeignored.

Notethatboththecommandandcommandargumenttypedefinitionsneedtobesharedacrossthe
driver(inkernelspace)andtheapplication(inuserspace).So,thesedefinitionsarecommonlyputinto
headerfilesforeachspace.

Queryingthedriverinternalvariables
Tobetterunderstandtheboringtheoryexplainedabove,heresthecodesetforthedebugginga
driverexamplementionedabove.Thisdriverhas3staticglobalvariablesstatus,dignity,ego,which
needtobequeriedandpossiblyoperatedfromanapplication.query_ioctl.hdefinesthecorresponding
commandsandcommandargumenttype.Listingfollows:

#ifndefQUERY_IOCTL_H
#defineQUERY_IOCTL_H
#include<linux/ioctl.h>
typedefstruct
{

intstatus,dignity,ego;

}query_arg_t;
#defineQUERY_GET_VARIABLES_IOR('q',1,query_arg_t*)
#defineQUERY_CLR_VARIABLES_IO('q',2)
#endif

Usingthese,thedriversioctl()implementationinquery_ioctl.cwouldbe:

staticintstatus=1,dignity=3,ego=5;
#if(LINUX_VERSION_CODE<KERNEL_VERSION(2,6,35))
staticintmy_ioctl(structinode*i,structfile*f,unsignedintcmd,

unsignedlongarg)

#else
staticlongmy_ioctl(structfile*f,unsignedintcmd,unsignedlongarg)
#endif
{

query_arg_tq;

switch(cmd)

caseQUERY_GET_VARIABLES:

q.status=status;

q.dignity=dignity;

q.ego=ego;

if(copy_to_user((query_arg_t*)arg,&q,

break;

caseQUERY_CLR_VARIABLES:

status=0;

dignity=0;

ego=0;

break;

default:

return0;

sizeof(query_arg_t)))
returnEACCES;

returnEINVAL;

Andfinallythecorrespondinginvocationfunctionsfromtheapplicationquery_app.cwouldbeasfollows:

#include<stdio.h>
#include<sys/ioctl.h>
#include"query_ioctl.h"
voidget_vars(intfd)
{

query_arg_tq;

if(ioctl(fd,QUERY_GET_VARIABLES,&q)==1)

else

printf("Status:%d\n",q.status);

printf("Dignity:%d\n",q.dignity);

printf("Ego

perror("query_appsioctlget");

:%d\n",q.ego);

}
voidclr_vars(intfd)
{

if(ioctl(fd,QUERY_CLR_VARIABLES)==1)

perror("query_appsioctlclr");

CompletecodeoftheabovementionedthreefilesisincludedinthefolderQueryIoctl,wherethe
requiredMakefileisalsopresent.Youmaydownloaditstarbzippedfileasquery_ioctl_code.tar.bz2,
untaritandthen,dothefollowingtotryout:

Buildthequery_ioctldriver(query_ioctl.kofile)andtheapplication(query_appfile)by
runningmakeusingtheprovidedMakefile.
Loadthedriverusinginsmodquery_ioctl.ko.
Withappropriateprivilegesandcommandlinearguments,runtheapplicationquery_app:
./query_app#Todisplaythedrivervariables
./query_appc#Toclearthedrivervariables
./query_appg#Todisplaythedrivervariables
./query_apps#Tosetthedrivervariables(Notmentionedabove)
Unloadthedriverusingrmmodquery_ioctl.

Definingtheioctl()commands
Visitingtimeisover,camecallingthesecurityguard.AndallofShwetasvisitorspackeduptoleave.
Stoppingthem,Shwetasaid,Hey!!Thanksalotforallthishelp.Icouldunderstandmostofthiscode,
includingtheneedforcopy_to_user(),aswehavelearntearlier.Butjustaquestion,whatare
these_IOR,_IO,etcusedindefiningthecommandsinquery_ioctl.h.Yousaidwecouldjustuse
numbersforthesame.Butyouareusingalltheseweirdthings.Actually,theyareusualnumbersonly.
Justthat,nowadditionally,someusefulcommandrelatedinformationisalsoencodedaspartofthese
numbersusingthesevariousmacros,asperthePortableOperatingSystemInterface(POSIX)standard
forioctl.Thestandardtalksaboutthe32bitcommandnumbersbeingformedoffourcomponents
embeddedintothe[31:0]bits:

1. Directionofcommandoperation[bits31:30]read,write,both,ornonefilledbythe
correspondingmacro(_IOR,_IOW,_IOWR,_IO)
2. Sizeofthecommandargument[bits29:16]computedusingsizeof()withthecommand
argumentstypethethirdargumenttothesemacros
3. 8bitmagicnumber[bits15:8]torenderthecommandsuniqueenoughtypicallyanASCII
character(thefirstargumenttothesemacros)
4. Originalcommandnumber[bits7:0]theactualcommandnumber(1,2,3,),definedasper
ourrequirementthesecondargumenttothesemacros

Checkouttheheader<asmgeneric/ioctl.h>forimplementationdetails,concludedRohanwhile
hurryingoutoftheroomwithasighofrelief.

TenthArticle>>

Notes:
1. TheintentionbehindthePOSIXstandardofencodingthecommandistobeabletoverifythe
parameters,direction,etcrelatedtothecommand,evenbeforeitispassedtothedriver,sayby
VFS.ItisjustthatLinuxhasnotyetimplementedtheverificationpart.

KernelSpaceDebuggersinLinux
2Replies

Thistentharticle,whichispartoftheseriesonLinuxdevicedrivers,talksaboutkernelspacedebugging
inLinux.

<<NinthArticle

Shwetawasbackfromhospitalandrelaxinginthelibrarybyreadingupvariousbooks.Sincethetime
shehasknowntheioctlwayofdebugging,shehasbeenimpatienttoknowmoreaboutdebuggingin
kernelspace.Thebasiccuriositycomingfromthefactthathowandwherewouldonerunthekernel
spacedebugger,ifthereisany.Contrastthiswithapplication/userspacedebugging,wherewehave
theOSrunningunderneath,andashelloraGUIoverittorunthedebugger,sayforexample,theGNU
debugger(gdb),thedatadisplaydebugger(ddd).Andviola,shecameacrossthisinterestingkernel
spacedebuggingmechanismusingkgdb,providedaspartofthekernelitself,sincekernel2.6.26

Debuggerchallengeinkernelspace
Asweneedsomeinterfacetobeup,torunadebuggertodebuganything,adebuggerfordebugging
thekernel,couldbevisualizedin2possibleways:

1. Putthedebuggerintothekernelitself.Andthenthedebuggerrunsfromwithin,accessible
throughtheusualmonitororconsole.Anexampleofitiskdb.Untilkernel2.6.35,itwasnot
official,meaningitssourcecodewasneededtobedownloadedas2setofpatches(one
architecturedependentandonearchitectureindependent)
fromftp://oss.sgi.com/projects/kdb/download/andthentobepatchedwiththekernelsource
thoughsincekernel2.6.35,majorityofitispartofthekernelsourcesofficialrelease.Ineither
case,thekdbsupportneedstobeenabledinthekernelsourceandthenthekernelistobe
compiled,installedandbootedwith.Thebootscreenitselfwouldgivethekdbdebugging
interface.
2. Putaminimaldebuggingserverintothekerneladebuggerclientwouldthenconnecttoitfroma
remotehostsystemsuserspaceoversomeinterface,sayserialorethernet.Anexampleforthat
iskgdbthekernelsgdbserver,tobeusedwithgdb(client)fromaremotesystemovereither
serialorethernet.Sincekernel2.6.26,itsserialinterfaceparthasbeenmergedwithkernel
sourcesofficialrelease.Though,ifinterestedinitsnetworkinterfacepart,itwouldstillneedtobe
patchedwithoneofthereleasesfromhttp://sourceforge.net/projects/kgdb/.Ineithercase,the
nextstepwouldbetoenablekgdbsupportinthekernelsourceandthenthekernelistobe
compiled,installedandbootedwiththoughthistimetobeconnectedwitharemotedebugger

client.

Pleasenotethatinboththeabovecases,thecompletekernelsourceforthekerneltobedebuggedis
needed,unlikeincaseofbuildingmodules,wherejustheadersaresufficient.Hereishowtoplay
aroundwithkgdboverserialinterface.

SettinguptheLinuxkernelwithkgdb
Prerequisite:Eitherkernelsourcepackagefortherunningkernelisinstalledonyoursystem,ora
correspondingkernelsourcereleasehasbeendownloadedfromhttp://kernel.org.

Firstofall,thekerneltobedebuggedneedtohavekgdbenabledandbuiltintoit.Toachievethat,the
kernelsourcehastobeconfiguredwithCONFIG_KGDB=y.Additionally,forkgdbover
serial,CONFIG_KGDB_SERIAL_CONSOLE=yneedstobeconfigured.AndCONFIG_DEBUG_INFOis
preferredforsymbolicdatatobebuiltintokernelformakingdebuggingwithgdbmore
meaningful.CONFIG_FRAME_POINTER=yenablesframepointersinthekernelallowinggdbto
constructmoreaccuratestackbacktraces.AlltheseoptionsareavailableunderKernelhackinginthe
menuobtainedbyissuingthefollowingcommandsinthekernelsourcedirectory(preferablyasrootor
usingsudo):

$makemrproper#Tocleanupproperly
$makeoldconfig#Configurethekernelsameasthecurrentrunningone
$makemenuconfig#Startthencursesbasedmenuforfurtherconfiguration

SeethehighlightedselectionsinFigure15,forhowandwherewouldtheseoptionsbe:

KGDB:kerneldebuggingwithremotegdbCONFIG_KGDB
KGDB:usekgdbovertheserialconsoleCONFIG_KGDB_SERIAL_CONSOLE
CompilethekernelwithdebuginfoCONFIG_DEBUG_INFO
CompilethekernelwithframepointersCONFIG_FRAME_POINTER

Figure15:Configuringkernelwithkgdb

Onceconfiguredandsaved,thekernelcanbebuiltbytypingmakeinthekernelsourcedirectory.And
thenamakeinstallisexpectedtoinstallit,alongwithaddinganentryfortheinstalledkernelin
thegrubconfigurationfile.Dependingonthedistribution,thegrubconfigurationfilemay
be/boot/grub/menu.lst,/etc/grub.cfg,orsomethingsimilar.Onceinstalled,thekgdbrelatedkernelboot
parameters,needtobeaddedtothisnewlyaddedentry.

Figure16:grubconfigurationforkernelwithkgdb

Figure16highlightsthekernelbootparametersaddedtothenewlyinstalledkernel,inthegrubs
configurationfile.

kgdbocisforgdbconnectingoverconsoleandthebasicformatis:kgdboc=<serial_device>,<baud_rate>
where:
<serial_device>istheserialdevicefileonthesystem,whichwouldrunthekerneltobedebugged,for
theserialporttobeusedfordebugging
<baud_rate>isthebaudrateoftheserialporttobeusedfordebugging

kgdbwaitenablesthebootingkerneltowaittillaremotegdb(i.e.gdbonanothersystem)connectstoit,
andthisparametershouldbepassedonlyafterkgdboc.

Withthis,thesystemisreadytorebootintothisnewlybuiltandinstalledkernel.Onreboot,atthegrubs
menu,selecttobootfromthisnewkernel,andthenitwillwaitforgdbtoconnectwithitfromananother
systemovertheserialport.

Alltheabovesnapshotshavebeenwithkernelsource2.6.33.14,downloadedfromhttp://kernel.org.
Andthesameshouldworkforattheleastany2.6.3xreleaseofkernelsource.Also,thesnapshotsare
capturedforkgdboverserialdevicefile/dev/ttyS0,i.e.thefirstserialport.

Settingupgdbonanothersystem
Prerequisite:Serialportsofthesystemtobedebuggedandtheanothersystemtorungdbfrom,
shouldbeconnectedusinganullmodem(i.e.acrossoverserial)cable.

Herearethegdbcommandstogetthegdbfromtheothersystemconnecttothewaitingkernel.All
thesecommandshavetobegivenonthegdbprompt,aftertypinggdbontheshell.

Connectingoverserialport/dev/ttyS0(ofthesystemrunninggdb)withbaudrate115200bps:

$gdb
...
(gdb)filevmlinux
(gdb)setremoteinterruptsequenceCtrlC
(gdb)setremotebaud115200
(gdb)targetremote/dev/ttyS0
(gdb)continue

Intheabovecommands,vmlinuxisthekernelimagebuiltwithkgdbenabledandneedstobecopied
intothedirectoryonthesystem,fromwheregdbisbeingexecuted.Also,theserialdevicefileandits
baudratehastobecorrectlygivenasperonessystemsetup.

Debuggingusinggdbwithkgdb
Afterthis,itisalllikedebugginganapplicationfromgdb.OnemaystopexecutionusingCtrl+C,add
breakpointsusingb[reak],stepexecutionusings[tep]orn[ext],theusualgdbway.Fordetailson
howtousegdb,thereareenoughtutorialsavailableonline.Infact,ifnotcomfortablewithtext
basedgdb,thedebuggingcouldbemadeGUIbased,usinganyofthestandardGUItoolsovergdb,for
example,ddd,Eclipse,etc.

Summingup
Bynow,Shwetawasallexcitedtotryoutthekernelspacedebuggingexperimentusingkgdb.Andas
sheneededtwosystemstotryitout,shedecidedtogototheLinuxdevicedriverslab.There,sheset
upthesystemtobedebugged,withthekgdbenabledkernel,andconnecteditwithananothersystem
usinganullmodemcable.Thenonthesecondsystem,sheexecutedgdbtoremotelyconnectwithand
stepthroughthekernelonthefirstsystem.

EleventhArticle>>

Notes
1. Parameterforusingkgdboverethernetiskgdboe.Ithasthefollowingformat:kgdboe=
[<this_udp_port>]@<this_ip>/[this_dev],
[<remote_udp_port>]@<remote_ip>/[<remote_mac_addr>]

where:
<this_udp_port>isoptionalanddefaultsto6443,
<this_ip>isIPaddressofthesystem,whichwouldrunthekerneltobedebugged,
<this_dev>isoptionalanddefaultstoeth0,
<remote_udp_port>isoptionalanddefaultsto6442,
<remote_ip>isIPaddressofthesystem,fromwhichgdbwouldbeconnectingfrom,
<remote_mac_addr>isoptionalanddefaultstobroadcast.

Herearethegdbcommandsforconnectingtokgdbovernetworkport6443toIPaddress
192.168.1.2:

$gdb
...
(gdb)filevmlinux
(gdb)setremotebreak0
(gdb)targetremoteudp:192.168.1.2:6443
(gdb)continue

USBDriversinLinux
2Replies

Thiseleventharticle,whichispartoftheseriesonLinuxdevicedrivers,getsyoustartedwithwriting
yourfirstUSBdriverinLinux.

<<TenthArticle

Pugspendrivewasthedevice,Shwetawasplayingwith,whenbothofthemsatdowntoexplorethe
worldofUSBdriversinLinux.Thefastestwaytogethangofone,theusualPugsway,wastopickupa
USBdeviceandwriteadriverforittoexperimentwith.So,theychosependriveakaUSBstick,
availableathand.ItwasJetFlashfromTranscendwithvendorID0x058fandproductID0x6387.

USBdevicedetectioninLinux
WhetheradriverofaUSBdeviceisthereornotonaLinuxsystem,avalidUSBdevicewouldalways
getdetectedatthehardwareandkernelspacesofaUSBenabledLinuxsystem.AvalidUSBdeviceis
adevicedesignedanddetectedasperUSBprotocolspecifications.Hardwarespacedetectionisdone
bytheUSBhostcontrollertypicallyanativebusdevice,e.g.aPCIdeviceonx86systems.The
correspondinghostcontrollerdriverwouldpickandtranslatethelowlevelphysicallayerinformationinto
higherlevelUSBprotocolspecificinformation.TheUSBprotocolformattedinformationabouttheUSB
deviceisthenpopulatedintothegenericUSBcorelayer(usbcoredriver)inthekernelspace,thus
enablingthedetectionofaUSBdeviceinthekernelspace,evenwithouthavingitsspecificdriver.

Afterthis,itisuptothevariousdrivers,interfaces,andapplications(whicharedependentonthe
variousLinuxdistributions),tohavetheuserspaceviewofthedetecteddevices.Figure17showsatop
tobottomviewofUSBsubsysteminLinux.AbasiclistingofalldetectedUSBdevicescanbeobtained
usingthelsusbcommand,asroot.Figure18showsthesame,withoutandwiththependrivebeing
insertedintothesystem.Avoptiontolsusbprovidesdetailedinformation.InmanyLinuxdistributions
likeMandriva,Fedora,usbfsdriverisloadedaspartofthedefaultconfiguration.Thisenablesthe
detectedUSBdevicedetailstobeviewedinamoretechnofriendlywaythroughthe/procwindow
usingcat/proc/bus/usb/devices.Figure19showsatypicalsnippetofthesame,clippedaroundthepen
drivespecificsection.Thecompletelistingbasicallycontainssuchsections,eachforoneofthevalid
USBdevicesdetectedontheLinuxsystem.

Figure17:USBsubsysteminLinux

Figure18:Outputoflsusb

Figure19:USBsprocwindowsnippet

DecodingaUSBdevicesection
Tofurtherdecodethesesections,avalidUSBdeviceneedstobeunderstoodfirst.AllvalidUSBdevices
containoneormoreconfigurations.AconfigurationofaUSBdeviceislikeaprofile,wherethedefault
oneisthecommonlyusedone.Assuch,Linuxsupportsonlyoneconfigurationperdevicethedefault
one.Foreveryconfiguration,thedevicemayhaveoneormoreinterfaces.Aninterfacecorrespondsto
thefunctionalityprovidedbythedevice.Therewouldbeasmanyinterfacesasthenumberof
independentfunctionalitiesprovidedbythedevice.So,sayanMFD(multifunctiondevice)USBprinter
candoprinting,scanning,andfaxing,thenitmostlikelywouldhaveatleastthreeinterfacesonefor
eachofthefunctionalities.So,unlikeotherdevicedrivers,aUSBdevicedriveristypically
associated/writtenperinterface,ratherthanthedeviceasawholemeaningoneUSBdevicemay
havemultipledevicedrivers.Thoughdefinitelyoneinterfacecanhaveamaximumofonedriveronly.

Moreover,itisokayandcommontohaveasingleUSBdevicedriverforalltheinterfacesofaUSB
device.TheDriver=entryintheprocwindowoutput(Figure19)showstheinterfaceistodriver
mappinga(none)indicatingnoassociateddriver.Foreveryinterface,therewouldbeoneormore

endpoints.Anendpointislikeapipefortransferringinformationeitherintoorfromtheinterfaceofthe
device,dependingonthefunctionality.Dependingonthetypeofinformation,theendpointshavefour
types:

Control
Interrupt
Bulk
Isochronous

AsperUSBprotocolspecification,allvalidUSBdeviceshaveanimplicitspecialcontrolendpointzero,
theonlybidirectionalendpoint.Figure20showsthecompletepictorialrepresentationofavalidUSB
device,basedontheaboveexplanation.

ComingbacktotheUSBdevicesections(Figure19),thefirstletteroneachlinerepresentsthevarious
partsoftheUSBdevicespecificationjustexplained.Forexample,Dfordevice,Cforconfiguration,Ifor
interface,Eforendpoint,etc.Detailsaboutthemandvariousothersareavailableunderthekernel
sourceDocumentation/usb/proc_usb_info.txt

Figure20:USBdeviceoverview

TheUSBpendrivedriverregistration
SeemsliketherearesomanythingstoknowabouttheUSBprotocoltobeabletowritethefirstUSB
driveritselfdeviceconfiguration,interfaces,transferpipes,theirfourtypes,andsomanyother

symbolslikeT,B,S,underaUSBdevicespecification,sighedShweta.Yes,butdontyouworry
allthesecanbetalkedindetail,later.Letsdothefirstthingfirstgetourpendrivesinterface
associatedwithourUSBdevicedriver(pen_register.ko),consoledPugs.LikeanyotherLinuxdevice
driver,herealsoweneedtheconstructorandthedestructorbasicallythesamedrivertemplatethat
hasbeenusedforallthedrivers.Thoughthecontentwouldvaryasthisisahardwareprotocollayer
driver,i.e.ahorizontaldriverunlikeacharacterdriver,whichwasoneoftheverticaldrivers,discussed
sofar.Andthedifferencewouldbethatinsteadofregisteringwith&unregisteringfromVFS,herewe
woulddothatwiththecorrespondingprotocollayertheUSBcoreinthiscaseinsteadofprovidinga
userspaceinterfacelikeadevicefile,itwouldgetconnectedwiththeactualdeviceinthehardware
space.TheUSBcoreAPIsforthesameareasfollows(prototypedin<linux/usb.h>):

intusb_register(structusb_driver*driver);
voidusb_deregister(structusb_driver*);

Aspartoftheusb_driverstructure,thefieldstobeprovidedarethedriversname,IDtableforauto
detectingtheparticulardeviceandthe2callbackfunctionstobeinvokedbyUSBcoreduringhot
pluggingandhotremovalofthedevice,respectively.Puttingitalltogether,pen_register.cwouldlook
like:

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/usb.h>
staticintpen_probe(structusb_interface*interface,conststructusb_device_id*id)
{

printk(KERN_INFO"Pendrive(%04X:%04X)plugged\n",id>idVendor,

return0;

}
staticvoidpen_disconnect(structusb_interface*interface)
{

printk(KERN_INFO"Pendriveremoved\n");

}
staticstructusb_device_idpen_table[]=
{

{USB_DEVICE(0x058F,0x6387)},

{}/*Terminatingentry*/

};

id>idProduct);

MODULE_DEVICE_TABLE(usb,pen_table);
staticstructusb_driverpen_driver=
{

.name="pen_driver",

.id_table=pen_table,

.probe=pen_probe,

.disconnect=pen_disconnect,

};
staticint__initpen_init(void)
{

returnusb_register(&pen_driver);

}
staticvoid__exitpen_exit(void)
{

usb_deregister(&pen_driver);

}
module_init(pen_init);
module_exit(pen_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("USBPenRegistrationDriver");

Then,theusualstepsforanyLinuxdevicedrivermayberepeated:

Buildthedriver(.kofile)byrunningmake
Loadthedriverusinginsmod
Listtheloadedmodulesusinglsmod
Unloadthedriverusingrmmod

Butsurprisinglytheresultswouldntbeasexpected.Checkfordmesgandtheprocwindowtoseethe
variouslogsanddetails.NotbecauseUSBdriverisdifferentfromacharacterdriver.Buttheresa
catch.Figure19showsthatthependrivehasoneinterface(numbered0),whichisalreadyassociated
withtheusualusbstoragedriver.Now,inordertogetourdriverassociatedwiththatinterface,weneed
tounloadtheusbstoragedriver(i.e.rmmodusbstorage)afterplugginginthependrive,andthenload
ourdriver.Oncethissequenceisfollowed,theresultswouldbeasexpected.Figure21showsa
glimpseofthepossiblelogsandprocwindowsnippet.Repeathotplugginginandhotpluggingoutthe
pendrivetoobservetheprobeanddisconnectcallsinactionbutdontforgetunloadingtheusb
storagedriver,everytimeyoupluginthependriver.

Figure21:Pendriverinaction

Summingup
Finally!!!Somethingintoaction.,relievedShweta.Butseemsliketherearesomanythingsaround
(likethedeviceIDtable,probe,disconnect,),yettobeassimilatedandunderstoodtogetacomplete
USBdevicedriver,inplace,shecontinued.Yes,youareright.Letstakethemonebyone,with
breaks,repliedPugstakingastretchingbreak.

TwelfthArticle>>

Notes
1. Makesurethatyoureplacethevendorid&deviceidintheabovecodeexamplesbytheones
ofyourpendrive.
2. Onemaywonder,ashowdoestheusbstoragegetautoloaded.Theanswerliesinthemodule
autoloadruleswrittendowninthefile/lib/modules/<kernel_version>/modules.usbmap.Ifyouare
anexpert,youmaycommentoutthecorrespondingline,forittonotgetautoloaded.And

uncommentitback,onceyouaredonewithyourexperiments.
3. Inlatestdistros,youmaynotfindthedetaileddescriptionoftheUSBdevicesusingcat
/proc/bus/usb/devices,asthe/proc/bus/usb/itselfhasbeendeprecated.Youcanfindthesame
detailedinfousingcat/sys/kernel/debug/usb/devicesthoughyoumayneedrootpermissions
forthesame.Also,ifyoudonotseeanyfileunder/sys/kernel/debug(evenasroot),thenyou
mayhavetofirstmountthedebugfilesystem,asfollows:mounttdebugfsnone
/sys/kernel/debug

USBDriversinLinux(Continued)
2Replies

Thistwelftharticle,whichispartoftheseriesonLinuxdevicedrivers,getsyoufurtherwithwritingyour
firstUSBdriverinLinuxacontinuationfromthepreviousarticle.

<<EleventhArticle

Pugscontinued,LetsbuildupontheUSBdevicedrivercodedinourprevioussession,usingthesame
handyJetFlashpendrivefromTranscendwithvendorid0x058fandproductid0x6387.Forthat,we
woulddigfurtherintotheUSBprotocolandthenconvertthelearningintocode.

USBendpointsandtheirtypes
Dependingonthetypeandattributesofinformationtobetransferred,aUSBdevicemayhaveoneor
moreendpoints,eachbelongingtooneofthefollowingfourcategories:

ControlFortransferringcontrolinformation.Examplesincluderesettingthedevice,querying
informationaboutthedevice,etc.AllUSBdevicesalwayshavethedefaultcontrolendpointpoint
zero.
InterruptForsmallandfastdatatransfer,typicallyofupto8bytes.Examplesincludedata
transferforserialports,humaninterfacedevices(HIDs)likekeyboard,mouse,etc.
BulkForbigbutcomparativelyslowdatatransfer.Atypicalexampleisdatatransfersformass
storagedevices.
IsochronousForbigdatatransferwithbandwidthguarantee,thoughdataintegritymaynotbe
guaranteed.Typicalpracticalusageexamplesincludetransfersoftimesensitivedatalikeof
audio,video,etc.

Additionally,allbutcontrolendpointscouldbeinorout,indicatingitsdirectionofdatatransfer.in
indicatesdataflowfromUSBdevicetothehostmachineandoutindicatesdataflowfromthehost
machinetoUSBdevice.Technically,anendpointisidentifiedusinga8bitnumber,mostsignificantbit
(MSB)ofwhichindicatesthedirection0meaningout,and1meaningin.Controlendpointsarebi
directionalandtheMSBisignored.

Figure19:USBsprocwindowsnippet

Figure19showsatypicalsnippetofUSBdevicespecificationsfordevicesconnectedonasystem.To
bespecific,theE:linesinthefigureshowsexampleofaninterruptendpointofaUHCIHostController
andtwobulkendpointsofthependriveunderconsideration.Also,theendpointnumbers(inhex)are
respectively0x81,0x01,0x82.TheMSBofthefirstandthirdbeing1indicatinginendpoints,
representedby(I)inthefigure.Secondoneisan(O)fortheoutendpoint.MaxPSspecifiesthe
maximumpacketsize,i.e.thedatasizethatcanbetransferredinasinglego.Againasexpected,for
theinterruptendpointitis2(<=8),and64forthebulkendpoints.Ivlspecifiestheintervalin
millisecondstobegivenbetweentwoconsecutivedatapackettransfersforpropertransferandismore
significantfortheinterruptendpoints.

DecodingaUSBdevicesection
AswehavejustdiscussedtheE:line,itisrighttimetodecodetherelevantfieldsofothersaswell.In
short,theselinesinaUSBdevicesectiongivesthecompleteoverviewoftheUSBdeviceasperthe
USBspecifications,asdiscussedinourpreviousarticle.ReferbacktoFigure19.Thefirstletterofthe
firstlineofeverydevicesectionisaTindicatingthepositionofthedeviceintheUSBtree,uniquely
identifiedbythetriplet<usbbusnumber,usbtreelevel,usbport>.Drepresentsthedevicedescriptor

containingatleastthedeviceversion,deviceclass/category,andthenumberofconfigurationsavailable
forthisdevice.TherewouldbeasmanyClinesasthenumberofconfigurations,typicallyone.C
representstheconfigurationdescriptorcontainingitsindex,deviceattributesinthisconfiguration,
maximumpower(actuallycurrent)thedevicewoulddrawinthisconfiguration,andthenumberof
interfacesunderthisconfiguration.DependingonthattherewouldbeatleastthatmanyIlines.There
couldbemoreincaseofaninterfacehavingalternates,i.e.sameinterfacenumberbutwithdifferent
propertiesatypicalscenarioforwebcams.

Irepresentstheinterfacedescriptorwithitsindex,alternatenumber,functionalityclass/categoryof
thisinterface,driverassociatedwiththisinterface,andthenumberofendpointsunderthisinterface.
Theinterfaceclassmayormaynotbesameasthatofthedeviceclass.Anddependingonthenumber
ofendpoints,therewouldbeasmanyElines,detailsofwhichhavealreadybeendiscussedabove.
The*aftertheC&Irepresentsthecurrentlyactiveconfigurationandinterface,respectively.P
lineprovidesthevendorid,productid,andtheproductrevision.Slinesarestringdescriptorsshowing
upsomevendorspecificdescriptiveinformationaboutthedevice.

Peepinginto/proc/bus/usb/devicesisgoodtofigureoutwhetheradevicehasbeendetectedornot
andpossiblytogetthefirstcutoverviewofthedevice.Butmostprobablythisinformationwouldbe
requiredinwritingthedriverforthedeviceaswell.So,isthereawaytoaccessitusingCcode?,
askedShweta.Yesdefinitely,thatswhatIamgoingtotellyounext.Doyourememberthatassoonas
aUSBdeviceispluggedintothesystem,theUSBhostcontrollerdriverpopulatesitsinformationinto
thegenericUSBcorelayer?Tobeprecise,itputsthatintoasetofstructuresembeddedintoone
another,exactlyaspertheUSBspecifications,repliedPugs.Thefollowingaretheexactdata
structuresdefinedin<linux/usb.h>orderedhereinreverseforflowclarity:

structusb_device
{

...

structusb_device_descriptordescriptor;

structusb_host_config*config,*actconfig;

...

};
structusb_host_config
{

structusb_config_descriptordesc;

...

structusb_interface*interface[USB_MAXINTERFACES];

...

};
structusb_interface
{

structusb_host_interface*altsetting/*array*/,*cur_altsetting;

...

};
structusb_host_interface
{

structusb_interface_descriptordesc;

structusb_host_endpoint*endpoint/*array*/;

...

};
structusb_host_endpoint
{

structusb_endpoint_descriptordesc;

...

};

So,withaccesstothestructusb_devicehandleforaspecificdevice,alltheUSBspecificinformation
aboutthedevicecanbedecoded,asshownthroughthe/procwindow.Buthowtogetthedevice
handle?Infact,thedevicehandleisnotavailabledirectlyinadriver,rathertheperinterfacehandles
(pointerstostructusb_interface)areavailable,asUSBdriversarewrittenfordeviceinterfacesrather
thanthedeviceasawhole.Recallthattheprobe&disconnectcallbacks,whichareinvokedbyUSB
coreforeveryinterfaceoftheregistereddevice,havethecorrespondinginterfacehandleastheirfirst
parameter.Refertheprototypesbelow:

int(*probe)(structusb_interface*interface,conststructusb_device_id*id);
void(*disconnect)(structusb_interface*interface);

Sowiththeinterfacepointer,allinformationaboutthecorrespondinginterfacecanbeaccessed.Andto
getthecontainerdevicehandle,thefollowingmacrocomestorescue:

structusb_devicedevice=interface_to_usbdev(interface);

Addingthesenewlearningintothelastmonthsregistrationonlydriver,getsthefollowingcodelisting
(pen_info.c):

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/usb.h>

staticstructusb_device*device;
staticintpen_probe(structusb_interface*interface,conststructusb_device_id*id)
{

structusb_host_interface*iface_desc;

structusb_endpoint_descriptor*endpoint;

inti;

iface_desc=interface>cur_altsetting;

printk(KERN_INFO"Peni/f%dnowprobed:(%04X:%04X)\n",

iface_desc>desc.bInterfaceNumber,

id>idVendor,id>idProduct);

printk(KERN_INFO"ID>bNumEndpoints:%02X\n",

printk(KERN_INFO"ID>bInterfaceClass:%02X\n",

for(i=0;i<iface_desc>desc.bNumEndpoints;i++)

endpoint=&iface_desc>endpoint[i].desc;

printk(KERN_INFO"ED[%d]>bEndpointAddress:0x%02X\n",

printk(KERN_INFO"ED[%d]>bmAttributes:0x%02X\n",

printk(KERN_INFO"ED[%d]>wMaxPacketSize:0x%04X(%d)\n",

i,endpoint>wMaxPacketSize,

endpoint>wMaxPacketSize);

device=interface_to_usbdev(interface);

return0;

iface_desc>desc.bNumEndpoints);
iface_desc>desc.bInterfaceClass);

i,endpoint>bEndpointAddress);
i,endpoint>bmAttributes);

}
staticvoidpen_disconnect(structusb_interface*interface)
{

printk(KERN_INFO"Peni/f%dnowdisconnected\n",

interface>cur_altsetting>desc.bInterfaceNumber);

}
staticstructusb_device_idpen_table[]=
{

{USB_DEVICE(0x058F,0x6387)},

{}/*Terminatingentry*/

};
MODULE_DEVICE_TABLE(usb,pen_table);
staticstructusb_driverpen_driver=

.name="pen_info",

.probe=pen_probe,

.disconnect=pen_disconnect,

.id_table=pen_table,

};
staticint__initpen_init(void)
{

returnusb_register(&pen_driver);

}
staticvoid__exitpen_exit(void)
{

usb_deregister(&pen_driver);

}
module_init(pen_init);
module_exit(pen_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("USBPenInfoDriver");

Then,theusualstepsforanyLinuxdevicedrivermayberepeated,alongwiththependrivesteps:

Buildthedriver(pen_info.kofile)byrunningmake.
Loadthedriverusinginsmodpen_info.ko.
Pluginthependrive(aftermakingsurethatusbstoragedriverisnotalreadyloaded).
Unplugoutthependrive.
Checktheoutputofdmesgforthelogs.
Unloadthedriverusingrmmodpen_info.

Figure22showsasnippetoftheabovestepsonPugssystem.Remembertoensure(intheoutput
ofcat/proc/bus/usb/devices)thattheusualusbstoragedriverisnottheoneassociatedwiththepen
driveinterface,ratheritshouldbethepen_infodriver.

Figure22:Outputofdmesg

Summingup
Beforetakinganotherbreak,Pugssharedtwoofthemanymechanismsforadrivertospecifyitsdevice
totheUSBcore,usingthestructusb_device_idtable.Firstoneisbyspecifyingthe<vendorid,product
id>pairusingtheUSB_DEVICE()macro(asdoneabove),andthesecondoneisbyspecifyingthe
deviceclass/categoryusingtheUSB_DEVICE_INFO()macro.Infact,manymoremacrosareavailable
in<linux/usb.h>forvariouscombinations.Moreover,multipleofthesemacroscouldbespecifiedin
theusb_device_idtable(terminatedbyanullentry),formatchingwithanyoneofthecriteria,enabling
towriteasingledriverforpossiblymanydevices.

Earlieryoumentionedwritingmultipledriversforasingledevice,aswell.Basically,howdowe
selectivelyregisterornotregisteraparticularinterfaceofaUSBdevice?,queriedShweta.Sure.
Thatsnextinlineofourdiscussion,alongwiththeultimatetaskinanydevicedriverthedatatransfer
mechanismsrepliedPugs.

ThirteenthArticle>>

Notes
1. Makesurethatyoureplacethevendorid&deviceidintheabovecodeexamplesbytheones
ofyourpendrive.
2. Onemaywonder,ashowdoestheusbstoragegetautoloaded.Theanswerliesinthemodule
autoloadruleswrittendowninthefile/lib/modules/<kernel_version>/modules.usbmap.Ifyouare
anexpert,youmaycommentoutthecorrespondingline,forittonotgetautoloaded.And
uncommentitback,onceyouaredonewithyourexperiments.
3. Inlatestdistros,youmaynotfindthedetaileddescriptionoftheUSBdevicesusingcat
/proc/bus/usb/devices,asthe/proc/bus/usb/itselfhasbeendeprecated.Youcanfindthesame
detailedinfousingcat/sys/kernel/debug/usb/devicesthoughyoumayneedrootpermissions
forthesame.Also,ifyoudonotseeanyfileunder/sys/kernel/debug(evenasroot),thenyou
mayhavetofirstmountthedebugfilesystem,asfollows:mounttdebugfsnone
/sys/kernel/debug

USBDriversinLinux:DataTransferto&fromUSBDevices
12Replies

Thisthirteentharticle,whichispartoftheseriesonLinuxdevicedrivers,detailsouttheultimatestepof
datatransfertoandfromaUSBdeviceusingyourfirstUSBdriverinLinuxacontinuationfromthe
previoustwoarticles.

<<TwelfthArticle

USBmiscellany
Pugscontinued,Toansweryourquestionabouthowadriverselectivelyregistersorskipsaparticular
interfaceofaUSBdevice,youneedtounderstandthesignificanceofthereturnvalueofprobe()
callback.NotethattheUSBcorewouldinvokeprobeforalltheinterfacesofadetecteddevice,except
theoneswhicharealreadyregistered.So,forthefirsttime,itwouldcallforall.Now,iftheprobereturns
0,itmeansthedriverhasregisteredforthatinterface.Returninganerrorcodeindicatesnotregistering
forit.Thatsall.Thatwassimple,commentedShweta.

Now,letstalkabouttheultimatedatatransfersto&fromaUSBdevice,continuedPugs.Butbefore
thattellmewhatisthisMODULE_DEVICE_TABLE?ThisisbotheringmesinceyouexplainedtheUSB
deviceidtablemacros,askedShwetapausingPugs.Thatsanothertrivialstuff.Itismainlyforthe
userspacedepmod,saidPugs.Moduleisanothernameforadriver,whichisdynamicallyloadable
andunloadable.ThemacroMODULE_DEVICE_TABLEgeneratestwovariablesinamodulesreadonly
section,whichisextractedbydepmodandstoredinglobalmapfiles
under/lib/modules/<kernel_version>.modules.usbmapandmodules.pcimaparetwosuchfilesforUSB
&PCIdevicedrivers,respectively.Thisenablesautoloadingofthesedrivers,aswesawusbstorage
drivergettingautoloaded.

USBdatatransfer
TimeforUSBdatatransfers.LetsbuildupontheUSBdevicedrivercodedinourprevioussessions,
usingthesamehandyJetFlashpendrivefromTranscendwithvendorid0x058fandproductid
0x6387.

USBbeingahardwareprotocol,itformstheusualhorizontallayerinthekernelspace.Andhenceforit
toprovideaninterfacetouserspace,ithastoconnectthroughoneoftheverticallayers.Ascharacter
(driver)verticalisalreadydiscussed,itisthecurrentpreferredchoicefortheconnectionwiththeUSB

horizontal,forunderstandingthecompletedatatransferflow.Also,wedonotneedtogetafree
unreservedcharactermajornumber,butcanusethecharactermajornumber180,reservedforUSB
basedcharacterdevicefiles.Moreover,toachievethiscompletecharacterdriverlogicwithUSB
horizontalinonego,thefollowingaretheAPIsdeclaredin<linux/usb.h>:

intusb_register_dev(structusb_interface*intf,

structusb_class_driver*class_driver);

voidusb_deregister_dev(structusb_interface*intf,

structusb_class_driver*class_driver);

Usually,wewouldexpectthesefunctionstobeinvokedintheconstructorandthedestructorofa
module,respectively.However,toachievethehotplugnplaybehaviourforthe(character)devicefiles
correspondingtoUSBdevices,theseareinsteadinvokedintheprobeandthedisconnectcallbacks,
respectively.Firstparameterintheabovefunctionsistheinterfacepointerreceivedasthefirst
parameterinbothprobeanddisconnect.Secondparameterstructusb_class_driverneedstobe
populatedwiththesuggesteddevicefilenameandthesetofdevicefileoperations,before
invokingusb_register_dev().Fortheactualusage,refertothe
functionspen_probe()andpen_disconnect()inthecodelistingofpen_driver.cbelow.

Moreover,asthefileoperations(write,read,)arenowprovided,thatiswhereexactlyweneedtodo
thedatatransferstoandfromtheUSBdevice.So,pen_write()andpen_read()belowshowsthe
possiblecallstousb_bulk_msg()(prototypedin<linux/usb.h>)todothetransfersoverthependrives
bulkendpoints0x01and0x82,respectively.RefertotheElinesofthemiddlesectioninFigure19for
theendpointnumberlistingsofourpendrive.Refertotheheaderfile<linux/usb.h>underkernel
sources,forthecompletelistofUSBcoreAPIprototypesfortheotherendpointspecificdatatransfer
functionslikeusb_control_msg(),usb_interrupt_msg(),etc.usb_rcvbulkpipe(),usb_sndbulkpipe(),and
manysuchothermacros,alsodefinedin<linux/usb.h>,computetheactualendpointbitmasktobe
passedtothevariousUSBcoreAPIs.

Figure19:USBsprocwindowsnippet

NotethatapendrivebelongstoaUSBmassstorageclass,whichexpectsasetofSCSIlikecommands
tobetransactedoverthebulkendpoints.So,arawread/writeasshowninthecodelistingbelowmay
notreallydoadatatransferasexpected,unlessthedataisappropriatelyformatted.Butstill,this
summarizestheoverallcodeflowofaUSBdriver.TogetafeelofrealworkingUSBdatatransferina
simpleandelegantway,onewouldneedsomekindofcustomUSBdevice,somethingliketheone
availableateSrijan.

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/usb.h>
#defineMIN(a,b)(((a)<=(b))?(a):(b))
#defineBULK_EP_OUT0x01
#defineBULK_EP_IN0x82
#defineMAX_PKT_SIZE512
staticstructusb_device*device;
staticstructusb_class_driverclass;
staticunsignedcharbulk_buf[MAX_PKT_SIZE];
staticintpen_open(structinode*i,structfile*f)

return0;

}
staticintpen_close(structinode*i,structfile*f)
{

return0;

}
staticssize_tpen_read(structfile*f,char__user*buf,size_tcnt,loff_t*off)
{

intretval;

intread_cnt;

/*Readthedatafromthebulkendpoint*/

retval=usb_bulk_msg(device,usb_rcvbulkpipe(device,BULK_EP_IN),

if(retval)

printk(KERN_ERR"Bulkmessagereturned%d\n",retval);

returnretval;

if(copy_to_user(buf,bulk_buf,MIN(cnt,read_cnt)))

returnMIN(cnt,read_cnt);

bulk_buf,MAX_PKT_SIZE,&read_cnt,5000);

returnEFAULT;

}
staticssize_tpen_write(structfile*f,constchar__user*buf,size_tcnt,

loff_t*off)

intretval;

intwrote_cnt=MIN(cnt,MAX_PKT_SIZE);

if(copy_from_user(bulk_buf,buf,MIN(cnt,MAX_PKT_SIZE)))

/*Writethedataintothebulkendpoint*/

retval=usb_bulk_msg(device,usb_sndbulkpipe(device,BULK_EP_OUT),

if(retval)

printk(KERN_ERR"Bulkmessagereturned%d\n",retval);

returnretval;

returnwrote_cnt;

returnEFAULT;

bulk_buf,MIN(cnt,MAX_PKT_SIZE),&wrote_cnt,5000);

staticstructfile_operationsfops=
{

.open=pen_open,

.release=pen_close,

.read=pen_read,

.write=pen_write,

};
staticintpen_probe(structusb_interface*interface,conststructusb_device_id*id)
{

intretval;

device=interface_to_usbdev(interface);

class.name="usb/pen%d";

class.fops=&fops;

if((retval=usb_register_dev(interface,&class))<0)

/*Somethingpreventedusfromregisteringthisdriver*/

printk(KERN_ERR"Notabletogetaminorforthisdevice.");

else

returnretval;

printk(KERN_INFO"Minorobtained:%d\n",interface>minor);

}
staticvoidpen_disconnect(structusb_interface*interface)
{

usb_deregister_dev(interface,&class);

}
/*Tableofdevicesthatworkwiththisdriver*/
staticstructusb_device_idpen_table[]=
{

{USB_DEVICE(0x058F,0x6387)},

{}/*Terminatingentry*/

};
MODULE_DEVICE_TABLE(usb,pen_table);
staticstructusb_driverpen_driver=
{

.name="pen_driver",

.probe=pen_probe,

.disconnect=pen_disconnect,

.id_table=pen_table,

};
staticint__initpen_init(void)
{

intresult;

/*RegisterthisdriverwiththeUSBsubsystem*/

if((result=usb_register(&pen_driver)))

returnresult;

printk(KERN_ERR"usb_registerfailed.Errornumber%d",result);

}
staticvoid__exitpen_exit(void)
{

/*DeregisterthisdriverwiththeUSBsubsystem*/

usb_deregister(&pen_driver);

}
module_init(pen_init);
module_exit(pen_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("USBPenDeviceDriver");

Asareminder,theusualstepsforanyLinuxdevicedrivermayberepeatedwiththeabovecode,along
withthependrivesteps:

Buildthedriver(pen_driver.kofile)byrunningmake.
Loadthedriverusinginsmodpen_driver.ko.
Pluginthependrive(aftermakingsurethatusbstoragedriverisnotalreadyloaded).
Checkforthedynamiccreationof/dev/pen0.(0beingtheminornumberobtained
checkdmesglogsforthevalueonyoursystem)
Possiblytrysomewrite/readon/dev/pen0.(Thoughyoumaymostlygetconnectiontimeout
and/orbrokenpipeerrorsbecauseofnonconformantSCSIcommands)
Unplugoutthependriveandlookoutforgone/dev/pen0.
Unloadthedriverusingrmmodpen_driver.

Summingup
Meanwhile,PugshookeduphisfirstofitskindcreationtheLinuxdevicedriverkit(LDDK)intohis

systemtoshowalivedemonstrationoftheUSBdatatransfers.Aha!Finallyacoolcompleteworking
USBdriver,quippedexcitedShweta.Wanttohavemorefun.Wecoulddoablockdriveroverit,
addedPugs.O!Really,Shwetaaskedwithagleeonherface.Yes.Butbeforethatwewouldneedto
understandthepartitioningmechanisms,commentedPugs.

FourteenthArticle>>

Notes
1. Makesurethatyoureplacethevendorid&deviceidintheabovecodeexamplesbytheones
ofyourpendrive.Also,makesurethattheendpointnumbersusedintheabovecodeexamples
matchtheendpointnumbersofyourpendrive.Otherwise,youmaygetanerrorlikebulk
messagereturnederror22Invalidargument,whilereadingfrompendevice.
2. Also,makesurethatthedriverfromthepreviousarticleisunloaded,i.e.pen_infoisnotloaded.
Otherwise,itmaygiveanerrormessageinsmod:errorinsertingpen_driver.ko':1Deviceor
resourcebusy,whiledoinginsmodpen_driver.ko.
3. Onemaywonder,ashowdoestheusbstoragegetautoloaded.Theanswerliesinthemodule
autoloadruleswrittendowninthefile/lib/modules/<kernel_version>/modules.usbmap.Ifyouare
anexpert,youmaycommentoutthecorrespondingline,forittonotgetautoloaded.And
uncommentitback,onceyouaredonewithyourexperiments.
4. Inlatestdistros,youmaynotfindthedetaileddescriptionoftheUSBdevicesusingcat
/proc/bus/usb/devices,asthe/proc/bus/usb/itselfhasbeendeprecated.Youcanfindthesame
detailedinfousingcat/sys/kernel/debug/usb/devicesthoughyoumayneedrootpermissions
forthesame.Also,ifyoudonotseeanyfileunder/sys/kernel/debug(evenasroot),thenyou
mayhavetofirstmountthedebugfilesystem,asfollows:mounttdebugfsnone
/sys/kernel/debug.

UnderstandingthePartitions:Adiveinsidetheharddisk
2Replies

Thisfourteentharticle,whichispartoftheseriesonLinuxdevicedrivers,takesyouforawalkinsidea
harddisk.

<<ThirteenthArticle

Harddiskdesign
Doesntitsoundlikeamechanicalengineeringsubject:Designofharddisk?,questionedShweta.
Yes,itdoes.Butunderstandingitgetsusaninsightintoitsprogrammingaspect,reasonedPugswhile
waitingforthecommencementoftheseminaronstoragesystems.

Figure23:Partitionlistingbyfdisk

Theseminarstartedwithafewharddisksinthepresentershandandthenadivedownintohersystem

showingtheoutputoffdiskl,asshowninFigure23.Thefirstlineshowstheharddisksizeinhuman
friendlyformatandinbytes.Thesecondlinementionsthenumberoflogicalheads,logicalsectorsper
track,andtheactualnumberofcylindersonthediskthesetogetherarereferredasthegeometryof
thedisk.The255headsindicatingthenumberofplattersordisks,asonereadwriteheadisneeded
perdisk.LetsnumberthemsayD1,D2,,D255.Now,eachdiskwouldhavethesamenumberof
concentriccirculartracks,startingfromoutsidetoinside.Intheabovecasethereare60801suchtracks
perdisk.LetsnumberthemsayT1,T2,,T60801.Andaparticulartracknumberfromallthedisks
formsacylinderofthesamenumber.Forexample,tracksT2fromD1,D2,,D255willalltogether
formthecylinderC2.Now,eachtrackhasthesamenumberoflogicalsectors63inourcase,sayS1,
S2,,S63.Andeachsectoristypically512bytes.Giventhisdata,onecanactuallycomputethetotal
usableharddisksize,usingthefollowingformula:

Usableharddisksizeinbytes=(Numberofheadsordisks)*(Numberoftracksperdisk)*(Numberof
sectorspertrack)*(Numberofbytespersector,i.e.sectorsize).

Forthediskunderconsiderationitwouldbe:255*60801*63*512bytes=500105249280bytes.Note
thatthisnumbermaybeslightlylessthantheactualharddisk500107862016bytesinourcase.The
reasonforthatisthattheformuladoesntconsiderthebytesinlastpartialorincompletecylinder.And
theprimaryreasonforthatisthedifferencebetweentodaystechnologyoforganizingtheactual
physicaldiskgeometryandthetraditionalgeometryrepresentationusingheads,cylinders,sectors.
Notethatinthefdiskoutput,wereferredtotheheads,andsectorspertrackaslogicalnottheactual
one.Onemayask,iftodaysdisksdoesnthavesuchphysicalgeometryconcepts,thenwhytostill
maintainthatandrepresenttheminmoreoflogicalform.Themainreasonistobeabletocontinuewith
sameconceptsofpartitioningandbeabletomaintainthesamepartitiontableformatsespeciallyforthe
mostprevalentDOStypepartitiontables,whichheavilydependonthissimplisticgeometry.Notethe
computationofcylindersize(255heads*63sectors/track*512bytes/sector=8225280bytes)inthe
thirdlineandthenthedemarcationofpartitionsinunitsofcompletecylinders.

DOStypepartitiontables
ThisbringsustothenextimportanttopicofunderstandingtheDOStypepartitiontables.Butinthefirst
place,whatisapartitionandratherwhytoevenpartition?Aharddiskcanbedividedintoonemore
logicaldisks,eachofwhichiscalledapartition.Andthisisusefulfororganizingdifferenttypesofdata
separately.Forexample,differentoperatingsystemdata,userdata,temporarydata,etc.So,partitions
arebasicallylogicaldivisionsandhenceneedtobemaintainedthroughsomemetadata,whichisthe
partitiontable.ADOStypepartitiontablecontains4partitionentries,eachbeinga16byteentry.And
eachoftheseentriescanbedepictedbythefollowingCstructure:

typedefstruct
{

unsignedcharboot_type;//0x00Inactive;0x80Active(Bootable)

unsignedcharstart_head;

unsignedcharstart_sec:6;

unsignedcharstart_cyl_hi:2;

unsignedcharstart_cyl;

unsignedcharpart_type;

unsignedcharend_head;

unsignedcharend_sec:6;

unsignedcharend_cyl_hi:2;

unsignedcharend_cyl;

unsignedintabs_start_sec;

unsignedintsec_in_part;

}PartEntry;

Andthispartitiontablefollowedbythetwobytesignature0xAA55residesattheendofharddisksfirst
sector,commonlyknownasMasterBootRecordorMBR(inshort).Hence,thestartingoffsetofthis
partitiontablewithintheMBRis512(4*16+2)=446.Also,a4bytedisksignatureisplacedatthe
offset440.Theremainingtop440bytesoftheMBRaretypicallyusedtoplacethefirstpieceofboot
code,thatisloadedbytheBIOStobootupthesystemfromthedisk.Listingofpart_info.ccontains
thesevariousdefines,andthecodeforparsingandprintingaformattedoutputofthepartitiontableof
onesharddisk.

Fromthepartitiontableentrystructure,itcouldbenotedthatthestartandendcylinderfieldsareonly
10bitslong,thusallowingamaximumof1023cylindersonly.However,fortodayshugeharddisks,this
sizeisnowaysufficient.Andhenceinoverflowcases,thecorresponding<head,cylinder,sector>triplet
inthepartitiontableentryissettothemaximumvalue,andtheactualvalueiscomputedusingthelast
twofields:theabsolutestartsectornumber(abs_start_sec)andthenumberofsectorsinthispartition
(sec_in_part).Listingofpart_info.ccontainsthecodeforthisaswell.

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#defineMBR_DISK_SIGNATURE_OFFSET440
#definePARTITION_ENTRY_SIZE16//sizeof(PartEntry)
#definePARTITION_TABLE_SIZE(4*PARTITION_ENTRY_SIZE)

typedefstruct
{

unsignedcharboot_type;//0x00Inactive;0x80Active(Bootable)

unsignedcharstart_head;

unsignedcharstart_sec:6;

unsignedcharstart_cyl_hi:2;

unsignedcharstart_cyl;

unsignedcharpart_type;

unsignedcharend_head;

unsignedcharend_sec:6;

unsignedcharend_cyl_hi:2;

unsignedcharend_cyl;

unsignedintabs_start_sec;

unsignedintsec_in_part;

}PartEntry;
typedefstruct
{

unsignedcharboot_code[MBR_DISK_SIGNATURE_OFFSET];

unsignedintdisk_signature;

unsignedshortpad;

unsignedcharpt[PARTITION_TABLE_SIZE];

unsignedshortsignature;

}MBR;
voidprint_computed(unsignedintsector)
{

unsignedintheads,cyls,tracks,sectors;

sectors=sector%63+1/*Asindexedfrom1*/;

tracks=sector/63;

cyls=tracks/255+1/*Asindexedfrom1*/;

heads=tracks%255;

printf("(%3d/%5d/%1d)",heads,cyls,sectors);

}
intmain(intargc,char*argv[])
{

char*dev_file="/dev/sda";

intfd,i,rd_val;

MBRm;

PartEntry*p=(PartEntry*)(m.pt);

if(argc==2)

if((fd=open(dev_file,O_RDONLY))==1)

dev_file=argv[1];

fprintf(stderr,"Failedopening%s:",dev_file);

perror("");

return1;

if((rd_val=read(fd,&m,sizeof(m)))!=sizeof(m))

fprintf(stderr,"Failedreading%s:",dev_file);

perror("");

close(fd);

return2;

close(fd);

printf("\nDOStypePartitionTableof%s:\n",dev_file);

printf("BStart(H/C/S)End(H/C/S)TypeStartSecTotSec\n");

for(i=0;i<4;i++)

printf("%d:%d(%3d/%4d/%2d)(%3d/%4d/%2d)%02X%10d%9d\n",

i+1,!!(p[i].boot_type&0x80),

p[i].start_head,

1+((p[i].start_cyl_hi<<8)|p[i].start_cyl),

p[i].start_sec,

p[i].end_head,

1+((p[i].end_cyl_hi<<8)|p[i].end_cyl),

p[i].end_sec,

p[i].part_type,

p[i].abs_start_sec,p[i].sec_in_part);

printf("\nRecomputedPartitionTableof%s:\n",dev_file);

printf("BStart(H/C/S)End(H/C/S)TypeStartSecTotSec\n");

for(i=0;i<4;i++)

printf("%d:%d",i+1,!!(p[i].boot_type&0x80));

print_computed(p[i].abs_start_sec);

printf("");

print_computed(p[i].abs_start_sec+p[i].sec_in_part1);

printf("%02X%10d%9d\n",p[i].part_type,

printf("\n");

return0;

p[i].abs_start_sec,p[i].sec_in_part);

Astheabovecode(part_info.c)isanapplication,compileittoanexecutable(./part_info)asfollows:gcc
part_info.copart_info,andthenrun./part_info/dev/sdatocheckoutyourprimarypartitioning
informationon/dev/sda.Figure24showstheoutputof./part_infoonthepresenterssystem.Compare
itwiththefdiskoutputasshowninfigure23.

Figure24:Outputof./part_info

PartitiontypesandBootrecords
Nowasthispartitiontableishardcodedtohave4entries,thatdictatesthemaximumnumberof
partitions,wecanhave,namely4.Thesearecalledprimarypartitions,eachhavingatypeassociated
withthemintheircorrespondingpartitiontableentry.Thevarioustypesaretypicallycoinedbythe
variousoperatingsystemvendorsandhenceitisasortofmappingtothevariousoperatingsystems,
forexample,DOS,Minix,Linux,Solaris,BSD,FreeBSD,QNX,W95,NovellNetware,etctobeused
for/withtheparticularoperatingsystem.However,thisismoreofegoandformalitythanareal
requirement.

Apartfromthese,oneofthe4primarypartitionscanactuallybelabeledassomethingreferredasan
extendedpartition,andthathasaspecialsignificance.Asnamesuggests,itisusedtofurtherextend
theharddiskdivision,i.e.tohavemorepartitions.Thesemorepartitionsarereferredaslogical
partitionsandarecreatedwithintheextendedpartition.Metadataofthelogicalpartitionsismaintained
inalinkedlistformat,allowingunlimitednumberoflogicalpartitions,atleasttheoretically.Forthat,the
firstsectoroftheextendedpartition,commonlyreferredtoasBootRecordorBR(inshort),isusedina

similarwayasMBRtostore(thelinkedlistheadof)thepartitiontableforthelogicalpartitions.
Subsequentlinkedlistnodesarestoredinthefirstsectorofthesubsequentlogicalpartitions,referred
toasLogicalBootRecordorLBR(inshort).Eachlinkedlistnodeisacomplete4entrypartitiontable,
thoughonlythefirsttwoentriesareusedthefirstforthelinkedlistdata,namely,informationaboutthe
immediatelogicalpartition,andsecondoneasthelinkedlistsnextpointer,pointingtothelistof
remaininglogicalpartitions.

Tocompareandunderstandtheprimarypartitioningdetailsonyoursystemsharddisk,followthese
steps(asrootuserandhencewithcare):

./part_info/dev/sda#Displaysthepartitiontableon/dev/sda
fdiskl/dev/sda#Todisplayandcomparethepartitiontableentrieswiththeabove

Incaseyouhavemultipleharddisks(/dev/sdb,),orharddiskdevicefilewithothernames(/dev/hda,
),oranextendedpartition,youmaytry./part_info<device_file_name>onthemaswell.Tryingonan
extendedpartitionwouldgivetheinformationaboutthestartingpartitiontableofthelogicalpartitions.

Summingup
Rightnowwecarefullyandselectivelyplayed(readonly)withoursystemsharddisk.Whycarefully?
Asotherwise,wemayrenderoursystemnonbootable.Butnolearningiscompletewithoutatotal
exploration.Hence,inourpostlunchsession,wewouldcreateadummydiskonRAManddothe
destructiveexplorations.

FifteenthArticle>>

DiskonRAM:Playingdestructively
6Replies

Thisfifteentharticle,whichispartoftheseriesonLinuxdevicedrivers,experimentswithadummyhard
diskonRAMtodemonstratetheblockdrivers.

<<FourteenthArticle

Playfirst,ruleslater
Afteradeliciouslunch,theorymakesaudiencesleepy.So,letsstartwiththecodeitself.Code
demonstratedisavailableatdor_code.tar.bz2.Thistarballcontains3Csourcefiles,2Cheaders,
andaMakefile.Asusual,executingmakewillbuildthediskonramdriver(dor.ko)thistime
combiningthe3Cfiles.CheckouttheMakefiletoseehow.makecleanwoulddotheusualcleanof
thebuiltstuff.

Oncebuilt,thefollowingaretheexperimentingsteps(RefertoFigures25,26,27):

Loadthedriverdor.kousinginsmod.Thiswouldcreatetheblockdevicefilesrepresentingthe
diskon512KibiBytes(KiB)ofRAM,with3primaryand3logicalpartitions.
Checkouttheautomaticallycreatedblockdevicefiles(/dev/rb*)./dev/rbistheentirediskof512
KiBsize.rb1,rb2,rb3aretheprimarypartitionswithrb2beingtheextendedpartitionand
containingthe3logicalpartitionsrb5,rb6,rb7.
Readtheentiredisk(/dev/rb)usingthediskdumputilitydd.
Zerooutthefirstsectorofthedisksfirstpartition(/dev/rb1)againusingdd.
Writesometextintothedisksfirstpartition(/dev/rb1)usingcat.
Displaytheinitialcontentsofthefirstpartition(/dev/rb1)usingthexxdutility.SeeFigure26for
thexxdoutput.
Displaythepartitioninfoforthediskusingfdisk.SeeFigure27forthefdiskoutput.
(Quick)Formatthethirdprimarypartition(/dev/rb3)asvfatfilesystem(likeyourpendrive),
usingmkfs.vfat(Figure27).
Mountthenewlyformattedpartitionusingmount,sayat/mnt(Figure27).
Diskusageutilitydfwouldnowshowthispartitionmountedat/mnt(Figure27).Youmaygo
aheadandstoreyourfilesthere.But,pleaserememberthatthesepartitionsareallonadiskon
RAM,andsononpersistent.Hence,
Unloadingthedriverusingrmmoddorwouldvanisheverything.Thoughthepartitionneedsto
beunmountedusingumount/mntbeforedoingthat.

Pleasenotethatalltheaboveexperimentingstepsneedtobeexecutedwithrootprivileges.

Figure25:PlayingwithDiskonRAMdriver

Figure26:xxdshowingtheinitialdataonthefirstpartition(/dev/rb1)

Figure27:Formattingthethirdpartition(/dev/rb3)

Now,letslearntherules
WehavejustnowplayedaroundwiththediskonRAMbutwithoutactuallyknowingtherules,i.e.the
internaldetailsofthegame.So,letsdigintothenittygrittiestodecodetherules.Eachofthethree.c
filesrepresentaspecificpartofthedriver.ram_device.candram_device.habstracttheunderlyingRAM
operationslikevmalloc/vfree,memcpy,etc,providingdiskoperationAPIslikeinit/cleanup,read/write,
etc.partition.candpartition.hprovidethefunctionalitytoemulatethevariouspartitiontablesonthedisk
onRAM.Recalltheprelunchsession(i.e.thepreviousarticle)tounderstandthedetailsofpartitioning.
Thecodeinthisisresponsibleforthepartitioninformationlikenumber,type,size,etcthatisshownup
onthediskonRAMusingfdisk.ram_block.cisthecoreblockdriverimplementationexposingthedisk
onRAMastheblockdevicefiles(/dev/rb*)totheuserspace.Inotherwords,thefour
filesram_device.*andpartition.*formthehorizontallayerofthedevicedriverandram_block.cformsthe
vertical(block)layerofthedevicedriver.So,letsunderstandthatindetail.

Theblockdriverbasics

Conceptually,theblockdriversareverysimilartocharacterdrivers,especiallywithregardstothe
following:

Usageofdevicefiles
Majorandminornumbers
Devicefileoperations
Conceptofdeviceregistration

So,ifonealreadyknowscharacterdriverimplementations,itwouldbesimilartounderstandtheblock
drivers.Though,theyaredefinitelynotidentical.Thekeydifferencescouldbelistedoutasfollows:

Abstractionforblockorientedversusbyteorienteddevices
BlockdriversaredesignedtobeusedbyI/Oschedulers,foroptimalperformance.Comparethat
withcharacterdriverstobeusedbyVFS.
BlockdriversaredesignedtobeintegratedwiththeLinuxbuffercachemechanismforefficient
dataaccess.Characterdriversarepassthroughdrivers,accessingthehardwaredirectly.

Andthesetriggertheimplementationdifferences.Letsanalyzethekeycodesnippets
fromram_block.c,startingatthedriversconstructorrb_init().

Firststepistoregisterfora8bit(block)majornumber.Andregisteringforthatimplicitlymeans
registeringforallthe2568bitminornumbersassociatedwiththat.Thefunctionforthatis:

intregister_blkdev(unsignedintmajor,constchar*name);

majoristhemajornumbertoberegistered.nameisaregistrationlabeldisplayedunderthekernel
window/proc/devices.Interestingly,register_blkdev()triestoallocate&registerafreelyavailablemajor
number,when0ispassedforitsfirstparametermajorandonsuccess,theallocatedmajornumberis
returned.Thecorrespondingderegistrationfunctionis:

voidunregister_blkdev(unsignedintmajor,constchar*name);

Bothareprototypedin<linux/fs.h>

Secondstepistoprovidethedevicefileoperations,throughthestruct

block_device_operations(prototypedin<linux/blkdev.h>)fortheregisteredmajornumberdevicefiles.
However,theseoperationsaretoofewcomparedtothecharacterdevicefileoperations,andmostly
insignificant.Toelaborate,therearenooperationseventoreadandwrite.Thatssurprising.Butaswe
alreadyknowthattheblockdriversneedtointegratewiththeI/Oschedulers,thereadwrite
implementationisachievedthroughsomethingcalledrequestqueues.So,alongwithprovidingthe
devicefileoperations,thefollowingneedstoprovided:

Requestqueueforqueuingtheread/writerequests
Spinlockassociatedwiththerequestqueueforitsconcurrentaccessprotection
Requestfunctiontoprocesstherequestsqueuedintherequestqueue

Also,thereisnoseparateinterfaceforblockdevicefilecreations,sothefollowingarealsoprovided:

Devicefilenameprefix,commonlyreferredasdisk_name(rbinthedordriver)
Startingminornumberforthedevicefiles,commonlyreferredasthefirst_minor

Finally,twoblockdevicespecificthingsarealsoprovidedalongwiththeabove,namely:

Maximumnumberofpartitionssupportedforthisblockdevice,byspecifyingthetotalminors
Underlyingdevicesizeinunitsof512bytesectors,forthelogicalblockaccessabstraction

Alltheseareregisteredthroughthestructgendiskusingthefunction:

voidadd_disk(structgendisk*disk);

Thecorrespondingdeletefunctionis:

voiddel_gendisk(structgendisk*disk);

Priortoadd_disk(),thevariousfieldsofstructgendiskneedtobeinitialized,eitherdirectlyorusing
variousmacros/functionslikeset_capacity().major,first_minor,fops,queue,disk_namearetheminimal
fieldstobeinitializeddirectly.Andevenbeforetheinitializationofthesefields,thestructgendiskneeds
tobeallocatedusingthefunction:

structgendisk*alloc_disk(intminors);

whereminorsisthetotalnumberofpartitionssupportedforthisdisk.Andthecorrespondinginverse
functionwouldbe:

voidput_disk(structgendisk*disk);

Alltheseareprototypedin<linux/genhd.h>.

Requestqueueanditsrequestprocessingfunction
Therequestqueuealsoneedstobeinitializedandsetupintothestructgendisk,beforetheadd_disk().
Therequestqueueisinitializedbycalling:

structrequest_queue*blk_init_queue(request_fn_proc*,spinlock_t*);

providingtherequestprocessingfunctionandtheinitializedconcurrencyprotectionspinlock,asits
parameters.Thecorrespondingqueuecleanupfunctionis:

voidblk_cleanup_queue(structrequest_queue*);

Therequest(processing)functionshouldbedefinedwiththefollowingprototype:

voidrequest_fn(structrequest_queue*q);

Anditshouldbecodedtofetcharequestfromitsparameterq,sayusing

structrequest*blk_fetch_request(structrequest_queue*q);

andtheneitherprocessitorinitiatetheprocessing.Whateveritdoes,itshouldbenonblocking,asthis
requestfunctioniscalledfromanonprocesscontext,andalsoaftertakingthequeuesspinlock.So,
moreoveronlythefunctionsnotreleasingortakingthequeuesspinlockshouldbeusedwithinthe
requestfunction.

Atypicalrequestprocessingasdemonstratedbythefunctionrb_request()inram_block.cis:

while((req=blk_fetch_request(q))!=NULL)/*Fetchingarequest*/
{

/*Processingtherequest:theactualdatatransfer*/

ret=rb_transfer(req);/*Ourcustomfunction*/

/*Informingthattherequesthasbeenprocessedwithreturnofret*/

__blk_end_request_all(req,ret);

Requestanditsprocessing
rb_transfer()isourkeyfunction,whichparsesastructrequestandaccordinglydoestheactualdata
transfer.Thestructrequestmainlycontainsthedirectionofdatatransfer,startingsectorforthedata
transfer,totalnumberofsectorsforthedatatransfer,andthescattergatherbufferfordatatransfer.
Thevariousmacrostoextracttheseinformationfromthestructrequestareasfollows:

rq_data_dir(req);/*Operation:0readfromdevice;otherwisewritetodevice*/
blk_req_pos(req);/*Startingsectortoprocess*/
blk_req_sectors(req);/*Totalsectorstoprocess*/
rq_for_each_segment(bv,req,iter)/*Iteratortoextractindividualbuffers*/

rq_for_each_segment()isthespecialonewhichiteratesoverthestructrequest(req)usingiter,and
extractingtheindividualbufferinformationintothestructbio_vec(bv:basicinput/outputvector)oneach
iteration.And,thenoneachextraction,theappropriatedatatransferisdone,basedontheoperation
type,invokingoneofthefollowingAPIsfromram_device.c:

voidramdevice_write(sector_tsector_off,u8*buffer,unsignedintsectors);
voidramdevice_read(sector_tsector_off,u8*buffer,unsignedintsectors);

Checkoutthecompletecodeofrb_transfer()inram_block.c

Summingup
Withthat,wehaveactuallylearntthebeautifulblockdriversbytraversingthroughthedesignofahard
disk,andplayingaroundwithpartitioning,formatting,andvariousotherrawoperationsonaharddisk.
Thanksforyourpatientlistening.Now,thesessionisopenforquestions.Or,youmaypostyourqueries
ascomments,below.

SixteenthArticle>>

KernelWindow:Peepingthrough/proc
5Replies

Thissixteentharticle,whichispartoftheseriesonLinuxdevicedrivers,demonstratesthecreationand
usageoffilesunderthe/procvirtualfilesystem.

<<FifteenthArticle

Usefulkernelwindows
Aftermanymonths,ShwetaandPugsgottogetherforapeacefultechnicalromancing.Allthrough,we
havebeenusingallkindsofkernelwindowsespeciallythroughthe/procvirtualfilesystem(usingcat),
tohelpusoutindecodingthevariousnittygrittiesofLinuxdevicedrivers.Heresanonexhaustive
summarylisting:

/proc/modulesListingofallthedynamicallyloadedmodules
/proc/devicesListingofalltheregisteredcharacterandblockmajornumbers
/proc/iomemListingofonsystemphysicalRAM&busdeviceaddresses
/proc/ioportsListingofonsystemI/Oportaddresses(speciallyforx86systems)
/proc/interruptsListingofalltheregisteredinterruptrequestnumbers
/proc/softirqsListingofalltheregisteredsoftirqs
/proc/kallsymsListingofalltherunningkernelsymbols,includingfromloadedmodules
/proc/partitionsListingofcurrentlyconnectedblockdevices&theirpartitions
/proc/filesystemsListingofcurrentlyactivefilesystemdrivers
/proc/swapsListingofcurrentlyactiveswaps
/proc/cpuinfoInformationabouttheCPU(s)onthesystem
/proc/meminfoInformationaboutthememoryonthesystem,viz.RAM,swap,

Customkernelwindows
Yes,thesehavebeenreallyhelpfulinunderstandinganddebuggingtheLinuxdevicedrivers.Butisit
possibleforustoalsoprovidesomehelp?Yes,Imeancanwecreateonesuchkernelwindow
through/proc?,questionedShweta.

Whyone?Asmanyasyouwant.AndthatssimplejustusetherightsetofAPIsandthereyougo.

Foryoueverythingissimple.

Noyaar,thisisseriouslysimple,smiledPugs.Justwatchout,mecreatingoneforyou.

Andinjiffies,Pugscreatedtheproc_window.cfilebelow(includingthechanges,whichhastakenplace
sincekernelv3.10):

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/version.h>
#if(LINUX_VERSION_CODE<KERNEL_VERSION(3,10,0))
#else
#include<linux/fs.h>
#include<linux/seq_file.h>
#endif
#include<linux/proc_fs.h>
#include<linux/jiffies.h>
#include<linux/uaccess.h>
#if(LINUX_VERSION_CODE<KERNEL_VERSION(3,10,0))
#defineSTR_PRINTF(str,args...)sprintf(page+len,str,##args);
#else
#defineSTR_PRINTF(str,args...)seq_printf(m,str,##args);
#endif
staticstructproc_dir_entry*parent,*file,*link;
staticintstate=0;
#if(LINUX_VERSION_CODE<KERNEL_VERSION(3,10,0))
staticinttime_read(char*page,char**start,off_toff,intcount,int*eof,

void*data)

#else
staticinttime_read(structseq_file*m,void*v)
#endif
{

intlen=0,val;

unsignedlongact_jiffies;

len+=STR_PRINTF("state=%d\n",state);

act_jiffies=jiffiesINITIAL_JIFFIES;

val=jiffies_to_msecs(act_jiffies);

switch(state)

case0:

len+=STR_PRINTF("time=%ldjiffies\n",act_jiffies);

break;

case1:

len+=STR_PRINTF("time=%dmsecs\n",val);

break;

case2:

len+=STR_PRINTF("time=%ds%dms\n",

break;

case3:

val/=1000;

len+=STR_PRINTF("time=%02d:%02d:%02d\n",

break;

default:

len+=STR_PRINTF("<notimplemented>\n");

break;

returnlen;

val/1000,val%1000);

val/3600,(val/60)%60,val%60);

}
#if(LINUX_VERSION_CODE<KERNEL_VERSION(3,10,0))
staticinttime_write(structfile*file,constchar__user*buffer,

unsignedlongcount,void*data)

#else
staticssize_ttime_write(structfile*file,constchar__user*buffer,size_tcount,

loff_t*off)

#endif
{

charkbuf[2];

if(count>2)

if(copy_from_user(kbuf,buffer,count))

if((count==2)&&(kbuf[1]!='\n'))

if((kbuf[0]<'0')||('9'<kbuf[0]))

state=kbuf[0]'0';

returncount;

returncount;

returnEFAULT;

returncount;
returncount;

}
#if(LINUX_VERSION_CODE<KERNEL_VERSION(3,10,0))
#else
staticinttime_open(structinode*inode,structfile*file)
{

returnsingle_open(file,time_read,NULL);

}
staticstructfile_operationsfops=
{

.open=time_open,

.read=seq_read,

.write=time_write

};
#endif
staticint__initproc_win_init(void)
{

if((parent=proc_mkdir("anil",NULL))==NULL)

return1;

#if(LINUX_VERSION_CODE<KERNEL_VERSION(3,10,0))

if((file=create_proc_entry("rel_time",0666,parent))==NULL)

#else

if((file=proc_create("rel_time",0666,parent,&fops))==NULL)

#endif

remove_proc_entry("anil",NULL);

return1;

#if(LINUX_VERSION_CODE<KERNEL_VERSION(3,10,0))

file>read_proc=time_read;

file>write_proc=time_write;

#endif

if((link=proc_symlink("rel_time_l",parent,"rel_time"))==NULL)

remove_proc_entry("rel_time",parent);

remove_proc_entry("anil",NULL);

return1;

#if(LINUX_VERSION_CODE<KERNEL_VERSION(3,10,0))

link>uid=0;

link>gid=100;

#else

proc_set_user(link,KUIDT_INIT(0),KGIDT_INIT(100));

#endif

return0;

}
staticvoid__exitproc_win_exit(void)
{

remove_proc_entry("rel_time_l",parent);

remove_proc_entry("rel_time",parent);

remove_proc_entry("anil",NULL);

}
module_init(proc_win_init);
module_exit(proc_win_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("Kernelwindow/procDemonstrationDriver");

AndthenPugsdidthefollowingsteps:

Builtthedriver(proc_window.kofile)usingtheusualdriversMakefile.
Loadedthedriverusinginsmod.
Showedvariousexperimentsusingthenewlycreatedprocwindows.(RefertoFigure28.)
Andfinally,unloadedthedriverusingrmmod.

Figure28:Peepingthrough/proc

Demystifyingthedetails
Startingfromtheconstructorproc_win_init(),threeprocentriesarecreated:

Directoryanilunder/proc(i.e.NULLparent)withdefaultpermissions0755,usingproc_mkdir()
Regularfilerel_timeundertheabovedirectoryanilwithpermissions0666,
usingcreate_proc_entry()orproc_create()(sincekernelv3.10)
Softlinkrel_time_ltotheabovefilerel_timeunderthesamedirectoryanil,usingproc_symlink()

Thecorrespondingremovalofthesethreeisachievedusingremove_proc_entry()inthe

destructorproc_win_exit(),inchronologicalreverseorder.

Foreveryentrycreatedunder/proc,acorrespondingstructproc_dir_entryiscreated.Afterwhichmany
ofitsfieldscouldbefurtherupdatedasneeded:

modePermissionsofthefile
uidUserIDofthefile
gidGroupIDofthefile

Sincekernelv3.10,specificAPIproc_set_user()hasbeenprovidedtoupdatetheuid&gid,insteadof
directlymodifyingthefields.

Additionally,foraregularfile,thefollowingtwofunctionpointersforreadandwriteoverthefilecouldbe
provided,respectively:

int(*read_proc)(char*page,char**start,off_toff,intcount,int*eof,void*data)
int(*write_proc)(structfile*file,constchar__user*buffer,unsignedlongcount,void*data)

Again,sincekernelv3.10,theread&writefieldsofstructfile_operationsaretobeusedrespectively,
insteadoftheabovetwo.

write_proc()isverysimilartothecharacterdevicefileoperationwrite.Andtheabovecode
implementation,letstheusertowriteadigitfrom0to9,andaccordinglysetstheinternalstate.

read_proc()intheabovecodeimplementationprovidesthecurrentstateandthetimesincethesystem
hasbeenbootedupindifferentunitsbasedonthecurrentstate.Theseare,jiffiesinstate0
millisecondsinstate1seconds&millisecondsinstate2hours:minutes:secondsinstate3and<not
implemented>inotherstates.Andtocheckthecomputationaccuracy,Figure29highlightsthesystem
uptimeintheoutputoftop.read_proc()spageparameterisapagesizedbuffer,typicallytobefilledup
withcountbytesfromoffsetoff.Butmoreoftenthannot(becauseofsmallcontent),justpageisfilled
up,ignoringallotherparameters.Sincekernelv3.10,thereadlogichastobeimplementedthrough
somethingcalledasequencefile,whichisimplementedbyspecifyingthepre
definedseq_read()functionforthefileoperationread,andthenbyprovidingtheabovelogicinthe
sequencefilesreadfunctionthroughthefileoperationopenusingsingle_open().

Figure29:Comparisonwithtopsoutput

Allthe/procrelatedstructuredefinitionsandfunctiondeclarationsareavailable
through<linux/proc_fs.h>.Thesequencefilerelatedstuff(sincekernelv3.10)areavailable
through<linux/seq_file.h>.Andthejiffiesrelatedfunctiondeclarationsandmacrodefinitionsare
in<linux/jiffies.h>.Onaspecialnote,theactualjiffiesarebeingcalculatedby
subtractingINITIAL_JIFFIES,asonbootup,jiffiesisinitializedtoINITIAL_JIFFIESinsteadofzero.

Summingup
HeyPugs!!!Whydidyoucreatethefoldernamedasanil?WhoisthisAnil?Youcouldhaveusedmy
nameormaybeyours.suggestedShweta.Ha!!Thatsasurprise.MyrealnameisAnilonly.Itisjust
thateveryoneincollegeknowsmeonlyasPugs,smiledPugs.Watchoutforfurthertechnical
romancingofPugsakaAnil.

SeventeenthArticle>>

Notes
1. Oneofthepowerfulusageofcreatingourownprocwindowisforcustomizeddebuggingby
querying.Asimplebutcoolmodificationoftheabovedriver,couldbeinsteadofgettingthejiffies,
itcouldread/writehardwareregisters.

ModuleInteractions
2Replies

Thisseventeentharticle,whichispartoftheseriesonLinuxdevicedrivers,demonstratesvarious
interactionswithaLinuxmodule.

<<SixteenthArticle

AsShwetaandPugsaregearingupfortheirfinalsemesterprojectinLinuxdrivers,theyareclosingon
somefinaltidbitsoftechnicalromancing.ThismainlyincludesthevariouscommunicationswithaLinux
module(dynamicallyloadableandunloadabledriver),namelyaccessingitsvariables,callingits
functions,andpassingparameterstoit.

Globalvariablesandfunctions
Onemightthinkaswhatabigdealinaccessingthevariablesandfunctionsofamodule,outsideit.Just
makethemglobal,declarethemexterninaheader,includetheheader,andaccess.Inthegeneral
applicationdevelopmentparadigm,itisthissimplebutinkerneldevelopmentenvironment,itisnotso.
Though,recommendationstomakeeverythingstaticbydefault,hasalwaysbeenthere,therewereand
arecaseswherenonstaticglobalsmaybeneeded.Asimpleexamplecouldbeadriverspanningover
multiplefilesandfunction(s)fromonefileneededtobecalledintheother.Nowtoavoidanykernel
collisionevenwithsuchcases,everymoduleisembodiedinitsownnamespace.Andweknowthattwo
moduleswiththesamenamecannotbeloadedatthesametime.Thusbydefaultzerocollisionis
achieved.However,thisalsoimpliesthatbydefaultnothingfromamodulecanbemadereallyglobal
throughoutthekernel,evenifwewantto.Andexactlyforsuchscenarios,the<linux/module.h>header
definesthefollowingmacros:

EXPORT_SYMBOL(sym)
EXPORT_SYMBOL_GPL(sym)
EXPORT_SYMBOL_GPL_FUTURE(sym)

Eachoftheseexportsthesymbolpassedastheirparameter,withadditionallyputtingtheminthe
default,_gpland_gpl_futuresections,respectively.Andhenceonlyoneofthemhastobeusedfora
particularsymbolthoughthesymbolcouldbeeitheravariablenameorafunctionname.Heresthe
completecode(our_glob_syms.c)todemonstratethesame:

#include<linux/module.h>
#include<linux/device.h>
staticstructclass*cool_cl;
staticstructclass*get_cool_cl(void)
{

returncool_cl;

}
EXPORT_SYMBOL(cool_cl);
EXPORT_SYMBOL_GPL(get_cool_cl);
staticint__initglob_sym_init(void)
{

if(IS_ERR(cool_cl=class_create(THIS_MODULE,"cool")))

/*Creates/sys/class/cool/*/

return0;

returnPTR_ERR(cool_cl);

}
staticvoid__exitglob_sym_exit(void)
{

/*Removes/sys/class/cool/*/

class_destroy(cool_cl);

}
module_init(glob_sym_init);
module_exit(glob_sym_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("GlobalSymbolsexportingDriver");

Eachexportedsymbolalsohaveacorrespondingstructureplacedintoeachofthekernelsymboltable
(__ksymtab),kernelstringtable(__kstrtab),andkernelCRCtable(__kcrctab)sections,markingittobe
globallyaccessible.Figure30showsafilteredsnippetofthe/proc/kallsymskernelwindow,beforeand
afterloadingthemoduleour_glob_syms.ko,whichhasbeencompiledusingthedriversusualmakefile.

Figure30:Ourglobalsymbolsmodule

Thefollowingcodeshowsthesupportingheaderfile(our_glob_syms.h),tobeincludedbymodules
usingtheexportedsymbolscool_clandget_cool_cl:

#ifndefOUR_GLOB_SYMS_H
#defineOUR_GLOB_SYMS_H
#ifdef__KERNEL__
#include<linux/device.h>
externstructclass*cool_cl;
externstructclass*get_cool_cl(void);
#endif
#endif

Figure30alsoshowsthefileModule.symvers,generatedbycompilationofthemoduleour_glob_syms.
Thiscontainsthevariousdetailsofalltheexportedsymbolsinitsdirectory.Apartfromincludingthe

aboveheaderfile,themodulesusingtheexportedsymbols,possiblyshouldhavethis
fileModule.symversintheirbuilddirectory.

Notethe<linux/device.h>headerintheaboveexamples,isbeingincludedforthevariousclassrelated
declarations&definitions,whichhasbeenalreadycoveredunderthecharacterdriversdiscussions.

ModuleParameters
Beingawareofpassingcommandlineargumentstoanapplication,itisanaturalquesttoaskif
somethingsimilarcanbedonewithamodule.Andtheanswerisyes.Parameterscanbepassedtoa
modulealongwithloadingit,sayusinginsmod.Interestinglyenoughandincontrastwiththecommand
lineargumentstoanapplication,thesecanbemodifiedevenlateraswell,throughsysfsinteractions.

Themoduleparametersaresetupusingthefollowingmacro(definedin<linux/moduleparam.h>,
includedthrough<linux/module.h>):

module_param(name,type,perm)

where,nameistheparametername,typeisthetypeoftheparameter,andpermisthepermissionsof
thesysfsfilecorrespondingtothisparameter.Supportedtypevalues
are:byte,short,ushort,int,uint,long,ulong,charp(characterpointer),boolorinvbool(inverted
boolean).Thefollowingmodulecode(module_param.c)demonstratesamoduleparameter:

#include<linux/module.h>
#include<linux/kernel.h>
staticintcfg_value=3;
module_param(cfg_value,int,0764);
staticint__initmod_par_init(void)
{

printk(KERN_INFO"Loadedwith%d\n",cfg_value);

return0;

}
staticvoid__exitmod_par_exit(void)
{

printk(KERN_INFO"Unloadedcfgvalue:%d\n",cfg_value);

}
module_init(mod_par_init);
module_exit(mod_par_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AnilKumarPugalia<email@sarikapugs.com>");
MODULE_DESCRIPTION("ModuleParameterdemonstrationDriver");

Notethatbeforetheparametersetup,avariableofthesamenameandcompatibletypeneedstobe
defined.

Subsequently,thefollowingstepsandexperimentsareshowninFigures31and32:

Buildingthedriver(module_param.kofile)usingthedriversusualmakefile
Loadingthedriverusinginsmod(withandwithoutparameters)
Variousexperimentsthroughthecorresponding/sysentries
Andfinally,unloadingthedriverusingrmmod

Figure31:Experimentswithmoduleparameter

Figure32:Experimentswithmoduleparameter(asroot)

Observethefollowing:

Initialvalue(3)ofcfg_valuebecomesitsdefaultvaluewheninsmodisdonewithoutany
parameters
Permission0764givesrwxtotheuser,rwtothegroup,andrfortheothersonthe
filecfg_valueunderparametersofmodule_paramunder/sys/module/

Checkforyourself:

Outputofdmesg|tailoneveryinsmodandrmmodfortheoutputofprintks
Trywritingintothe/sys/module/module_param/parameters/cfg_valuefileasanormaluser

Summingup
Withthis,theduohaveafairlygoodunderstandingofLinuxdriversandtheyareallsettostartworking

ontheirfinalsemesterproject.Anyguessesonwhattheirtopicisallabout?Hint:Theyhavepickedup
oneofthemostdauntingLinuxdrivertopic.Letussee,howtheyfairatit.

FileSystems:TheSemesterProject
2Replies

Thiseighteentharticle,whichispartoftheseriesonLinuxdevicedrivers,kickstartswithintroducingthe
conceptofafilesystem,bysimulatingoneinuserspace.

<<SeventeenthArticle

Ifyouhavenotyetguessedthesemesterprojecttopic,thenyoushouldhaveacarefullookatthetable
ofcontentsofthevariousbooksonLinuxdevicedrivers.Youllfindthatnobooklistsachapteronfile
systems.Notbecausetheyarenotusedornotuseful.Infact,therearecloseto100differentfile
systemsexisting,asoftoday,andpossiblymostoftheminactiveuse.Then,whyaretheynottalkedin
general?Andaresomanyfilesystemsrequired?Whichoneofthemisthebest?Letsunderstand
theseonebyone.

Inreality,thereisnobestfilesystem,andinfacttheremaynotbeonetocome,aswell.Thisis
becauseaparticularfilesystemsuitsbestonlyforaparticularsetofrequirements.Thus,different
requirementsaskfordifferentbestfilesystems,leadingtosomanyactivefilesystemsandmanymore
tocome.So,basicallytheyarenotgenericenoughtobetalkedgenerically,rathermoreofaresearch
topic.Andthereasonforthembeingthetoughestofalldriversissimple:Leastavailabilityofgeneric
writtenmaterialthebestdocumentationbeingthecodeofvariousfilesystems.Hmmm!Sounds
perfectforasemesterproject,right?

Inordertogettodosucharesearchproject,alotofsmallerstepshastobetaken.Thefirstonebeing
understandingtheconceptofafilesystemitself.Thatcouldbedonebestbysimulatingoneinuser
space.Shwetatooktheownershipofgettingthisfirststepdone,asfollows:

Usearegularfileforapartition,thatisfortheactualdatastorageofthefilesystem(Hardware
spaceequivalent)
Designabasicfilesystemstructure(Kernelspaceequivalent)andimplementthe
format/mkfscommandforit,overtheregularfile
Provideaninterface/shelltotypecommandsandoperateonthefilesystem,similartotheusual
bashshell.Ingeneral,thisstepisachievedbytheshellalongwiththecorrespondingfilesystem
driverinkernelspace.Butherethattranslationisembodied/simulatedintotheuserspace
interface,itself.(Nextarticleshalldiscussthisindetail)

Thedefaultregularfilechosenis.sfsf(simulatingfilesystemfile)inthecurrentdirectory.Starting.
(dot)isforittobehidden.Thebasicfilesystemdesignmainlycontainstwostructures,thesuperblock

containingtheinfoaboutthefilesystem,andthefileentrystructurecontainingtheinfoabouteachfilein
thefilesystem.HereareShwetasdefinesandstructuresdefinedinsfs_ds.h

#ifndefSFS_DS_H
#defineSFS_DS_H
#defineSIMULA_FS_TYPE0x13090D15/*MagicNumberforourfilesystem*/
#defineSIMULA_FS_BLOCK_SIZE512/*inbytes*/
#defineSIMULA_FS_ENTRY_SIZE64/*inbytes*/
#defineSIMULA_FS_DATA_BLOCK_CNT((SIMULA_FS_ENTRY_SIZE(16+3*4))/4)
#defineSIMULA_DEFAULT_FILE".sfsf"
typedefunsignedintuint4_t;
typedefstructsfs_super_block
{

uint4_ttype;/*Magicnumbertoidentifythefilesystem*/

uint4_tblock_size;/*Unitofallocation*/

uint4_tpartition_size;/*inblocks*/

uint4_tentry_size;/*inbytes*/

uint4_tentry_table_size;/*inblocks*/

uint4_tentry_table_block_start;/*inblocks*/

uint4_tentry_count;/*Totalentriesinthefilesystem*/

uint4_tdata_block_start;/*inblocks*/

uint4_treserved[SIMULA_FS_BLOCK_SIZE/48];

}sfs_super_block_t;/*MakingitofSIMULA_FS_BLOCK_SIZE*/
typedefstructsfs_file_entry
{

charname[16];

uint4_tsize;/*inbytes*/

uint4_ttimestamp;/*SecondssinceEpoch*/

uint4_tperms;/*Permissionsforuser*/

uint4_tblocks[SIMULA_FS_DATA_BLOCK_CNT];

}sfs_file_entry_t;
#endif

NotethatShwetahasputsomeredundantfieldsinthesfs_super_block_tratherthancomputingthem
everytime.Andpracticallythatisnotspaceinefficient,asanywaylotofemptyreservedspaceis
availableinthesuperblock,whichisexpectedtobethecompletefirstblockofapartition.For
example,entry_countisaredundantfieldasitissameastheentrytablessize(inbytes)dividedbythe
entryssize,whichbotharepartofthesuperblockstructure.Moreover,thedata_block_startnumber

couldalsobecomputedbyhowmanyblockshavebeenusedbeforeit,butagainnotpreferred.

Also,notethehardcodedassumptionsmadeabouttheblocksizeof512bytes,andsomerandom
magicnumberforthesimulafilesystem.Thismagicnumberisusedtoverifythatwearedealingwith
therightfilesystem,whenoperatingonit.

Comingtothefileentry,itcontainsthefollowingforeveryfile:

Nameofupto15characters(1bytefor)
Sizeinbytes
Timestamp(creationormodification?NotyetdecidedbyShweta)
Permissions(justoneset,fortheuser)
Arrayofblocknumbersforupto9datablocks.Why9?Tomakethecompleteentryof
SIMULA_FS_ENTRY_SIZE(64).

Withthefilesystemdesignready,themakefilesystem(mkfs)ormorecommonlyknownformat
applicationisimplementednext,asformat_sfs.c:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include"sfs_ds.h"
#defineSFS_ENTRY_RATIO0.10/*10%ofallblocks*/
#defineSFS_ENTRY_TABLE_BLOCK_START1
sfs_super_block_tsb=
{

.type=SIMULA_FS_TYPE,

.block_size=SIMULA_FS_BLOCK_SIZE,

.entry_size=SIMULA_FS_ENTRY_SIZE,

.entry_table_block_start=SFS_ENTRY_TABLE_BLOCK_START

};
sfs_file_entry_tfe;/*All0's*/
voidwrite_super_block(intsfs_handle,sfs_super_block_t*sb)
{

write(sfs_handle,sb,sizeof(sfs_super_block_t));

voidclear_file_entries(intsfs_handle,sfs_super_block_t*sb)
{

inti;

for(i=0;i<sb>entry_count;i++)

write(sfs_handle,&fe,sizeof(fe));

}
voidmark_data_blocks(intsfs_handle,sfs_super_block_t*sb)
{

charc=0;

lseek(sfs_handle,sb>partition_size*sb>block_size1,SEEK_SET);

write(sfs_handle,&c,1);/*Tomakethefilesizetopartitionsize*/

}
intmain(intargc,char*argv[])
{

intsfs_handle;

if(argc!=2)

fprintf(stderr,"Usage:%s<partitionsizein512byteblocks>\n",

return1;

sb.partition_size=atoi(argv[1]);

sb.entry_table_size=sb.partition_size*SFS_ENTRY_RATIO;

sb.entry_count=sb.entry_table_size*sb.block_size/sb.entry_size;

sb.data_block_start=SFS_ENTRY_TABLE_BLOCK_START+sb.entry_table_size;

sfs_handle=creat(SIMULA_DEFAULT_FILE,

if(sfs_handle==1)

perror("Nopermissionstoformat");

return2;

write_super_block(sfs_handle,&sb);

clear_file_entries(sfs_handle,&sb);

mark_data_blocks(sfs_handle,&sb);

close(sfs_handle);

return0;

argv[0]);

S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

Notetheheuristicof10%oftotalspacetobeusedforthefileentries,definedbySFS_ENTRY_RATIO.

Apartfromthat,thisformatapplicationtakesthepartitionsizeasinputandaccordinglycreatesthe
defaultfile.sfsf,with:

Itsfirstblockasthesuperblock,usingwrite_super_block()
Thenext10%oftotalblocksasthefileentryszeroedouttable,usingclear_file_entries()
Andtheremainingasthedatablocks,usingmark_data_blocks().Thisbasicallywritesazeroat
theend,toactuallyextendtheunderlyingfile.sfsftothepartitionsize

Asanyotherformat/mkfsapplication,format_sfs.ciscompiledusinggcc,andexecutedontheshellas
follows:

$gccformat_sfs.coformat_sfs
$./format_sfs1024#Partitionsizeinblocksof512bytes
$lsal#Listthe.sfsfcreatedwithasizeof512KiBytes

Figure33showsthe.sfsfpictoriallyforthepartitionsizeof1024blocksof512byteseach.

Figure33:Simulafilesystemon512KiBofpartition(.sfsf)

Summingup
Withtheabovedesignofsimulafilesystem(sfs_ds.h),alongwiththeimplementationforitsformat
command(format_sfs.c),Shwetahasthuscreatedtheemptyfilesystemoverthesimulated
partition.sfsf.Now,aslistedearlier,thefinalstepinsimulatingtheuserspacefilesystemistocreate
theinterface/shelltotypecommandsandoperateontheemptyfilesystemjustcreatedon.sfsf.Lets

watchoutforShwetacodingthataswell,completingthefirstsmallsteptowardsunderstandingtheir
project.

NineteenthArticle>>

FileSystems:TheSemesterProjectPartII
2Replies

Thisnineteentharticle,whichispartoftheseriesonLinuxdevicedrivers,continueswithintroducingthe
conceptofafilesystem,bysimulatingoneinuserspace.

<<EighteenthArticle

Inthepreviousarticle,Shwetareadiedthepartitiononthe.sfsffilebyformatingitwith
theformat_sfsapplication.Tocompletetheunderstandingofafilesystem,thenextstepinits
simulationistobrowseandplayaroundwiththefilesystemcreated.HereisShwetasfirstcutbrowser
applicationtoachievethesame.Letshaveacloserlook.sfs_ds.histhesameheaderfile,already
createdbyShweta.

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<time.h>
#include"sfs_ds.h"
sfs_super_block_tsb;
voidsfs_list(intsfs_handle)
{

inti;

sfs_file_entry_tfe;

lseek(sfs_handle,sb.entry_table_block_start*sb.block_size,SEEK_SET);

for(i=0;i<sb.entry_count;i++)

read(sfs_handle,&fe,sizeof(sfs_file_entry_t));

if(!fe.name[0])continue;

printf("%15s%10dbytes%c%c%c%s",

fe.name,fe.size,

fe.perms&04?'r':'',

fe.perms&02?'w':'',

fe.perms&01?'x':'',

ctime((time_t*)&fe.timestamp)

);

}
voidsfs_create(intsfs_handle,char*fn)
{

inti;

sfs_file_entry_tfe;

lseek(sfs_handle,sb.entry_table_block_start*sb.block_size,SEEK_SET);

for(i=0;i<sb.entry_count;i++)

read(sfs_handle,&fe,sizeof(sfs_file_entry_t));

if(!fe.name[0])break;

if(strcmp(fe.name,fn)==0)

printf("File%salreadyexists\n",fn);

return;

if(i==sb.entry_count)

printf("Noentriesleft\n",fn);

return;

lseek(sfs_handle,(off_t)(sb.entry_size),SEEK_CUR);

strncpy(fe.name,fn,15);

fe.name[15]=0;

fe.size=0;

fe.timestamp=time(NULL);

fe.perms=07;

for(i=0;i<SIMULA_FS_DATA_BLOCK_CNT;i++)

write(sfs_handle,&fe,sizeof(sfs_file_entry_t));

fe.blocks[i]=0;

}
voidsfs_remove(intsfs_handle,char*fn)
{

inti;

sfs_file_entry_tfe;

lseek(sfs_handle,sb.entry_table_block_start*sb.block_size,SEEK_SET);

for(i=0;i<sb.entry_count;i++)

read(sfs_handle,&fe,sizeof(sfs_file_entry_t));

if(!fe.name[0])continue;

if(strcmp(fe.name,fn)==0)break;

if(i==sb.entry_count)

printf("File%sdoesn'texist\n",fn);

return;

lseek(sfs_handle,(off_t)(sb.entry_size),SEEK_CUR);

memset(&fe,0,sizeof(sfs_file_entry_t));

write(sfs_handle,&fe,sizeof(sfs_file_entry_t));

}
voidbrowse_sfs(intsfs_handle)
{

intdone;

charcmd[256],*fn;

intret;

done=0;

printf("WelcometoSFSBrowsingShellv1.0\n\n");

printf("Blocksize

printf("Partitionsize:%dblocks\n",sb.partition_size);

printf("Fileentrysize:%dbytes\n",sb.entry_size);

printf("Entrytblsize:%dblocks\n",sb.entry_table_size);

printf("Entrycount

printf("\n");

while(!done)

printf("$>");

ret=scanf("%[^\n]",cmd);

if(ret<0)

done=1;

printf("\n");

continue;

else

getchar();

if(ret==0)continue;

if(strcmp(cmd,"?")==0)

printf("Supportedcommands:\n");

printf("\t?\tquit\tlist\tcreate\tremove\n");

continue;

:%dbytes\n",sb.block_size);

:%d\n",sb.entry_count);

elseif(strcmp(cmd,"quit")==0)

done=1;

continue;

elseif(strcmp(cmd,"list")==0)

sfs_list(sfs_handle);

continue;

elseif(strncmp(cmd,"create",6)==0)

if(cmd[6]=='')

fn=cmd+7;

while(*fn=='')fn++;

if(*fn!='')

sfs_create(sfs_handle,fn);

continue;

elseif(strncmp(cmd,"remove",6)==0)

if(cmd[6]=='')

fn=cmd+7;

while(*fn=='')fn++;

if(*fn!='')

sfs_remove(sfs_handle,fn);

continue;

printf("Unknown/Incorrectcommand:%s\n",cmd);

printf("Supportedcommands:\n");

printf("\t?\tquit\tlist\tcreate<file>\tremove<file>\n");

}
intmain(intargc,char*argv[])
{

char*sfs_file=SIMULA_DEFAULT_FILE;

intsfs_handle;

if(argc>2)

fprintf(stderr,"Incorrectinvocation.Possibilitiesare:\n");

fprintf(stderr,

"\t%s/*Picksup%sasthedefaultpartition_file*/\n",

argv[0],SIMULA_DEFAULT_FILE);

fprintf(stderr,"\t%s[partition_file]\n",argv[0]);

return1;

if(argc==2)

sfs_handle=open(sfs_file,O_RDWR);

if(sfs_handle==1)

fprintf(stderr,"UnabletobrowseSFSover%s\n",sfs_file);

return2;

read(sfs_handle,&sb,sizeof(sfs_super_block_t));

if(sb.type!=SIMULA_FS_TYPE)

fprintf(stderr,"InvalidSFSdetected.Givingup.\n");

close(sfs_handle);

return3;

browse_sfs(sfs_handle);

close(sfs_handle);

return0;

sfs_file=argv[1];

Theabove(shelllike)programprimarilyreadsthesuperblockfromthepartitionfile(.sfsfbydefault,or
thefileprovidedfromcommandline),andthengetsintobrowsingthefilesystembasedonthat
information,usingthebrowse_sfs()function.Notethecheckperformedforthevalidfilesystemonthe
partitionfileusingthemagicnumberSIMULA_FS_TYPE.

browse_sfs()printsthefilesysteminformationandprovidesfourbasicfilesystemfunctionalitiesusing
thefollowingcommands:

quittoquitthefilesystembrowser
listtolistthecurrentfilesinthefilesystem(usingsfs_list())
create<filename>tocreateanewfileinthefilesystem(usingsfs_create(filename))
remove<filename>toremoveanexistingfilefromthefilesystem(usingsfs_remove(filename))

Figure34showsthebrowserinaction,usingtheabovecommands.

Figure34:Simulafilesystembrowseroutput

sfs_list()traversesthroughallthefileentriesinthepartitionandprintsallthenonnullfilenameentries
withfilename,size,permissions,anditscreationtimestamp.sfs_create()looksupforanavailable(null
filename)entryandthenupdatesitwiththegivenfilename,sizeof0bytes,permissionsofrwx,and
thecurrenttimestamp.Andsfs_remove()looksupforanexistingfileentry,havingthefilenametobe
removed,andthennullifiesit.Theotherpartsintheabovecodearemoreofbasicerrorhandlingcases
likeinvalidcommandinbrowse_sfs(),existingfilenameinsfs_create(),nonexistingfilename
insfs_remove(),etc.

Notethattheaboveapplicationisthefirstcutoneofafullfledgedapplication.Andsothefilescreated
rightnowarejustwiththebasicfixedparameters.And,thereisnowaytochangetheirpermissions,
content,etc,yet.However,itmustbeclearbynowthataddingthosefeaturesisjustamatterofadding
commandsandtheircorrespondingfunctions,whichwouldbeprovidedforafewmoreinteresting
featuresbyShwetaasshefurtherworksoutthedetailsofthebrowsingapplication.

Summingup
Oncedonewiththeotherfeaturesaswell,thenextsetofstepswouldtaketheprojectduofurther
closertotheirfinalgoals.Watchoutforwhotakeschargeofthenextstepofmovingthefilesystem
logictothekernelspace.

TwentiethArticle>>

FileSystems:TheSemesterProjectPartIII
2Replies

Thistwentietharticle,whichispartoftheseriesonLinuxdevicedrivers,completesthebasicsimulation
ofafilesysteminuserspace.

<<NineteenthArticle

Tillnow,Shwetahadimplemented4basicfunctionalitiesofthesimulatedfilesystem(sfs)browser,
namelyquit(browser),list(files),create(anemptyfile),remove(afile).Heresheadds3littleadvanced
functionalitiestogetafeelofacompletebasicfilesystem:

Changingpermissionsofafile
Readingfromafile
Writingintoafile

Heresasneakpeekintoherthinkingprocessashowshecameupwithherimplementations.

Forthevariouscommandimplementations,twoverycommonrequirementskeeppoppingquiteoften:

Gettingtheindexoftheentryforaparticularfilename
Updatingtheentryforagivenfilename

Hencethesetworequirements,capturedasthefunctionssfs_lookup()andsfs_update():

intsfs_lookup(intsfs_handle,char*fn,sfs_file_entry_t*fe)
{

inti;

lseek(sfs_handle,sb.entry_table_block_start*sb.block_size,SEEK_SET);

for(i=0;i<sb.entry_count;i++)

read(sfs_handle,fe,sizeof(sfs_file_entry_t));

if(!fe>name[0])continue;

if(strcmp(fe>name,fn)==0)returni;

return1;

voidsfs_update(intsfs_handle,char*fn,int*size,intupdate_ts,int*perms)
{

inti;

sfs_file_entry_tfe;

if((i=sfs_lookup(sfs_handle,fn,&fe))==1)

printf("File%sdoesn'texist\n",fn);

return;

if(size)fe.size=*size;

if(update_ts)fe.timestamp=time(NULL);

if(perms&&(*perms<=07))fe.perms=*perms;

lseek(sfs_handle,sb.entry_table_block_start*sb.block_size+

write(sfs_handle,&fe,sizeof(sfs_file_entry_t));

i*sb.entry_size,SEEK_SET);

sfs_lookup()traversesthroughalltheentries(skippingtheinvalidi.e.emptyfilenameentries),tillitfinds
thefilenamematchandthenreturnstheindexandtheentryofthematchedentryinthefunctionsthird
parameter.Itreturns1incaseofnomatchisfound.

sfs_update()usessfs_lookup()togettheentryanditsindexforthegivenfilename.Then,itupdatesit
backintothefilesystemwith:a)size,ifpassed(i.e.nonNULL),b)currenttimestampifupdate_tsisset,
c)permissions,ifpassed(i.e.nonNULL).

Changingfilepermissions
IntheSimulafilesystem,filepermissionsarebasicallyacombinationrwxfortheuseronly,storedas
aninteger,withLinuxlikenotationof4forr,2forw,1forx.Forchangingthepermissionsofagiven
filename,sfs_update()canbeusedbestbypassingNULLpointerforsize,zeroforupdate_ts,andthe
pointertopermissionstochangeforperms.Sosfs_chperm()wouldbeasfollows:

voidsfs_chperm(intsfs_handle,char*fn,intperm)
{

sfs_update(sfs_handle,fn,NULL,0,&perm);

Readingafile
Readingafileisbasicallysequentiallyreading&displayingthecontentsofthedatablocksindicatedby
theirpositionfromtheblocksarrayoffilesentryanddisplayingthatonstdoutsfiledescriptor1.A
coupleofthingstobetakencareof:

Fileisassumedtobewithoutholes,i.e.blockpositionof0intheblocksarrayindicatesnomore
datablocksforthefile
Readingshouldnotgobeyondthefilesize.Specialcaretobetakenwhilereadingthelastblock
withdata,asitmaybepartiallyvalid

Heresthecompletereadfunction,keepingtrackofvaliddatausingbyteslefttoread:

uint1_tblock[SIMULA_FS_BLOCK_SIZE];//Sharedasglobalwithsfs_write
voidsfs_read(intsfs_handle,char*fn)
{

inti,block_i,already_read,rem_to_read,to_read;

sfs_file_entry_tfe;

if((i=sfs_lookup(sfs_handle,fn,&fe))==1)

printf("File%sdoesn'texist\n",fn);

return;

already_read=0;

rem_to_read=fe.size;

for(block_i=0;block_i<SIMULA_FS_DATA_BLOCK_CNT;block_i++)

if(!fe.blocks[block_i])break;

to_read=(rem_to_read>=sb.block_size)?

lseek(sfs_handle,fe.blocks[block_i]*sb.block_size,SEEK_SET);

read(sfs_handle,block,to_read);

write(1,block,to_read);

already_read+=to_read;

rem_to_read=to_read;

if(!rem_to_read)break;

sb.block_size:rem_to_read;

Writingafile
Interestingly,writeisnotatrivialfunction.Gettingdatafromtheuserthroughbrowserisokay.But
basedonthat,freeavailableblockshastobeobtained,filledandthentheirpositionbenoted
sequentiallyintheblocksarrayofthefilesentry.Typically,wedothiswheneverwehavereceiveda
blockfulldata,exceptthelastblock.Thatstrickyhowdoweknowthelastblock?So,wereadtillend
ofinput,markedbyControlDonitsownlinefromtheuserandthatisindicatedbyareturnof0from
read.Andinthatcase,wecheckifanynonfullblockofdataislefttobewritten,andifyesfollowthe
sameprocedureofobtainingafreeavailableblock,fillingitup(withthepartialdata),andupdatingits
positionintheblocksarray.

Afterallthis,wehavefinallywrittenthefiledata,alongwiththedatablockpositionsintheblocksarray
ofthefilesentry.Andnowitstimetoupdatefilesentrywiththetotalsizeofdatawritten,aswellas
timestamptocurrentlymodified.Oncedone,thisentryhastobeupdatedbackintotheentrytable,
whichisthelaststep.Andinthisflow,thefollowingshouldntbemissedoutduringgetting&fillingup
freeblocks:

Checkfornomoreblockpositionsavailableinblocksarrayofthefilesentry
Checkfornomorefreeblocksavailableinthefilesystem

Ineitherofthe2cases,thethoughtistodoagracefulstopwithdatabeingwrittenuptothemaximum
possible,anddiscardingtherest.

Onceagainalloftheseputtogetherareinthefunctionbelow:

uint1_tblock[SIMULA_FS_BLOCK_SIZE];//Sharedasglobalwithsfs_read
voidsfs_write(intsfs_handle,char*fn)
{

inti,cur_read_i,to_read,cur_read,total_size,block_i,free_i;

sfs_file_entry_tfe;

if((i=sfs_lookup(sfs_handle,fn,&fe))==1)

printf("File%sdoesn'texist\n",fn);

return;

cur_read_i=0;

to_read=sb.block_size;

total_size=0;

block_i=0;

while((cur_read=read(0,block+cur_read_i,to_read))>0)

if(cur_read==to_read)

/*Writethisblock*/

if(block_i==SIMULA_FS_DATA_BLOCK_CNT)

if((free_i=get_data_block(sfs_handle))==1)

lseek(sfs_handle,free_i*sb.block_size,SEEK_SET);

write(sfs_handle,block,sb.block_size);

fe.blocks[block_i]=free_i;

block_i++;

total_size+=sb.block_size;

/*Resetvariousvariables*/

cur_read_i=0;

to_read=sb.block_size;

else

cur_read_i+=cur_read;

to_read=cur_read;

if((cur_read<=0)&&(cur_read_i))

/*Writethispartialblock*/

if((block_i!=SIMULA_FS_DATA_BLOCK_CNT)&&

lseek(sfs_handle,fe.blocks[block_i]*sb.block_size,

write(sfs_handle,block,cur_read_i);

total_size+=cur_read_i;

fe.size=total_size;

fe.timestamp=time(NULL);

lseek(sfs_handle,sb.entry_table_block_start*sb.block_size+

write(sfs_handle,&fe,sizeof(sfs_file_entry_t));

Thelaststride

break;/*Filesizelimit*/
break;/*Filesystemfull*/

((fe.blocks[block_i]=get_data_block(sfs_handle))!=1))

SEEK_SET);

i*sb.entry_size,SEEK_SET);

Withtheabove3sfscommandfunctions,thefinalchangetothebrowse_sfs()functionin(previous
articles)browse_sfs.cwouldbetoaddthecasesforhandlingthese3newcommandsofchperm,write,
andread.

Oneofthedauntingquestions,ifithasnotyetbotheredyou,ishowdoyoufindthefreeavailable
blocks.Noticethatinsfs_write(),wejustcalledafunctionget_data_block()andeverythingwent
smooth.Butthinkthroughhowwouldthatbeimplemented.Doyouneedtotraverseallthefileentrys
everytimetofigureoutwhichallhasbeenusedandtheremainingarefree.Thatwouldbekilling.
Instead,aneasiertechniquewouldbetogettheusedonesbyparsingallthefileentrys,onlyinitially
once,andthenkeeptrackofthemwhenevermoreentriesareusedorfreedup.Butforthatacomplete
frameworkneedstobeputinplace,whichincludes:

uint1_ttypedef(insfs_ds.h)
used_blocksdynamicarraytokeeptrackofusedblocks(inbrowse_sfs.c)
Functioninit_browsing()toinitializethedynamicarrayused_blocks,i.e.allocateandmarkthe
initialusedblocks(inbrowse_sfs.c)
Correspondinglytheinversefunctionshut_browsing()tocleanupthesame(inbrowse_sfs.c)
Anddefinitelythefunctionsget_data_block()andput_data_block()torespectivelygetandput
backthefreedatablocksbasedonthedynamicarrayused_blocks(inbrowse_sfs.c)

Allthesethoughts,incorporatedintheearliersfs_ds.handbrowse_sfs.cfiles,alongwithaMakefileand
theearlierformatterapplicationformat_sfs.c,areavailablefromsfs_code.tar.bz2.Oncecompiled
intobrowse_sfsandexecutedas./browse_sfs,itshowsupassomethinglikeinFigure35.

Figure35:DemoofSimulafilesystembrowsersnewfeatures

Summingup
AsShweta,showedtheaboveworkingdemotoherprojectmate,heobservedsomemissouts,and
challengedhertofindthemoutonherown.Hehintedthemtoberelatedtothenewlyadded
functionalityandgettingfreeblockframeworksomeevenvisiblefromthedemo,i.e.Figure35.Can
youhelpShweta,findthemout?Ifyes,posttheminthecommentsbelow.

TheSemesterProjectPartIV:FormattingaPenDrive
2Replies

Thistwentyfirstarticle,whichispartoftheseriesonLinuxdevicedrivers,takesthenextsteptowards
writingafilesystemmodulebywritingaformattingapplicationforyourrealpendrive.

<<TwentiethArticle

ThanksfriendsforyourconfidenceinShwetaandnottryingtohelpheroutinfiguringouttheissues
withhercode.Sheindeedfiguredoutandfixedthefollowingissuesinhercode:

sfs_read()andsfs_write()needtocheckforthereadandwritefilepermissionsbefore
proceedingtoreadandwrite,respectively
sfs_write()shouldfreeanypreviouslyallocatedblocks,aswriteisalwaysoverwrite
Moreover,theearlierwrittensfs_remove()also,nowneedstofreeuptheallocatedblocks

SFSFormatforarealpartition
ThereafterPugstooktheleadandslightlymodifiedShwetasformat_sfs.candsfs_ds.hfilestoformata
realpendrivespartition.Thekeychangeisthatinsteadofcreatingthedefaultregularfile.sfsfto
format,nowitwouldbeoperatingonanexistingblockdevicefilecorrespondingtoanunderlying
partition,saysomethinglike/dev/sdb1.So,

Itwouldgetthepartitionssizefromthepartitionsblockdevicefileitself,ratherthantakingitasa
commandlineargument
Accordingly,itwouldnowexpectthepartitionsblockdevicefilenameinsteadofsize,asmain()s
firstargument.
Also,itwouldnotneedthemark_data_block()togrowthefileequaltothepartitionsize

TheioctlcommandBLKGETSIZE64getsthe64bitsizeoftheunderlyingblockdevicepartition,in
bytes.Then,itisdividedbytheblocksize(SIMULA_FS_BLOCK_SIZE)togetthepartitionssizein
blockunits.Hereisthecorrespondingmodifiedsnippetofthemain()function
informat_real_sfs.c(updatedformat_sfs.c),alongwiththe
requiredtypedefinreal_sfs_ds.h(updatedsfs_ds.h).(Note:Forreadability,Pugsrenamed
alluint*bybyte*):

typedefunsignedlonglongbyte8_t;
...
byte8_tsize;
sfs_handle=open(argv[1],O_RDWR);
if(sfs_handle==1)
{

fprintf(stderr,"Errorformatting%s:%s\n",argv[1],strerror(errno));

return2;

}
if(ioctl(sfs_handle,BLKGETSIZE64,&size)==1)
{

fprintf(stderr,"Errorgettingsizeof%s:%s\n",argv[1],strerror(errno));

return3;

}
sb.partition_size=size/SIMULA_FS_BLOCK_SIZE;

Aspertheabovecode,thefollowingadditionalheaderfilesneedtobeincluded:

#include<errno.h>/*Forerrno*/
#include<string.h>/*Forstrerror()*/
#include<sys/ioctl.h>/*Forioctl()*/
#include<linux/fs.h>/*ForBLKGETSIZE64*/

Withalltheabovechangescompiledintoformat_real_sfs,Pugspluggedinhispendrive,partitionof
whichgotautomounted.Then,hetookbackupofitscontentandunmountedthesamereadyfora
realSFSformatofthependrivepartition.

Caution:Takeabackupofyourpendrivescontentyouareformattingitforreal.Becareful
inchoosingtherightpartitionofyourpendrive.Otherwise,youmayforeverlosedatafrom
yourharddiskorevenmakeyoursystemunbootable.Youhavebeenwarned.

Figure36:Formattingthependrive

Figure36demonstratesalltheabovebutbackupstepsatrootprompt#.Instead,onemayusesudo,
aswell.NotethatPugsgothispendrivepartitionmountedat/media/10ACBF1C,andthe
correspondingdevicefileis/dev/sdb1(/dev/sdbbeingthecompletependrive).Youmayhaveboth
thesedifferently.Accordingly,followthestepsforyourself.Also,notethat,therealSFSformattingis
thenstartedusingthefollowingcommand:

#./format_real_sfs/dev/sdb1

Andthen,thereisa^C(CtrlC)immediatelyafterthat,basicallyterminatingtheformatting.Aha!Did
Pugsrealizesomethingimportantwasthereonthependrive?Notreally,ashispendriveisalready
empty.Actually,whathappenedisthatformattingwasgoingonforquitesometimesoPugshad
somedoubtabouthiscodechangesandsoheterminatedit.Reviewinghiscodedidntyieldmuch,so
hereissuedtheformatting,thistimewiththetimecommand,tofigureoutexactlyhowmuchtimeisthe
formattingtakingandthenmaybedebug/fixthat.Andfinally!Theformattingiscompletebutaftera
whopping430.88seconds(7+minutes),yesminutes.timebasicallyshowstherealtimetaken(includes
thetimewhenotherprocesseshasbeenrunningaftercontextswitch),timeexecutedinuserspace,
timeexecutedinkernelspace.Thatshugesomethingneedsoptimization.Anditdidnttakemuch
timeforaclosereviewtounderminetheissue.Thekeytimetakercodewouldbe
theclear_file_entries()function.Rightnowitsclearingthefileentriesonebyone,i.e.writing64byte
sizedfileentriesonebyonethatsprettynonoptimal.Abetterapproachwouldbetofillupablock
withsuchentries,andthenwritetheseblocksonebyone.Incaseofa512byteblock
(i.e.SIMULA_FS_BLOCK_SIZEdefinedas512),thatwouldmean8fileentriesina512byteblockand

thenwritingthese512byteblocksonebyone.So,Pugschangedtheclear_file_entries()functiontodo
thesame,andviola!formattingiscompleteinalittlelessthan26seconds.Herestheanswertoyour
curiositytherewrittenclear_file_entries()function:

voidclear_file_entries(intsfs_handle,sfs_super_block_t*sb)
{

inti;

byte1_tblock[SIMULA_FS_BLOCK_SIZE];

for(i=0;i<sb>block_size/sb>entry_size;i++)

for(i=0;i<sb>entry_table_size;i++)

memcpy(block+i*sb>entry_size,&fe,sizeof(fe));

write(sfs_handle,block,sizeof(block));

Now,youmayplugoutandpluginthependriveback.Andyoumaywonderthatneitheritisauto
mounted,noryouareabletomountit.Thatsexpected,asnowitisformattedwithafilesystemwhichis
notyetcodedas(kernel)moduleandthereisnooneinthekerneltodecodethesame.So,codingthat
kernelmodulewouldbetheultimatesteptogeteverythingworkinglikewithanyotherexistingfile
systems(vfat,ext3,).Ifyouareworriedthatyourpendriveisspoiled,youmayreformatitwiththe
FAT32(vfat)filesystemasfollows(asrootorwithsudo):

#mkfs.vfat/dev/sdb1#Becarefulwiththecorrectpartitiondevicefile

andthenplugout&pluginthependrivetogetautomounted.But,youknowPugsbeingacool
carefreeguy,insteadwentaheadtotryoutbrowsingtheSimulafilesystemcreatedonthependrive
partition.

Browsingthependrivepartition
Obviously,therewereslightmodificationstothebrowse_sfs.capplicationaswell,inlinewiththe
changestoformat_sfs.c.Majoronebeingcompulsorilytakingthepartitionsdevicefiletobrowseasthe
commandlineargument,insteadofbrowsingthedefaultregularfile.sfsf.

Alltheupdatedfiles(real_sfs_ds.h,format_real_sfs.c,browse_real_sfs.candMakefile)areavailable
fromrsfs_code.tar.bz2.

Figure37:SFSbrowseronpendrive

Figure37showsthebrowserinaction.However,thecoolestbrowsingwouldbethesamewayasis
donewithallotherfilesystems,usingtheshellcommandscd,ls,Yes,andforthatwewouldneed
therealSFSmoduleinplace.KeepfollowingwhatsPugsuptoforgettingthatinplace.

TwentysecondArticle>>

TheSemesterProjectPartV:FileSystemModuleTemplate
2Replies

Thistwentysecondarticle,whichispartoftheseriesonLinuxdevicedrivers,laysoutabarebonefile
systemmodule.

<<TwentyfirstArticle

Withtheformattingofthependrive,thefilesystemisallsetinthehardwarespace.Now,itistheturnto
decodethatusingacorrespondingfilesystemmoduleinthekernelspace,andaccordinglyprovidethe
userspacefilesysteminterface,forittobebrowsedlikeanyotherfilesystems.

The5setsofSystemCalls
Unlikecharacterorblockdrivers,thefilesystemdriversinvolvenotjustonestructureoffunction
pointers,butinstead5structuresoffunctionpointers,forthevariousinterfaces,providedbyafile
system.Theseare:

structfile_system_typecontainsfunctionstooperateonthesuperblock
structsuper_operationscontainsfunctionstooperateontheinodes
structinode_operationscontainsfunctionstooperateonthedirectoryentries
structfile_operationscontainsfunctionstooperateonthefiledata(throughpagecache)
structaddress_space_operationscontainspagecacheoperationsforthefiledata

Withthese,thereweremanynewtermsforPugs.Hereferredthefollowingglossarytounderstandthe
varioustermsusedaboveandlaterinthefilesystemmoduledevelopment:

PagecacheorBuffercache:PoolofRAMbuffers,eachofpagesize(typically4096bytes).
Thesebuffersareusedasthecacheforthefiledatareadfromtheunderlyinghardware,thus
increasingtheperformanceoffileoperations
Inode:Structurecontainingthemetadata/informationofafile,likepermissions,owner,etc.
Thoughfilenameisametadataofafile,forbetterspaceutilization,intypicalLinuxfilesystems,
itisnotkeptininode,insteadinsomethingcalleddirectoryentries.Collectionofinodes,iscalled
aninodetable
Directoryentry:Structurecontainingthenameandinodenumberofafileordirectory.Intypical
Linuxbasedfilesystems,acollectionofdirectoryentriesfortheimmediatefilesanddirectoriesof
saydirectoryD,isstoredinthedatablocksofthedirectoryD

Superblock:Structurecontainingtheinformationaboutthevariousdatastructuresofthefile
systems,liketheinodetables,Basicallythemetametadata,i.e.metadataforthemetadata
VirtualFileSystem(VFS):Conceptualfilesystemlayerinterfacingthekernelspacetouser
spaceinanabstractmanner,showingeverythingasafile,andtranslatingtheiroperationsfrom
usertotheappropriateentityinthekernelspace

Eachoneoftheabovefivestructurescontainsalistoffunctionpointers,whichneedstobepopulated
dependingonwhatallfeaturesarethereortobesupportedinthefilesystem(module).For
example,structfile_system_typemaycontainsystemcallsformountingandunmountingafilesystem,
basicallyoperatingonitssuperblockstructsuper_operationsmaycontaininoderead/writesystem
callsstructinode_operationsmaycontainfunctiontolookupdirectoryentriesstruct
file_operationsmaygenericallyoperateonthepagecachedfiledata,whichmayinturninvokepage
cacheoperations,definedinthestructaddress_space_operations.Forthesevariousoperations,most
ofthesefunctionswilltheninterfacewiththecorrespondingunderlyingblockdevicedrivertoultimately
operatewiththeformattedfilesysteminthehardwarespace.

TostartwithPugslaidoutthecompleteframeworkofhisrealSFSmodule,butwithminimal
functionality,goodenoughtocompile,load,andnotcrashthekernel.Hepopulatedonlythefirstof
thesefivestructuresthestructfile_system_typeandleftalltheothersempty.Herestheexactcode
ofthestructuredefinitions:

#include<linux/fs.h>/*Forsystemcalls,structures,...*/
staticstructfile_system_typesfs;
staticstructsuper_operationssfs_sops;
staticstructinode_operationssfs_iops;
staticstructfile_operationssfs_fops;
staticstructaddress_space_operationssfs_aops;

#include<linux/version.h>/*ForLINUX_VERSION_CODE&KERNEL_VERSION*/
staticstructfile_system_typesfs=
{

name:"sfs",/*Nameofourfilesystem*/

#if(LINUX_VERSION_CODE<KERNEL_VERSION(2,6,38))

get_sb:sfs_get_sb,

#else

#endif

mount:sfs_mount,

kill_sb:kill_block_super,

owner:THIS_MODULE

};

NotethatbeforeLinuxkernelversion2.6.38,themountfunctionpointerwasreferredasget_sb,and
also,itusedtohaveslightlydifferentparameters.Andhence,theabove#ifforittobecompatibleat
leastacross2.6.3xandpossiblywith3.xkernelversionsnoguaranteeforothers.Accordingly,the
correspondingfunctionssfs_get_sb()andsfs_mount(),arealso#ifd,asfollows:

#include<linux/kernel.h>/*Forprintk,...*/
if(LINUX_VERSION_CODE<KERNEL_VERSION(2,6,38))
staticintsfs_get_sb(structfile_system_type*fs_type,intflags,

constchar*devname,void*data,structvfsmount*vm)

printk(KERN_INFO"sfs:devname=%s\n",devname);

/*sfs_fill_superthiswillbecalledtofillthesuperblock*/

returnget_sb_bdev(fs_type,flags,devname,data,&sfs_fill_super,vm);

}
#else
staticstructdentry*sfs_mount(structfile_system_type*fs_type,

intflags,constchar*devname,void*data)

printk(KERN_INFO"sfs:devname=%s\n",devname);

/*sfs_fill_superthiswillbecalledtofillthesuperblock*/

returnmount_bdev(fs_type,flags,devname,data,&sfs_fill_super);

}
#endif

Theonlydifferenceintheabove2functionsisthatinthelater,theVFSmountpointrelatedstructure
hasbeenremoved.Theprintk()intherewoulddisplaytheunderlyingpartitionsdevicefilewhichthe
userisgoingtomount,basicallythependrivesSFSformatted
partition.get_sb_bdev()andmount_bdev()aregenericblockdevicemountfunctionsfortherespective
kernelversions,definedinfs/super.candprototypedin<linux/fs.h>.Pugsalsousedthem,asmost
otherfilesystemwritersdo.Areyouwondering:Doesallfilesystemmountablockdevice,thesame
way?Mostofityes,exceptthepartwherethemountoperationneedstofillintheVFSsuperblock
structure(structsuper_block),asperthesuperblockoftheunderlyingfilesystemobviouslythatmost
probablywouldbedifferent.Butthenhowdoesitdothat?Observecarefully,intheabovefunctions,

apartfrompassingalltheparametersasis,thereisanadditionalparametersfs_fill_super,andthatis
PugscustomfunctiontofilltheVFSsuperblock,aspertheSFSfilesystem.

Unlikethemountfunctionpointer,theunmountfunctionpointerhasbeensame(kill_sb)forquitesome
kernelversionsandinunmounting,thereisnoteventheminimaldistinctionrequiredacrossdifferent
filesystems.So,thegenericblockdeviceunmountfunctionkill_block_super()hasbeenuseddirectlyas
thefunctionpointer.

Insfs_fill_super(),Pugsisideallysupposedtoreadthesuperblockfromtheunderlyinghardwarespace
SFS,andthenaccordinglytranslateandfillthatintoVFSsuperblocktoenableVFStoprovidetheuser
spacefilesysteminterface.Butheisyettofigurethatout,ashowtoreadfromtheunderlyingblock
device,inthekernelspace.Informationofwhichblockdevicetouse,isalreadyembeddedinto
thesuper_blockstructureitself,obtainedfromtheuserissuingthemountcommand.ButasPugs
decidedtogetthebarebonerealSFSup,first,hewentaheadwritingthissfs_super_fill()functionalso
asahardcodedfillfunction.Andwiththatitself,heregisteredtheSimulafilesystemwiththeVFS.As
anyotherLinuxdriver,heresthefilesystemdriversconstructoranddestructorforthat:

#include<linux/module.h>/*Formodulerelatedmacros,...*/
staticint__initsfs_init(void)
{

interr;

err=register_filesystem(&sfs);

returnerr;

}
staticvoid__exitsfs_exit(void)
{

unregister_filesystem(&sfs);

}
module_init(sfs_init);
module_exit(sfs_exit);

Bothregister_filesystem()andunregister_filesystem()takespointertothethestructfile_system_type
sfs(filledabove),astheirparameter,torespectivelyregisterandunregisterthefilesystemdescribedby
it.

HardcodedSFSsuperblockandrootinode

Andyes,heresthehardcodedsfs_fill_super()function:

#include"real_sfs_ds.h"/*ForSFSrelateddefines,datastructures,...*/
staticintsfs_fill_super(structsuper_block*sb,void*data,intsilent)
{

printk(KERN_INFO"sfs:sfs_fill_super\n");

sb>s_blocksize=SIMULA_FS_BLOCK_SIZE;

sb>s_blocksize_bits=SIMULA_FS_BLOCK_SIZE_BITS;

sb>s_magic=SIMULA_FS_TYPE;

sb>s_type=&sfs;//file_system_type

sb>s_op=&sfs_sops;//superblockoperations

sfs_root_inode=iget_locked(sb,1);//obtainaninodefromVFS

if(!sfs_root_inode)

if(sfs_root_inode>i_state&I_NEW)//allocatedfreshnow

printk(KERN_INFO"sfs:Gotnewrootinode,let'sfillin\n");

sfs_root_inode>i_op=&sfs_iops;//inodeoperations

sfs_root_inode>i_mode=S_IFDIR|S_IRWXU|

sfs_root_inode>i_fop=&sfs_fops;//fileoperations

sfs_root_inode>i_mapping>a_ops=&sfs_aops;//addressoperations

unlock_new_inode(sfs_root_inode);

else

returnEACCES;

S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;

printk(KERN_INFO"sfs:Gotrootinodefrominodecache\n");

#if(LINUX_VERSION_CODE<KERNEL_VERSION(3,4,0))

sb>s_root=d_alloc_root(sfs_root_inode);

#else

sb>s_root=d_make_root(sfs_root_inode);

#endif

if(!sb>s_root)

iget_failed(sfs_root_inode);

returnENOMEM;

return0;

Asmentionedearlier,thisfunctionisbasicallysupposedtoreadtheunderlyingSFSsuperblock,and
accordinglytranslateandfillthestructsuper_block,pointedtobyitsfirstparametersb.So,
understandingitissameasunderstandingtheminimalfieldsofthestructsuper_block,whichare
gettingfilledup.Thefirstthreearetheblocksize,itslogarithmbase2,andthetype/magiccodeofthe
Simulafilesystem.AsPugscodesfurther,weshallseethatoncehegetsthesuperblockfromthe
hardwarespace,hewouldinsteadgetthesevaluesfromthatsuperblock,andmoreimportantlyverify
them,toensurethatthecorrectpartitionisbeingmounted.

Afterthat,thevariousstructurepointersarepointedtotheircorrespondingstructureofthefunction
pointers.Lastbutnotleast,therootinodespointers_rootispointedtothestructinodestructure,
obtainedfromVFSinodecache,basedontheinodenumberofrootrightnow,whichhasbeenhard
codedto1itmaypossiblychange.Iftheinodestructureisobtainedfresh,i.e.forthefirsttime,itis
thenfilledaspertheunderlyingSFSrootinodescontent.Also,themodefieldisbeinghardcodedto
drwxrxrx.Apartfromthat,theusualstructurepointersarebeinginitializedbythecorresponding
structureaddresses.Andfinally,therootsinodeisbeingattachedtothesuperblock
usingd_alloc_root()ord_make_root(),asperthekernelversion.

Alltheabovecodepiecesputintogetherasthebarebonereal_sfs_bb.c,alongwith
thereal_sfs_ds.h(basedonthesamefilecreatedearlier),andasupportingMakefileareavailable
fromrsfsbb_code.tbz2.

BareboneSFSmoduleinaction
Oncecompiledusingmake,gettingthereal_sfs_bb.kodriver,Pugsdidhisusualunusualexperiments,
shownasinFigure38.

Figure38:BarebonerealSFSexperiments

Pugsexperiments(ExplanationofFigure38):

Checkedthekernelwindow/proc/filesystemsforthekernelsupportedfilesystems
Loadedthereal_sfs_bb.kodriver
Recheckedthekernelwindow/proc/filesystemsforthekernelsupportedfilesystems.Now,it
showssfslistedattheend
Didamountofhispendrivepartition/dev/sdb1onto/mntusingthesfsfilesystem.Checked
thedmesglogsontheadjacentwindow.(Keepinmind,thatrightnow,thesfs_fill_super()isnot
reallyreadingthepartition,andhencenotdoinganychecks.So,itreallydoesntmatterasto
howthe/dev/sdb1isformatted.)Butyes,themountoutputshowsthatitismountedusing
thesfsfilesystem

Oops!!!ButdfoutputshowsFunctionnotimplemented,cdgivesNotadirectory.Aha!!Pugshavent
implementedanyotherfunctionsinanyoftheotherfourfunctionpointerstructures,yet.So,thats
expected.

Note:Theaboveexperimentsareusingsudo.Insteadonemaygetintorootshellanddothesame
withoutasudo.

Okay,sonokernelcrashes,andabarebonefilesysteminactionYippee.Ya!Ya!Pugsknows
thatdf,cd,arenotyetfunctional.Forthat,heneedstostartaddingthevarioussystemcallsinthe
other(four)functionpointerstructurestobeabletodocoolcoolbrowsing,thesamewayasisdone
withallotherfilesystems,usingthevariousshellcommands.Andyes,Pugsisalreadyontohistask
afterallheneedstohaveageekydemoforhisfinalsemesterproject.

TwentythirdArticle>>

PlayingwithSystems
SysPlay'sBlogs

Menu

TheSemesterProjectPartVI:FileSystemonBlockDevice
1Reply

Thistwentythirdarticle,whichispartoftheseriesonLinuxdevicedrivers,enhancesthepreviously
writtenbarebonefilesystemmodule,toconnectwitharealhardwarepartition.

<<TwentysecondArticle

Sincethelastbarebonefilesystem,thefirstthingwhichPugsfiguredoutwashowtoreadfromthe
underlyingblockdevice.Followingisatypicalwayofdoingit:

structbuffer_head*bh;
bh=sb_bread(sb,block);/*sbisthestructsuper_blockpointer*/
//bh>b_datacontainsthedata
//Oncedone,bhshouldbereleasedusing:
brelse(bh);

TodotheaboveandvariousotherrealSFS(SimulaFileSystem)operations,Pugsfeltaneedtohave
hisownhandletobeakeyparameter,whichheaddedasfollows(inpreviousreal_sfs_ds.h):

typedefstructsfs_info
{

structsuper_block*vfs_sb;/*SuperblockstructurefromVFSforthisfs*/

sfs_super_block_tsb;/*Ourfssuperblock*/

byte1_t*used_blocks;/*Usedblockstracker*/

}sfs_info_t;

Themainideabehindthiswastoputallrequiredstaticglobalvariablesinasinglestructure,andpoint
thatbytheprivatedatapointerofthefilesystem,whichiss_fs_infopointerinthestruct
super_blockstructure.Withthat,thekeychangesinthefill_super_block()(in
previousreal_sfs_bb.cfile)becomes:

Allocatethestructureforthehandle,usingkzalloc()
Initializethestructureforthehandle(throughinit_browsing())
Readthephysicalsuperblock,verifyandtranslateinfofromitintotheVFSsuperblock
(throughinit_browsing())
Pointitbys_fs_info(throughinit_browsing())
UpdatetheVFSsuperblock,accordingly

Accordingly,theerrorhandlingcodewouldneedtodotheshut_browsing(info)andkfree(info).And,that
wouldadditionallyalsoneedtogoalongwiththefunctioncorrespondingtothekill_sbfunctionpointer,
definedinthepreviousreal_sfs_bb.cbykill_block_super,calledduringumount.

Heresthevariouscodepieces:

#include<linux/slab.h>/*Forkzalloc,...*/
...
staticintsfs_fill_super(structsuper_block*sb,void*data,intsilent)
{

sfs_info_t*info;

if(!(info=(sfs_info_t*)(kzalloc(sizeof(sfs_info_t),GFP_KERNEL))))

info>vfs_sb=sb;

if(init_browsing(info)<0)

kfree(info);

returnEIO;

/*UpdatingtheVFSsuper_block*/

sb>s_magic=info>sb.type;

sb>s_blocksize=info>sb.block_size;

sb>s_blocksize_bits=get_bit_pos(info>sb.block_size);

...

returnENOMEM;

}
staticvoidsfs_kill_sb(structsuper_block*sb)

sfs_info_t*info=(sfs_info_t*)(sb>s_fs_info);

kill_block_super(sb);

if(info)

shut_browsing(info);

kfree(info);

kzalloc()incontrasttokmalloc(),alsozeroesouttheallocatedlocation.get_bit_pos()isasimplePugs
waytocomputelogarithmbase2,asfollows:

staticintget_bit_pos(unsignedintval)
{

inti;

for(i=0;val;i++)

return(i1);

val>>=1;

Andinit_browsing(),shut_browsing()arebasicallythetransformationsoftheearlieruserspace
functionsofbrowse_real_sfs.cintokernelspacecodereal_sfs_ops.c,prototypedinreal_sfs_ops.h.
Thisbasicallyinvolvesthefollowingtransformations:

intsfs_handleintosfs_info_t*info
lseek()&read()intothereadfromtheblockdeviceusingsb_bread()
calloc()intovmalloc()&thenappropriateinitializationbyzeros.
free()intovfree()

Heresthetransformedinit_browsing()andshut_browsing()inreal_sfs_ops.c:

#include<linux/fs.h>/*Forstructsuper_block*/
#include<linux/errno.h>/*Forerrorcodes*/
#include<linux/vmalloc.h>/*Forvmalloc,...*/

#include"real_sfs_ds.h"
#include"real_sfs_ops.h"
intinit_browsing(sfs_info_t*info)
{

byte1_t*used_blocks;

inti,j;

sfs_file_entry_tfe;

intretval;

if((retval=read_sb_from_real_sfs(info,&info>sb))<0)

if(info>sb.type!=SIMULA_FS_TYPE)

printk(KERN_ERR"InvalidSFSdetected.Givingup.\n");

returnEINVAL;

/*Markusedblocks*/

used_blocks=(byte1_t*)(vmalloc(info>sb.partition_size));

if(!used_blocks)

for(i=0;i<info>sb.data_block_start;i++)

for(;i<info>sb.partition_size;i++)

for(i=0;i<info>sb.entry_count;i++)

if((retval=read_from_real_sfs(info,

info>sb.entry_table_block_start,

i*sizeof(sfs_file_entry_t),

&fe,sizeof(sfs_file_entry_t)))<0)

vfree(used_blocks);

returnretval;

if(!fe.name[0])continue;

for(j=0;j<SIMULA_FS_DATA_BLOCK_CNT;j++)

returnretval;

returnENOMEM;

used_blocks[i]=1;

used_blocks[i]=0;

if(fe.blocks[j]==0)break;

used_blocks[fe.blocks[j]]=1;

info>used_blocks=used_blocks;

info>vfs_sb>s_fs_info=info;

return0;

}
voidshut_browsing(sfs_info_t*info)
{

if(info>used_blocks)

vfree(info>used_blocks);

Similarly,allotherfunctionsinbrowse_real_sfs.cwouldalsohavetobetransformed,onebyone.Also,
notethereadfromtheunderlyingblockdeviceisbeingcapturedbythetwofunctions,
namelyread_sb_from_real_sfs()andread_from_real_sfs(),whicharecodedasfollows:

#include<linux/buffer_head.h>/*structbuffer_head,sb_bread,...*/
#include<linux/string.h>/*Formemcpy*/
#include"real_sfs_ds.h"
staticintread_sb_from_real_sfs(sfs_info_t*info,sfs_super_block_t*sb)
{

structbuffer_head*bh;

if(!(bh=sb_bread(info>vfs_sb,0/*Superblockisthe0thblock*/)))

memcpy(sb,bh>b_data,SIMULA_FS_BLOCK_SIZE);

brelse(bh);

return0;

returnEIO;

}
staticintread_from_real_sfs(sfs_info_t*info,byte4_tblock,

byte4_toffset,void*buf,byte4_tlen)

byte4_tblock_size=info>sb.block_size;

byte4_tbd_block_size=info>vfs_sb>s_bdev>bd_block_size;

byte4_tabs;

structbuffer_head*bh;

/*

*TranslatingtherealSFSblocknumberingtounderlyingblockdevice

*blocknumbering,forsb_bread()

*/

abs=block*block_size+offset;

block=abs/bd_block_size;

offset=abs%bd_block_size;

if(offset+len>bd_block_size)//Shouldneverhappen

if(!(bh=sb_bread(info>vfs_sb,block)))

memcpy(buf,bh>b_data+offset,len);

brelse(bh);

return0;

returnEINVAL;

returnEIO;

Alltheabovecodepiecesputintogetherasthereal_sfs_minimal.c(basedonthe
filereal_sfs_bb.ccreatedearlier),real_sfs_ops.c,real_sfs_ds.h(basedonthesamefilecreated
earlier),real_sfs_ops.h,andasupportingMakefile,alongwiththepreviously
createdformat_real_sfs.capplicationareavailablefromrsfs_on_block_device_code.tbz2.

RealSFSonblockdevice
Oncecompiledusingmake,gettingthereal_sfs_first.kodriver,Pugsdidntexpectittobewaydifferent
fromthepreviousreal_sfs_bb.kodriver,butatleastnowitshouldbereadingandverifyingthe
underlyingpartition.Andforthathefirsttriedmountingtheusualpartitionofapendrivetogetan
InvalidSFSdetectedmessageindmesgoutputandthenafterformattingit.Notethesameerrorof
Notadirectory,etcasinpreviousarticle,stillexistingasanywaysitisstillverysimilartotheprevious
barebonedriverthecorefunctionalitiesyettobeimplementedjustthatitisnowonarealblock
devicepartition.Figure39showstheexactcommandsforallthesesteps.

Figure39:ConnectingtheSFSmodulewiththependrivepartition

Note:./format_real_sfsandmountcommandsmaytakelotoftime(maybeinminutes),depending
onthepartitionsize.So,preferablyuseapartition,saylessthan1MB.

Withthisimportantstepofgettingthefilesystemmoduleinteractingwiththeunderlyingblockdevice,
thelaststepforPugswouldbetodotheothertransformationsfrombrowse_real_sfs.candaccordingly
usethemintheSFSmodule.

TwentyfourthArticle>>

TheSemesterProjectPartVII:FileSysteminAction
1Reply

Thistwentyfourtharticle,whichispartoftheseriesonLinuxdevicedrivers,getsthecompletereal
SIMULAfilesystemmoduleinaction,witharealhardwarepartitiononyourpendrive.

<<TwentythirdArticle

RealSFSinaction
Codeavailablefromrsfs_in_action_code.tbz2getstothefinaltestedimplementationofthefinal
semesterprojectofPugs&Shweta.Thiscontainsthefollowing:

real_sfs.ccontainsthecodeofearlierreal_sfs_minimal.cplustheremainingrealSIMULAfile
systemfunctionalities.Notethefilesystemsnamechangefromsfstoreal_sfs
real_sfs_ops.c&real_sfs_ops.hcontainstheearliercodeplustheadditionaloperations
neededfortheenhancedreal_sfs.cimplementations
real_sfs_ds.h(almostsamefileasinthepreviousarticle,plusaspinlockaddedintothereal
SFSinfostructuretobeusedforpreventingraceconditionsinaccessingtheused_blocksarray
inthesamestructure)
format_real_sfs.c(samefileasinthepreviousarticles)therealSFSformatterapplication
Makefilecontainstherulesforbuildingthedriverreal_sfs_final.kousingthereal_sfs_*.*files,
andtheformat_real_sfsapplicationusingtheformat_real_sfs.c

Withalltheseandearlierdetails,Shwetacompletedtheirprojectdocumentation.Andsofinally,Shweta
&Pugswereallsetfortheirfinalsemesterprojectdemo,presentationandviva.

Thehighlightsoftheirdemo(onrootshell)areasfollows:

Loadingreal_sfs_finaldriver:insmodreal_sfs_final.ko
Usingthepreviouslyformattedpendrivepartition/dev/sdb1orReformattingitusing
theformat_real_sfsapplication:./format_real_sfs/dev/sdb1.Caution:Pleasecheckoutthe
completedetailedstepsonthisfromthepreviousarticle,beforeyouactuallyformatit
MounttherealSFSformattedpartition:mounttreal_sfs/dev/sdb1/mnt
AndAndwhat?Browsethemountingfilesystem.Useyourusualshellcommandstooperate
onthefilesystem:ls,cd,touch,vi,rm,chmod,

Figure40showstherealSIMULAfilesysteminaction

Figure40:TherealSIMULAfilesystemmoduleinaction

Realitiesbehindtheaction
Andifyoureallywanttoknow,whataretheadditionalenhancementsPugsaddedtotheprevious
articlescodetogettothislevel,itisbasicallythefollowingcoresystemcallsaspartoftheremaining4
outof5setofstructuresoffunctionpointers(inreal_sfs.c):

1. write_inode(understructsuper_operations)sfs_write_inode()basicallygetsapointertoan
inodeintheVFSinodecache,andisexpectedtosyncthatwiththeinodeinphysicalhardware
spacefilesystem.Thatisachievedbycallingtheappropriatelymodifiedsfs_update()(defined
inreal_sfs_ops.c)(adaptedfromtheearlierbrowse_real_sfsapplication).Thekeyparameter
changesbeingpassingtheinodenumberinsteadofthefilenameandtheactualtimestamp
insteadoftheflagforitsupdatestatus.Andaccordingly,thecalltofilename
basedsfs_lookup()isbeingreplacedbyinodenumberbasedsfs_get_file_entry()(defined
inreal_sfs_ops.c),andadditionallynowthedatablocksarealsobeingfreed
(usingsfs_put_data_block()(definedinreal_sfs_ops.c)),ifthefilesizehasreduced.Notethat
sfs_put_data_block()(definedinreal_sfs_ops.c)isatransformationoftheput_data_block()from
thebrowse_real_sfsapplication.Also,notetheinterestingmappingto/fromtheVFSinode
numberfrom/toourzerobasedfileentryindices,usingthe
macrosS2V_INODE_NUM()/V2S_INODE_NUM()inreal_sfs_ops.h.
Andfinally,underlyingwriteisbeingachievedusingwrite_to_real_sfs(),afunctionadded
inreal_sfs_ops.c,verysimilartoread_from_real_sfs()(alreadythereinreal_sfs_ops.c),except
thedirectionreversalofthedatatransferandmarkingthebufferdirtytobesyncedupwiththe
physicalcontent.Alongwith,inreal_sfs_ops.c,twowrapperfunctionsread_entry_from_real_sfs()
(usingread_from_real_sfs())andwrite_entry_to_real_sfs()(usingwrite_to_real_sfs())havebeen
writtenandusedrespectivelyforthespecificrequirementsofreadingandwritingthefileentries,
toincreasethecodereadability.sfs_write_inode()andsfs_update()areshowninthecode
snippetbelow.sfs_write_inode()hasbeenwritteninthefilereal_sfs.c.Forothers,checkoutthe
filereal_sfs_ops.c

#if(LINUX_VERSION_CODE<KERNEL_VERSION(2,6,34))
staticintsfs_write_inode(structinode*inode,intdo_sync)
#else
staticintsfs_write_inode(structinode*inode,structwriteback_control*wbc)
#endif
{

sfs_info_t*info=(sfs_info_t*)(inode>i_sb>s_fs_info);

intsize,timestamp,perms;

printk(KERN_INFO"sfs:sfs_write_inode(i_ino=%ld)\n",inode>i_ino);

if(!(S_ISREG(inode>i_mode)))//RealSFSdealsonlywithregularfiles

size=i_size_read(inode);

timestamp=inode>i_mtime.tv_sec>inode>i_ctime.tv_sec?

perms=0;

perms|=(inode>i_mode&(S_IRUSR|S_IRGRP|S_IROTH))?4:0;

perms|=(inode>i_mode&(S_IWUSR|S_IWGRP|S_IWOTH))?2:0;

perms|=(inode>i_mode&(S_IXUSR|S_IXGRP|S_IXOTH))?1:0;

printk(KERN_INFO"sfs:sfs_write_inodewith%dbytes@%dsecsw/%o\n",

returnsfs_update(info,inode>i_ino,&size,&timestamp,&perms);

return0;

inode>i_mtime.tv_sec:inode>i_ctime.tv_sec;

size,timestamp,perms);

}
intsfs_update(sfs_info_t*info,intvfs_ino,int*size,int*timestamp,int*perms)
{

sfs_file_entry_tfe;

inti;

intretval;

if((retval=sfs_get_file_entry(info,vfs_ino,&fe))<0)

if(size)fe.size=*size;

if(timestamp)fe.timestamp=*timestamp;

if(perms&&(*perms<=07))fe.perms=*perms;

for(i=(fe.size+info>sb.block_size1)/info>sb.block_size;

if(fe.blocks[i])

sfs_put_data_block(info,fe.blocks[i]);

fe.blocks[i]=0;

returnwrite_entry_to_real_sfs(info,V2S_INODE_NUM(vfs_ino),&fe);

returnretval;

i<SIMULA_FS_DATA_BLOCK_CNT;i++)

2. create,unlink,lookup(understructinode_operations)Allthe3
functionssfs_inode_create(),sfs_inode_unlink(),sfs_inode_lookup()havethe2common

parameters(theparentsinodepointerandthepointertothedirectoryentryforthefilein
consideration),andtheserespectivelycreate,delete,andlookupaninodecorrespondingtotheir
directoryentrypointedbytheirparameter,saydentry.
sfs_inode_lookup()basicallysearchesfortheexistenceofthefilenameunderneathusingthe
appropriatelymodifiedsfs_lookup()(definedinreal_sfs_ops.c)(adaptedfromthe
earlierbrowse_real_sfsapplicationtheadoptionbeingreplacingtheuser
spacelseek()+read()combobytheread_entry_from_real_sfs()).Iffilenameisnotfound,thenit
invokesthegenerickernelfunctiond_splice_alias()tocreateanewinodeentryintheunderlying
filesystem,forthesame,andthenattachesittothedirectoryentrypointedbydentry.Otherwise,
itjustattachestheinodefromVFSinodecache(usinggenerickernelfunctiond_add()).This
inode,ifobtainedfresh(I_NEW),needstobefilledinwiththerealSFSlookedupfileattributes.
Inalltheaboveimplementationsandinthosetocome,afewbasicassumptionshavebeen
made,namely:

RealSFSmaintainsmodeonlyforuserandthatismappedtoall3ofuser,group,otherof
theVFSinode
RealSFSmaintainsonlyonetimestampandthatismappedtoall3ofcreated,modified,
accessedtimesoftheVFSinode.
sfs_inode_create()andsfs_inode_unlink()correspondinglyinvokesthe
transformedsfs_create()andsfs_remove()(definedinreal_sfs_ops.c)(adaptedfromthe
earlierbrowse_real_sfsapplication),forrespectivelycreatingandclearingtheinodeentriesat
theunderlyinghardwarespacefilesystem,apartfromtheusualinodecacheoperations,
usingnew_inode()+insert_inode_locked(),d_instantiate()&inode_dec_link_count(),insteadof
theearlierlearntiget_locked(),d_add().Apartfromthepermissionsandfileentryparameters,
andreplacinglseek()+read()combobyread_entry_from_real_sfs(),sfs_create()hasan
interestingtransformationfromuserspacetokernelspace:time(NULL)toget_seconds().Andin
bothofsfs_create()andsfs_remove()theuserspacelseek()+write()combohasbeenreplaced
bytheobviouswrite_entry_to_real_sfs().Checkoutalltheabovementionedcodepiecesinthe
filesreal_sfs.candreal_sfs_ops.c,asmentioned.

3. readpage,write_begin,writepage,write_end(understructaddress_space_operations)All
theseaddressspaceoperationsarebasicallytoreadandwriteblocksontheunderlying
filesystem,andareachievedusingtherespectivegenerickernel
functionsmpage_readpage(),block_write_begin(),block_write_full_page(),generic_write_end().
Firstoneisprototypedin<linux/mpage.h>andremaining3in<linux/buffer_head.h>.Now,
thoughthesefunctionsaregenericenough,alittlethoughtwouldshowthatthefirstthreeof
thesewouldultimatelyhavetodoarealSFSspecifictransactionwiththeunderlyingblockdevice
(thehardwarepartition),usingthecorrespondingblocklayerAPIs.Andthatexactlyisachieved
bytherealSFSspecificfunctionsfs_get_block(),whichisbeingpassedintoandusedbythefirst
threefunctions,mentionedabove.

sfs_get_block()(definedinreal_sfs.c)isinvokedtoreadaparticularblocknumber(iblock)ofa
file(denotedbyaninode),intoabufferhead(bh_result),optionallyfetching(allocating)anew
block.Soforthat,theblockarrayofcorrespondingrealSFSinodeislookedupintoandthenthe
correspondingblockofthephysicalpartitionisfetchedusingthekernelAPImap_bh().Again
notethattofetchanewblock,weinvokethesfs_get_data_block()(definedinreal_sfs_ops.c),
whichisagainatransformationoftheget_data_block()fromthebrowse_real_sfsapplication.
Also,incaseofanewblockallocation,therealSFSinodeisalsoupdatedunderneath,
usingsfs_update_file_entry()aonelinerimplementationinreal_sfs_ops.c.Codesnippetbelow
showsthesfs_get_block()implementation.

staticintsfs_get_block(structinode*inode,sector_tiblock,

structbuffer_head*bh_result,intcreate)

structsuper_block*sb=inode>i_sb;

sfs_info_t*info=(sfs_info_t*)(sb>s_fs_info);

sfs_file_entry_tfe;

sector_tphys;

intretval;

printk(KERN_INFO"sfs:sfs_get_blockcalledforI:%ld,B:%lld,C:%d\n",

if(iblock>=SIMULA_FS_DATA_BLOCK_CNT)

if((retval=sfs_get_file_entry(info,inode>i_ino,&fe))<0)

if(!fe.blocks[iblock])

if(!create)

else

if((fe.blocks[iblock]=sfs_get_data_block(info))==

if((retval=sfs_update_file_entry(info,inode>i_ino,&fe))

inode>i_ino,iblock,create);

returnENOSPC;

returnretval;

returnEIO;

INV_BLOCK)
returnENOSPC;

<0)

phys=fe.blocks[iblock];

map_bh(bh_result,sb,phys);

return0;

returnretval;

4. open,release,read,write,aio_read/read_iter(sincekernelv3.16),aio_write/write_iter(since
kernelv3.16),fsync(underaregularfilesstructfile_operations)Alltheseoperationsshould
basicallygothroughthebuffercache,i.e.shouldbeimplementedusingtheaddressspace
operations.Andthisbeingacommonrequirement,thekernelprovidesagenericsetofkernel
APIs,namelygeneric_file_open(),do_sync_read()/new_sync_read()(sincekernel
v3.16),do_sync_write()/new_sync_write()(sincekernel
v3.16),generic_file_aio_read()/generic_file_read_iter()(sincekernel
v3.16),generic_file_aio_write()/generic_file_write_iter()(sincekernel
v3.16),simple_sync_file()/noop_fsync()(sincekernelv2.6.35).NotethatthereisnoAPIfor
release,asitisareturn0API.Checkoutthereal_sfs.cfilefortheexactdefinitionofstruct
file_operationssfs_fops.
5. readdir/iterate(sincekernelv3.11)(underadirectorysstructfile_operations)
sfs_readdir()/sfs_iterate()primarilyreadsthedirectoryentriesofanunderlyingdirectory
(denotedbyfile),andfillsitupintotheVFSdirectoryentrycache(pointedbydirentparameter)
usingtheparameterfunctionfilldir,orintothedirectorycontext(pointedbyctxparameter)(since
kernelv3.11).
AsrealSFShasonlyonedirectory,thetopone,thisfunctionbasicallyfillsupthedirectoryentry
cachewithdirectoryentriesforallthefilesintheunderlyingfilesystem,usingthe
transformedsfs_list()(definedinreal_sfs_ops.c),adaptedfromthebrowse_real_sfsapplication.
Checkoutthereal_sfs.cfileforthecompletesfs_readdir()/sfs_iterate()implementation,which
startswithfillingdirectoryentriesfor.(currentdirectory)and..(parentdirectory)using
parameterfunctionfilldir(),orgenerickernelfunctiondir_emit_dots()(sincekernelv3.11),and
thenforallthefilesoftherealSFS,usingsfs_list().
6. put_super(understructsuper_operations)Thepreviouscustom
implementationsfs_kill_sb()(pointedbykill_sb)hasbeenenhancedbyseparatingitintothe
custompartbeingputintosfs_put_super()(andnowpointedbyput_super),andthe
standardkill_block_super()beingdirectlypointedbykill_sb.Checkoutthefilereal_sfs.cforall
thesechanges.

Withalltheseinplace,onecouldseetheamazingdemobyPugsinaction,asshowninFigure40.And

dontforgetwatchingthelivelogin/var/log/messagesusingatailf/var/log/messages,matchingitwith
everycommandyouissueonthemountedrealSFSfilesystem.Thiswouldgiveyouthebestinsight
intowhendoeswhichsystemcallgetscalled.Or,inotherwordswhichapplicationinvokeswhichsystem
callfromthefilesystemfront.Fortracingallthesystemcallsinvokedbyanapplication/command,use
stracewiththecommand,e.g.typestracelsinsteadofjustls.

Notes
1. OndistroslikeUbuntu,youmayfindthelogunder/var/log/sysloginsteadof/var/log/messages

Vous aimerez peut-être aussi