#include <linux/slab.h>
#include "usb.h"
#include "scsiglue.h"
#include "transport.h"

#include "smcommon.h"
#include "smil.h"

void   _Set_D_SsfdcRdCmd(BYTE);
void   _Set_D_SsfdcRdAddr(BYTE);
void   _Set_D_SsfdcRdChip(void);
void   _Set_D_SsfdcRdStandby(void);
void   _Start_D_SsfdcRdHwECC(void);
void   _Stop_D_SsfdcRdHwECC(void);
void   _Load_D_SsfdcRdHwECC(BYTE);
void   _Set_D_SsfdcWrCmd(BYTE);
void   _Set_D_SsfdcWrAddr(BYTE);
void   _Set_D_SsfdcWrBlock(void);
void   _Set_D_SsfdcWrStandby(void);
void   _Start_D_SsfdcWrHwECC(void);
void   _Load_D_SsfdcWrHwECC(BYTE);
int    _Check_D_SsfdcBusy(WORD);
int    _Check_D_SsfdcStatus(void);
void   _Reset_D_SsfdcErr(void);
void   _Read_D_SsfdcBuf(BYTE *);
void   _Write_D_SsfdcBuf(BYTE *);
void   _Read_D_SsfdcByte(BYTE *);
void   _ReadRedt_D_SsfdcBuf(BYTE *);
void   _WriteRedt_D_SsfdcBuf(BYTE *);
BYTE   _Check_D_DevCode(BYTE);

void   _Set_D_ECCdata(BYTE, BYTE *);
void   _Calc_D_ECCdata(BYTE *);


struct SSFDCTYPE                Ssfdc;
struct ADDRESS                  Media;
struct CIS_AREA                 CisArea;

static BYTE                            EccBuf[6];
extern PBYTE                    SMHostAddr;
extern DWORD                    ErrXDCode;

extern WORD  ReadBlock;
extern WORD  WriteBlock;



#define EVEN                    0             /* Even Page for 256byte/page */
#define ODD                     1             /* Odd Page for 256byte/page */


/* SmartMedia Redundant buffer data Control Subroutine
 *----- Check_D_DataBlank() --------------------------------------------
 */
int Check_D_DataBlank(BYTE *redundant)
{
	char i;

	for (i = 0; i < REDTSIZE; i++)
		if (*redundant++ != 0xFF)
			return  ERROR;

	return SMSUCCESS;
}

/* ----- Check_D_FailBlock() -------------------------------------------- */
int Check_D_FailBlock(BYTE *redundant)
{
	redundant += REDT_BLOCK;

	if (*redundant == 0xFF)
		return SMSUCCESS;
	if (!*redundant)
		return ERROR;
	if (hweight8(*redundant) < 7)
		return ERROR;

	return SMSUCCESS;
}

/* ----- Check_D_DataStatus() ------------------------------------------- */
int Check_D_DataStatus(BYTE *redundant)
{
	redundant += REDT_DATA;

	if (*redundant == 0xFF)
		return SMSUCCESS;
	if (!*redundant) {
		ErrXDCode = ERR_DataStatus;
		return ERROR;
	} else
		ErrXDCode = NO_ERROR;

	if (hweight8(*redundant) < 5)
		return ERROR;

	return SMSUCCESS;
}

/* ----- Load_D_LogBlockAddr() ------------------------------------------ */
int Load_D_LogBlockAddr(BYTE *redundant)
{
	WORD addr1, addr2;

	addr1 = (WORD)*(redundant + REDT_ADDR1H)*0x0100 + (WORD)*(redundant + REDT_ADDR1L);
	addr2 = (WORD)*(redundant + REDT_ADDR2H)*0x0100 + (WORD)*(redundant + REDT_ADDR2L);

	if (addr1 == addr2)
		if ((addr1 & 0xF000) == 0x1000) {
			Media.LogBlock = (addr1 & 0x0FFF) / 2;
			return SMSUCCESS;
		}

	if (hweight16((WORD)(addr1^addr2)) != 0x01)
		return ERROR;

	if ((addr1 & 0xF000) == 0x1000)
		if (!(hweight16(addr1) & 0x01)) {
			Media.LogBlock = (addr1 & 0x0FFF) / 2;
			return SMSUCCESS;
		}

	if ((addr2 & 0xF000) == 0x1000)
		if (!(hweight16(addr2) & 0x01)) {
			Media.LogBlock = (addr2 & 0x0FFF) / 2;
			return SMSUCCESS;
		}

	return ERROR;
}

/* ----- Clr_D_RedundantData() ------------------------------------------ */
void Clr_D_RedundantData(BYTE *redundant)
{
	char i;

	for (i = 0; i < REDTSIZE; i++)
		*(redundant + i) = 0xFF;
}

/* ----- Set_D_LogBlockAddr() ------------------------------------------- */
void Set_D_LogBlockAddr(BYTE *redundant)
{
	WORD addr;

	*(redundant + REDT_BLOCK) = 0xFF;
	*(redundant + REDT_DATA) = 0xFF;
	addr = Media.LogBlock*2 + 0x1000;

	if ((hweight16(addr) % 2))
		addr++;

	*(redundant + REDT_ADDR1H) = *(redundant + REDT_ADDR2H) = (BYTE)(addr / 0x0100);
	*(redundant + REDT_ADDR1L) = *(redundant + REDT_ADDR2L) = (BYTE)addr;
}

/*----- Set_D_FailBlock() ---------------------------------------------- */
void Set_D_FailBlock(BYTE *redundant)
{
	char i;
	for (i = 0; i < REDTSIZE; i++)
		*redundant++ = (BYTE)((i == REDT_BLOCK) ? 0xF0 : 0xFF);
}

/* ----- Set_D_DataStaus() ---------------------------------------------- */
void Set_D_DataStaus(BYTE *redundant)
{
	redundant += REDT_DATA;
	*redundant = 0x00;
}

/* SmartMedia Function Command Subroutine
 * 6250 CMD 6
 */
/* ----- Ssfdc_D_Reset() ------------------------------------------------ */
void Ssfdc_D_Reset(struct us_data *us)
{
	return;
}

/* ----- Ssfdc_D_ReadCisSect() ------------------------------------------ */
int Ssfdc_D_ReadCisSect(struct us_data *us, BYTE *buf, BYTE *redundant)
{
	BYTE zone, sector;
	WORD block;

	zone = Media.Zone; block = Media.PhyBlock; sector = Media.Sector;
	Media.Zone = 0;
	Media.PhyBlock = CisArea.PhyBlock;
	Media.Sector = CisArea.Sector;

	if (Ssfdc_D_ReadSect(us, buf, redundant)) {
		Media.Zone = zone; Media.PhyBlock = block; Media.Sector = sector;
		return ERROR;
	}

	Media.Zone = zone; Media.PhyBlock = block; Media.Sector = sector;
	return SMSUCCESS;
}

/* 6250 CMD 1 */
/* ----- Ssfdc_D_ReadSect() --------------------------------------------- */
int Ssfdc_D_ReadSect(struct us_data *us, BYTE *buf, BYTE *redundant)
{
	struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
	int	result;
	WORD	addr;

	result = ENE_LoadBinCode(us, SM_RW_PATTERN);
	if (result != USB_STOR_XFER_GOOD) {
		printk("Load SM RW Code Fail !!\n");
		return USB_STOR_TRANSPORT_ERROR;
	}

	addr = (WORD)Media.Zone*Ssfdc.MaxBlocks + Media.PhyBlock;
	addr = addr*(WORD)Ssfdc.MaxSectors + Media.Sector;

	/* Read sect data */
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));
	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength	= 0x200;
	bcb->Flags			= 0x80;
	bcb->CDB[0]			= 0xF1;
	bcb->CDB[1]			= 0x02;
	bcb->CDB[4]			= (BYTE)addr;
	bcb->CDB[3]			= (BYTE)(addr / 0x0100);
	bcb->CDB[2]			= Media.Zone / 2;

	result = ENE_SendScsiCmd(us, FDIR_READ, buf, 0);
	if (result != USB_STOR_XFER_GOOD)
		return USB_STOR_TRANSPORT_ERROR;

	/* Read redundant */
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));
	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength	= 0x10;
	bcb->Flags			= 0x80;
	bcb->CDB[0]			= 0xF1;
	bcb->CDB[1]			= 0x03;
	bcb->CDB[4]			= (BYTE)addr;
	bcb->CDB[3]			= (BYTE)(addr / 0x0100);
	bcb->CDB[2]			= Media.Zone / 2;
	bcb->CDB[8]			= 0;
	bcb->CDB[9]			= 1;

	result = ENE_SendScsiCmd(us, FDIR_READ, redundant, 0);
	if (result != USB_STOR_XFER_GOOD)
		return USB_STOR_TRANSPORT_ERROR;

	return USB_STOR_TRANSPORT_GOOD;
}

/* ----- Ssfdc_D_ReadBlock() --------------------------------------------- */
int Ssfdc_D_ReadBlock(struct us_data *us, WORD count, BYTE *buf, BYTE *redundant)
{
	struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
	int	result;
	WORD	addr;

	result = ENE_LoadBinCode(us, SM_RW_PATTERN);
	if (result != USB_STOR_XFER_GOOD) {
		printk("Load SM RW Code Fail !!\n");
		return USB_STOR_TRANSPORT_ERROR;
	}

	addr = (WORD)Media.Zone*Ssfdc.MaxBlocks + Media.PhyBlock;
	addr = addr*(WORD)Ssfdc.MaxSectors + Media.Sector;

	/* Read sect data */
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));
	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength	= 0x200*count;
	bcb->Flags			= 0x80;
	bcb->CDB[0]			= 0xF1;
	bcb->CDB[1]			= 0x02;
	bcb->CDB[4]			= (BYTE)addr;
	bcb->CDB[3]			= (BYTE)(addr / 0x0100);
	bcb->CDB[2]			= Media.Zone / 2;

	result = ENE_SendScsiCmd(us, FDIR_READ, buf, 0);
	if (result != USB_STOR_XFER_GOOD)
		return USB_STOR_TRANSPORT_ERROR;

	/* Read redundant */
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));
	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength	= 0x10;
	bcb->Flags			= 0x80;
	bcb->CDB[0]			= 0xF1;
	bcb->CDB[1]			= 0x03;
	bcb->CDB[4]			= (BYTE)addr;
	bcb->CDB[3]			= (BYTE)(addr / 0x0100);
	bcb->CDB[2]			= Media.Zone / 2;
	bcb->CDB[8]			= 0;
	bcb->CDB[9]			= 1;

	result = ENE_SendScsiCmd(us, FDIR_READ, redundant, 0);
	if (result != USB_STOR_XFER_GOOD)
		return USB_STOR_TRANSPORT_ERROR;

	return USB_STOR_TRANSPORT_GOOD;
}


/* ----- Ssfdc_D_CopyBlock() -------------------------------------------- */
int Ssfdc_D_CopyBlock(struct us_data *us, WORD count, BYTE *buf, BYTE *redundant)
{
	struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
	int	result;
	WORD	ReadAddr, WriteAddr;

	result = ENE_LoadBinCode(us, SM_RW_PATTERN);
	if (result != USB_STOR_XFER_GOOD) {
		printk("Load SM RW Code Fail !!\n");
		return USB_STOR_TRANSPORT_ERROR;
	}

	ReadAddr = (WORD)Media.Zone*Ssfdc.MaxBlocks + ReadBlock;
	ReadAddr = ReadAddr*(WORD)Ssfdc.MaxSectors;
	WriteAddr = (WORD)Media.Zone*Ssfdc.MaxBlocks + WriteBlock;
	WriteAddr = WriteAddr*(WORD)Ssfdc.MaxSectors;

	/* Write sect data */
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));
	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength	= 0x200*count;
	bcb->Flags			= 0x00;
	bcb->CDB[0]			= 0xF0;
	bcb->CDB[1]			= 0x08;
	bcb->CDB[7]			= (BYTE)WriteAddr;
	bcb->CDB[6]			= (BYTE)(WriteAddr / 0x0100);
	bcb->CDB[5]			= Media.Zone / 2;
	bcb->CDB[8]			= *(redundant + REDT_ADDR1H);
	bcb->CDB[9]			= *(redundant + REDT_ADDR1L);
	bcb->CDB[10]		= Media.Sector;

	if (ReadBlock != NO_ASSIGN) {
		bcb->CDB[4]		= (BYTE)ReadAddr;
		bcb->CDB[3]		= (BYTE)(ReadAddr / 0x0100);
		bcb->CDB[2]		= Media.Zone / 2;
	} else
		bcb->CDB[11]	= 1;

	result = ENE_SendScsiCmd(us, FDIR_WRITE, buf, 0);
	if (result != USB_STOR_XFER_GOOD)
		return USB_STOR_TRANSPORT_ERROR;

	return USB_STOR_TRANSPORT_GOOD;
}

/* ----- Ssfdc_D_WriteSectForCopy() ------------------------------------- */
int Ssfdc_D_WriteSectForCopy(struct us_data *us, BYTE *buf, BYTE *redundant)
{
	struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
	int	result;
	WORD	addr;

	result = ENE_LoadBinCode(us, SM_RW_PATTERN);
	if (result != USB_STOR_XFER_GOOD) {
		printk("Load SM RW Code Fail !!\n");
		return USB_STOR_TRANSPORT_ERROR;
	}


	addr = (WORD)Media.Zone*Ssfdc.MaxBlocks + Media.PhyBlock;
	addr = addr*(WORD)Ssfdc.MaxSectors + Media.Sector;

	/* Write sect data */
	memset(bcb, 0, sizeof(struct bulk_cb_wrap));
	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength	= 0x200;
	bcb->Flags			= 0x00;
	bcb->CDB[0]			= 0xF0;
	bcb->CDB[1]			= 0x04;
	bcb->CDB[7]			= (BYTE)addr;
	bcb->CDB[6]			= (BYTE)(addr / 0x0100);
	bcb->CDB[5]			= Media.Zone / 2;
	bcb->CDB[8]			= *(redundant + REDT_ADDR1H);
	bcb->CDB[9]			= *(redundant + REDT_ADDR1L);

	result = ENE_SendScsiCmd(us, FDIR_WRITE, buf, 0);
	if (result != USB_STOR_XFER_GOOD)
		return USB_STOR_TRANSPORT_ERROR;

	return USB_STOR_TRANSPORT_GOOD;
}

/* 6250 CMD 5 */
/* ----- Ssfdc_D_EraseBlock() ------------------------------------------- */
int Ssfdc_D_EraseBlock(struct us_data *us)
{
	struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
	int	result;
	WORD	addr;

	result = ENE_LoadBinCode(us, SM_RW_PATTERN);
	if (result != USB_STOR_XFER_GOOD) {
		printk("Load SM RW Code Fail !!\n");
		return USB_STOR_TRANSPORT_ERROR;
	}

	addr = (WORD)Media.Zone*Ssfdc.MaxBlocks + Media.PhyBlock;
	addr = addr*(WORD)Ssfdc.MaxSectors;

	memset(bcb, 0, sizeof(struct bulk_cb_wrap));
	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength	= 0x200;
	bcb->Flags			= 0x80;
	bcb->CDB[0]			= 0xF2;
	bcb->CDB[1]			= 0x06;
	bcb->CDB[7]			= (BYTE)addr;
	bcb->CDB[6]			= (BYTE)(addr / 0x0100);
	bcb->CDB[5]			= Media.Zone / 2;

	result = ENE_SendScsiCmd(us, FDIR_READ, NULL, 0);
	if (result != USB_STOR_XFER_GOOD)
		return USB_STOR_TRANSPORT_ERROR;

	return USB_STOR_TRANSPORT_GOOD;
}

/* 6250 CMD 2 */
/*----- Ssfdc_D_ReadRedtData() ----------------------------------------- */
int Ssfdc_D_ReadRedtData(struct us_data *us, BYTE *redundant)
{
	struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
	int	result;
	WORD	addr;
	BYTE	*buf;

	result = ENE_LoadBinCode(us, SM_RW_PATTERN);
	if (result != USB_STOR_XFER_GOOD) {
		printk("Load SM RW Code Fail !!\n");
		return USB_STOR_TRANSPORT_ERROR;
	}

	addr = (WORD)Media.Zone*Ssfdc.MaxBlocks + Media.PhyBlock;
	addr = addr*(WORD)Ssfdc.MaxSectors + Media.Sector;

	memset(bcb, 0, sizeof(struct bulk_cb_wrap));
	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength	= 0x10;
	bcb->Flags			= 0x80;
	bcb->CDB[0]			= 0xF1;
	bcb->CDB[1]			= 0x03;
	bcb->CDB[4]			= (BYTE)addr;
	bcb->CDB[3]			= (BYTE)(addr / 0x0100);
	bcb->CDB[2]			= Media.Zone / 2;
	bcb->CDB[8]			= 0;
	bcb->CDB[9]			= 1;

	buf = kmalloc(0x10, GFP_KERNEL);
	result = ENE_SendScsiCmd(us, FDIR_READ, buf, 0);
	memcpy(redundant, buf, 0x10);
	kfree(buf);
	if (result != USB_STOR_XFER_GOOD)
		return USB_STOR_TRANSPORT_ERROR;

	return USB_STOR_TRANSPORT_GOOD;
}

/* 6250 CMD 4 */
/* ----- Ssfdc_D_WriteRedtData() ---------------------------------------- */
int Ssfdc_D_WriteRedtData(struct us_data *us, BYTE *redundant)
{
	struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
	int	result;
	WORD                    addr;

	result = ENE_LoadBinCode(us, SM_RW_PATTERN);
	if (result != USB_STOR_XFER_GOOD) {
		printk("Load SM RW Code Fail !!\n");
		return USB_STOR_TRANSPORT_ERROR;
	}

	addr = (WORD)Media.Zone*Ssfdc.MaxBlocks + Media.PhyBlock;
	addr = addr*(WORD)Ssfdc.MaxSectors + Media.Sector;

	memset(bcb, 0, sizeof(struct bulk_cb_wrap));
	bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
	bcb->DataTransferLength	= 0x10;
	bcb->Flags			= 0x80;
	bcb->CDB[0]			= 0xF2;
	bcb->CDB[1]			= 0x05;
	bcb->CDB[7]			= (BYTE)addr;
	bcb->CDB[6]			= (BYTE)(addr / 0x0100);
	bcb->CDB[5]			= Media.Zone / 2;
	bcb->CDB[8]			= *(redundant + REDT_ADDR1H);
	bcb->CDB[9]			= *(redundant + REDT_ADDR1L);

	result = ENE_SendScsiCmd(us, FDIR_READ, NULL, 0);
	if (result != USB_STOR_XFER_GOOD)
		return USB_STOR_TRANSPORT_ERROR;

	return USB_STOR_TRANSPORT_GOOD;
}

/* ----- Ssfdc_D_CheckStatus() ------------------------------------------ */
int Ssfdc_D_CheckStatus(void)
{
	return SMSUCCESS;
}



/* SmartMedia ID Code Check & Mode Set Subroutine
 * ----- Set_D_SsfdcModel() ---------------------------------------------
 */
int Set_D_SsfdcModel(BYTE dcode)
{
	switch (_Check_D_DevCode(dcode)) {
	case SSFDC1MB:
		Ssfdc.Model        = SSFDC1MB;
		Ssfdc.Attribute    = FLASH | AD3CYC | BS16 | PS256;
		Ssfdc.MaxZones     = 1;
		Ssfdc.MaxBlocks    = 256;
		Ssfdc.MaxLogBlocks = 250;
		Ssfdc.MaxSectors   = 8;
		break;
	case SSFDC2MB:
		Ssfdc.Model        = SSFDC2MB;
		Ssfdc.Attribute    = FLASH | AD3CYC | BS16 | PS256;
		Ssfdc.MaxZones     = 1;
		Ssfdc.MaxBlocks    = 512;
		Ssfdc.MaxLogBlocks = 500;
		Ssfdc.MaxSectors   = 8;
		break;
	case SSFDC4MB:
		Ssfdc.Model        = SSFDC4MB;
		Ssfdc.Attribute    = FLASH | AD3CYC | BS16 | PS512;
		Ssfdc.MaxZones     = 1;
		Ssfdc.MaxBlocks    = 512;
		Ssfdc.MaxLogBlocks = 500;
		Ssfdc.MaxSectors   = 16;
		break;
	case SSFDC8MB:
		Ssfdc.Model        = SSFDC8MB;
		Ssfdc.Attribute    = FLASH | AD3CYC | BS16 | PS512;
		Ssfdc.MaxZones     = 1;
		Ssfdc.MaxBlocks    = 1024;
		Ssfdc.MaxLogBlocks = 1000;
		Ssfdc.MaxSectors   = 16;
		break;
	case SSFDC16MB:
		Ssfdc.Model        = SSFDC16MB;
		Ssfdc.Attribute    = FLASH | AD3CYC | BS32 | PS512;
		Ssfdc.MaxZones     = 1;
		Ssfdc.MaxBlocks    = 1024;
		Ssfdc.MaxLogBlocks = 1000;
		Ssfdc.MaxSectors   = 32;
		break;
	case SSFDC32MB:
		Ssfdc.Model        = SSFDC32MB;
		Ssfdc.Attribute    = FLASH | AD3CYC | BS32 | PS512;
		Ssfdc.MaxZones     = 2;
		Ssfdc.MaxBlocks    = 1024;
		Ssfdc.MaxLogBlocks = 1000;
		Ssfdc.MaxSectors   = 32;
		break;
	case SSFDC64MB:
		Ssfdc.Model        = SSFDC64MB;
		Ssfdc.Attribute    = FLASH | AD4CYC | BS32 | PS512;
		Ssfdc.MaxZones     = 4;
		Ssfdc.MaxBlocks    = 1024;
		Ssfdc.MaxLogBlocks = 1000;
		Ssfdc.MaxSectors   = 32;
		break;
	case SSFDC128MB:
		Ssfdc.Model        = SSFDC128MB;
		Ssfdc.Attribute    = FLASH | AD4CYC | BS32 | PS512;
		Ssfdc.MaxZones     = 8;
		Ssfdc.MaxBlocks    = 1024;
		Ssfdc.MaxLogBlocks = 1000;
		Ssfdc.MaxSectors   = 32;
		break;
	case SSFDC256MB:
		Ssfdc.Model        = SSFDC256MB;
		Ssfdc.Attribute    = FLASH | AD4CYC | BS32 | PS512;
		Ssfdc.MaxZones     = 16;
		Ssfdc.MaxBlocks    = 1024;
		Ssfdc.MaxLogBlocks = 1000;
		Ssfdc.MaxSectors   = 32;
		break;
	case SSFDC512MB:
		Ssfdc.Model        = SSFDC512MB;
		Ssfdc.Attribute    = FLASH | AD4CYC | BS32 | PS512;
		Ssfdc.MaxZones     = 32;
		Ssfdc.MaxBlocks    = 1024;
		Ssfdc.MaxLogBlocks = 1000;
		Ssfdc.MaxSectors   = 32;
		break;
	case SSFDC1GB:
		Ssfdc.Model        = SSFDC1GB;
		Ssfdc.Attribute    = FLASH | AD4CYC | BS32 | PS512;
		Ssfdc.MaxZones     = 64;
		Ssfdc.MaxBlocks    = 1024;
		Ssfdc.MaxLogBlocks = 1000;
		Ssfdc.MaxSectors   = 32;
		break;
	case SSFDC2GB:
		Ssfdc.Model        = SSFDC2GB;
		Ssfdc.Attribute    = FLASH | AD4CYC | BS32 | PS512;
		Ssfdc.MaxZones     = 128;
		Ssfdc.MaxBlocks    = 1024;
		Ssfdc.MaxLogBlocks = 1000;
		Ssfdc.MaxSectors   = 32;
		break;
	default:
		Ssfdc.Model = NOSSFDC;
		return ERROR;
	}

    return SMSUCCESS;
}

/* ----- _Check_D_DevCode() --------------------------------------------- */
BYTE _Check_D_DevCode(BYTE dcode)
{
	switch (dcode) {
	case 0x6E:
	case 0xE8:
	case 0xEC: return SSFDC1MB;   /* 8Mbit (1M) NAND */
	case 0x64:
	case 0xEA: return SSFDC2MB;   /* 16Mbit (2M) NAND */
	case 0x6B:
	case 0xE3:
	case 0xE5: return SSFDC4MB;   /* 32Mbit (4M) NAND */
	case 0xE6: return SSFDC8MB;   /* 64Mbit (8M) NAND */
	case 0x73: return SSFDC16MB;  /* 128Mbit (16M)NAND */
	case 0x75: return SSFDC32MB;  /* 256Mbit (32M)NAND */
	case 0x76: return SSFDC64MB;  /* 512Mbit (64M)NAND */
	case 0x79: return SSFDC128MB; /* 1Gbit(128M)NAND */
	case 0x71: return SSFDC256MB;
	case 0xDC: return SSFDC512MB;
	case 0xD3: return SSFDC1GB;
	case 0xD5: return SSFDC2GB;
	default: return NOSSFDC;
	}
}




/* SmartMedia ECC Control Subroutine
 * ----- Check_D_ReadError() ----------------------------------------------
 */
int Check_D_ReadError(BYTE *redundant)
{
	return SMSUCCESS;
}

/* ----- Check_D_Correct() ---------------------------------------------- */
int Check_D_Correct(BYTE *buf, BYTE *redundant)
{
	return SMSUCCESS;
}

/* ----- Check_D_CISdata() ---------------------------------------------- */
int Check_D_CISdata(BYTE *buf, BYTE *redundant)
{
	BYTE cis[] = {0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02,
		      0xDF, 0x01, 0x20};

	int cis_len = sizeof(cis);

	if (!IsSSFDCCompliance && !IsXDCompliance)
		return SMSUCCESS;

	if (!memcmp(redundant + 0x0D, EccBuf, 3))
		return memcmp(buf, cis, cis_len);

	if (!_Correct_D_SwECC(buf, redundant + 0x0D, EccBuf))
		return memcmp(buf, cis, cis_len);

	buf += 0x100;
	if (!memcmp(redundant + 0x08, EccBuf + 0x03, 3))
		return memcmp(buf, cis, cis_len);

	if (!_Correct_D_SwECC(buf, redundant + 0x08, EccBuf + 0x03))
		return memcmp(buf, cis, cis_len);

	return ERROR;
}

/* ----- Set_D_RightECC() ---------------------------------------------- */
void Set_D_RightECC(BYTE *redundant)
{
    /* Driver ECC Check */
    return;
}