12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202 |
- MODULE UsbStorageBase; (** AUTHOR "staubesv"; PURPOSE " USB Mass Storage Driver Base Class"; *)
- (**
- * Bluebottle USB Mass Storage Driver Base Class
- *
- * This module is the base for all USB mass storage device drivers. There are three different transport layers:
- *
- * UsbStorageCbi.Mod CB/I transport layer
- * UsbStorageBot.Mod Bulk-only transport layer
- * UsbStorageScm.Mod SCM transport layer
- *
- * This driver is based on the work of Christian Plattner.
- *
- * Usage:
- *
- * UsbStorageBase.Show ~ displays a list of all USB storage devices
- *
- * References:
- *
- * [1] Universal Serial Bus Mass Storage Class Specification Overview, Revision 1.2, June 23, 2003
- * [2] Universal Serial Bus Mass Storage Class UFI Command Specification, Revision 1.0, December 1998
- * [3] Universal Serial Bus Mass Storage Specification for Bootability, Revision 1.0, October 25, 2004
- *
- * All references available at www.usb.org
- *
- * History:
- *
- * 30.09.2000 cp first release
- * 20.10.2000 cp many improvements (BulkOnly, UTS)
- * 20.11.2005 Added support for logical devices, use Disks.Mod-compatible result codes on transport layer (staubesv)
- * 24.11.2005 Added UFI commands TestUnitReady, Allow/Prevent medium removal (staubesv)
- * 29.11.2005 Added UFI commands SendDiagnostic and Start/Stop Unit (used and therefore named to EjectMedia,
- * moved ReadCapacity to USB layer (staubesv)
- * 13.12.2005 Adapted to USBDI changes (staubesv)
- * 14.12.2005 UsbStorageDevice.Transfer, GetSize & Handle made exclusive (staubesv)
- * 15.12.2005 Fixed Bulk-Only reset recovery (staubesv)
- * 19.12.2005 Trim vendor/product strings (staubesv)
- * 03.01.2006 Use sequence number for CSW tag (staubesv)
- * 05.01.2006 Added TraceCBWs & TraceCSWs trace options (staubesv)
- * 12.01.2006 Removed data copying/memory allocation (staubesv)
- * 09.02.2006 Refactored driver: Introduced UsbStorageBase, UsbStorageCbi, UsbStorageBot,
- * UsbStorageScm, UsbStorageBoot (staubesv)
- * 28.06.2006 Use UsbUtilties (staubesv)
- * 09.08.2006 Inquiry, TestUnitReady, ReadCapacity, Read & Write commands length set to 12 bytes since lower values broke the CBI Layer (staubesv)
- * 26.03.2007 Added NnofReads, NnofWrites, NnofOthers and NnofErrors statistics (staubesv)
- *)
- IMPORT SYSTEM, KernelLog, Kernel, Commands, Disks, Plugins, Usbdi, Debug := UsbDebug, Lib := UsbUtilities;
- CONST
- NoData* = {};
- DataIn* = {1};
- DataOut* = {2};
- (* Result codes *)
- (* From Disks.Mod *)
- ResOk* = Disks.Ok;
- ResWriteProtected* = Disks.WriteProtected;
- ResDeviceInUse* = Disks.DeviceInUse;
- ResMediaChanged* = Disks.MediaChanged;
- ResMediaMissing* = Disks.MediaMissing;
- ResUnsupported* = Disks.Unsupported;
- (* USB Storage Device Driver specific *)
- ResTimeout* = 30;
- ResShortTransfer* = 31;
- ResDeviceNotReady* = 32;
- ResDeviceNotReadyInit* = 33; (* Device need to be initialized *)
- ResSenseError* = 34; (* Do sensing *)
- ResError* = 35;
- ResFatalError* = 36; (* Device needs to be resetted *)
- ResDisconnected* = 37;
- MethodCBI* = 1;
- MethodCB* = 2;
- MethodBulkOnly* = 3;
- MethodSCMShuttle* = 4;
- ProtocolUFI* = 100; (* UFI *)
- ProtocolUTS* = 101; (* USB TRANSPARENT SCSI => UTS (plattner definition) *)
- ProtocolRBC* = 102; (* RBC = reduced block commands, often used for flash devices *)
- Protocol8020* = 103; (* ATAPI for CDROM *)
- Protocol8070* = 104; (* ATAPI for floppy drives and similar devices *)
- ProtocolQIC157* = 105; (* QIC, meaning the tape company. Typically used by tape devices *)
- (* UFI Commands according UFI Command Specification *)
- UfiFormatUnit = 04H; (* Format unformatted media; not implemented *)
- UfiInquiry = 12H; (* Get device information *)
- UfiStartStop = 1BH; (* Request a removable-media device to load or unload its media; partially implemented *)
- UfiModeSelect = 55H; (* Allow the host to set parameters in a peripheral (mode sense should be issued prior to mode select); not implemented *)
- UfiModeSense = 5AH; (* Report parameters to the host. *)
- UfiAllowRemoval = 1EH; (* Prevent or allow the removal of media from a removable media device *)
- UfiRead10* = 28H; (* Transfer binary data from the media to the host *)
- UfiRead12 = 0A8H; (* Transfer binary data from the media to the host not implemented *)
- UfiReadCapacity = 25H; (* Report current media capacity *)
- UfiReadFormatCapacity = 23H; (* Read current media capacity and formattable capacities supported by media; not implemented *)
- UfiRequestSense = 03H; (* Tansfer status sense data to the host *)
- UfiRezeroUnit = 01H; (* Position a head of the drive to zero track not implemented *)
- UfiSeek10 = 2BH; (* Seek the device to a specified address not implemented *)
- UfiSendDiag = 1DH; (* Perform a hard reset and execute diagnostics *)
- UfiTestUnitReady = 00H; (* Request the device to report if it's ready *)
- UfiVerify = 2FH; (* Verify data on the media not implemented *)
- UfiWrite10 = 2AH; (* Transfer binary data from the host to the media *)
- UfiWrite12 = 0AAH; (* Transfer binary data from the host to the media not implemented *)
- UfiWriteNVerify = 2EH; (* Transfer binary data from the host to the media and verify data not implemented *)
- (* Device types as reported by the UFI Inquiry command *)
- DtSbcDirectAccess = 00H;
- DtCDROM = 05H;
- DtOpticalMemory = 07H;
- DtRbcDirectAccess = 0EH;
- RemovableBit = {7};
- (* UfiModeSense constants: Page Control Field *)
- PcCurrentValues = 0;
- PcChangeableValues = 1;
- PcDefaultValues = 2;
- PcSavedValues = 3;
- (* UfiModeSense constants: Page Code field *)
- PageRwErrorRecovery = 01H;
- PageFlexibleDisk = 05H;
- PageBlockAccessCapacities = 1BH;
- PageTimerAndProtect = 1CH;
- PageAll = 3FH; (* only for mode sense command *)
- (* Timeout values in milliseconds *)
- TransferTimeout = 20000;
- CommandTimeout = 5000;
- TYPE
- (* Information delivered by UFI Inquiry command *)
- InquiryResult = POINTER TO RECORD
- deviceType : LONGINT; (* Peripheral device type; 00h: direct access device (floppy), 1FH > none *)
- removable : BOOLEAN; (* Removable media bit *)
- ansiVersion : LONGINT; (* Should be 0 for compatible devices *)
- additionalLength : LONGINT;
- validStrings : BOOLEAN; (* Are the fields below valid? *)
- vendor : ARRAY 9 OF CHAR;
- product : ARRAY 17 OF CHAR;
- revision : ARRAY 5 OF CHAR;
- END;
- (* Information delivered by UFI Mode Sense Command when Flexible Disk Page is requested *)
- FlexibleDiskPage = POINTER TO RECORD
- TransferRate : LONGINT; (* kbits/s *)
- NumberOfHeads : LONGINT;
- SectorsPerTrack : LONGINT; (* 1 - 63 *)
- BytesPerSector : LONGINT;
- NumberOfCylinders : LONGINT;
- MotorOnDelay, MotorOffDelay : LONGINT;
- MediumRotationRate : LONGINT; (* r.p.m. *)
- END;
- TYPE
- UsbStorageDevice = OBJECT (Disks.Device)
- VAR
- usbDriver : StorageDriver;
- lun : LONGINT; (* Logical Unit Number of this storage device*)
- transportProtocol : LONGINT;
- (* Fields used by the DiskManager *)
- number : LONGINT; (* Suffix appended to name to get unique device name *)
- next : UsbStorageDevice;
- PROCEDURE Transfer* (op, block, num: LONGINT; VAR data: ARRAY OF CHAR; ofs: LONGINT; VAR diskres: WORD);
- VAR direction : SET; cmd : ARRAY 12 OF CHAR; i, tlen, trans, offset, num0 : LONGINT;
- BEGIN {EXCLUSIVE}
- IF (op = Disks.Read) OR (op = Disks.Write) THEN
- FOR i := 0 TO 11 DO cmd[i] := 0X; END;
- IF op = Disks.Read THEN
- cmd[0] := CHR(UfiRead10); direction := DataIn;
- ELSE
- cmd[0] := CHR(UfiWrite10); direction := DataOut;
- END;
- cmd[1] := CHR(LSH(lun, 5)); (* Logical device number *)
- offset := ofs; num0 := num;
- WHILE num > 0 DO
- IF num > 65000 THEN trans := 65000; ELSE trans := num END;
- cmd[2] := CHR(LSH(block, -24));
- cmd[3] := CHR(LSH(block, -16));
- cmd[4] := CHR(LSH(block, -8));
- cmd[5] := CHR(block);
- cmd[7] := CHR(LSH(trans, -8));
- cmd[8] := CHR(trans);
- cmd[9] := 0X;
- diskres := usbDriver.InvokeTransport(cmd, 12, direction, data, offset, trans * blockSize, tlen, TransferTimeout);
- IF diskres # Disks.Ok THEN RETURN; END;
- block := block + trans;
- num := num - trans;
- offset := offset + (trans * blockSize);
- END;
- IF Disks.Stats THEN
- IF (op = Disks.Read) THEN
- INC (NnofReads);
- IF (diskres = ResOk) THEN INC (NbytesRead, num0 * blockSize);
- ELSE INC (NnofErrors);
- END;
- ELSE
- INC (NnofWrites);
- IF (diskres = ResOk) THEN INC (NbytesWritten, num0 * blockSize);
- ELSE INC (NnofErrors);
- END;
- END;
- END;
- ELSE
- diskres := Disks.Unsupported;
- IF Disks.Stats THEN INC (NnofOthers); END;
- END;
- END Transfer;
- (** Get number of blocks and size of blocks using the UFI Read Capacity command *)
- PROCEDURE GetSize* (VAR size: LONGINT; VAR res: WORD);
- BEGIN {EXCLUSIVE}
- IF Debug.Trace & Debug.traceInfo THEN KernelLog.String("UsbStorage: GetSize: "); KernelLog.Ln; END;
- (* Some devices I've seen didn't like to be asked for their capacity when no medium was inserted... *)
- res := usbDriver.WaitForReady(lun, 5000);
- IF (res # ResOk) & (res # ResMediaChanged) THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: GetCapacity: command failed: "); ShowRes(res); KernelLog.Ln; END;
- RETURN;
- END;
- blockSize := 0; size := 0;
- res := usbDriver.ReadCapacity(lun, size, blockSize);
- IF ((res # ResOk) & (res # ResShortTransfer)) THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: ReadCapacity: command failed: "); ShowRes(res); KernelLog.Ln; END;
- RETURN;
- END;
- res := ResOk; (* Allow short transfer *)
- END GetSize;
- PROCEDURE Handle*(VAR msg: Disks.Message; VAR diskres: WORD);
- VAR cmd : ARRAY 12 OF CHAR; i : LONGINT; fdp : FlexibleDiskPage;
- BEGIN {EXCLUSIVE}
- FOR i := 0 TO 11 DO cmd[i] := CHR(0); END;
- IF msg IS Disks.GetGeometryMsg THEN
- IF transportProtocol = ProtocolUFI THEN
- WITH msg : Disks.GetGeometryMsg DO
- (* TODO: But what if the user changes the medium... *)
- fdp := usbDriver.ModeSense(lun, 0, PageFlexibleDisk, 32);
- IF fdp # NIL THEN
- msg.spt := fdp.SectorsPerTrack;
- msg.hds := fdp.NumberOfHeads;
- msg.cyls := fdp.NumberOfCylinders;
- ELSE
- (* assume 1440KB floppy disk *)
- msg.spt := 18; msg.hds := 2; msg.cyls := 80;
- END;
- IF Debug.Trace & Debug.traceScRequests THEN
- KernelLog.String("UsbStorageBase: Disk geometry CHS:");
- KernelLog.Int(msg.cyls, 0); KernelLog.String("x"); KernelLog.Int(msg.hds, 0); KernelLog.String("x"); KernelLog.Int(msg.spt, 0); KernelLog.Ln;
- END;
- END;
- diskres := Disks.Ok;
- ELSE
- diskres := Disks.Unsupported;
- END;
- ELSIF msg IS Disks.LockMsg THEN
- diskres := usbDriver.PreventMediumRemoval(lun, TRUE);
- ELSIF msg IS Disks.UnlockMsg THEN
- diskres := usbDriver.PreventMediumRemoval(lun, FALSE);
- ELSIF msg IS Disks.EjectMsg THEN
- diskres := usbDriver.EjectMedia(lun);
- ELSE
- diskres := Disks.Unsupported;
- END;
- END Handle;
- END UsbStorageDevice;
- TYPE
- StorageDriver* = OBJECT (Usbdi.Driver);
- VAR
- sdevs- : UsbStorageDevice; (* list of storage devices associated to this driver *)
- description* : Plugins.Description;
- transportProtocol*, transportMethod* : LONGINT;
- bulkIn*, bulkOut*, interrupt* : LONGINT; (* adresses of the used endpoints *)
- bulkInPipe-, bulkOutPipe-, interruptPipe-, defaultPipe- : Usbdi.Pipe;
- initialize* : BOOLEAN; (* if TRUE, the Initialization() procedure is called *)
- timer : Kernel.Timer;
- (** Transport layer specific reset procedure *)
- PROCEDURE Reset*(timeout : LONGINT) : WORD;
- BEGIN
- HALT(301); RETURN 0; (* abstract *)
- END Reset;
- (** Transport layer specific transfer procedure *)
- PROCEDURE Transport*(cmd : ARRAY OF CHAR; cmdlen : LONGINT; dir : SET;
- VAR buffer : ARRAY OF CHAR; ofs, bufferlen : LONGINT; VAR tlen : LONGINT; timeout : LONGINT) : WORD;
- BEGIN
- HALT(301); RETURN 0; (* abstract *)
- END Transport;
- (* Bulk-only transport layer only *)
- PROCEDURE GetMaxLun*(VAR lun : LONGINT) : LONGINT;
- BEGIN
- HALT(301); RETURN 0; (* abstract *)
- END GetMaxLun;
- (* UFI command: Test whether the specified logical unit is ready *)
- PROCEDURE TestUnitReady(lun : LONGINT) : WORD;
- VAR cmd : ARRAY 12 OF CHAR; i, ignore : LONGINT;
- BEGIN
- cmd[0] := CHR(UfiTestUnitReady);
- cmd[1] := CHR(LSH(lun, 5));
- FOR i := 2 TO 11 DO cmd[i] := 0X; END;
- RETURN InvokeTransport(cmd, 12, NoData, cmd, 0, 0, ignore, 30000);
- END TestUnitReady;
- (* Issues TestUnitReady commands until device is ready or an error occurs *)
- PROCEDURE WaitForReady(lun, timeout : LONGINT) : WORD;
- VAR retry : LONGINT; res : WORD;
- BEGIN
- IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageBase: WaitForReady..."); KernelLog.Ln; END;
- retry := 0;
- LOOP
- res := TestUnitReady(lun);
- IF (res = ResDisconnected) THEN
- EXIT;
- ELSIF (res = ResDeviceNotReady) OR (res = ResMediaChanged) THEN
- (* continue *)
- ELSE
- INC(retry);
- END;
- IF (retry > 3) OR (timeout < 0) THEN EXIT; END;
- Wait(100);
- timeout := timeout - 100;
- END;
- IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageBase: WaitForReady done, res: "); ShowRes(res); KernelLog.Ln; END;
- RETURN res;
- END WaitForReady;
- (* UFI command: Start/Stop Unit. *)
- (* Note: Since UFI devices control the motor on/off themselves, it's not implemented here. *)
- (* We just use the command to eject the media, thus the name *)
- PROCEDURE EjectMedia(lun : LONGINT) : WORD;
- VAR cmd, data : ARRAY 12 OF CHAR; i, tlen : LONGINT; res : WORD;
- BEGIN
- cmd[0] := CHR(UfiStartStop);
- cmd[1] := CHR(LSH(lun, 5));
- FOR i := 2 TO 11 DO cmd[i] := 0X; END;
- cmd[4] := CHR(2); (* Eject *)
- res := InvokeTransport(cmd, 12, DataOut, data, 0, 0, tlen, CommandTimeout);
- RETURN res;
- END EjectMedia;
- (* UFI command: Prevent/Allow Medium Removal *)
- PROCEDURE PreventMediumRemoval(lun : LONGINT; prevent : BOOLEAN) : WORD;
- VAR cmd, data : ARRAY 12 OF CHAR; i, tlen : LONGINT; res : WORD;
- BEGIN
- cmd[0] := CHR(UfiAllowRemoval);
- cmd[1] := CHR(LSH(lun, 5));
- FOR i := 2 TO 11 DO cmd[i] := 0X; END;
- IF prevent THEN cmd[4] := CHR(1); END;
- res := InvokeTransport(cmd, 12, DataOut, data, 0, 0, tlen, CommandTimeout);
- RETURN res;
- END PreventMediumRemoval;
- (* UFI command: Request UFI device to do a reset or perform a self-test *)
- PROCEDURE SendDiagnostic(lun : LONGINT) : WORD;
- VAR cmd, data : ARRAY 12 OF CHAR; i, tlen : LONGINT; res : WORD;
- BEGIN
- cmd[0] := CHR(UfiSendDiag);
- cmd[1] := CHR(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, LSH(lun, 5)) + {2}));
- FOR i := 2 TO 11 DO cmd[i] := 0X; END;
- res := InvokeTransport(cmd, 12, DataOut, data, 0, 0, tlen, CommandTimeout);
- RETURN res;
- END SendDiagnostic;
- (* UFI command: Get the number of blocks and the blocksize of the specified logical unit *)
- PROCEDURE ReadCapacity(lun : LONGINT; VAR blocks, blocksize : LONGINT) : WORD;
- VAR cmd, data : ARRAY 12 OF CHAR; res: WORD; i, tlen : LONGINT;
- BEGIN
- IF Debug.Trace & Debug.traceInfo THEN KernelLog.String("UsbStorage: ReadCapacity: "); KernelLog.Ln; END;
- cmd[0] := CHR(UfiReadCapacity);
- cmd[1] := CHR(LSH(lun, 5));
- FOR i := 2 TO 11 DO cmd[i] := CHR(0); END;
- blocks := 0; blocksize := 0;
- res := InvokeTransport(cmd, 12, DataIn, data, 0, 8, tlen, TransferTimeout);
- IF (res # ResOk) THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: ReadCapacity: command failed: "); ShowRes(res); KernelLog.Ln; END;
- RETURN res;
- END;
- FOR i := 0 TO 3 DO
- blocks := blocks*100H + ORD(data[i]);
- blocksize := blocksize*100H + ORD(data[4+i])
- END;
- INC(blocks); (* The device reports the highest valid block address -> also count block zero *)
- res := ResOk; (* allow short transfers *)
- IF Debug.Trace & Debug.traceInfo THEN
- KernelLog.String("UsbStorage: Disk info: Blocks: "); KernelLog.Int(blocks, 0);
- KernelLog.String(" blocksize: "); KernelLog.Int(blocksize, 0); KernelLog.String(" size: "); KernelLog.Int(blocks*blocksize, 0);
- KernelLog.Ln;
- END;
- RETURN ResOk;
- END ReadCapacity;
- (* UFI command: Inquiry the specified locigal unit; Returns NIL in error case *)
- PROCEDURE Inquiry(lun : LONGINT) : InquiryResult;
- VAR cmd : ARRAY 12 OF CHAR; data : ARRAY 36 OF CHAR; result : InquiryResult; i, j, tlen : LONGINT; res : WORD;
- BEGIN
- IF Debug.Trace & Debug.traceInfo THEN
- KernelLog.String("UsbStorage: Inquiry logical device "); KernelLog.Int(lun, 0); KernelLog.String("... "); KernelLog.Ln;
- END;
- FOR i := 0 TO 11 DO cmd[i] := CHR(0); END;
- cmd[0] := CHR(UfiInquiry);
- cmd[1] := CHR(LSH(lun, 5));
- cmd[4] := CHR(36); (* maximum allocation length *)
- res := InvokeTransport(cmd, 12, DataIn, data, 0, 36, tlen, 50000);
- IF ((res # Disks.Ok) & (res # ResShortTransfer)) OR (tlen < 5) THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Fatal, inquiry device error: "); ShowRes(res); KernelLog.Ln; END;
- RETURN NIL;
- END;
- NEW(result);
- result.deviceType := ORD(data[0]) MOD 32;
- IF SYSTEM.VAL(SET, ORD(data[1])) * RemovableBit # {} THEN
- result.removable := TRUE;
- END;
- result.ansiVersion := ORD(data[2]) MOD 8;
- result.additionalLength := ORD(data[4]);
- IF transportProtocol = ProtocolRBC THEN
- IF result.deviceType # DtRbcDirectAccess THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: RBC device type is not 0EH"); KernelLog.Ln; END;
- RETURN NIL;
- END;
- ELSIF (result.deviceType # DtSbcDirectAccess) & (result.deviceType # DtCDROM) & (result.deviceType # DtOpticalMemory) THEN
- IF Debug.Trace & Debug.traceInfo THEN KernelLog.String("UsbStorage: Device is not a storage device"); KernelLog.Ln; END;
- RETURN NIL;
- END;
- IF tlen >= 36 THEN
- result.validStrings := TRUE;
- j := 0; FOR i := 8 TO 15 DO result.vendor[j] := data[i]; INC(j); END; result.vendor[8] := 0X;
- j := 0; FOR i := 16 TO 31 DO result.product[j] := data[i]; INC(j); END; result.product[16] := 0X;
- j := 0; FOR i := 32 TO 35 DO result.revision[j] := data[i]; INC(j); END; result.revision[4] := 0X;
- END;
- IF Debug.Trace & Debug.traceInfo THEN ShowInquiryResult(result); END;
- RETURN result;
- END Inquiry;
- (* The mode sense command allows the UFI device to report medium or device parameters to the host *)
- PROCEDURE ModeSense(lun, pagecontrol, page, length : LONGINT) : FlexibleDiskPage;
- VAR cmd : ARRAY 12 OF CHAR; data : Usbdi.BufferPtr; actLen : LONGINT; res : WORD; fdp : FlexibleDiskPage;
- (* page[index] is first byte of flexible disk page *)
- PROCEDURE ParseFlexibleDiskPage(page : Usbdi.BufferPtr; index : LONGINT) : FlexibleDiskPage;
- VAR temp : LONGINT; fdp : FlexibleDiskPage;
- BEGIN
- ASSERT((page # NIL) & (LEN(page) - index >= 32));
- ASSERT(SYSTEM.VAL(LONGINT, (SYSTEM.VAL(SET, page[index]) * {0..5})) = PageFlexibleDisk);
- NEW(fdp);
- temp := LSH(ORD(page[index+2]), 8) + ORD(page[index+3]);
- IF temp = 00FAH THEN fdp.TransferRate := 250;
- ELSIF temp = 012CH THEN fdp.TransferRate := 300;
- ELSIF temp = 01F4H THEN fdp.TransferRate := 500;
- ELSIF temp = 03E8H THEN fdp.TransferRate := 1000;
- ELSIF temp = 07D0H THEN fdp.TransferRate := 2000;
- ELSIF temp = 1388H THEN fdp.TransferRate := 5000;
- ELSIF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: ParseFlexibleDiskPage: Warning: Parse error"); KernelLog.Ln;
- END;
- fdp.NumberOfHeads := ORD(page[index+4]);
- fdp.SectorsPerTrack := ORD(page[index+5]);
- fdp.BytesPerSector := LSH(ORD(page[index+6]), 8) + ORD(page[index+7]);
- fdp.NumberOfCylinders := LSH(ORD(page[index+8]), 8) + ORD(page[index+9]);
- fdp.MotorOnDelay := ORD(page[index+19]);
- fdp.MotorOffDelay := ORD(page[index+20]);
- IF fdp.MotorOffDelay = 0FFH THEN fdp.MotorOffDelay := 0; (* don't turn it off !! *) END;
- fdp.MediumRotationRate := LSH(ORD(page[index+28]), 8) + ORD(page[index+29]);
- IF Debug.Trace & Debug.traceInfo THEN ShowFlexibleDiskPage(fdp); END;
- RETURN fdp;
- END ParseFlexibleDiskPage;
- BEGIN
- length := length + 8; (* 8 additional bytes for mode parameter header *)
- cmd[0] := CHR(UfiModeSense);
- cmd[1] := CHR(LSH(lun, 5)); (* logical unit number *)
- cmd[2] := CHR(LSH(pagecontrol, 6) + page);
- cmd[3] := CHR(0); (* reserved *)
- cmd[5] := CHR(0); (* reserved *)
- cmd[6] := CHR(0); (* reserved *)
- cmd[7] := CHR(LSH(length, -8)); (* Parameter List Length (MSB)*)
- cmd[8] := CHR(length); (* Parameter List Length (LSB) *)
- cmd[9] := CHR(0); (* reserved *)
- cmd[10] := CHR(0); (* reserved *)
- cmd[11] := CHR(0); (* reserved *)
- NEW(data, length);
- res := InvokeTransport (cmd, 12, DataIn, data^, 0, length, actLen, CommandTimeout);
- IF ((res # Disks.Ok) OR ((res # ResShortTransfer) & (actLen < length))) THEN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: UFI Mode Sense command failed."); KernelLog.Ln; END;
- RETURN NIL;
- END;
- IF (ORD(data[1]) + 100H*SYSTEM.VAL(LONGINT, ORD(data[0]))) # length + 8 THEN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: ModeSense: Error: Wrong Mode Data Length returned."); KernelLog.Ln; END;
- RETURN NIL;
- END;
- (* parse the result *)
- CASE page OF
- PageFlexibleDisk : fdp := ParseFlexibleDiskPage(data, 8);
- ELSE
- IF Debug.Level >= Debug.Warnings THEN
- KernelLog.String("UsbStorage: ModeSense: Parsing of page type "); KernelLog.Hex(page,-2);
- KernelLog.String(" not (yet) supported."); KernelLog.Ln;
- END;
- END;
- RETURN fdp;
- END ModeSense;
- (* UFI command: The Request Sense command instructs the UFI device to transfer sense data to the host for
- * the specified logical unit *)
- PROCEDURE RequestSense(lun, cmdlen : LONGINT) : WORD;
- VAR
- cmd : ARRAY 12 OF CHAR; data : ARRAY 36 OF CHAR;
- key, asc, ascq : CHAR;
- information : LONGINT;
- i, tlen : LONGINT; res : WORD;
- BEGIN
- IF Debug.Trace & Debug.traceSensing THEN KernelLog.String("UsbStorage: Doing auto sense..."); KernelLog.Ln; END;
- IF (transportProtocol = ProtocolUTS) OR (transportProtocol = ProtocolRBC) THEN
- cmdlen := 6;
- END;
- FOR i := 0 TO 11 DO cmd[i] := CHR(0); END;
- cmd[0] := CHR(UfiRequestSense);
- cmd[1] := CHR(LSH(lun, 5));
- cmd[4] := CHR(18); (* allocation length (max. 18 Bytes) *)
- res := Transport(cmd, cmdlen, DataIn, data, 0, 18, tlen, 2000);
- IF (res = ResDisconnected) THEN
- RETURN res;
- ELSIF (res = ResShortTransfer) THEN
- IF (tlen < 14) THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Crappy device gives not enough sense data"); KernelLog.Ln; END;
- RETURN ResSenseError;
- ELSE
- (* sense >= 14 is ok *)
- END;
- ELSIF (transportProtocol = ProtocolUFI) & (res = ResSenseError) THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: UFI autosense TransportSenseError"); KernelLog.Ln; END;
- (* thats ok *)
- ELSIF res # ResOk THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Sense error on protocol sense"); KernelLog.Ln; END;
- res := Reset(5000);
- RETURN ResSenseError;
- END;
- key := CHR(ORD(data[2]) MOD 16); asc := data[12]; ascq := data[13];
- IF Debug.Trace & Debug.traceSensing THEN
- IF SYSTEM.VAL(SET, ORD(data[0])) * {7} # {} THEN (* information field is valid *)
- information := ORD(data[3]) + 10H*ORD(data[4]) + 100H*ORD(data[5]) + 1000H*ORD(data[6]);
- END;
- ShowSenseData(key, asc, ascq, information, SYSTEM.VAL(SET, ORD(data[0])) * {7} # {});
- END;
- IF key = 0X THEN (* No sense -> okay *)
- IF (res = ResOk) OR (res = ResShortTransfer) THEN RETURN ResOk; END;
- RETURN ResError;
- ELSIF (key = 1X) THEN RETURN ResOk; (* Recovered error -> okay *)
- ELSIF (key = 2X) & (asc = 4X) & (ascq = 01X) THEN RETURN ResDeviceNotReady; (* Logical drive not ready - becoming ready *)
- ELSIF (key = 2X) & (asc = 4X) & (ascq = 02X) THEN RETURN ResDeviceNotReadyInit; (* Logical drive not ready - initialization required *)
- ELSIF (key = 2X) & (asc = 4X) & (ascq = 04X) THEN RETURN ResDeviceInUse; (* Logical drive not ready - format in progress *)
- ELSIF (key = 2X) & (asc = 4X) & (ascq = 0FFX) THEN RETURN ResDeviceInUse; (* Logical drive not ready - device is busy *)
- ELSIF (key = 2X) & (asc = 6X) & (ascq = 00X) THEN RETURN ResError; (* No reference position found *)
- ELSIF (key = 2X) & (asc = 8X) & (ascq = 00X) THEN RETURN ResError; (* Logical unit communication failure *)
- ELSIF (key = 2X) & (asc = 8X) & (ascq = 01X) THEN RETURN ResTimeout; (* Logical unit communication timeout *)
- ELSIF (key = 2X) & (asc = 8X) & (ascq = 80X) THEN RETURN ResError; (* Logical unit communication overrun *)
- ELSIF (key = 2X) & (asc = 3AX) & (ascq = 0X) THEN RETURN ResMediaMissing; (* Medium not present *)
- ELSIF (key = 2X) THEN RETURN ResError;
- ELSIF (key = 3X) THEN RETURN ResError; (* Medium Error *)
- ELSIF (key = 4X) THEN RETURN ResError; (* Hardware Error *)
- ELSIF (key = 5X) THEN RETURN ResUnsupported; (* Illegal Request *)
- ELSIF (key = 6X) & (asc = 28X) & (ascq = 00X) THEN RETURN ResMediaChanged;
- ELSIF (key = 6X) & (asc = 29X) & (ascq = 0X) THEN RETURN ResDeviceNotReady; (* PowerOnReset *)
- ELSIF (key = 6X) THEN RETURN ResDeviceNotReady;
- ELSIF (key = 7X) THEN RETURN ResWriteProtected;
- ELSE
- RETURN ResError;
- END;
- END RequestSense;
- (* PROCEDURE ReadWrite10(lun, blockSize, op, block, num : LONGINT; VAR data : ARRAY OF CHAR; ofs : LONGINT; VAR diskres : LONGINT);
- VAR cmd : ARRAY 12 OF CHAR; trans, tlen, i, res : LONGINT; direction : SET;
- BEGIN
- FOR i:= 0 TO 11 DO cmd[i] := 0X; END;
- IF op = Disks.Read THEN
- cmd[0] := CHR(UfiRead10); direction := DataIn;
- ELSE
- cmd[0] := CHR(UfiWrite10); direction := DataOut;
- END;
- cmd[1] := CHR(LSH(lun, 5)); (* Logical device number *)
- WHILE num > 0 DO
- IF num > 65000 THEN trans := 65000; ELSE trans := num END;
- trans := trans * blockSize;
- cmd[2] := CHR(LSH(block, -24));
- cmd[3] := CHR(LSH(block, -16));
- cmd[4] := CHR(LSH(block, -8));
- cmd[5] := CHR(block);
- cmd[7] := CHR(LSH(num, -8));
- cmd[8] := CHR(num);
- diskres := InvokeTransport(cmd, 12, direction, data, ofs, trans, tlen, TransferTimeout);
- IF diskres # Disks.Ok THEN RETURN; END;
- block := block + trans;
- num := num - trans;
- END;
- END ReadWrite10;
- PROCEDURE ReadWrite12(lun, op, block, num : LONGINT; VAR data : ARRAY OF CHAR; ofs : LONGINT; VAR diskres : LONGINT);
- VAR cmd : ARRAY 12 OF CHAR; trans, tlen, i, res : LONGINT; direction : SET;
- BEGIN
- ASSERT(num < MAX(LONGINT));
- FOR i:= 0 TO 11 DO cmd[i] := 0X; END;
- IF op = Disks.Read THEN
- cmd[0] := CHR(UfiRead12); direction := DataIn;
- ELSE
- cmd[0] := CHR(UfiWrite12); direction := DataOut;
- END;
- cmd[1] := CHR(LSH(lun, 5)); (* Logical device number *)
- cmd[2] := CHR(LSH(block, -24));
- cmd[3] := CHR(LSH(block, -16));
- cmd[4] := CHR(LSH(block, -8));
- cmd[5] := CHR(block);
- cmd[6] := CHR(LSH(num, -24));
- cmd[7] := CHR(LSH(num, -16));
- cmd[8] := CHR(LSH(num, -8));
- cmd[9] := CHR(num);
- diskres := InvokeTransport(cmd, 12, direction, data, ofs, trans, tlen, TransferTimeout);
- END ReadWrite12; *)
- (* Generic Transport Handler *)
- PROCEDURE InvokeTransport (cmd : ARRAY OF CHAR; cmdlen : INTEGER; dir : SET;
- VAR data : ARRAY OF CHAR; ofs, datalen : LONGINT; VAR tlen : LONGINT; timeout : LONGINT) : WORD;
- VAR
- sensecmdlen, retry, i : LONGINT; res: WORD; forceRetry : BOOLEAN;
- BEGIN {EXCLUSIVE}
- (* here one could add additional stuff for the different protocols *)
- IF transportProtocol = ProtocolUFI THEN
- cmdlen := 12; sensecmdlen := 12;
- ELSIF (transportProtocol = ProtocolUTS) OR (transportProtocol = ProtocolRBC) THEN
- (* all ok *) sensecmdlen := 6;
- ELSIF (transportProtocol = Protocol8020) OR (transportProtocol = Protocol8070) THEN
- cmdlen := 12; sensecmdlen := 12;
- ELSIF transportProtocol = ProtocolQIC157 THEN
- cmdlen := 12; sensecmdlen := 12;
- END;
- retry := 0; (* retries for "power up/reset" and "lun becoming ready" *)
- LOOP
- IF Debug.Trace & Debug.traceScTransfers THEN
- KernelLog.String("UsbStorageBase: [cmd: ");
- IF transportProtocol = ProtocolUFI THEN ShowUFICmd(cmd[0]); END;
- FOR i := 0 TO cmdlen-1 DO KernelLog.String(" "); KernelLog.Hex(ORD(cmd[i]), -2); END;
- KernelLog.String(" BufferLen: "); KernelLog.Int(datalen, 0); KernelLog.String(" Bytes]");
- KernelLog.Ln;
- END;
- res := Transport(cmd, cmdlen, dir, data, ofs, datalen, tlen, timeout);
- IF Debug.Trace & Debug.traceScTransfers THEN
- KernelLog.String("UsbStorageBase: Sent "); KernelLog.Int(cmdlen, 0); KernelLog.String(" bytes commands, res: ");
- ShowRes(res); KernelLog.Ln;
- END;
- IF (res = ResDisconnected) THEN
- RETURN res;
- END;
- forceRetry := FALSE;
- IF (res = ResFatalError) OR (res = ResTimeout) THEN
- res := Reset(5000); (* ignore res *)
- IF res # ResOk THEN
- (* TODO:
- lun := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, ORD(cmd[1])) * {5..7}, -5));
- res := SendDiagnostic(lun); (* hardware reset; ignore res *) *)
- RETURN ResFatalError;
- ELSE
- forceRetry := TRUE;
- res := ResError; (* retry *)
- END;
- END;
- IF (res = ResShortTransfer) & (transportMethod # MethodCB) THEN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageBase: Had a short read"); KernelLog.Ln; END;
- RETURN ResShortTransfer;
- END;
- (* Do an auto-sense if something was not ok or if we are using the CB (not CBI) transport method *)
- IF (res = ResOk) & (transportMethod # MethodCB) THEN
- RETURN ResOk;
- END;
- (* It makes no sense to auto-sense on Inquiry/Sense on UFI/CB (not CBI)*)
- IF (transportMethod = MethodCB) & (transportProtocol = ProtocolUFI) & ((res = ResOk) OR (res = ResShortTransfer)) THEN
- IF (ORD(cmd[0]) = UfiInquiry) OR (ORD(cmd[0]) = UfiRequestSense) THEN RETURN res; END;
- END;
- res := RequestSense(SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, ORD(cmd[1])) * {5..7}, -5)), sensecmdlen);
- IF Debug.Trace & Debug.traceScTransfers THEN KernelLog.String("UsbStorageBase: Sent sense command, res: "); ShowRes(res); KernelLog.Ln; END;
- IF (res = ResWriteProtected) OR (res = ResDeviceInUse) OR (res = ResMediaMissing) OR (res = ResUnsupported) OR (res = ResDisconnected)THEN
- RETURN res; (* don't retry *)
- END;
- IF forceRetry OR (res = ResDeviceNotReady) THEN
- INC(retry);
- IF retry = 4 THEN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageBase: Too many protocol retries, giving up"); KernelLog.Ln; END;
- EXIT;
- ELSE
- IF Debug.Trace & Debug.traceScTransfers THEN KernelLog.String("UsbStorageBase: Retry #"); KernelLog.Int(retry, 0); KernelLog.Ln; END;
- END;
- ELSE
- EXIT;
- END;
- Wait(50); (* try again *)
- END;
- RETURN res;
- END InvokeTransport;
- PROCEDURE Initialization*() : BOOLEAN;
- BEGIN
- (* dummy *)
- RETURN TRUE;
- END Initialization;
- PROCEDURE RegisterDevice(lun : LONGINT) : BOOLEAN;
- VAR stordev : UsbStorageDevice; info : InquiryResult; i, j : LONGINT;
- BEGIN
- info := Inquiry(lun);
- IF info # NIL THEN
- NEW(stordev);
- stordev.SetName("USB");
- IF info.validStrings THEN (* override description *)
- Lib.TrimWS(info.vendor);
- stordev.desc := ""; i := 0; j := 0;
- LOOP (* append vendor string *)
- IF (i >= LEN(info.vendor)) OR (info.vendor[i] = 0X) THEN EXIT; END;
- stordev.desc[j] := info.vendor[i];
- INC(i); INC(j);
- END;
- IF j # 0 THEN stordev.desc[j] := " "; INC(j); END;
- Lib.TrimWS(info.product);
- i := 0;
- LOOP (* append product string *)
- IF (i >= LEN(info.product)) OR (info.product[i] = 0X) THEN EXIT; END;
- stordev.desc[j] := info.product[i];
- INC(i); INC(j);
- END;
- stordev.desc[j] := 0X;
- ELSE (* use USB vendor/product strings *)
- stordev.desc := description;
- END;
- stordev.lun := lun;
- stordev.transportProtocol := transportProtocol;
- stordev.blockSize := 0;
- stordev.flags := {}; stordev.table := NIL; stordev.openCount := 0;
- stordev.usbDriver := SELF;
- IF (info.deviceType = DtCDROM) OR (info.deviceType = DtOpticalMemory) THEN
- stordev.flags := stordev.flags + {Disks.ReadOnly};
- END;
- IF info.removable THEN stordev.flags := stordev.flags + {Disks.Removable}; END;
- diskManager.Add(stordev);
- RETURN TRUE;
- ELSE
- IF Debug.Level >= Debug.Default THEN
- KernelLog.String("UsbStorage: Inquiry for device LUN "); KernelLog.Int(lun, 0); KernelLog.String(" failed."); KernelLog.Ln;
- END;
- RETURN FALSE;
- END;
- END RegisterDevice;
- PROCEDURE RegisterDevices() : BOOLEAN;
- VAR lun, maxlun, res : LONGINT; succeeded : LONGINT; (* How many storage device were added to the disk manager? *)
- BEGIN
- maxlun := 0; succeeded := 0;
- IF transportMethod = MethodBulkOnly THEN (* logical devices support *)
- res := GetMaxLun(maxlun);
- IF res = ResOk THEN
- IF Debug.Trace & Debug.traceInfo THEN KernelLog.String("UsbStorageBase: MaxLUN is "); KernelLog.Int(maxlun, 0); KernelLog.Ln; END;
- ELSIF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageBase: GetMaxLun failed: "); ShowRes(res); KernelLog.Ln;
- END;
- END;
- FOR lun := 0 TO maxlun DO
- IF RegisterDevice(lun) THEN INC(succeeded); END;
- END;
- RETURN succeeded > 0;
- END RegisterDevices;
- PROCEDURE Connect*(): BOOLEAN;
- BEGIN
- (* note that this procedure is common to the Bulkonly, CB and CB/I transport layer *)
- (* get the default control pipe *)
- defaultPipe := device.GetPipe(0);
- (* get the bulk pipes *)
- bulkInPipe := device.GetPipe(bulkIn);
- bulkOutPipe := device.GetPipe(bulkOut);
- IF (bulkInPipe=NIL) OR (bulkOutPipe=NIL) THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Could not allocate pipes"); KernelLog.Ln; END;
- RETURN FALSE;
- END;
- (* if the interface support a interrupt endpoint, get the corresponding pipe *)
- IF (interrupt # 0) & (transportMethod # MethodBulkOnly) THEN
- interruptPipe := device.GetPipe(interrupt);
- IF interruptPipe = NIL THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Could not allocate interrupt pipe."); KernelLog.Ln; END;
- RETURN FALSE;
- END;
- END;
- IF initialize & ~Initialization() THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Could not initialize device."); KernelLog.Ln; END;
- RETURN FALSE;
- END;
- IF ~RegisterDevices() THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Error while registering new devices."); KernelLog.Ln; END;
- RETURN FALSE;
- END;
- RETURN TRUE;
- END Connect;
- PROCEDURE Disconnect*;
- BEGIN
- diskManager.RemovedDriver(SELF);
- END Disconnect;
- PROCEDURE Wait(ms : LONGINT);
- BEGIN
- timer.Sleep(ms);
- END Wait;
- PROCEDURE &Init*;
- BEGIN
- NEW(timer);
- END Init;
- END StorageDriver;
- TYPE
- (* Manages a list of all installed USB storage devices and registers/unregisters them at the Disks.registry *)
- DiskManager = OBJECT
- VAR
- storageDeviceList : UsbStorageDevice;
- suffixUsed : ARRAY 100 OF BOOLEAN;
- regName : ARRAY 32 OF CHAR;
- res : WORD;
- PROCEDURE Add*(dev : UsbStorageDevice);
- VAR i : LONGINT;
- BEGIN {EXCLUSIVE}
- ASSERT(dev#NIL);
- (* add new device to list *)
- dev.next := storageDeviceList.next; storageDeviceList.next := dev;
- (* get unused suffix *)
- i := 0; WHILE suffixUsed[i] & (i < 100) DO INC(i) END;
- IF (i = 99) & suffixUsed[99] THEN
- KernelLog.String("UsbStorage: Can't register storage device. Maximal 100 devices supported."); KernelLog.Ln;
- RETURN;
- END;
- (* generate unique device name *)
- suffixUsed[i] := TRUE; dev.number := i;
- i := 1; WHILE (dev.name[i] # 0X) & (i < 32) DO INC(i); END;
- IF (dev.name[i] # 0X) THEN
- KernelLog.String("UsbStorage: Error: Couldn't register the device "); KernelLog.String(dev.name);
- KernelLog.String(" (device names shall be 2-30 characters long (incl. 0X)"); KernelLog.Ln;
- suffixUsed[dev.number] := FALSE;
- RETURN;
- END;
- COPY(dev.name, regName);
- IF dev.number < 10 THEN
- regName[i] := CHR(ORD("0") + dev.number); regName[i+1] := 0X;
- ELSIF dev.number < 100 THEN
- regName[i] := CHR(ORD("0") + dev.number DIV 10);
- regName[i+1] := CHR(ORD("0") + dev.number MOD 10); regName[i+2] := 0X;
- END;
- dev.SetName(regName);
- Disks.registry.Add(dev, res);
- IF res # Plugins.Ok THEN
- KernelLog.String("UsbStorage: Error: Couldn't add device to Disks.registry (Error code: ");
- KernelLog.Int(res, 0); KernelLog.String(")"); KernelLog.Ln;
- suffixUsed[dev.number] := FALSE;
- RETURN;
- END;
- IF Debug.Verbose THEN
- KernelLog.String("UsbStorage: Storage device "); KernelLog.String(dev.name);
- KernelLog.String(" ("); KernelLog.String(dev.desc); KernelLog.String(") is now accessible."); KernelLog.Ln;
- END;
- END Add;
- (** Remove storage device from Disks.registry. *)
- PROCEDURE Remove*(dev : UsbStorageDevice);
- VAR temp : UsbStorageDevice;
- BEGIN {EXCLUSIVE}
- temp:=storageDeviceList;
- WHILE (temp.next#NIL) & (temp.next.name#dev.name) DO temp:=temp.next; END;
- IF (temp.next=NIL) OR (temp.next.name#dev.name) THEN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: Warning: Couldn't remove device from registry (device not found)"); KernelLog.Ln; END;
- ELSE
- temp.next := temp.next.next;
- Disks.registry.Remove(dev);
- suffixUsed[dev.number]:=FALSE;
- END;
- IF Debug.Verbose THEN
- KernelLog.String("UsbStorage: Removed storage device "); KernelLog.String(dev.name);
- KernelLog.String(" ("); KernelLog.String(dev.desc); KernelLog.String(")"); KernelLog.Ln;
- END;
- END Remove;
- (* Remove all storage devices associated to the specifed storage device driver *)
- PROCEDURE RemovedDriver*(driver : StorageDriver);
- VAR temp : UsbStorageDevice;
- BEGIN {EXCLUSIVE}
- temp := storageDeviceList;
- WHILE(temp # NIL) & (temp.next # NIL) DO
- IF temp.next.usbDriver = driver THEN
- Disks.registry.Remove(temp.next);
- suffixUsed[temp.next.number] := FALSE;
- IF Debug.Verbose THEN
- KernelLog.String("UsbStorage: Remove storage device "); KernelLog.String(temp.next.name);
- KernelLog.String(" ("); KernelLog.String(temp.next.desc); KernelLog.String(")"); KernelLog.Ln;
- END;
- temp.next := temp.next.next;
- ELSE
- temp := temp.next;
- END;
- END;
- END RemovedDriver;
- (* Removes all USB storage devices from the Disks.registry. *)
- PROCEDURE RemoveAll*;
- VAR temp : UsbStorageDevice;
- BEGIN {EXCLUSIVE}
- temp := storageDeviceList.next;
- WHILE temp # NIL DO
- Disks.registry.Remove(temp);
- suffixUsed[temp.number] := FALSE;
- temp := temp.next;
- END;
- storageDeviceList.next := NIL;
- IF Debug.Verbose THEN KernelLog.Enter; KernelLog.String("UsbStorage: All storage devices removed."); KernelLog.Exit; END;
- END RemoveAll;
- (* Displays a list of all USB storage devices which are registered at the DiskManager *)
- PROCEDURE Show;
- VAR temp : UsbStorageDevice;
- BEGIN
- KernelLog.String("UsbStorage: Accessible USB storage devices:"); KernelLog.Ln;
- IF storageDeviceList.next=NIL THEN
- KernelLog.String("No devices registred.");
- ELSE
- temp := storageDeviceList.next;
- WHILE temp # NIL DO
- KernelLog.String(temp.name); KernelLog.String(" ("); KernelLog.String(temp.desc); KernelLog.String(")"); KernelLog.Ln;
- temp := temp.next;
- END;
- KernelLog.Ln;
- END;
- END Show;
- PROCEDURE &Init*;
- BEGIN
- NEW(storageDeviceList);
- END Init;
- END DiskManager;
- VAR
- diskManager- : DiskManager;
- performance- : LONGINT;
- (* displays the UFI command to KernelLog *)
- PROCEDURE ShowUFICmd(cmd : CHAR) ;
- BEGIN
- IF Debug.Trace THEN
- CASE ORD(cmd) OF
- 04H : KernelLog.String("Format Unit");
- | 12H : KernelLog.String("Inquiry");
- | 55H : KernelLog.String("Mode Select");
- | 5AH : KernelLog.String("Mode Sense");
- | 1EH : KernelLog.String("Prevent-Allow Media Removal");
- | 28H : KernelLog.String("Read(10)");
- | 0A8H : KernelLog.String("Read(12)");
- | 25H : KernelLog.String("Read Capacity");
- | 23H : KernelLog.String("Read Format Capabilities");
- | 03H : KernelLog.String("Request Sense");
- | 01H : KernelLog.String("Rezero");
- | 2BH : KernelLog.String("Seek(10)");
- | 1DH : KernelLog.String("Send Diagnostic");
- | 1BH :KernelLog.String("Start-Stop Unit");
- | 00H : KernelLog.String("Test Unit Ready");
- | 2FH : KernelLog.String("Verify");
- | 2AH : KernelLog.String("Write(10)");
- | 0AAH : KernelLog.String("Write(12)");
- | 2EH : KernelLog.String("Write and Verify");
- ELSE
- KernelLog.String("Unknown Command("); KernelLog.Int(ORD(cmd), 0); KernelLog.String(")");
- END;
- END;
- END ShowUFICmd;
- PROCEDURE ShowInquiryResult(i : InquiryResult);
- BEGIN
- IF Debug.Trace THEN
- KernelLog.String("Inquiry data:"); KernelLog.Ln;
- IF i # NIL THEN
- KernelLog.String(" Peripheral device type: "); KernelLog.Int(i.deviceType, 0);
- IF i.removable THEN KernelLog.String(" [removable]"); ELSE KernelLog.String(" [not removable]"); END; KernelLog.Ln;
- KernelLog.String(" ANSI version: "); KernelLog.Int(i.ansiVersion, 0); KernelLog.Ln;
- KernelLog.String(" Additional Length: "); KernelLog.Int(i.additionalLength, 0); KernelLog.String("B"); KernelLog.Ln;
- KernelLog.String(" Vendor: "); KernelLog.String(i.vendor); KernelLog.Ln;
- KernelLog.String(" Product: "); KernelLog.String(i.product); KernelLog.Ln;
- KernelLog.String(" Revision: "); KernelLog.String(i.revision); KernelLog.Ln;
- ELSE
- KernelLog.String("Information not available");
- END;
- END;
- END ShowInquiryResult;
- PROCEDURE ShowFlexibleDiskPage(fdp : FlexibleDiskPage);
- BEGIN
- IF Debug.Trace THEN
- KernelLog.String("UsbStorage: Flexible Disk Page Contents: "); KernelLog.Ln;
- IF fdp # NIL THEN
- KernelLog.String(" Transfer rate: "); KernelLog.Int(fdp.TransferRate, 0); KernelLog.String(" kbits/s"); KernelLog.Ln;
- KernelLog.String(" Cylinders: "); KernelLog.Int(fdp.NumberOfCylinders, 0);
- KernelLog.String(" Heads: "); KernelLog.Int(fdp.NumberOfHeads, 0);
- KernelLog.String(" SectorsPerTrack: "); KernelLog.Int(fdp.SectorsPerTrack, 0);
- KernelLog.String(" BytesPerSector: "); KernelLog.Int(fdp.BytesPerSector, 0); KernelLog.Ln;
- KernelLog.String(" Motordelay On: "); KernelLog.Int(fdp.MotorOnDelay, 0);
- KernelLog.String(" Off: "); IF fdp.MotorOffDelay = 0 THEN KernelLog.String("Don't turn off!!"); ELSE KernelLog.Int(fdp.MotorOffDelay, 0); END;
- KernelLog.Ln;
- KernelLog.String(" Medium Rotation rate: "); KernelLog.Int(fdp.MediumRotationRate, 0); KernelLog.String(" rpm"); KernelLog.Ln;
- ELSE
- KernelLog.String("Information not available"); KernelLog.Ln;
- END;
- END;
- END ShowFlexibleDiskPage;
- PROCEDURE ShowSenseData(key, asc, ascq : CHAR; information : LONGINT; valid : BOOLEAN);
- BEGIN
- IF Debug.Trace THEN
- KernelLog.String("UsbStorage: Sense Key: "); KernelLog.Int(ORD(key), 0);
- KernelLog.String(" asc: "); KernelLog.Hex(ORD(asc), 0); KernelLog.String(" ascq: "); KernelLog.Hex(ORD(ascq), 0); KernelLog.String(" -> ");
- IF key = 0X THEN KernelLog.String("Device reports error, but auto-sense gives 0X");
- ELSIF (key = 1X) & (asc = 17X) & (ascq = 01X) THEN KernelLog.String("Recovered data with retries");
- ELSIF (key = 1X) & (asc = 18X) & (ascq = 00X) THEN KernelLog.String("Recovered data with ECC");
- ELSIF (key = 1X) THEN KernelLog.String("Recovered Error");
- ELSIF (key = 2X) & (asc = 04X) & (ascq = 01X) THEN KernelLog.String("Logical drive not ready, becoming ready");
- ELSIF (key = 2X) & (asc = 04X) & (ascq = 02X) THEN KernelLog.String("Logical drive not ready - initialization required");
- ELSIF (key = 2X) & (asc = 04X) & (ascq = 04X) THEN KernelLog.String("Logical unit not ready - format in progress");
- ELSIF (key = 2X) & (asc = 04X) & (ascq = 0FFX) THEN KernelLog.String("Logical drive not ready - device is busy");
- ELSIF (key = 2X) & (asc = 06X) & (ascq = 00X) THEN KernelLog.String("No reference position found");
- ELSIF (key = 2X) & (asc = 08X) & (ascq = 00X) THEN KernelLog.String("Logical unit communication failure");
- ELSIF (key = 2X) & (asc = 08X) & (ascq = 01X) THEN KernelLog.String("Logical unit communication timeout");
- ELSIF (key = 2X) & (asc = 08X) & (ascq = 80X) THEN KernelLog.String("Logical unit communication overrun");
- ELSIF (key = 2X) & (asc = 3AX) & (ascq = 00X) THEN KernelLog.String("Medium not present");
- ELSIF (key = 2X) & (asc = 54X) & (ascq = 00X) THEN KernelLog.String("USB to host system interface failure");
- ELSIF (key = 2X) & (asc = 80X) & (ascq = 00X) THEN KernelLog.String("Insufficient resources");
- ELSIF (key = 2X) & (asc = 0FFX) & (ascq = 0FFX) THEN KernelLog.String("Unknown error");
- ELSIF (key = 2X) THEN KernelLog.String("Not Ready");
- ELSIF (key = 3X) & (asc = 02X) & (ascq = 00X) THEN KernelLog.String("No seek complete");
- ELSIF (key = 3X) & (asc = 03X) & (ascq = 00X) THEN KernelLog.String("Write fault");
- ELSIF (key = 3X) & (asc = 10X) & (ascq = 00X) THEN KernelLog.String("ID CRC Error");
- ELSIF (key = 3X) & (asc = 11X) & (ascq = 00X) THEN KernelLog.String("Unrecovered read error");
- ELSIF (key = 3X) & (asc = 12X) & (ascq = 00X) THEN KernelLog.String("Address mark not found for ID field");
- ELSIF (key = 3X) & (asc = 13X) & (ascq = 00X) THEN KernelLog.String("Address mark not found for data field");
- ELSIF (key = 3X) & (asc = 14X) & (ascq = 00X) THEN KernelLog.String("Recorded entity not found");
- ELSIF (key = 3X) & (asc = 30X) & (ascq = 01X) THEN KernelLog.String("Cannot read medium - unknown format");
- ELSIF (key = 3X) & (asc = 31X) & (ascq = 01X) THEN KernelLog.String("Format command failed");
- ELSIF (key = 3X) THEN KernelLog.String("Medium Error");
- ELSIF (key = 4X) & (asc = 40X) THEN KernelLog.String("Diagnostic failure on component "); KernelLog.Int(ORD(ascq), 0);
- ELSIF (key = 4X) THEN KernelLog.String("Hardware Error");
- ELSIF (key = 5X) & (asc = 1AX) & (ascq = 00X) THEN KernelLog.String("Parameter list length error");
- ELSIF (key = 5X) & (asc = 20X) & (ascq = 00X) THEN KernelLog.String("Invalid command operation code");
- ELSIF (key = 5X) & (asc = 21X) & (ascq = 00X) THEN KernelLog.String("Logical block address out of range");
- ELSIF (key = 5X) & (asc = 24X) & (ascq = 00X) THEN KernelLog.String("Invalid field in command packet");
- ELSIF (key = 5X) & (asc = 25X) & (ascq = 00X) THEN KernelLog.String("Logical unit not supported");
- ELSIF (key = 5X) & (asc = 26X) & (ascq = 00X) THEN KernelLog.String("Invalid field in parameter list");
- ELSIF (key = 5X) & (asc = 26X) & (ascq = 01X) THEN KernelLog.String("Parameter not supported");
- ELSIF (key = 5X) & (asc = 26X) & (ascq = 02X) THEN KernelLog.String("Parameter value invalid");
- ELSIF (key = 5X) & (asc = 39X) & (ascq = 00X) THEN KernelLog.String("Saving parameters not supported");
- ELSIF (key = 5X) THEN KernelLog.String("Illegal Request");
- ELSIF (key = 6X) & (asc = 28X) & (ascq = 00X) THEN KernelLog.String("Not ready to ready transition - media changed");
- ELSIF (key = 6X) & (asc = 29X) & (ascq = 00X) THEN KernelLog.String("PowerOnReset, retrying");
- ELSIF (key = 6X) & (asc = 2FX) & (ascq = 00X) THEN KernelLog.String("Commands cleared by another indicator");
- ELSIF (key = 6X) THEN KernelLog.String("Unit Attention");
- ELSIF (key = 7X) & (asc = 27X) & (ascq = 00X) THEN KernelLog.String("Write protected media");
- ELSIF (key = 7X) THEN KernelLog.String("Data Protected");
- ELSIF (key = 8X) THEN KernelLog.String("Blank Check");
- ELSIF (key = 9X) THEN KernelLog.String("Vendor Specific");
- ELSIF (key = 0AX) THEN KernelLog.String("Reserved");
- ELSIF (key = 0BX) & (asc = 4EX) & (ascq = 00X) THEN KernelLog.String("Overlapped command attemted");
- ELSIF (key = 0BX) THEN KernelLog.String("Aborted command");
- ELSIF (key = 0CX) THEN KernelLog.String("Reserved");
- ELSIF (key = 0DX) THEN KernelLog.String("Volume Overflow");
- ELSIF (key = 0EX) THEN KernelLog.String("Miscompare");
- ELSIF (key = 0FX) THEN KernelLog.String("Reserved");
- ELSE KernelLog.String("Unkown");
- END;
- IF valid THEN KernelLog.String(" [information: "); KernelLog.Int(information, 0); KernelLog.String("]"); END;
- KernelLog.Ln;
- END;
- END ShowSenseData;
- PROCEDURE ShowRes(res : WORD);
- BEGIN
- IF Debug.Level >= Debug.Errors THEN
- CASE res OF
- ResOk: KernelLog.String("OK");
- |ResWriteProtected: KernelLog.String("Write-Protected");
- |ResDeviceInUse: KernelLog.String("DeviceInUse");
- |ResMediaChanged: KernelLog.String("MediaChanged");
- |ResMediaMissing: KernelLog.String("MediaMissing");
- |ResUnsupported: KernelLog.String("Unsupported");
- |ResTimeout: KernelLog.String("Timeout");
- |ResShortTransfer: KernelLog.String("ShortTransfer");
- |ResDeviceNotReady: KernelLog.String("DeviceNotReady");
- |ResDeviceNotReadyInit: KernelLog.String("DeviceNotReady-need init");
- |ResSenseError: KernelLog.String("SenseError");
- |ResError: KernelLog.String("Error");
- |ResFatalError: KernelLog.String("FatalError");
- |ResDisconnected: KernelLog.String("Disconnected");
- ELSE
- KernelLog.String("Unknown(res="); KernelLog.Int(res, 0); KernelLog.String(")");
- END;
- END;
- END ShowRes;
- (** Shows all devices which are registered at the UsbStorage Disk Manager and the Disks.registry *)
- PROCEDURE Show*(context : Commands.Context);
- VAR table : Plugins.Table; i : LONGINT;
- BEGIN
- (* show all devices which are registered at the UsbStorage disk manager *)
- diskManager.Show;
- (* show all devices registered at Disks.registry *)
- Disks.registry.GetAll(table);
- context.out.String("Storage devices registered at Disks.registry:"); context.out.Ln;
- IF table = NIL THEN
- context.out.String("No devices registered."); context.out.Ln;
- ELSE
- FOR i := 0 TO LEN(table)-1 DO
- context.out.String(table[i].name); context.out.String(" ("); context.out.String(table[i].desc); context.out.String(")"); context.out.Ln;
- END;
- END;
- END Show;
- (* ToBeRemoved *)
- PROCEDURE SetMax*(context : Commands.Context);
- BEGIN
- context.out.String("UsbStorage: Max performance mode."); context.out.Ln;
- performance := Usbdi.MaxPerformance;
- END SetMax;
- (* ToBeRemoved *)
- PROCEDURE SetNormal*(context : Commands.Context);
- BEGIN
- context.out.String("UsbStorage: Normal performance mode."); context.out.Ln;
- performance := Usbdi.Normal;
- END SetNormal;
- BEGIN
- NEW(diskManager);
- performance := Usbdi.MaxPerformance;
- END UsbStorageBase.
- UsbStorage.Install ~ System.Free UsbStorage ~
- UsbStorage.Show ~
|