/*
 * Copyright (C) 1993, 1994 by Ralf Baechle (linux@uni-koblenz.de)
 *
 * This file is part of Minix-Handler, an AmigaDOS Filehandler to access
 * Minix filesystems via AmigaDOS.
 *
 * Minix-Handler 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, or (at your option)
 * any later version.
 *
 * Minix-Handler 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.
 */

#include <string.h>

#include <exec/memory.h>
#include <proto/exec.h>

#include "td.h"
#include "debug.h"

/*
 * IORequest for all trackdisk.device style IO.
 */
static struct IOExtTD *TD_Req = NULL;

/*
 * Number of cache buffers
 */
static ULONG TD_Buffers = 0;

/*
 * Offset to start of our disk
 */
static ULONG TD_Offset;

static struct DriveGeometry TD_Geometry;

static BOOL TD_Removable;

/*
 * static prototypes
 */
static BOOL TD_DriveGeometry(VOID);

/*
 * Opens trackdisk.device and returns address.
 */
BOOL TD_Open(UBYTE *device, ULONG unit, ULONG flags)
{
	struct MsgPort	*TDPort;
	struct IOExtTD	*TDRequest;

	TD_Buffers = 0;

	if(TDPort = CreateMsgPort())
		if(TDRequest = (struct IOExtTD *) CreateIORequest(TDPort, sizeof(struct IOExtTD)))
			if(OpenDevice(device, unit, (struct IORequest *) TDRequest, flags)) {
				DeleteMsgPort(TDPort);
				DeleteIORequest(TDRequest);
				return FALSE;
				}
			else {
				TD_Req = TDRequest;
				/*
				 * If we had an error assume non changable type of drive
				 */
				TD_Removable = TD_DriveGeometry() ? 0 : TD_Geometry.dg_Flags & DGB_REMOVABLE;

				return TRUE;
				}
		else
			DeletePort(TDPort);
	/* Error */
	return FALSE;
}

void TD_Close(VOID)
{
	if(TD_Req) {
		CloseDevice((struct IORequest *) TD_Req);
		DeleteMsgPort(TD_Req->iotd_Req.io_Message.mn_ReplyPort );
		DeleteIORequest(TD_Req);
		TD_Req = NULL;
		}
}

ULONG TD_AddBuffers(ULONG more)
{
	return FALSE;
}

/*
 * Add caching here...
 */

BYTE TD_Read(APTR Data, ULONG Length, ULONG Offset)
{
	TD_Req->iotd_Req.io_Command = CMD_READ;
	TD_Req->iotd_Req.io_Flags   = 0;
	TD_Req->iotd_Req.io_Data    = Data;
	TD_Req->iotd_Req.io_Length  = Length;
	TD_Req->iotd_Req.io_Offset  = TD_Offset + Offset;
	/*
	 * Exec changes the ln_Type field when the request is
	 * complete - so we must to set it again.
	 */
	TD_Req->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	DoIO((struct IORequest *) TD_Req);

	return TD_Req->iotd_Req.io_Error;
}

BYTE TD_Write(APTR Data, ULONG Length, ULONG Offset)
{
	TD_Req->iotd_Req.io_Command = CMD_WRITE;
	TD_Req->iotd_Req.io_Flags   = 0;
	TD_Req->iotd_Req.io_Data    = Data;
	TD_Req->iotd_Req.io_Length  = Length;
	TD_Req->iotd_Req.io_Offset  = TD_Offset + Offset;
	/* Exec changes the ln_Type field when the request is
	 * complete - so we must to set it again.
	 */
	TD_Req->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	DoIO((struct IORequest *) TD_Req);

	return TD_Req->iotd_Req.io_Error;
}

/*
 * Update disk
 */
BYTE TD_Update(VOID)
{
	TD_Req->iotd_Req.io_Command = CMD_UPDATE;
	TD_Req->iotd_Req.io_Flags   = 0;
	TD_Req->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	DoIO((struct IORequest *) TD_Req);

	return TD_Req->iotd_Req.io_Error;
}

/*
 * Clear trackbuffer
 */
BYTE TD_Clear(VOID)
{
	TD_Req->iotd_Req.io_Command = CMD_CLEAR;
	TD_Req->iotd_Req.io_Flags   = 0;
	TD_Req->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	DoIO( (struct IORequest *) TD_Req );

	return TD_Req->iotd_Req.io_Error;
}

/*
 * Eject media
 */
BYTE TD_Eject(VOID)
{
	TD_Req->iotd_Req.io_Command = TD_EJECT;
	TD_Req->iotd_Req.io_Flags   = 0;
	TD_Req->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	DoIO( (struct IORequest *) TD_Req );

	return TD_Req->iotd_Req.io_Error;
}

/*
 * Turn motor on/off
 */
BYTE TD_Motor(enum MotorState on)
{
	TD_Req->iotd_Req.io_Command = TD_MOTOR;
	TD_Req->iotd_Req.io_Length  = on;
	TD_Req->iotd_Req.io_Flags   = 0;
	TD_Req->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	DoIO( (struct IORequest *) TD_Req );

	return TD_Req->iotd_Req.io_Error;
}

/*
 * Query write protection status
 */
BOOL TD_QueryProtection(VOID)
{
	TD_Req->iotd_Req.io_Command = TD_PROTSTATUS;
	TD_Req->iotd_Req.io_Flags   = IOF_QUICK;
	TD_Req->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	DoIO( (struct IORequest *) TD_Req );

	return (BOOL) (TD_Req->iotd_Req.io_Actual ?  TRUE : FALSE);
}

BOOL TD_QueryRemoveable(VOID)
{
	return TD_Removable;
}

/*
 * Query disk geometry
 */
static BOOL TD_DriveGeometry(VOID)
{
	TD_Req->iotd_Req.io_Command = TD_GETGEOMETRY;
	TD_Req->iotd_Req.io_Flags   = IOF_QUICK;
	TD_Req->iotd_Req.io_Data    = &TD_Geometry;
	TD_Req->iotd_Req.io_Length  = sizeof(struct DriveGeometry);
	TD_Req->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	DoIO( (struct IORequest *) TD_Req );

	DMSG1("TD_ChangeNum error  : %d.\n", TD_Req->iotd_Req.io_Error);
	DMSG1("TD_ChangeNum changes: %d.\n", TD_Req->iotd_Req.io_Actual);

	return (BOOL) (TD_Req->iotd_Req.io_Actual ?  TRUE : FALSE);
}

/*
 * Query number of disk changes
 */
ULONG TD_ChangeNum(VOID)
{
	TD_Req->iotd_Req.io_Command = TD_CHANGENUM;
	TD_Req->iotd_Req.io_Flags   = IOF_QUICK;
	TD_Req->iotd_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	DoIO( (struct IORequest *) TD_Req );

	DMSG1("TD_ChangeNum error  : %d.\n", TD_Req->iotd_Req.io_Error);
	DMSG1("TD_ChangeNum changes: %d.\n", TD_Req->iotd_Req.io_Actual);
	DMSG1("TD_Removable        : %d.\n", TD_Removable);

	return (TD_Req->iotd_Req.io_Error || !TD_Removable) ? 1 : TD_Req->iotd_Req.io_Actual;
}

/*
 * Write out delayed blocks
 * Not implemented yet.
 */
VOID TD_Sync(VOID)
{
}

/*
 * Set the offset to the start of our disk
 */
VOID TD_SetOffset(ULONG Offset)
{
	TD_Offset = Offset;
	DMSG1("TD_Offset = %d.\n", TD_Offset);
}
