program UsbDriveEraser_SearchForErrors; {$mode delphi} {$H+} uses RaspberryPi, GlobalConst, GlobalTypes, Platform, Threads, Console, Framebuffer, SysUtils, Classes, Devices, Storage, FileSystem, Ultibo, HTTP, WebStatus, RemoteShell, ShellFilesystem, ShellUpdate, GPIO, Logging; const {Define constants for the states of our switch (On or Off)} FORMAT_SWITCH_STATE_OFF = 0; FORMAT_SWITCH_STATE_ON = 1; var UserInput:String; WindowHandle:TWindowHandle; HTTPListener:THTTPListener; CurrentDiskDevice:TDiskDevice; CurrentStorageDevice:PStorageDevice; FormatSwitchState:LongWord; function NotifyStorageDevice(Device:PDevice;Data:Pointer;Notification:LongWord):LongWord; {This function will be called when a storage device event that we have registered for occurs} var Count:Integer; DiskDevice:TDiskDevice; begin Result:=ERROR_SUCCESS; {Check Device} if Device = nil then Exit; {Check Notification} if (Notification and DEVICE_NOTIFICATION_INSERT) <> 0 then begin ConsoleWindowWriteLn(WindowHandle,'Storage Device inserted'); {Get (and Lock) Device} Count:=0; DiskDevice:=FileSysDriver.GetDeviceByStorage(PStorageDevice(Device),True,FILESYS_LOCK_READ); while DiskDevice = nil do begin {Sleep to allow time for the filesystem to mount the device} Sleep(500); {Try to get the device again} DiskDevice:=FileSysDriver.GetDeviceByStorage(PStorageDevice(Device),True,FILESYS_LOCK_READ); Inc(Count); if Count >= 5 then Break; end; try if DiskDevice <> nil then begin ConsoleWindowWriteLn(WindowHandle,'Device Name: ' + DiskDevice.Name); {Check for a USB device} if Device.DeviceBus = DEVICE_BUS_USB then begin ConsoleWindowWriteLn(WindowHandle,'USB device detected'); {Save the details for later} CurrentDiskDevice:=DiskDevice; CurrentStorageDevice:=PStorageDevice(Device); end else begin ConsoleWindowWriteLn(WindowHandle,'Device is not USB'); end; end else begin ConsoleWindowWriteLn(WindowHandle,'Device Name: '); end; finally {Unlock Device} DiskDevice.ReaderUnlock; end; end else if (Notification and DEVICE_NOTIFICATION_EJECT) <> 0 then begin ConsoleWindowWriteLn(WindowHandle,'Storage Device ejected'); {Turn off USB-drive-mounted LED} GPIOOutputSet(GPIO_PIN_6,GPIO_LEVEL_LOW); {Check Device} if PStorageDevice(Device) = CurrentStorageDevice then begin ConsoleWindowWriteLn(WindowHandle,'Current USB device ejected'); {Clear the details} CurrentDiskDevice:=nil; CurrentStorageDevice:=nil; end else begin ConsoleWindowWriteLn(WindowHandle,'Other device ejected'); end; end; end; function PartitionTypeToFileSysType(PartitionType:Byte):TFileSysType; {A little function to determine the file system type from the partition id} begin Result:=fsUNKNOWN; case PartitionType of pidFAT12:Result:=fsFAT12; pidFAT16,pidFAT16HUGE,pidFAT16LBA:Result:=fsFAT16; pidFAT32,pidFAT32LBA:Result:=fsFAT32; pidHPFSNTFS:Result:=fsNTFS51; end; end; procedure FlashFormattingLED; {This procedure will toggle the LED GPIO on and off} begin while True do GPIOOutputSet(GPIO_PIN_13,GPIO_LEVEL_HIGH); Sleep(250); GPIOOutputSet(GPIO_PIN_13,GPIO_LEVEL_LOW); Sleep(250); end; procedure FormatDiskDevice(DiskDevice:TDiskDevice;StorageDevice:PStorageDevice); {This procedure will format each partition found on the supplied disk device} var Count:Integer; Volumes:TList; Partitions:TList; DiskVolume:TDiskVolume; DiskPartition:TDiskPartition; FloppyType:TFloppyType; FileSysType:TFileSysType; begin ConsoleWindowWriteLn(WindowHandle,'Formatting Disk Device'); {Start flashing of Formatting-in-Progress LED} FlashFormattingLED(); {Lock Device} if FileSysDriver.CheckDevice(DiskDevice,True,FILESYS_LOCK_READ) then begin try ConsoleWindowWriteLn(WindowHandle,'Checking device ' + DiskDevice.Name); {Get Partitions} Partitions:=FileSysDriver.GetPartitionsByDevice(DiskDevice,False,FILESYS_LOCK_NONE); try if Partitions.Count > 0 then begin {Check Partitions} for Count:=0 to Partitions.Count - 1 do begin DiskPartition:=Partitions.Items[Count]; if FileSysDriver.CheckPartition(DiskPartition,True,FILESYS_LOCK_READ) then begin try ConsoleWindowWriteLn(WindowHandle,'Checking partition ' + DiskPartition.Path); {Get Volume} DiskVolume:=FileSysDriver.GetVolumeByPartition(DiskPartition,True,FILESYS_LOCK_WRITE); if DiskVolume <> nil then begin try ConsoleWindowWriteLn(WindowHandle,'Formatting volume ' + DiskVolume.Name); {Get Format Type} FileSysType:=DiskVolume.FileSysType; if FileSysType = fsUNKNOWN then FileSysType:=PartitionTypeToFileSysType(DiskPartition.PartitionId); if FileSysType <> fsUNKNOWN then begin {Get Floppy Type} FloppyType:=DiskDevice.FloppyType; {Format Volume} if DiskVolume.FormatVolume(FloppyType,FileSysType) then begin ConsoleWindowWriteLn(WindowHandle,'Format completed ' + DiskVolume.Name); end else begin ConsoleWindowWriteLn(WindowHandle,'Could not format ' + DiskVolume.Name); {Illuminate Error LED} GPIOOutputSet(GPIO_PIN_26,GPIO_LEVEL_HIGH); end; end else begin ConsoleWindowWriteLn(WindowHandle,'Unrecognized type for volume ' + DiskVolume.Name); end; finally {Unlock Volume} DiskVolume.WriterUnlock; end; end else begin ConsoleWindowWriteLn(WindowHandle,'No volume found to format on partition ' + DiskPartition.Path); {Illuminate Error LED} GPIOOutputSet(GPIO_PIN_26,GPIO_LEVEL_HIGH); end; finally {Unlock Partition} DiskPartition.ReaderUnlock; end; end; end; end else begin {Get Volumes} Volumes:=FileSysDriver.GetVolumesByDevice(DiskDevice,False,FILESYS_LOCK_NONE); try if Volumes.Count > 0 then begin {Check Volumes} for Count:=0 to Volumes.Count - 1 do begin DiskVolume:=Volumes.Items[Count]; if FileSysDriver.CheckVolume(DiskVolume,True,FILESYS_LOCK_WRITE) then begin try ConsoleWindowWriteLn(WindowHandle,'Formatting volume ' + DiskVolume.Name); {Get Format Type} FileSysType:=DiskVolume.FileSysType; if FileSysType = fsUNKNOWN then FileSysType:=fsFAT32; {Get Floppy Type} FloppyType:=DiskDevice.FloppyType; {Format Volume} if DiskVolume.FormatVolume(FloppyType,FileSysType) then begin ConsoleWindowWriteLn(WindowHandle,'Format completed ' + DiskVolume.Name); end else begin ConsoleWindowWriteLn(WindowHandle,'Could not format ' + DiskVolume.Name); {Illuminate Error LED} GPIOOutputSet(GPIO_PIN_26,GPIO_LEVEL_HIGH); end; finally {Unlock Volume} DiskVolume.WriterUnlock; end; end; end; end else begin ConsoleWindowWriteLn(WindowHandle,'No volume found to format on device ' + DiskDevice.Name); {Illuminate Error LED} GPIOOutputSet(GPIO_PIN_26,GPIO_LEVEL_HIGH); end; finally Volumes.Free; end; end; finally Partitions.Free; end; finally {Unlock Device} DiskDevice.ReaderUnlock; {Stop flashing of Formatting-in-Progress LED} GPIOOutputSet(GPIO_PIN_13,GPIO_LEVEL_LOW); {Illuminate Wipe completion LED} GPIOOutputSet(GPIO_PIN_19,GPIO_LEVEL_HIGH); end; end; end; {This is the function called when a GPIO event occurs} procedure GPIOEventFunction(Data:Pointer;Pin,Trigger:LongWord); begin {Sleep for a few milliseconds to allow the switch to settle} Sleep(10); {Read the current state of the switch (High or Low)} if GPIOInputGet(GPIO_PIN_12) = GPIO_LEVEL_LOW then begin {If the state is Low the switch is On so update the state variable} FormatSwitchState:=FORMAT_SWITCH_STATE_ON; {Print a message on the console in green to say the switch is on} ConsoleWindowSetForecolor(WindowHandle,COLOR_GREEN); ConsoleWindowWriteLn(WindowHandle,'Format switch is ON'); {Reregister this function to be called when the state changes to High} GPIOInputEvent(GPIO_PIN_12,GPIO_TRIGGER_HIGH,INFINITE,@GPIOEventFunction,nil); end else begin {The level is High so the switch must be Off, save that to the state variable} FormatSwitchState:=FORMAT_SWITCH_STATE_OFF; {Print the switch is off message in red} ConsoleWindowSetForecolor(WindowHandle,COLOR_RED); ConsoleWindowWriteLn(WindowHandle,'Format switch is OFF'); {And register again for an event when the level changes to Low} GPIOInputEvent(GPIO_PIN_12,GPIO_TRIGGER_LOW,INFINITE,@GPIOEventFunction,nil); end; end; begin HTTPListener:=THTTPListener.Create; HTTPListener.Active:=True; WebStatusRegister(HTTPListener,'','',True); {Create a console window at the top half of the screen} WindowHandle:=ConsoleWindowCreate(ConsoleDeviceGetDefault,CONSOLE_POSITION_TOP,True); {Output some welcome text on the console window} ConsoleWindowWriteLn(WindowHandle,'Disk detection and formatting example'); { Console logging} {Enable console logging and set the logging position to where we want it, then call LoggingConsoleDeviceAdd passing the default console. Finally make the console logging device the default so all logging output goes directly to it} { ConsoleWindowCreate(ConsoleDeviceGetDefault,CONSOLE_POSITION_BOTTOM,True)} (* CONSOLE_REGISTER_LOGGING:=True; CONSOLE_LOGGING_POSITION:=CONSOLE_POSITION_BOTTOM; LoggingConsoleDeviceAdd(ConsoleDeviceGetDefault); LoggingDeviceSetDefault(LoggingDeviceFindByType(LOGGING_TYPE_CONSOLE)); *) {Set GPIO pin 12 (our switch) to Pull Up so the pin will be High when the switch is off and Low when the switch is On. Then set the function of pin 12 to be an Input so we can read the switch state using the GPIOInputGet() function} GPIOPullSelect(GPIO_PIN_12,GPIO_PULL_UP); GPIOFunctionSelect(GPIO_PIN_12,GPIO_FUNCTION_IN); {Set GPIO pin 5 (System Ready LED) to Pull None and make the function of the pin an Output so we can set the level to High or Low to turn the LED On or Off. After setting the function we also set the current level to Low so the LED is Off} GPIOPullSelect(GPIO_PIN_5,GPIO_PULL_NONE); GPIOFunctionSelect(GPIO_PIN_5,GPIO_FUNCTION_OUT); GPIOOutputSet(GPIO_PIN_5,GPIO_LEVEL_LOW); {Do the same for GPIO pins 6, 13, 19 and 26 (the USB Drive Mounted, Wipe-in-Progress, Wipe Complete and Error LEDs)} GPIOPullSelect(GPIO_PIN_6,GPIO_PULL_NONE); GPIOFunctionSelect(GPIO_PIN_6,GPIO_FUNCTION_OUT); GPIOOutputSet(GPIO_PIN_6,GPIO_LEVEL_LOW); GPIOPullSelect(GPIO_PIN_13,GPIO_PULL_NONE); GPIOFunctionSelect(GPIO_PIN_13,GPIO_FUNCTION_OUT); GPIOOutputSet(GPIO_PIN_13,GPIO_LEVEL_LOW); GPIOPullSelect(GPIO_PIN_19,GPIO_PULL_NONE); GPIOFunctionSelect(GPIO_PIN_19,GPIO_FUNCTION_OUT); GPIOOutputSet(GPIO_PIN_19,GPIO_LEVEL_LOW); GPIOPullSelect(GPIO_PIN_26,GPIO_PULL_NONE); GPIOFunctionSelect(GPIO_PIN_26,GPIO_FUNCTION_OUT); GPIOOutputSet(GPIO_PIN_26,GPIO_LEVEL_LOW); {Register for Insert and Eject notifications for Storage Devices} StorageDeviceNotification(nil,NotifyStorageDevice,nil,DEVICE_NOTIFICATION_EJECT or DEVICE_NOTIFICATION_INSERT,NOTIFIER_FLAG_WORKER); {Loop waiting for a device} while True do begin {After booting, the Device Ready indicator LED should illuminate} GPIOOutputSet(GPIO_PIN_5,GPIO_LEVEL_HIGH); if (CurrentDiskDevice <> nil) and (CurrentStorageDevice <> nil) then begin {When drive is mounted, the USB Drive Mounted LED should illuminate} GPIOOutputSet(GPIO_PIN_6,GPIO_LEVEL_HIGH); {Prompt the user to format the device, this uses the keyboard but it could be a GPIO input or something else} ConsoleWindowWriteLn(WindowHandle,'USB device detected, type FORMAT to proceed'); ConsoleReadLn(UserInput); if UserInput = 'FORMAT' then begin {Proceed with format} FormatDiskDevice(CurrentDiskDevice,CurrentStorageDevice); end; (* if FormatSwitchState = 1 then begin {Proceed with format} FormatDiskDevice(CurrentDiskDevice,CurrentStorageDevice); end *) end; {Sleep on each loop} Sleep(500); end; {Halt the thread} ThreadHalt(0); end.