#ifdef USB_DISK
/*******************************************************************************
 *
 *
 *	Copyright 2003 Steven James <pyro@linuxlabs.com> and
 *	LinuxLabs http://www.linuxlabs.com
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 ******************************************************************************/
#include <etherboot.h>
#include <pci.h>
#include <timer.h>
#include <lib.h>
        
#define DEBUG_THIS DEBUG_USB
#include <debug.h>

#define DPRINTF debug


#include "scsi.h"


#include "usb_scsi_low.h"

#ifndef NULL
#define NULL (void *) 0x0
#endif

#include "scsi_cmds.h"

devhandle sgh;

typedef struct sense_data {
	uchar code;
	
	uchar sense_key:4;
	uchar res1:4;

	uchar additional_code;
	uchar qualifier;

	uchar res2[3];

	uchar length;
} __attribute__ ((packed)) sense_data_t;

typedef struct fixed_sense_data {
	uchar code:7;
	uchar valid:1;

	uchar obs1;

	uchar sense_key:4;
	uchar res1:1;
	uchar ili:1;
	uchar eom:1;
	uchar mark:1;

	unsigned int  info;

	uchar add_len;
} __attribute__ ((packed)) fixed_sense_data_t;

typedef struct additional_fixed_data {
	unsigned int info;
	
	uchar code;
	uchar qualifier;
	uchar fru;

	uchar specific[3];
} __attribute__ ((packed)) additional_fixed_data_t;
	

void PrintSense(uchar *sense, int len)
{
	int i;

	DPRINTF( "sense data ");
	for(i=0;i<len; i++) {
		DPRINTF(  ":%02x", sense[i]);
	}
	DPRINTF("\n\n");

	if( (sense[0] & 0x7f) >=0x72) {
		sense_data_t *sd = (sense_data_t *) sense;
		uchar *pos = sense+sizeof(sense_data_t);
		uchar remaining = sd->length;
		int dlen;
		
		DPRINTF("code = %02x, key = %1x, additional = %02x, qual = %02x\n", sd->code, sd->sense_key, sd->additional_code, sd->qualifier);

		while(remaining) {
			DPRINTF("type = %02x", pos[0]);
			dlen = pos[1];
			pos+=2;
			remaining -=2;

			for(i=0; i<dlen; i++) 
				DPRINTF( ": %02x", pos[i]);

			DPRINTF("\n");
			pos+=i;
			remaining -=i;
		}

	} else {
		fixed_sense_data_t *fd = (fixed_sense_data_t *) sense;
		uchar remaining = fd->add_len;
		additional_fixed_data_t *afd;

		
		DPRINTF("code = %02x key = %1x\n", fd->code, fd->sense_key);
		if(fd->mark) {
			DPRINTF("filemark ");
		}

		if(fd->eom) {
			DPRINTF(" End Of Media ");
		}

		if(fd->ili) {
			DPRINTF("Illegal instruction");
		}

		DPRINTF("\n");

		if(fd->valid) {
			DPRINTF( "(valid) ");
		}

		DPRINTF( "Info: %08x\n", ntohl(fd->info));

		afd = (additional_fixed_data_t *) (sense + 8);

//		while(remaining) {
		if(remaining) {
			DPRINTF("command info = %08x\n", ntohl(afd->info));
			DPRINTF("code = %02x, qual = %02x, fru = %02x\n", afd->code, afd->qualifier, afd->fru);
			DPRINTF("sense key data = %02x:%02x:%02x\n\n", afd->specific[2], afd->specific[1], afd->specific[0]);

			afd++;
			remaining -= sizeof(additional_fixed_data_t);
		}
	}
		
}
	
typedef struct query_response {
	uchar type:5;
	uchar qualifier:3;

	uchar reserved1:7;
	uchar removable:1;

	uchar version;

	uchar ResponseDataFormat:4;	// should == 2
	uchar HiSup:1;			// report luns cmd supported
	uchar NormACA:1;
	uchar obsolete:1;
	uchar aerc:1;

	uchar AdditionalLength;		// length of vendor specific data (beyond 96 bytes)

	uchar reserved2:7;
	uchar sccs:1;			// have raid controller

	uchar addr16:1;			// 
	uchar obsolete2:2;
	uchar MChnger:1;		// media changer
	uchar MultiP:1;			// multi port
	uchar vs:1;			// ???
	uchar EncServ:1;		// enclosure service
	uchar BQue:1;			// basic command queueing

	uchar vs2:1;
	uchar CmdQue:1;			// full command queueing
	uchar obsolete4:1;
	uchar linked:1;
	uchar sync:1;
	uchar wbus16:1;			//
	uchar obsolete3:1;
	uchar RelAddr:1;		// treletive addressing

	char vendor[8];
	char product[16];
	char revision[4];
	char vendor_data[20];

	uchar ius:1;
	uchar qas:1;
	uchar clocking:2;		// 
	uchar reserved3:4;

	unsigned short version_desc[8];

	char reserved4[21];
} query_response_t;

typedef struct ReadBlockCMD {
	uchar cmd;

	uchar reladdr:1;
	uchar reserved:2;
	uchar fua:1;		// force unit access flush to media
	uchar dpo:1;		// direct page out, do not cache
	uchar reserved2:3;

	unsigned int block_address;
	uchar reserved3;
	
	unsigned short block_count;

	uchar control;
} __attribute__ ((packed)) ReadBlockCMD_t ;

int ll_read_block(devhandle sgd, char *buffer, int blocknum, int count)
{
	int ret;
	ReadBlockCMD_t rb;
	char sensedat[32];

	memset(&rb,0,sizeof(rb));
	rb.cmd = READ_10;
	rb.block_address = htonl(blocknum);
	rb.block_count = htons(count);
	
	ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV,  buffer, count * 512, sensedat, sizeof(sensedat));

	if(ret<0) {
		DPRINTF("ERROR: ll_read_block( %x, %x, %x, %x) = %d\n", sgd, buffer, blocknum, count, ret);
		PrintSense(sensedat, 32);
	}

	return(ret);

}

int ll_write_block(devhandle sgd, char *buffer, int blocknum, int count)
{
	int ret;
	ReadBlockCMD_t rb;
	char sensedat[32];

	memset(&rb,0,sizeof(rb));
	rb.cmd = WRITE_10;
	rb.block_address = htonl(blocknum);
	rb.block_count = htons(count);
	
	ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_TO_DEV,  buffer, count * 512, sensedat, sizeof(sensedat));

	return(ret);
}

typedef struct ReadLongCMD {
	uchar cmd;

	uchar reladdr:1;
	uchar correct:1;
	uchar reserved:5;

	unsigned int block_address;
	uchar reserved3;
	
	unsigned short length;

	uchar control;
} __attribute__ ((packed)) ReadLongCMD_t ;

int ll_read_long(devhandle sgd, char *buffer, int blocknum, int size)
{
	int ret;
	ReadLongCMD_t rb;
	char sensedat[32];

	memset(&rb,0,sizeof(rb));
	rb.cmd = READ_LONG;
	rb.block_address = htonl(blocknum);
	rb.length = htons(size);
	
	ret = scsi_command( sgd, (uint8_t *)&rb, sizeof(rb), SG_DXFER_FROM_DEV,  buffer, size, sensedat, sizeof(sensedat));
	return(ret);
}

unsigned char ReadCapacityCMD[10] = { READ_CAPACITY, 0, 0,0,0,0, 0,0,0, 0};

struct ReadCapacityResponse {
	unsigned int block_address;
	unsigned int block_length;
};

int get_capacity(devhandle sgd, unsigned long *block_count, unsigned int *blk_len)
{
	int ret;
	struct ReadCapacityResponse response;
	char sensedat[32];

	ret = scsi_command(sgd, ReadCapacityCMD, sizeof(ReadCapacityCMD), SG_DXFER_FROM_DEV, (uint8_t *)&response, sizeof(response), sensedat, sizeof(sensedat) );
	if(ret<0) {
		DPRINTF("ERROR:get capacity: %d\n", ret);
		PrintSense(sensedat,32);
	}


	*block_count = ntohl(response.block_address) +1;
	*blk_len = ntohl(response.block_length);

	return(ret);
}

#define INQ_REP_LEN 96
unsigned char InquiryCMD[6] = { INQUIRY, 0, 0, 0, INQ_REP_LEN, 0};

int query(devhandle sgd, query_response_t *qr)
{
	int ret;
	char sensedat[32];

	ret = scsi_command(sgd, InquiryCMD, sizeof(InquiryCMD), SG_DXFER_FROM_DEV, (uint8_t *)qr, sizeof(query_response_t), sensedat, sizeof(sensedat) );

	if(ret<0){
		DPRINTF("query: IOCTL");
	}

	return(ret);
}

typedef struct lun_list {
	unsigned int list_length;
	unsigned int reserved;
	unsigned long long lun[16];
} lun_list_t;

#define REPORT_LUNS 0xa0
unsigned char ReportLunsCMD[12] = { REPORT_LUNS, 0, 2, 0, 0, 0, 0, 0, 0, 128, 0, 0 };

int ReportLUNS(devhandle sgd, lun_list_t *list)
{
	int ret;
	char sensedat[32];

	memset (list, 0, sizeof(lun_list_t));
	ret = scsi_command(sgd, ReportLunsCMD, sizeof(ReportLunsCMD), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(lun_list_t), sensedat, sizeof(sensedat) );

	if(ret<0) {
		DPRINTF("Report Luns: IOCTL");
	}

	list->list_length = ntohl(list->list_length);

	return(ret);
}

typedef struct command_descriptor {
	uchar opcode;
	uchar reserved;
	unsigned short service_action;
	uchar reserved2;

	uchar action_valid:1;
	uchar reserved3:7;

	unsigned short cdb_len;
} __attribute__ ((packed)) command_descriptor_t;

typedef struct report_opcodes_result {
	unsigned long length;

	command_descriptor_t command[256];
} __attribute__ ((packed)) report_opcode_result_t;


#define REPORT_OPCODES 0xa3

typedef struct report_opcodes_cmd {
	uchar cmd;
	uchar reserved[5];
	unsigned int reply_len;
	uchar reserved2;
	uchar control;
} __attribute__ ((packed)) ReportOpcodesCMD_t;

//ReportOpcodesCMD_t ReportOpcodesCMD = { cmd : REPORT_OPCODES, reply_len: htonl(sizeof(report_opcode_result_t)) };

int ReportOpCodes(devhandle sgd, report_opcode_result_t *list)
{
	int ret;
	char sensedat[32];
	ReportOpcodesCMD_t ReportOpcodesCMD;

	memset (list, 0, sizeof(report_opcode_result_t));
	ReportOpcodesCMD.cmd = REPORT_OPCODES;
	ReportOpcodesCMD.reply_len = htonl( sizeof(report_opcode_result_t));

	ret = scsi_command(sgd, (uint8_t *)&ReportOpcodesCMD, sizeof(ReportOpcodesCMD_t), SG_DXFER_FROM_DEV, (uint8_t *)list, sizeof(report_opcode_result_t), sensedat, sizeof(sensedat) );

	if(ret<0) {
		DPRINTF("Report Luns: IOCTL");
	}

	list->length = ntohl(list->length);

	return(ret);
}


#define READ_ATTRIBUTE 0x8c
#define VOLUME_LIST 2
#define PARTITION_LIST 3

typedef struct read_attribute_cmd {
	uchar cmd;
	
	uchar action:5;
	uchar res:3;

	uchar restricted[3];

	uchar volume;
	uchar res2;
	uchar partition;

	ushort attribute;
	unsigned int reply_len;
	uchar res3;
	uchar control;
} __attribute__ ((packed)) ReadAttributeCMD_t;

int CheckVolumes(devhandle sgd)
{
	int ret;
	uchar reply[4];
	uchar sensedat[32];
	ReadAttributeCMD_t cmd;

	memset(&cmd,0,sizeof(cmd));

	cmd.cmd=READ_ATTRIBUTE;
	cmd.action = VOLUME_LIST;
	cmd.reply_len = htonl(4);

	ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) );
	if(ret<0) {
		DPRINTF("Report Volumes: IOCTL");
		return(-1);
	}

	if(! reply[0] && !reply[1])
		return(0);

	return(reply[3]);
}

int CheckPartitions(devhandle sgd)
{
	int ret;
	uchar reply[4];
	uchar sensedat[32];
	ReadAttributeCMD_t cmd;

	memset(&cmd,0,sizeof(cmd));

	cmd.cmd=READ_ATTRIBUTE;
	cmd.action = PARTITION_LIST;
	cmd.reply_len = htonl(4);

	ret = scsi_command(sgd, (uint8_t *)&cmd, sizeof(cmd), SG_DXFER_FROM_DEV, reply, sizeof(reply), sensedat, sizeof(sensedat) );
	if(ret<0) {
		DPRINTF("Report PARTITIONVolumes: IOCTL");
		return(-1);
	}

	if(! reply[0] && !reply[1])
		return(0);

	return(reply[3]);
}

int UnitReady(devhandle sgd)
{
	uchar cmd[6];
	uchar sensedat[32];
	int ret;

	memset(cmd,0,sizeof(cmd));
	
	ret = scsi_command(sgd, &cmd, sizeof(cmd), SG_DXFER_FROM_DEV, NULL, 0, sensedat, sizeof(sensedat) );
	if(ret<0) {
		DPRINTF("UnitReady :");
		return(0);
	}

	return(1);
}
	

#endif
