0% found this document useful (0 votes)
299 views163 pages

Device Drivers For Your Girlfriend Sysplay Dot in

This document discusses Linux device drivers and introduces the concept to someone unfamiliar. It begins by explaining what a device driver is and provides examples. It then discusses different types of device drivers in Linux based on the interface they provide. It also discusses driver organization and relationships between different components like bus drivers, device controllers, and the kernel. The document uses conversations between two characters, Pugs and Shweta, to engage the reader and explain concepts in an accessible way for newcomers to the topic.

Uploaded by

myhomenet1191881
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
299 views163 pages

Device Drivers For Your Girlfriend Sysplay Dot in

This document discusses Linux device drivers and introduces the concept to someone unfamiliar. It begins by explaining what a device driver is and provides examples. It then discusses different types of device drivers in Linux based on the interface they provide. It also discusses driver organization and relationships between different components like bus drivers, device controllers, and the kernel. The document uses conversations between two characters, Pugs and Shweta, to engage the reader and explain concepts in an accessible way for newcomers to the topic.

Uploaded by

myhomenet1191881
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 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

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy