/*
DiskWiper 0.1 (freeware)
Copyright © 1999 Peter L. Phillips
mailto:peter@phillips.net
http://www.phillips.net/bedev/index.html
*/

#include <iostream>
#include <be/device/scsi.h>
#include <posix/unistd.h>
#include <stdlib.h>


status_t get_logic_block_info (int fd, uint32 * pui32NumBlocks, uint32 * pui32BlockLength)
{
	status_t			stat					= B_NO_ERROR;
	uint8				scsidata[16];
	uint16				block_descriptor_length	= 0;
	raw_device_command	rawcmd;


	if ((fd == 0) || (pui32NumBlocks == 0) || (pui32BlockLength == 0))
	{
		cout << "invalid parameter(s)" << endl;
		
		return -1;
	}


	rawcmd.command[0] = 0x5a;
	rawcmd.command[1] = 0x00;
	rawcmd.command[2] = 0x00;
	rawcmd.command[3] = 0x00;
	rawcmd.command[4] = 0x00;
	rawcmd.command[5] = 0x00;
	rawcmd.command[6] = 0x00;
	rawcmd.command[7] = 0x10;
	rawcmd.command[8] = 0x00;
	rawcmd.command[9] = 0x00;
	rawcmd.command_length = 10;
	rawcmd.flags = B_RAW_DEVICE_DATA_IN;
	rawcmd.scsi_status = 0;
	rawcmd.cam_status = 0;
	rawcmd.data = scsidata;
	rawcmd.data_length = 16;
	rawcmd.sense_data = 0;	
	rawcmd.sense_data_length = 0;
	rawcmd.timeout = 0;
	

	stat = ioctl (fd, B_RAW_DEVICE_COMMAND, &rawcmd, sizeof (raw_device_command));

	if (stat != B_NO_ERROR)
	{
		cout << "ioctl failed." << endl;
	
		return -1;
	}


	block_descriptor_length = scsidata[6];
	block_descriptor_length = block_descriptor_length << 8;
	block_descriptor_length |= scsidata[7];
	
	if (block_descriptor_length != 8)
	{
		cout << "block descriptor length is unexpected" << endl;
	
		return -1;
	}


	*pui32NumBlocks = scsidata[9];
	*pui32NumBlocks = *pui32NumBlocks << 8;
	*pui32NumBlocks |= scsidata[10];
	*pui32NumBlocks = *pui32NumBlocks << 8;
	*pui32NumBlocks |= scsidata[11];


	*pui32BlockLength = scsidata[13];
	*pui32BlockLength = *pui32BlockLength << 8;
	*pui32BlockLength |= scsidata[14];
	*pui32BlockLength = *pui32BlockLength << 8;
	*pui32BlockLength |= scsidata[15];


	return B_NO_ERROR;
}


status_t write_same_data_block (int fd, uint8 pData[], uint16 ui16BlockSize)
{
	status_t			stat					= B_NO_ERROR;
	raw_device_command	rawcmd;
	uint8				sensedata[4096];
	uint16				ui16Index				= 0;
	uint32				ui32Index				= 0;


	if ((fd == 0) ||(pData == 0) || (ui16BlockSize == 0))
	{
		cout << "invalid parameter(s)" << endl;
		
		return -1;
	}

	for (ui32Index = 0; ui32Index < 4096; ui32Index++)
	{
		sensedata[ui32Index] = 0;
	}


	rawcmd.command[0] = 0x41;
	rawcmd.command[1] = 0x00;
	rawcmd.command[2] = 0x00;
	rawcmd.command[3] = 0x00;
	rawcmd.command[4] = 0x00;
	rawcmd.command[5] = 0x00;
	rawcmd.command[6] = 0x00;
	rawcmd.command[7] = 0x00;
	rawcmd.command[8] = 0x08;
	rawcmd.command[9] = 0x00;
	rawcmd.command_length = 10;
	rawcmd.flags = 0;
	rawcmd.scsi_status = 0;
	rawcmd.cam_status = 0;
	rawcmd.data = pData;
	rawcmd.data_length = ui16BlockSize;
	rawcmd.sense_data = sensedata;	
	rawcmd.sense_data_length = 4096;
	rawcmd.timeout = 0;
	

	stat = ioctl (fd, B_RAW_DEVICE_COMMAND, &rawcmd, sizeof (raw_device_command));

	if (stat != B_NO_ERROR)
	{
		cout << "ioctl failed." << endl;
	
		return -1;
	}

	if ((rawcmd.scsi_status != 0) || (rawcmd.cam_status != 0))
	{
		cout << "scsi_status = " << (uint16)rawcmd.scsi_status << endl;
		cout << "cam_status = " << (uint16)rawcmd.cam_status << endl;

		cout << "rawcmd.sense_data_length = " << rawcmd.sense_data_length << endl;

		for (ui16Index = 0; ui16Index < rawcmd.sense_data_length; ui16Index++)
		{
			cout << hex << (uint16)sensedata[ui16Index];

			if ((ui16Index % 8) == 7)
			{
				cout << endl;
				}
			else
			{
				cout << " ";
			}
		}
		
		cout << endl; 
	}
	

	return B_NO_ERROR;
}


status_t write_data_block (int fd, uint8 pData[], uint16 ui16DataSize, uint32 ui32BlockAddress, uint16 ui16NumBlocks)
{
	status_t			stat					= B_NO_ERROR;
	raw_device_command	rawcmd;


	if ((fd == 0) ||(pData == 0) || (ui16NumBlocks == 0))
	{
		cout << "invalid parameter(s)" << endl;
		
		return -1;
	}


	rawcmd.command[0] = 0x2a;
	rawcmd.command[1] = 0x00;
	rawcmd.command[2] = (ui32BlockAddress >> 24) & 0xff;
	rawcmd.command[3] = (ui32BlockAddress >> 16) & 0xff;
	rawcmd.command[4] = (ui32BlockAddress >> 8) & 0xff;
	rawcmd.command[5] = ui32BlockAddress & 0xff;
	rawcmd.command[6] = 0x00;
	rawcmd.command[7] = (ui16NumBlocks >> 8) & 0xff;
	rawcmd.command[8] = ui16NumBlocks & 0xff;
	rawcmd.command[9] = 0x00;
	rawcmd.command_length = 10;
	rawcmd.flags = 0;
	rawcmd.scsi_status = 0;
	rawcmd.cam_status = 0;
	rawcmd.data = pData;
	rawcmd.data_length = ui16DataSize;
	rawcmd.sense_data = 0;	
	rawcmd.sense_data_length = 0;
	rawcmd.timeout = 0;
	

	stat = ioctl (fd, B_RAW_DEVICE_COMMAND, &rawcmd, sizeof (raw_device_command));

	if (stat != B_NO_ERROR)
	{
		cout << "ioctl failed." << endl;
	
		return -1;
	}

	return B_NO_ERROR;
}


int main (int argc, char *argv[])
{
	status_t	stat				= B_NO_ERROR;
	uint32		num_blocks			= 0;
	uint32		blocks_written		= 0;
	uint32		block_length		= 0;
	int			fd					= 0;
	uint8 *		pData				= 0;
	uint32		ui32Index			= 0;
	uint16		ui16BlocksIn32k		= 0;
	uint32		ui32BlocksInPercent	= 0;
	uint32		ui32DevNameLen		= 0;	
	uint8		ui8PercentComplete	= 0;
	uint8		ui8ByteValue		= 0;

	cout << "DiskWiper 0.1 (freeware) Copyright © 1999 Peter L. Phillips" << endl;
	cout << endl;

	if (argc != 3)
	{
		cout << "Usage:" << endl;
		cout << &(argv[0][2]) << " devname fillbyte" << endl;
		cout << endl;
		cout << "Where:" << endl;
		cout << "devname = raw scsi device name" << endl;
		cout << "fillbyte = the byte value between 0 and 255 to be used" << endl;
		cout << endl;
		cout << "Examples:" << endl;
		cout << &(argv[0][2]) << " /dev/bus/scsi/0/10/0/raw 0" << endl;
		cout << &(argv[0][2]) << " /dev/bus/scsi/0/11/0/raw 255" << endl;
		cout << endl;
		
		return 1;
	}


	ui32DevNameLen = strlen (argv[1]);
	ui8ByteValue = atoi (argv[2]);
	
	
	cout << "Device name = " << argv[1] << endl;
	cout << "Fill byte = " << (uint16)ui8ByteValue << endl;


	if (0 != strcmp ("/raw", &(argv[1][ui32DevNameLen - 4])))
	{
		cout << argv[1] << " isn't a raw device." << endl;
		
		return 2;
	}


	fd = open (argv[1], O_RDWR);

	stat = get_logic_block_info (fd, &num_blocks, &block_length);

	cout << "Number of blocks = " << num_blocks << endl;
	cout << "Block length = " << block_length << " bytes" << endl;
	cout << "Device size = " << ((num_blocks * block_length) / (1024 * 1024)) << "MB" << endl;


	ui16BlocksIn32k = 32 * 1024 / block_length;
	ui32BlocksInPercent = num_blocks / 100;


	pData = new uint8[32 * 1024];
	
	for (ui32Index = 0; ui32Index < 32 * 1024; ui32Index++)
	{
		pData[ui32Index] = ui8ByteValue;
	}

	cout << "Starting write..." << endl;
	
	cout << "0" << flush;


	while ((blocks_written + ui16BlocksIn32k) < num_blocks)
	{
		if (blocks_written > ((ui8PercentComplete + 1) * ui32BlocksInPercent))
		{
			ui8PercentComplete++;
			
			if ((ui8PercentComplete % 10) != 0)
			{
				cout << "-" << flush;
			}
			else
			{
				cout << (ui8PercentComplete / 10) << flush;
			}
		}
		
		stat = write_data_block (fd, pData, 32 * 1024, blocks_written, ui16BlocksIn32k);
		
		if (stat != B_NO_ERROR)
			break;
		
		blocks_written += ui16BlocksIn32k;
	}
	
	if ((blocks_written != num_blocks) && (stat == B_NO_ERROR))
	{
		stat = write_data_block (fd, pData, ((num_blocks - blocks_written) * block_length), blocks_written, num_blocks - blocks_written);
	
		if (stat == B_NO_ERROR)
		{
			blocks_written += num_blocks - blocks_written;
		}
	}

	cout << "|" << endl;
	
	cout << "Write finished." << endl;
	

	delete [] pData;
	pData = 0;

	close(fd);
	

	return 0;
}
