/*
 * $Id$
 */

/*
	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., 59 Temple Place - Suite 330, Boston,
	MA 02111-1307, USA.
*/

#include <string.h>

#include <exec/types.h>
#include <exec/resident.h>
#include <exec/io.h>
#include <exec/ports.h>
#include <exec/errors.h>

#include <devices/sana2.h>
#include <devices/sana2specialstats.h>
#include <devices/newstyle.h>

#include <utility/utility.h>
#include <utility/tagitem.h>
#include <utility/hooks.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/battclock.h>

#include <stdlib.h>

#include "rtl8139.h"
#include "unit.h"
#include LC_LIBDEFS_FILE

#define KNOWN_EVENTS \
	(S2EVENT_ERROR | S2EVENT_TX | S2EVENT_RX | S2EVENT_ONLINE \
	| S2EVENT_OFFLINE | S2EVENT_BUFF | S2EVENT_HARDWARE | S2EVENT_SOFTWARE)

static BOOL CmdInvalid(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdRead(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdWrite(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdFlush(LIBBASETYPEPTR LIBBASE, struct IORequest *request);
static BOOL CmdS2DeviceQuery(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdGetStationAddress(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdConfigInterface(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdBroadcast(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdTrackType(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdUntrackType(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdGetTypeStats(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdGetSpecialStats(LIBBASETYPEPTR LIBBASE,
	struct IOSana2Req *request);
static BOOL CmdGetGlobalStats(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdDeviceQuery(LIBBASETYPEPTR LIBBASE, struct IOStdReq *request);
static BOOL CmdOnEvent(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdReadOrphan(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdOnline(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdOffline(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdAddMulticastAddresses(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);
static BOOL CmdDelMulticastAddresses(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request);

static const UWORD supported_commands[] =
{
	CMD_READ,
	CMD_WRITE,
	CMD_FLUSH,
	S2_DEVICEQUERY,
	S2_GETSTATIONADDRESS,
	S2_CONFIGINTERFACE,
	S2_ADDMULTICASTADDRESS,
	S2_DELMULTICASTADDRESS,
	S2_MULTICAST,
	S2_BROADCAST,
	S2_TRACKTYPE,
	S2_UNTRACKTYPE,
	S2_GETTYPESTATS,
	S2_GETSPECIALSTATS,
	S2_GETGLOBALSTATS,
	S2_ONEVENT,
	S2_READORPHAN,
	S2_ONLINE,
	S2_OFFLINE,
	NSCMD_DEVICEQUERY,
	S2_ADDMULTICASTADDRESSES,
	S2_DELMULTICASTADDRESSES,
	0
};

const TEXT badmulticast_name[] = "Bad multicasts";
const TEXT retries_name[] = "Retries";
const TEXT fifo_underruns_name[] = "Underruns";

static const TEXT *const special_stat_names[] =
{
	badmulticast_name,
	retries_name,
	fifo_underruns_name,
};

void handle_request(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	BOOL complete;

	/* The request is not on any queue while it is being processed - mark
	   it so AbortIO() won't try to Remove() it. Requeueing (PutMsg) or
	   replying restores a valid node type. */
	request->ios2_Req.io_Message.mn_Node.ln_Type = NT_FREEMSG;

	switch(request->ios2_Req.io_Command)
	{
		case CMD_READ:
			complete = CmdRead(LIBBASE, request);
			break;

		case CMD_WRITE:
		case S2_MULTICAST:
			complete = CmdWrite(LIBBASE, request);
			break;

		case CMD_FLUSH:
			complete = CmdFlush(LIBBASE, (struct IORequest *)request);
			break;

		case S2_DEVICEQUERY:
			complete = CmdS2DeviceQuery(LIBBASE, request);
			break;

		case S2_GETSTATIONADDRESS:
			complete = CmdGetStationAddress(LIBBASE, request);
			break;

		case S2_CONFIGINTERFACE:
			complete = CmdConfigInterface(LIBBASE, request);
			break;

		case S2_BROADCAST:
			complete = CmdBroadcast(LIBBASE, request);
			break;

		case S2_TRACKTYPE:
			complete = CmdTrackType(LIBBASE, request);
			break;

		case S2_UNTRACKTYPE:
			complete = CmdUntrackType(LIBBASE, request);
			break;

		case S2_GETTYPESTATS:
			complete = CmdGetTypeStats(LIBBASE, request);
			break;

		case S2_GETSPECIALSTATS:
			complete = CmdGetSpecialStats(LIBBASE, request);
			break;

		case S2_GETGLOBALSTATS:
			complete = CmdGetGlobalStats(LIBBASE, request);
			break;

		case S2_ONEVENT:
			complete = CmdOnEvent(LIBBASE, request);
			break;

		case S2_READORPHAN:
			complete = CmdReadOrphan(LIBBASE, request);
			break;

		case S2_ONLINE:
			complete = CmdOnline(LIBBASE, request);
			break;

		case S2_OFFLINE:
			complete = CmdOffline(LIBBASE, request);
			break;

		case S2_ADDMULTICASTADDRESS:
		case S2_ADDMULTICASTADDRESSES:
			complete = CmdAddMulticastAddresses(LIBBASE, request);
			break;

		case S2_DELMULTICASTADDRESS:
		case S2_DELMULTICASTADDRESSES:
			complete = CmdDelMulticastAddresses(LIBBASE, request);
			break;

		case NSCMD_DEVICEQUERY:
			complete = CmdDeviceQuery(LIBBASE, (struct IOStdReq *)request);
			break;

		default:
			complete = CmdInvalid(LIBBASE, request);
	}

	if(complete && (request->ios2_Req.io_Flags & IOF_QUICK) == 0)
		ReplyMsg((APTR)request);

	ReleaseSemaphore(&((struct RTL8139Unit *)request->ios2_Req.io_Unit)->rtl8139u_unit_lock);
}

static BOOL CmdInvalid(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	request->ios2_Req.io_Error = IOERR_NOCMD;
	request->ios2_WireError = S2WERR_GENERIC_ERROR;

	return TRUE;
}

static BOOL CmdRead(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	struct Opener *opener;
	BOOL complete = FALSE;

	unit = (APTR)request->ios2_Req.io_Unit;

RTLD(bug("[%s] S2CmdRead()\n", unit->rtl8139u_name))

	if((unit->rtl8139u_flags & IFF_UP) != 0)
	{
		opener = request->ios2_BufferManagement;
		request->ios2_Req.io_Flags &= ~IOF_QUICK;
		PutMsg(&opener->read_port, (struct Message *)request);
	}
	else
	{
		request->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
		request->ios2_WireError = S2WERR_UNIT_OFFLINE;
		complete = TRUE;
	}

	/* Return */

	return complete;
}

static BOOL CmdWrite(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	BYTE error = 0;
	ULONG wire_error = S2WERR_GENERIC_ERROR;
	BOOL complete = FALSE;

	/* Check request is valid */

	unit = (APTR)request->ios2_Req.io_Unit;
	
RTLD(bug("[%s] S2CmdWrite()\n", unit->rtl8139u_name))
	
	if((unit->rtl8139u_flags & IFF_UP) == 0)
	{
		error = S2ERR_OUTOFSERVICE;
		wire_error = S2WERR_UNIT_OFFLINE;
	}
	else if((request->ios2_Req.io_Command == S2_MULTICAST) &&
			((request->ios2_DstAddr[0] & 0x1) == 0))
	{
		error = S2ERR_BAD_ADDRESS;
		wire_error = S2WERR_BAD_MULTICAST;
	}
	else if(request->ios2_DataLength >
		((request->ios2_Req.io_Flags & SANA2IOF_RAW) != 0 ?
			(ULONG)(ETH_HEADERSIZE + ETH_MTU) : (ULONG)ETH_MTU))
	{
		/* Oversized requests would overflow the UWORD frame accounting
		   and the chip's Tx buffers */
		error = S2ERR_MTU_EXCEEDED;
	}

	/* Queue request for sending */

	if(error == 0) {
		request->ios2_Req.io_Flags &= ~IOF_QUICK;
		PutMsg(unit->rtl8139u_request_ports[WRITE_QUEUE], (APTR)request);
	}
	else
	{
		request->ios2_Req.io_Error = error;
		request->ios2_WireError = wire_error;
		complete = TRUE;
	}

	/* Return */

	return complete;
}

static BOOL CmdFlush(LIBBASETYPEPTR LIBBASE, struct IORequest *request)
{
	FlushUnit(LIBBASE, (APTR)request->io_Unit, EVENT_QUEUE, IOERR_ABORTED);
	return TRUE;
}

static BOOL CmdS2DeviceQuery(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit = (APTR)request->ios2_Req.io_Unit;
	// struct fe_priv *np = unit->rtl8139u_fe_priv;
	struct Sana2DeviceQuery *info;
	ULONG size_available, size;

RTLD(bug("[%s] S2CmdDeviceQuery()\n", unit->rtl8139u_name))

	/* Copy device info */

	info = request->ios2_StatData;
	size = size_available = info->SizeAvailable;
	if(size < sizeof(struct Sana2DeviceQuery))
	{
		/* Don't write beyond what the caller supplied */
		request->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
		request->ios2_WireError = S2WERR_BAD_STATDATA;
		return TRUE;
	}
	size = sizeof(struct Sana2DeviceQuery);

	CopyMem(&unit->rtl8139u_Sana2Info, info, size);

	info->BPS = unit->rtl8139u_rtl_LinkSpeed;
	info->MTU = ETH_MTU;
	info->HardwareType = S2WireType_Ethernet;
	info->SizeAvailable = size_available;
	info->SizeSupplied = size;

	/* Return */

	return TRUE;
}

static BOOL CmdGetStationAddress(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;

	/* Copy addresses */

	unit = (APTR)request->ios2_Req.io_Unit;
	
RTLD(bug("[%s] S2CmdGetStationAddress()\n", unit->rtl8139u_name))

	CopyMem(unit->rtl8139u_dev_addr, request->ios2_SrcAddr, ETH_ADDRESSSIZE);
	CopyMem(unit->rtl8139u_org_addr, request->ios2_DstAddr, ETH_ADDRESSSIZE);

	/* Return */

	return TRUE;
}

static BOOL CmdConfigInterface(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;

	/* Configure adapter */

	unit = (APTR)request->ios2_Req.io_Unit;

RTLD(bug("[%s] S2CmdConfigInterface()\n", unit->rtl8139u_name))

	if((unit->rtl8139u_flags & IFF_CONFIGURED) == 0)
	{
		CopyMem(request->ios2_SrcAddr, unit->rtl8139u_dev_addr, ETH_ADDRESSSIZE);
		unit->set_mac_address(unit);
		unit->rtl8139u_flags |= IFF_CONFIGURED;
	}
	else
	{
		request->ios2_Req.io_Error = S2ERR_BAD_STATE;
		request->ios2_WireError = S2WERR_IS_CONFIGURED;
	}

	/* Return */

	return TRUE;
}

static BOOL CmdBroadcast(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	/* Fill in the broadcast address as destination */

        memset(request->ios2_DstAddr, 0xff, 6);

	/* Queue the write as normal */

	return CmdWrite(LIBBASE, request);
}

static BOOL CmdTrackType(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	struct Opener *opener;
	ULONG packet_type, wire_error=0;
	struct TypeTracker *tracker;
	struct TypeStats *initial_stats;
	BYTE error = 0;

	unit = (APTR)request->ios2_Req.io_Unit;
	
RTLD(bug("[%s] S2CmdTrackType()\n", unit->rtl8139u_name))
	
	packet_type = request->ios2_PacketType;

	/* Get global tracker */

	tracker = (struct TypeTracker *)
		FindTypeStats(LIBBASE, unit, &unit->rtl8139u_type_trackers, packet_type);

	if(tracker != NULL)
		tracker->user_count++;
	else
	{
		tracker =
			AllocMem(sizeof(struct TypeTracker), MEMF_PUBLIC|MEMF_CLEAR);
		if(tracker != NULL)
		{
			tracker->packet_type = packet_type;
			tracker->user_count = 1;

			Disable();
			AddTail((APTR)&unit->rtl8139u_type_trackers, (APTR)tracker);
			Enable();
		}
   }

	if(tracker == NULL)
	{
		error = S2ERR_NO_RESOURCES;
		wire_error = S2WERR_GENERIC_ERROR;
	}

	/* Store initial figures for this opener */

	opener = request->ios2_BufferManagement;
	initial_stats = FindTypeStats(LIBBASE, unit, &opener->initial_stats, packet_type);

	if((error == 0) && (initial_stats != NULL))
	{
		error = S2ERR_BAD_STATE;
		wire_error = S2WERR_ALREADY_TRACKED;
	}

	if(error == 0)
	{
		initial_stats = AllocMem(sizeof(struct TypeStats), MEMF_PUBLIC);
		if(initial_stats == NULL)
		{
			error = S2ERR_NO_RESOURCES;
			wire_error = S2WERR_GENERIC_ERROR;
		}
	}

	if(error == 0)
	{
		CopyMem(tracker, initial_stats, sizeof(struct TypeStats));
		AddTail((APTR)&opener->initial_stats, (APTR)initial_stats);
	}

	/* Return */

	request->ios2_Req.io_Error = error;
	request->ios2_WireError = wire_error;
	return TRUE;
}

static BOOL CmdUntrackType(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	struct Opener *opener;
	ULONG packet_type;
	struct TypeTracker *tracker;
	struct TypeStats *initial_stats;

	unit = (APTR)request->ios2_Req.io_Unit;
	
RTLD(bug("[%s] S2CmdUntrackType()\n", unit->rtl8139u_name))
	
	packet_type = request->ios2_PacketType;

	/* Get global tracker and initial figures */

	tracker = (struct TypeTracker *)
		FindTypeStats(LIBBASE, unit, &unit->rtl8139u_type_trackers, packet_type);
	opener = request->ios2_BufferManagement;
	initial_stats = FindTypeStats(LIBBASE, unit, &opener->initial_stats, packet_type);

	/* Decrement tracker usage and free unused structures */

	if((initial_stats != NULL) && (tracker != NULL))
	{
		if((--tracker->user_count) == 0)
		{
			Disable();
			Remove((APTR)tracker);
			Enable();
			FreeMem(tracker, sizeof(struct TypeTracker));
		}

		Remove((APTR)initial_stats);
		FreeMem(initial_stats, sizeof(struct TypeStats));
	}
	else
	{
		request->ios2_Req.io_Error = S2ERR_BAD_STATE;
		request->ios2_WireError = S2WERR_NOT_TRACKED;
	}

	/* Return */

	return TRUE;
}

static BOOL CmdGetTypeStats(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	struct Opener *opener;
	ULONG packet_type;
	struct TypeStats *initial_stats, *tracker;
	struct Sana2PacketTypeStats *stats;

	unit = (APTR)request->ios2_Req.io_Unit;

RTLD(bug("[%s] S2CmdGetTypeStats()\n", unit->rtl8139u_name))

	packet_type = request->ios2_PacketType;

	/* Get global tracker and initial figures */

	tracker = FindTypeStats(LIBBASE, unit, &unit->rtl8139u_type_trackers, packet_type);
	opener = request->ios2_BufferManagement;
	initial_stats = FindTypeStats(LIBBASE, unit, &opener->initial_stats, packet_type);

	/* Copy and adjust figures */
	if((initial_stats != NULL) && (tracker != NULL))
	{
		stats = request->ios2_StatData;
		CopyMem(&tracker->stats, stats, sizeof(struct Sana2PacketTypeStats));
		stats->PacketsSent -= initial_stats->stats.PacketsSent;
		stats->PacketsReceived -= initial_stats->stats.PacketsReceived;
		stats->BytesSent -= initial_stats->stats.BytesSent;
		stats->BytesReceived -= initial_stats->stats.BytesReceived;
		stats->PacketsDropped -= initial_stats->stats.PacketsDropped;
	}
	else
	{
		request->ios2_Req.io_Error = S2ERR_BAD_STATE;
		request->ios2_WireError = S2WERR_NOT_TRACKED;
	}

	/* Return */

	return TRUE;
}

static BOOL CmdGetSpecialStats(LIBBASETYPEPTR LIBBASE,
	struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	UWORD i, stat_count;
	struct Sana2SpecialStatHeader *header;
	struct Sana2SpecialStatRecord *record;

	/* Fill in stats */

	unit = (APTR)request->ios2_Req.io_Unit;
	header = request->ios2_StatData;
	record = (APTR)(header + 1);

	stat_count = header->RecordCountMax;
	if(stat_count > STAT_COUNT)
		stat_count = STAT_COUNT;

	for(i = 0; i < stat_count; i++)
	{
		record->Type = (S2WireType_Ethernet << 16) + i;
		record->Count = unit->rtl8139u_special_stats[i];
		record->String = (STRPTR)special_stat_names[i];
		record++;
	}

	header->RecordCountSupplied = stat_count;

	/* Return */

	return TRUE;
}

static BOOL CmdGetGlobalStats(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;

	/* Update and copy stats */

	unit = (APTR)request->ios2_Req.io_Unit;

RTLD(bug("[%s] S2CmdGetGlobalStats()\n", unit->rtl8139u_name))

	CopyMem(&unit->rtl8139u_stats, request->ios2_StatData,
		sizeof(struct Sana2DeviceStats));

	/* Return */

	return TRUE;
}

static BOOL CmdDeviceQuery(LIBBASETYPEPTR LIBBASE, struct IOStdReq *request)
{
	struct NSDeviceQueryResult *info;

	/* Set structure size twice */

	info = request->io_Data;
	request->io_Actual = info->SizeAvailable =
		offsetof(struct NSDeviceQueryResult, SupportedCommands) + sizeof(APTR);

	/* Report device details */

	info->DeviceType = NSDEVTYPE_SANA2;
	info->DeviceSubType = 0;

	info->SupportedCommands = (APTR)supported_commands;

	/* Return */

	return TRUE;
}

static BOOL CmdOnEvent(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	ULONG events, wanted_events;
	BOOL complete = FALSE;

	/* Check if we understand the event types */

	unit = (APTR)request->ios2_Req.io_Unit;

RTLD(bug("[%s] S2CmdOnEvent()\n", unit->rtl8139u_name))

	wanted_events = request->ios2_WireError;
	if((wanted_events & ~KNOWN_EVENTS) != 0)
	{
		request->ios2_Req.io_Error = S2ERR_NOT_SUPPORTED;
		events = S2WERR_BAD_EVENT;
	}
	else
	{
		if((unit->rtl8139u_flags & IFF_UP) != 0)
			events = S2EVENT_ONLINE;
		else
			events = S2EVENT_OFFLINE;

		events &= wanted_events;
	}

	/* Reply request if a wanted event has already occurred */

	if(events != 0)
	{
		request->ios2_WireError = events;
		complete = TRUE;
	}
	else
	{
		request->ios2_Req.io_Flags &= ~IOF_QUICK;
		PutMsg(unit->rtl8139u_request_ports[EVENT_QUEUE], (APTR)request);
	}

	/* Return */

	return complete;
}

static BOOL CmdReadOrphan(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	BYTE error = 0;
	ULONG wire_error;
	BOOL complete = FALSE;

	/* Check request is valid */

	unit = (APTR)request->ios2_Req.io_Unit;
RTLD(bug("[%s] S2CmdReadOrphan()\n", unit->rtl8139u_name))

	if((unit->rtl8139u_flags & IFF_UP) == 0)
	{
		error = S2ERR_OUTOFSERVICE;
		wire_error = S2WERR_UNIT_OFFLINE;
	}

	/* Queue request */

	if(error == 0)
	{
		request->ios2_Req.io_Flags &= ~IOF_QUICK;
		PutMsg(unit->rtl8139u_request_ports[ADOPT_QUEUE], (struct Message *)request);
	}
	else
	{
		request->ios2_Req.io_Error = error;
		request->ios2_WireError = wire_error;
		complete = TRUE;
	}

	/* Return */

	return complete;
}

static BOOL CmdOnline(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit = (struct RTL8139Unit *)request->ios2_Req.io_Unit;
	BYTE error = 0;
	ULONG wire_error = 0;
	UWORD i;

RTLD(bug("[%s] S2CmdOnline()\n", unit->rtl8139u_name))

	/* Check request is valid */
	if((unit->rtl8139u_flags & IFF_CONFIGURED) == 0)
	{
		error = S2ERR_BAD_STATE;
		wire_error = S2WERR_NOT_CONFIGURED;
	}

	/* Clear global and special stats and put adapter back online */

	if((error == 0) && ((unit->rtl8139u_flags & IFF_UP) == 0))
	{
		unit->rtl8139u_stats.PacketsReceived = 0;
		unit->rtl8139u_stats.PacketsSent = 0;
		unit->rtl8139u_stats.BadData = 0;
		unit->rtl8139u_stats.Overruns = 0;
		unit->rtl8139u_stats.UnknownTypesReceived = 0;
		unit->rtl8139u_stats.Reconfigurations = 0;

		for(i = 0; i < STAT_COUNT; i++)
			unit->rtl8139u_special_stats[i] = 0;

		if (unit->start(unit))
		{
			error = S2ERR_OUTOFSERVICE;
			wire_error = S2WERR_GENERIC_ERROR;
		}
	}

	/* Return */

	request->ios2_Req.io_Error = error;
	request->ios2_WireError = wire_error;
	return TRUE;
}

static BOOL CmdOffline(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;

	/* Put adapter offline */

	unit = (APTR)request->ios2_Req.io_Unit;

RTLD(bug("[%s] S2CmdOffline()\n", unit->rtl8139u_name))

	if((unit->rtl8139u_flags & IFF_UP) != 0)
		unit->stop(unit);

	/* Return */
	return TRUE;
}

static BOOL CmdAddMulticastAddresses(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	UBYTE *lower_bound, *upper_bound;

	unit = (APTR)request->ios2_Req.io_Unit;

RTLD(bug("[%s] S2CmdAddMulticastAddresses()\n", unit->rtl8139u_name))

	lower_bound = request->ios2_SrcAddr;
	if(request->ios2_Req.io_Command == S2_ADDMULTICASTADDRESS)
		upper_bound = lower_bound;
	else
		upper_bound = request->ios2_DstAddr;

	if(!AddMulticastRange(LIBBASE, unit, lower_bound, upper_bound))
	{
		request->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
		request->ios2_WireError = S2WERR_GENERIC_ERROR;
	}

	/* Return */

	return TRUE;
}

static BOOL CmdDelMulticastAddresses(LIBBASETYPEPTR LIBBASE, struct IOSana2Req *request)
{
	struct RTL8139Unit *unit;
	UBYTE *lower_bound, *upper_bound;

	unit = (APTR)request->ios2_Req.io_Unit;

RTLD(bug("[%s] S2CmdDelMulticastAddresses()\n", unit->rtl8139u_name))

	lower_bound = request->ios2_SrcAddr;
	if(request->ios2_Req.io_Command == S2_DELMULTICASTADDRESS)
		upper_bound = lower_bound;
	else
		upper_bound = request->ios2_DstAddr;

	if(!RemMulticastRange(LIBBASE, unit, lower_bound, upper_bound))
	{
		request->ios2_Req.io_Error = S2ERR_BAD_STATE;
		request->ios2_WireError = S2WERR_BAD_MULTICAST;
	}

	/* Return */

	return TRUE;
}
