AnintroductiontoSCSIdriversAnintroductiontoSCSIdriversAnintroductiontoSCSIdriversAlanCoxalan@redhat.
com1.
CopyrightandLicensingCopyright(c)1999byAlanCox.
ThismaterialmaybedistributedonlysubjecttothetermsandconditionssetforthintheOpenPublicationLicense,v0.
4(8June1999)orlater(thelatestversionispresentlyavailableathttp://www.
opencontent.
org/openpub/).
2.
AnIntroductiontoSCSIDriversForthismonthsarticleI'mgoingtoattempttocoverwritingadriverforasimpleSCSIcontrollerunderLinux.
TheLinuxkerneldoesmostoftheworkforSCSIdevicessoa'dumb'SCSIinterfacecanactuallyberelativelypainlesstowrite.
FormoreadvanceddeviceshowevertheSCSIlayerisactuallytooclever.
Thereareplansafoottostreamlineitbecauseofthis.
ThejoboftheSCSIdriverisdifferenttothatofablockdriver.
TheupperlayersoftheSCSIcodehandletheCD-ROM,Diskandotherdevices.
RequestsareturnedintoSCSIcommandblocksbeforetheyarefedtoyourdriver.
ThismeansyourSCSIdriverneedonlyworryaboutSCSIandnotaboutotheraspectsofthekerneldevicestructure.
InordertoillustratetheSCSIdriversI'mgoingtoinventaSCSIcontrollerthathasasimpleandeasytousecommandinterface.
Theysadlydon'ttendtoexist.
Itdoeshowevermakeitaloteasiertofollowtheexample.
EvensowithinthelimitsofamagazineIcanonlycoverthebasicsandhopefullyenoughtogetpeoplestarted.
ALinuxSCSIdrivercontainssevenmainfunctionsThedetectfunctioniscalledfirstbytheSCSIlayer.
IthasthejobofscanningforthecontrollersandregisteringthoseitfindswiththeSCSIlayer.
OncetheyareregisteredtheSCSIlayerwillissuecommandstothecontrollertoprobetheSCSIdevicesonthechain.
Thecommandfunctionissuesacommandsynchronouslyandwaitsforittocomplete.
Mostdriversimplementthisbycallingtheirownqueuecommandfunction.
Thequeuecommandfunctionissuesacommandanddoesnotwaitforittofinish.
Thisisusedbyalmostalloperations.
WhenthecommandcompletesthedrivercallsbackintotheSCSIlayertoinformtheSCSIlayerofthecompletionandpassesbackanyerrorinformation.
AbortandResetareusedtohandleerrorsituationsorcaseswheretheSCSIlayerthinksacommandhasgonemissing.
TheSCSIlayerwillfirstattempttoabortthecommandthenifneedbestartusingalargerhammerontheproblemuntilitgetstothepointoftryingtoresettheentirecontroller.
Hopefullythiswillneverhappen.
Theinfofunctionreturnsadescriptionofthecontrolleritself.
Thisisgenerallyaveryshortpieceofcodeindeed.
FinallythemappingofSCSItoPCdiskgeometryhasneverbeenexactlyastandard.
Thebios_paramfunctioniscalledbytheSCSIlayertoaskthecontrollertoeitherqueryitsBIOSforthefakeddiskgeometryortocomputeageometry(hopefullyusingthesamealgorithmasthecontrollerBIOSitself).
Thefirstfunctiontolookatindetailistheprobefunction.
ThisiscalledatboottimeorwhenaSCSImoduleisloaded.
Forourexampleweassumetherecanonlybeonecardandthatitbehavessanely.
intmyscsi_detect(Scsi_Host_Template*tpnt){structScsi_Host*shpnt;intio=0x320,irq=11;/*Assumefixedforexample*/ForourexamplewewilluseafixedIOandIRQ.
ArealcontrollerwouldeitherreadPCIspaceorwouldprobealistofcommonaddresses.
Wewillalsohidetheprobelogicinafunction.
if(myscsi_probe(io,irq)==0){/*Found-createaninstanceofthiscontroller*/shpnt=scsi_register(tpnt,0);if(shpnt==NULL)return0;Thefirstthingwedoistoaskscsi_registertomakeusadevice.
Thetpntisthetemplatepassedintothisfunctionandwhosedefinitionwewilldescribelaterinthearticle.
Itbasicallydefinesthistypeofcard.
Thereturnedpointisaninstanceofthecard.
Wepass0forthesecondargumentasweneednoprivatedataareaattaching.
Passingasizearrangesforaprivateblocktobeallocatedasshpnt->hostdatashpnt->unique_id=io;shpnt->io_port=io;shpnt->n_io_port=MY_PORT_RANGE;shpnt->irq=irq;shpnt->this_id=MY_SCSI_ID;Nowwestarttofillinthestructure.
Theunique_idisfortellingcardsapart.
InourcasetheI/Oportisaconvenientchoiceforthis.
'this_id'holdstheIDofthecontrolleritself.
EachSCSIdevicehasanidentityincludingthecontroller.
TheSCSIlayerneedstoknowthecontrollersID.
Weassumeforthiscaseitisfixed.
my_hardware_init(shpnt);Initializemyhardware.
Yougettowriteallofthisbit.
if(request_irq(irq,my_irq_handler,0,"myscsi",shpnt)){scsi_unregister(shpnt);printk("my_scsi:IRQ%disbusy.
",irq);return0;}Ifwecan'tregisterourinterrupthandlerweareabitstuck.
Ifsoweunregisterourscsicontrollerandreportnocontrollersfound.
Wealsolettheuserknowsoastoavoidconfusion.
}return1;}Andifitworkedwereportthatwefound1controller.
TheSCSIlayerwillnowgooffandscanallourdevices.
Timetowritethecommandfunctions.
intmyscsi_queuecommand(Scsi_Cmnd*SCpnt,void(*done)(Scsi_Cmnd*)){intio,i;unsignedlongflags;io=SCpnt->host->io_port;/*DigoutourI/Oport*/current_command=Scpnt;Forthisexamplewewillassumethatthecontrollerhandlesonlyonecommandatatime.
TypicalforacheapISAcontroller,notfordecenthardware.
Ifwesupportedmanycommandswecouldn'tkeepaglobalcurrent_commandbutwouldneedtokeepsomekindoflistandmatchrepliesfromthecardtothelistentries.
current_command->scsi_done=done;current_command->SCp.
Status=0;Weneedtorememberwhattocallwhenthecommandcompletes.
Nextwesetupthecommand.
Ourcardishypotheticalandratherover-smartforabasicISAdevice.
Youwon'tbesolucky.
.
.
save_flags(flags);outb(SCpnt->target,io+TARGET_PORT);for(i=0;icmd_len;i++)outb(SCpnt->cmnd[i],io+BUF+i);outb(COMMAND_BEGIN,io+COMMAND);Firstlyweloadthetargetdeviceintothecard,thenthecommand.
SCSIcommandsareblocksofupto16bytesincludinglengthinformation.
Aftershovingitontothecardwecanletthecardbeginoperation,andalsowecanallowinterruptsaswearereadytohandletheresultofthecommand.
restore_flags(flags);return0;andwereturnbacktotheSCSIlayer,thecommandisqueuedandhopefullysomethingwillhappen.
IfnotthentheSCSIlayerwillbotherusafteratimeout.
WhentheSCSIlayerdoeswanttobotherusaboutcommandsthathavegonewalkiesthenitwillcallourabortfunctionthenifthatfailsourresetfunction.
Manysimplercontrollerscannotsupporttheabortfunction.
Ifsotheabortfunctionisniceandsimpleintmyscsi_abort(Scsi_Cmnd*SCpnt){returnSCSI_ABORT_SNOOZE;}Weaskthekerneltowaitabitlongerandhope.
Intheendthekernelwillgetboredofwaitingandcallourresethandler.
WecanalsoreportSCSI_ABORT_PENDINGtoindicatethecommandisbeingabortedbutthatithasnotyetaborted-forexampleifaninterruptmustoccurfromthecardconfirmingtheabort,andwecanreturnSCSI_ABORT_SUCCESSifweabortedthecommand.
FinallywecanreportSCSI_ABORT_BUSYifwearebusyorthereissomeotherreasonwewouldliketoabortbutcannotdosorightnow.
AftertryingtoabortandreissuefailingcommandstheSCSIlayerwilltrytoresetthings.
Ittriestoresetfirstthedeviceincasethathasbecomeconfused,thentoresettheSCSIbusincasethebusitselfhaslockedup.
Finallyittriestoresetthecontrollerincasethehardwarehaschoked.
Howyouhandlethisdependsontheabilityofthecontrolleritself.
intmyscsi_reset(Scsi_Cmnd*SCpnt,unsignedintflags){myhardware_reset(SCpnt->host);returnSCSI_RESET_PENDING;}Forourexampleweassumethatthecontrollerisfairlydumb.
Weignoretheflaghintsandweresetthedevice.
TheSCSI_RESET_PENDINGreturnindicatesthatthebushasbeenresetbutthatcommandswillbereturnedwithafailurestatuslater.
IfthecontrollerresetreturnedthecommandsimmediatelywecouldreissuethecommandsandreturnSCSI_RESET_SUCCESS.
IfwedonotthinkthistypeofresetisappropriatewecanreturnSCSI_RESET_PUNT.
Youshouldatleastsupportresettingthebus.
Theflagsfieldisasetoffourflagsdesignedtoprovidehintsastowhattoresetandhow.
TheimportantflagsareSCSI_RESET_SUGGEST_BUS_RESETwhentheSCSIlayerthinkstheentirebusshouldberesetandSCSI_RESET_SUGGEST_HOST_RESETwhichisthelastresorthinttothedriverthatthingsarebadandthatitmightbeappropriatetocompletelyrestarttheboarditself.
We'veissuedcommandsandwecanstartanabort.
Atthispointwecan'tgetanyfurtherwithoutconsideringtheinterrupthandler.
Theneedsoftheinterrupthandlercanvaryalotbetweencards.
ForourexampledriverI'mgoingtoassumethatitwillinterruptusoncewhenitwantsthedatatosend/receiveandonceoncommandcompletion.
intmy_irq_handler(intirq,void*dev_id,structpt_regs*regs){structScsi_Host*shpnt=dev_id;intio=shpnt->io_port;u16data;Whenwerequestedtheinterruptweusedthehostpointerasthe'dev_id'-adevicespecificfieldthatispassedtothehandlerbythekernel.
Thismakesitveryeasyforustofindwhichcardwearehandlinginadriverthatissupportingmultipleinterfacecards.
WethendigoutourI/Oportaswewillprobablyneedthisalotinamoment.
data=inw(io+READ_STATUS);if(data&RESET_DONE){current_command->result=DID_RESETscsi_done(current_command);return;}Firstlywecheckifthebushasbeenreset(eitherbyusorotherdevices).
Ifsowereportthecommandwasreset.
ThiswillalsotelltheSCSIlayerthattheresetwereportedaspendinginourresethandlerhasnowcompleted.
if(data&PARITY_ERROR){current_command->result=DID_PARITYscsi_done(current_command);return;}Wecheckforparityerrors.
Wewouldcheckforasmanyerrorsaswecanidentifycleanlyonarealcard.
Foranerrorwithnoexactdetailweif(data&GENERAL_ERROR){current_command->result=DID_ERRORscsi_done(current_command);return;}TheSCSImidlayerwillhandledoingtherightthingstorecoverfromanerrorsituation.
NextwelooktoseeifthisisaSCSIphasechange(SCSIcommandspassthroughasetofphases.
Asmartcontrollerhandlesallofthisadumboneless.
Inourcasewewillassumethattheonlyphasesthatneedhelpare'datain'and'dataout'-wherewecopybytestoorfromtheSCSIdeviceweissuedacommand.
if(data&DATA_OUT){outsw(port+DATA_FIFO,current_command->request_buffer,current_command->request_bufflen);}Tosenddataweblastthebuffertothecontroller.
ThismaywellbedonebyDMAinarealcontroller.
Ourexamplewekeepsimple.
Oninputwecheckhowmanybyteswerereceivedandcopythemtotherequestbuffer-whichisprobablyapageofdiskcachemostofthetime.
Wedon'thavetoworrywhereitgoeshowever,justthatitfits.
if(data&DATA_IN){intlen=inw(port+DATA_LEN);if(len>current_command->request_bufflen)len=current_command->request_bufflen;insw(port+DATA_FIFO,current_command->request_buffer,current_command->request_bufflen);}Finallycheckifacommandfinished.
IfsoputthedeviceSCSIstatusinthelowbyteoftheresponseandtelltheSCSIlayerthecommandhascompleted.
Thetop16bitsholdthekernelinfo,thebottomtheSCSIinfo.
Thetop16bitsfornoerrorare0preciselytomakethissimple.
if(data&COMMAND_DONE){current_command->status=inb(port+CMD_STATUS);current_command->scsi_done(current_command);}}andweexitourinterrupt.
SCSIcommandscanbeissuedsynchronouslyalthoughthisisnowbasicallydeadandwedothingsproperly.
Supportingthesynchronouscommandsisbestdoneintermsofthequeuecommandfunctionandthecodebelowisbasicallyboilerplateusedbyalmosteverydriver.
staticvoidit_finished(Scsi_Cmnd*SCpnt){SCpnt->SCp.
Status++;}intmyscsi_command(Scsi_Cmnd*SCpnt){myscsi_queuecommand(SCpnt,it_finished);while(!
SCpnt->SCp.
Status)barrier();returnSCpnt->result;}Wequeueacommandandtellthequeuefunctionthatthe'completion'handler(scsi_done)istoincrementthestatus.
Havingissuedthecommandwespininaloopuntilthecommandfinishes.
Thebarrier()statementisimportanthere.
Gccmightotherwiseoptimizewhile(variable)toif(variable)while(1);Barriertellsitthatitcannotcachevaluesfromvariablesacrossthebarrier()functioncall.
Thisensuresthatthestatus,whichischangedbyaninterruptwillbeseenbytheloopingcode.
ThiscompletestheSCSIcommandhandlersforoursimplecard.
Theyarenotoptimizedandourcardisalittlesimplistic.
Westillneedtofillinthegeometryfunctionandtheinfofunction.
Theinfofunctionreturnsatextdescriptionforourcontroller.
constchar*myscsi_info(structScsi_Host*SChost){return("MySCSIdevice");}itcould(perhapsshouldinfact)returntheI/OandIRQinformation,driverversionandothervaluableinformationtoo.
Thebios_paramfunctionmapsourSCSIdisktoaPCBIOSfakedgeometry.
Realdisksdon'thavethesimplegeometrythePChas,buteveryonehascarriedonfakingitratherthanfixingalltheoperatingsystems.
Thuswehavetocontinuethisfiction.
WeneedtousethesamealgorithmastheBIOSorlifewillbemessy.
ThisexampleistakenfromtheSymbios53c416driverandisquitetypicalintsym53c416_bios_param(Disk*disk,kdev_tdev,int*ip){intsize;size=disk->capacity;ip[0]=64;/*heads*/ip[1]=32;/*sectors*/if((ip[2]=size>>11)>1024)/*cylinders,testforbigdisk*/{ip[0]=255;/*heads*/ip[1]=63;/*sectors*/ip[2]=size/(255*63);/*cylinders*/}return0;}Giventhedisksizewefillinanarrayofintegersfortheheads,sectorsandcylindersofourdisk.
Weactuallywanttobesurethattheseareright.
GettingthemappingwrongwillgivepeoplewhousemixedLinux/DOSdiskscorruptedfilesystemsandgenerateunhappymail.
Allisnowfineexceptthattounloadthemoduleweneedtocleanupourresources.
Weprovideareleasefunctionforthis.
intmyscsi_release(structScsi_Host*SChost){free_irq(SChost->irq,SChost);return0;}ArealdrivershouldofcoursehaveallocatedandfreedtheI/Oportsitusedtoo.
TomakeourdriveraSCSImodulewehavetoincludesomemagicattheendofthefile#ifdefMODULEScsi_Host_Templatedriver_template=MYSCSI;#include"scsi_module.
c"#endifThisgeneratestheinit_moduleandcleanup_modulecodeneededforaSCSIdevice,ratherthantheauthorhavingtoreplicateiteachtime.
TheMYSCSIobjectisadefineweneedtocreateinaheaderfilewealsoinclude.
Itisadefineinaseparatefileasforacompiledindriverwewillneeditagain.
Ourmyscsi.
hfilelookslikeexternintmyscsi_detect(Scsi_Host_Template*);externconstchar*myscsi_info(structScsi_Host*).
.
.
todeclaretheroutinesweprovide.
ThenwedefinedtheMYSCSItemplate#defineMYSCSI{name:"MySCSIDemo",detect:myscsi_detect,info:myscsi_info,command:myscsi_command,queuecommand:myscsi_queuecommand,abort:myscsi_abort,reset:myscsi_reset,bios_param:myscsi_bios_param,ThispartdefinestheSCSIfunctionsweuse.
The"field:value"formatisagccextensionwhichsetsagivenfieldinastructureratherthanlistingallthefieldsinorder.
can_queue:1,Totellthekernelwecanqueuecommandsandreturnthis_id:MY_SCSI_ID,OurhostSCSIidsg_tablesize:SG_NONE,Scattergatherisaveryusefulextensionforperformance.
Forthissimpledriverwedon'tsupportit.
cmd_per_lun:1,WecanhaveatmostonecommandoutstandingperLUN(logicalunit).
unchecked_isa_dma:1,IfyousetthistozerothekernelwilldothehardworkofensuringallthediskbuffersarecopiedintoISAbusaccessiblememorywhenneeded.
ThisonlymatterstoISAbuscontrollersthatdoDMA.
use_clustering:ENABLE_CLUSTERING,Weturnonclustering.
ClusteringtellstheSCSIlayerthatitisworthtryingtomergemultiplediskreadorwriterequestsintoasingleSCSIcommand.
Averyintelligentcontrollermaywellnotsetthis.
proc_dir:&myscsi_proc}Lastlywedefineourdirectoryfor/proc/scsi.
Wehaven'tputthisintothedriveryetsoweaddstructproc_dirmyscsi_proc={PROC_SCSI_MYSCSI,"myscsi",6,/*Lengthofname*/S_IFDIR|S_IRUGO|S_IXUGO,2};whichwillbeusedtoinstallourdirectoryin/proc/scsi.
ThePROC_SCSI_MYSCSIneedstobeaddedtoinclude/linux//proc_fs.
htogetauniqueinodenumberforthisdirectoryin/proc/scsi.
Thescsi_directory_inosenumerationissimplyalistofallthepossibledevices.
WedropourentryinbeforethedebuggingdriverPROC_SCSI_FCAL,PROC_SCSI_I2O,PROC_SCSI_MYSCSI,/*here*/PROC_SCSI_SCSI_DEBUG,HopefullythisarticlehasprovidedenoughgroundingthatthoseinterestedinwritingSCSIdriverscannowfollowthroughexistingdrivers-especiallysimpleoneslikethesymbios53c416driverandseehowtoimplementanewone.
Pia云是一家2018的开办的国人商家,原名叫哔哔云,目前整合到了魔方云平台上,商家主要销售VPS服务,采用KVM虚拟架构 ,机房有美国洛杉矶、中国香港和深圳地区,洛杉矶为crea机房,三网回程CN2 GIA,带20G防御,常看我测评的朋友应该知道,一般带防御去程都是骨干线路,香港的线路也是CN2直连大陆,目前商家重新开业,价格非常美丽,性价比较非常高,有需要的朋友可以关注一下。活动方案...
HostKvm是一家成立于2013年的国外主机服务商,主要提供VPS主机,基于KVM架构,可选数据中心包括日本、新加坡、韩国、美国、俄罗斯、中国香港等多个地区机房,均为国内直连或优化线路,延迟较低,适合建站或者远程办公等。商家本月针对香港国际机房提供特别7折优惠码,其他机房全场8折,优惠后2G内存香港VPS每月5.95美元起,支持使用PayPal或者支付宝付款。下面以香港国际(HKGlobal)为...
41云怎么样?41云是国人主机品牌,目前经营产品有国内外云服务器、CDN(高防CDN)和物理机,其中国内外云服务器又细分小类有香港限流量VPS、香港大带宽VPS、香港弹性自选VPS、香港不限流VPS、香港BGP线路VPS、香港Cera+大带宽机器、美国超防VPS、韩国原生VPS、仁川原生VPS、日本CN2 VPS、枣庄高防VPS和金华高防VPS;物理机有美国Cera服务器、香港单程CN2服务器、香...
ignore_user_abort为你推荐
淘宝门户淘宝电脑端登录首页站酷zcool北京站酷网络科技有限公司怎么样?sherylsandberg这个文章什么意思 给个翻译好吗 谢谢了7788k.comwww.k6320.com 大家给我看看这网站是真是假...haole018.com为什么www.haole008.com在我这里打不开啊,是不是haole008换新的地址了?www.baitu.com谁有免费的动漫网站?百度指数词百度指数我创建的新词lcoc.toptop weenie 是什么?www.ijinshan.com驱动人生是电脑自带的还是要安装啊!?在哪里呢?没有找到www.jizzbo.comwww.toubai.com是什么网站
我的世界服务器租用 联通vps 国外vps租用 duniu 联通c套餐 zpanel t楼 googleapps 海外服务器 12306抢票攻略 监控宝 刀片服务器是什么 免费吧 免费测手机号 33456 电信主机 免费外链相册 ledlamp 阿里云邮箱登陆 ipower 更多