mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
1744 lines
47 KiB
C++
1744 lines
47 KiB
C++
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* SanaII emulation
|
|
*
|
|
* partially based on code from 3c589 PCMCIA driver by Neil Cafferke
|
|
*
|
|
* Copyright 2007 Toni Wilen
|
|
*
|
|
*/
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
|
|
#include "threaddep/thread.h"
|
|
#include "options.h"
|
|
#include "memory.h"
|
|
#include "custom.h"
|
|
#include "events.h"
|
|
#include "newcpu.h"
|
|
#include "autoconf.h"
|
|
#include "traps.h"
|
|
#include "execlib.h"
|
|
#include "native2amiga.h"
|
|
#include "blkdev.h"
|
|
#include "uae.h"
|
|
#include "sana2.h"
|
|
#if defined(_WIN32) && defined(WITH_UAENET_PCAP)
|
|
#include "win32_uaenet.h"
|
|
#else
|
|
#include "ethernet.h"
|
|
#endif
|
|
#include "execio.h"
|
|
|
|
static void uaenet_gotdata (void *dev, const uae_u8 *data, int len);
|
|
static int uaenet_getdata (void *dev, uae_u8 *d, int *len);
|
|
|
|
#define SANA2NAME _T("uaenet.device")
|
|
|
|
#define MAX_ASYNC_REQUESTS 200
|
|
#define MAX_OPEN_DEVICES 20
|
|
|
|
#define S2_START (CMD_NONSTD) // 9
|
|
#define S2_DEVICEQUERY (S2_START+ 0) // 9
|
|
#define S2_GETSTATIONADDRESS (S2_START+ 1) // 10
|
|
#define S2_CONFIGINTERFACE (S2_START+ 2) // 11
|
|
#define S2_ADDMULTICASTADDRESS (S2_START+ 5) // 14
|
|
#define S2_DELMULTICASTADDRESS (S2_START+ 6) // 15
|
|
#define S2_MULTICAST (S2_START+ 7) // 16
|
|
#define S2_BROADCAST (S2_START+ 8) // 17
|
|
#define S2_TRACKTYPE (S2_START+ 9) // 18
|
|
#define S2_UNTRACKTYPE (S2_START+10) // 19
|
|
#define S2_GETTYPESTATS (S2_START+11) // 20
|
|
#define S2_GETSPECIALSTATS (S2_START+12) // 21
|
|
#define S2_GETGLOBALSTATS (S2_START+13) // 22
|
|
#define S2_ONEVENT (S2_START+14) // 23
|
|
#define S2_READORPHAN (S2_START+15) // 24
|
|
#define S2_ONLINE (S2_START+16) // 25
|
|
#define S2_OFFLINE (S2_START+17) // 26
|
|
#define S2_ADDMULTICASTADDRESSES 0xC000
|
|
#define S2_DELMULTICASTADDRESSES 0xC001
|
|
#define S2_GETPEERADDRESS 0xC002
|
|
#define S2_GETDNSADDRESS 0xC003
|
|
#define S2_GETEXTENDEDGLOBALSTATS 0xC004
|
|
#define S2_CONNECT 0xC005
|
|
#define S2_DISCONNECT 0xC006
|
|
#define S2_SAMPLE_THROUGHPUT 0xC007
|
|
|
|
#define S2WireType_Ethernet 1
|
|
#define S2WireType_IEEE802 6
|
|
|
|
#define SANA2_MAX_ADDR_BITS (128)
|
|
#define SANA2_MAX_ADDR_BYTES ((SANA2_MAX_ADDR_BITS+7)/8)
|
|
#define ADDR_SIZE 6
|
|
#define ETH_HEADER_SIZE (ADDR_SIZE + ADDR_SIZE + 2)
|
|
|
|
#define S2ERR_NO_ERROR 0 /* peachy-keen */
|
|
#define S2ERR_NO_RESOURCES 1 /* resource allocation failure */
|
|
#define S2ERR_BAD_ARGUMENT 3 /* garbage somewhere */
|
|
#define S2ERR_BAD_STATE 4 /* inappropriate state */
|
|
#define S2ERR_BAD_ADDRESS 5 /* who? */
|
|
#define S2ERR_MTU_EXCEEDED 6 /* too much to chew */
|
|
#define S2ERR_NOT_SUPPORTED 8 /* hardware can't support cmd */
|
|
#define S2ERR_SOFTWARE 9 /* software error detected */
|
|
#define S2ERR_OUTOFSERVICE 10 /* driver is OFFLINE */
|
|
#define S2ERR_TX_FAILURE 11 /* Transmission attempt failed */
|
|
|
|
#define S2WERR_GENERIC_ERROR 0 /* no specific info available */
|
|
#define S2WERR_NOT_CONFIGURED 1 /* unit not configured */
|
|
#define S2WERR_UNIT_ONLINE 2 /* unit is currently online */
|
|
#define S2WERR_UNIT_OFFLINE 3 /* unit is currently offline */
|
|
#define S2WERR_ALREADY_TRACKED 4 /* protocol already tracked */
|
|
#define S2WERR_NOT_TRACKED 5 /* protocol not tracked */
|
|
#define S2WERR_BUFF_ERROR 6 /* buff mgt func returned error */
|
|
#define S2WERR_SRC_ADDRESS 7 /* source address problem */
|
|
#define S2WERR_DST_ADDRESS 8 /* destination address problem */
|
|
#define S2WERR_BAD_BROADCAST 9 /* broadcast address problem */
|
|
#define S2WERR_BAD_MULTICAST 10 /* multicast address problem */
|
|
#define S2WERR_MULTICAST_FULL 11 /* multicast address list full */
|
|
#define S2WERR_BAD_EVENT 12 /* unsupported event class */
|
|
#define S2WERR_BAD_STATDATA 13 /* statdata failed sanity check */
|
|
#define S2WERR_IS_CONFIGURED 15 /* attempt to config twice */
|
|
#define S2WERR_NULL_POINTER 16 /* null pointer detected */
|
|
#define S2WERR_TOO_MANY_RETIRES 17 /* tx failed - too many retries */
|
|
#define S2WERR_RCVREL_HDW_ERR 18 /* Driver fixable HW error */
|
|
|
|
#define S2EVENT_ERROR (1L<<0) /* error catch all */
|
|
#define S2EVENT_TX (1L<<1) /* transmitter error catch all */
|
|
#define S2EVENT_RX (1L<<2) /* receiver error catch all */
|
|
#define S2EVENT_ONLINE (1L<<3) /* unit is in service */
|
|
#define S2EVENT_OFFLINE (1L<<4) /* unit is not in service */
|
|
#define S2EVENT_BUFF (1L<<5) /* buff mgt function error */
|
|
#define S2EVENT_HARDWARE (1L<<6) /* hardware error catch all */
|
|
#define S2EVENT_SOFTWARE (1L<<7) /* software error catch all */
|
|
|
|
#define KNOWN_EVENTS (S2EVENT_ERROR|S2EVENT_TX|S2EVENT_RX|S2EVENT_ONLINE|\
|
|
S2EVENT_OFFLINE|S2EVENT_BUFF|S2EVENT_HARDWARE|S2EVENT_SOFTWARE)
|
|
|
|
#define SANA2OPB_MINE 0
|
|
#define SANA2OPF_MINE (1<<SANA2OPB_MINE)
|
|
#define SANA2OPB_PROM 1
|
|
#define SANA2OPF_PROM (1<<SANA2OPB_PROM)
|
|
#define SANA2IOB_RAW 7
|
|
#define SANA2IOF_RAW (1<<SANA2IOB_RAW)
|
|
#define SANA2IOB_BCAST 6
|
|
#define SANA2IOF_BCAST (1<<SANA2IOB_BCAST)
|
|
#define SANA2IOB_MCAST 5
|
|
#define SANA2IOF_MCAST (1<<SANA2IOB_MCAST)
|
|
|
|
#define S2_Dummy (TAG_USER + 0xB0000)
|
|
#define S2_CopyToBuff (S2_Dummy + 1)
|
|
#define S2_CopyFromBuff (S2_Dummy + 2)
|
|
#define S2_PacketFilter (S2_Dummy + 3)
|
|
#define S2_CopyToBuff16 (S2_Dummy + 4)
|
|
#define S2_CopyFromBuff16 (S2_Dummy + 5)
|
|
#define S2_CopyToBuff32 (S2_Dummy + 6)
|
|
#define S2_CopyFromBuff32 (S2_Dummy + 7)
|
|
#define S2_DMACopyToBuff32 (S2_Dummy + 8)
|
|
#define S2_DMACopyFromBuff32 (S2_Dummy + 9)
|
|
|
|
#define SANA2_IOREQSIZE (32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2 + 4 + 4 + 4 + 4)
|
|
|
|
struct s2packet {
|
|
struct s2packet *next;
|
|
uae_u8 *data;
|
|
int len;
|
|
};
|
|
|
|
volatile int uaenet_int_requested;
|
|
volatile int uaenet_vsync_requested;
|
|
static int uaenet_int_late;
|
|
|
|
static uaecptr timerdevname;
|
|
|
|
static uaecptr ROM_netdev_resname = 0,
|
|
ROM_netdev_resid = 0,
|
|
ROM_netdev_init = 0;
|
|
|
|
static const TCHAR *getdevname (void)
|
|
{
|
|
return _T("uaenet.device");
|
|
}
|
|
|
|
struct asyncreq {
|
|
struct asyncreq *next;
|
|
uaecptr request;
|
|
struct s2packet *s2p;
|
|
int ready;
|
|
};
|
|
|
|
struct mcast {
|
|
struct mcast *next;
|
|
uae_u64 start;
|
|
uae_u64 end;
|
|
int cnt;
|
|
};
|
|
|
|
struct s2devstruct {
|
|
int unit, opencnt, exclusive, promiscuous;
|
|
struct asyncreq *ar;
|
|
struct asyncreq *s2p;
|
|
struct mcast *mc;
|
|
smp_comm_pipe requests;
|
|
int thread_running;
|
|
uae_sem_t sync_sem;
|
|
void *sysdata;
|
|
uae_u32 packetsreceived;
|
|
uae_u32 packetssent;
|
|
uae_u32 baddata;
|
|
uae_u32 overruns;
|
|
uae_u32 unknowntypesreceived;
|
|
uae_u32 reconfigurations;
|
|
uae_u32 online_micro;
|
|
uae_u32 online_secs;
|
|
int configured;
|
|
int adapter;
|
|
int online;
|
|
struct netdriverdata *td;
|
|
struct s2packet *readqueue;
|
|
uae_u8 mac[ADDR_SIZE];
|
|
int flush_timeout;
|
|
int flush_timeout_cnt;
|
|
};
|
|
|
|
#define FLUSH_TIMEOUT 20
|
|
|
|
struct priv_s2devstruct {
|
|
int inuse;
|
|
int unit;
|
|
int flags; /* OpenDevice() */
|
|
int promiscuous;
|
|
|
|
uae_u8 tracks[65536];
|
|
int trackcnt;
|
|
uae_u32 packetsreceived;
|
|
uae_u32 packetssent;
|
|
uae_u32 bytessent;
|
|
uae_u32 bytesreceived;
|
|
uae_u32 packetsdropped;
|
|
|
|
uaecptr copytobuff;
|
|
uaecptr copyfrombuff;
|
|
uaecptr packetfilter;
|
|
uaecptr tempbuf;
|
|
|
|
uaecptr timerbase;
|
|
|
|
struct netdriverdata *td;
|
|
|
|
int tmp;
|
|
};
|
|
|
|
static struct netdriverdata *td[MAX_TOTAL_NET_DEVICES + 1];
|
|
static struct s2devstruct devst[MAX_TOTAL_NET_DEVICES];
|
|
static struct priv_s2devstruct pdevst[MAX_OPEN_DEVICES];
|
|
static uae_u32 nscmd_cmd;
|
|
static uae_sem_t change_sem, async_sem;
|
|
|
|
static struct s2devstruct *gets2devstruct (int unit)
|
|
{
|
|
if (unit >= MAX_TOTAL_NET_DEVICES || unit < 0)
|
|
return 0;
|
|
return &devst[unit];
|
|
}
|
|
|
|
static struct priv_s2devstruct *getps2devstruct (uaecptr request)
|
|
{
|
|
int i = get_long (request + 24);
|
|
if (i < 0 || i >= MAX_OPEN_DEVICES || pdevst[i].inuse == 0) {
|
|
write_log (_T("%s: corrupt iorequest %08X %d\n"), SANA2NAME, request, i);
|
|
return 0;
|
|
}
|
|
return &pdevst[i];
|
|
}
|
|
|
|
static void *dev_thread (void *devs);
|
|
static int start_thread (struct s2devstruct *dev)
|
|
{
|
|
if (dev->thread_running)
|
|
return 1;
|
|
init_comm_pipe (&dev->requests, 100, 1);
|
|
uae_sem_init (&dev->sync_sem, 0, 0);
|
|
uae_start_thread (SANA2NAME, dev_thread, dev, NULL);
|
|
uae_sem_wait (&dev->sync_sem);
|
|
return dev->thread_running;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dev_close_2 (TrapContext *context)
|
|
{
|
|
uae_u32 request = m68k_areg (regs, 1);
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
struct s2devstruct *dev;
|
|
|
|
if (!pdev) {
|
|
write_log (_T("%s close with unknown request %08X!?\n"), SANA2NAME, request);
|
|
return 0;
|
|
}
|
|
dev = gets2devstruct (pdev->unit);
|
|
if (!dev) {
|
|
write_log (_T("%s:%d close with unknown request %08X!?\n"), SANA2NAME, pdev->unit, request);
|
|
return 0;
|
|
}
|
|
if (log_net)
|
|
write_log (_T("%s:%d close, open=%d req=%08X\n"), SANA2NAME, pdev->unit, dev->opencnt, request);
|
|
put_long (request + 24, 0);
|
|
dev->opencnt--;
|
|
pdev->inuse = 0;
|
|
if (!dev->opencnt) {
|
|
dev->exclusive = 0;
|
|
if (pdev->tempbuf) {
|
|
m68k_areg (regs, 1) = pdev->tempbuf;
|
|
m68k_dreg (regs, 0) = pdev->td->mtu + ETH_HEADER_SIZE + 2;
|
|
CallLib (context, get_long (4), -0xD2); /* FreeMem */
|
|
pdev->tempbuf = 0;
|
|
}
|
|
ethernet_close (pdev->td, dev->sysdata);
|
|
xfree (dev->sysdata);
|
|
dev->sysdata = NULL;
|
|
write_comm_pipe_u32 (&dev->requests, 0, 1);
|
|
write_log (_T("%s: opencnt == 0, all instances closed\n"), SANA2NAME);
|
|
}
|
|
put_word (m68k_areg (regs, 6) + 32, get_word (m68k_areg (regs, 6) + 32) - 1);
|
|
return 0;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dev_close (TrapContext *context)
|
|
{
|
|
return dev_close_2 (context);
|
|
}
|
|
static uae_u32 REGPARAM2 diskdev_close (TrapContext *context)
|
|
{
|
|
return dev_close_2 (context);
|
|
}
|
|
|
|
static int openfail (uaecptr ioreq, int error)
|
|
{
|
|
put_long (ioreq + 20, -1);
|
|
put_byte (ioreq + 31, error);
|
|
put_long (ioreq + 32, 0); /* io_device */
|
|
if (log_net)
|
|
write_log (_T("-> failed with error %d\n"), error);
|
|
return (uae_u32)-1;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 uaenet_int_handler (TrapContext *ctx);
|
|
static int irq_init;
|
|
static int initint (TrapContext *ctx)
|
|
{
|
|
uae_u32 tmp1;
|
|
uaecptr p;
|
|
|
|
if (irq_init)
|
|
return 1;
|
|
m68k_dreg (regs, 0) = 26;
|
|
m68k_dreg (regs, 1) = 65536 + 1;
|
|
p = CallLib (ctx, get_long (4), -0xC6); /* AllocMem */
|
|
if (!p)
|
|
return 0;
|
|
tmp1 = here ();
|
|
calltrap (deftrap2 (uaenet_int_handler, TRAPFLAG_EXTRA_STACK, _T("uaenet_int_handler")));
|
|
put_word (p + 8, 0x020a);
|
|
put_long (p + 10, ROM_netdev_resid);
|
|
put_long (p + 18, tmp1);
|
|
m68k_areg (regs, 1) = p;
|
|
m68k_dreg (regs, 0) = 3; /* PORTS */
|
|
dw (0x4a80); /* TST.L D0 */
|
|
dw (0x4e75); /* RTS */
|
|
CallLib (ctx, get_long (4), -168); /* AddIntServer */
|
|
irq_init = 1;
|
|
return 1;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dev_open_2 (TrapContext *context)
|
|
{
|
|
uaecptr ioreq = m68k_areg (regs, 1);
|
|
uae_u32 unit = m68k_dreg (regs, 0);
|
|
uae_u32 flags = m68k_dreg (regs, 1);
|
|
uaecptr buffermgmt;
|
|
struct s2devstruct *dev = gets2devstruct (unit);
|
|
struct priv_s2devstruct *pdev = 0;
|
|
int i;
|
|
uaecptr tagp, tagpnext;
|
|
|
|
if (!dev)
|
|
return openfail (ioreq, IOERR_OPENFAIL);
|
|
if (!initint(context))
|
|
return openfail (ioreq, IOERR_SELFTEST);
|
|
if (log_net)
|
|
write_log (_T("opening %s:%d opencnt=%d ioreq=%08X\n"), SANA2NAME, unit, dev->opencnt, ioreq);
|
|
if (get_word (ioreq + 0x12) < IOSTDREQ_SIZE)
|
|
return openfail (ioreq, IOERR_BADLENGTH);
|
|
if ((flags & SANA2OPF_PROM) && dev->opencnt > 0)
|
|
return openfail (ioreq, IOERR_UNITBUSY);
|
|
|
|
for (i = 0; i < MAX_OPEN_DEVICES; i++) {
|
|
pdev = &pdevst[i];
|
|
if (pdev->inuse == 0)
|
|
break;
|
|
}
|
|
if (i == MAX_OPEN_DEVICES)
|
|
return openfail (ioreq, IOERR_UNITBUSY);
|
|
|
|
put_long (ioreq + 24, pdev - pdevst);
|
|
pdev->unit = unit;
|
|
pdev->flags = flags;
|
|
pdev->inuse = 1;
|
|
pdev->td = td ? td[unit] : NULL;
|
|
pdev->promiscuous = (flags & SANA2OPF_PROM) ? 1 : 0;
|
|
|
|
if (pdev->td == NULL || pdev->td->active == 0)
|
|
return openfail (ioreq, IOERR_OPENFAIL);
|
|
|
|
if (dev->opencnt == 0) {
|
|
dev->unit = unit;
|
|
dev->sysdata = xcalloc (uae_u8, ethernet_getdatalenght (pdev->td));
|
|
if (!ethernet_open (pdev->td, dev->sysdata, dev, uaenet_gotdata, uaenet_getdata, pdev->promiscuous)) {
|
|
xfree (dev->sysdata);
|
|
dev->sysdata = NULL;
|
|
return openfail (ioreq, IOERR_OPENFAIL);
|
|
}
|
|
write_log (_T("%s: initializing unit %d\n"), getdevname (), unit);
|
|
dev->td = pdev->td;
|
|
dev->adapter = pdev->td ? pdev->td->active : 0;
|
|
if (dev->adapter) {
|
|
dev->online = 1;
|
|
dev->configured = 1;
|
|
}
|
|
start_thread (dev);
|
|
}
|
|
|
|
if (kickstart_version >= 36) {
|
|
m68k_areg (regs, 0) = get_long (4) + 350;
|
|
m68k_areg (regs, 1) = timerdevname;
|
|
CallLib (context, get_long (4), -0x114); /* FindName('timer.device') */
|
|
pdev->timerbase = m68k_dreg (regs, 0);
|
|
}
|
|
|
|
pdev->copyfrombuff = pdev->copytobuff = pdev->packetfilter = 0;
|
|
pdev->tempbuf = 0;
|
|
if (get_word (ioreq + 0x12) >= SANA2_IOREQSIZE) {
|
|
buffermgmt = get_long (ioreq + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2 + 4 + 4 + 4);
|
|
tagpnext = buffermgmt;
|
|
while (tagpnext) {
|
|
uae_u32 tag = get_long (tagpnext);
|
|
uae_u32 val = get_long (tagpnext + 4);
|
|
tagp = tagpnext;
|
|
tagpnext += 8;
|
|
switch (tag)
|
|
{
|
|
case TAG_DONE:
|
|
tagpnext = 0;
|
|
break;
|
|
case TAG_IGNORE:
|
|
break;
|
|
case TAG_MORE:
|
|
tagpnext = val;
|
|
break;
|
|
case TAG_SKIP:
|
|
tagpnext = tagp + val * 8;
|
|
break;
|
|
case S2_CopyToBuff:
|
|
pdev->copytobuff = val;
|
|
break;
|
|
case S2_CopyFromBuff:
|
|
pdev->copyfrombuff = val;
|
|
break;
|
|
case S2_PacketFilter:
|
|
pdev->packetfilter = val;
|
|
break;
|
|
}
|
|
}
|
|
if (log_net)
|
|
write_log (_T("%s:%d CTB=%08x CFB=%08x PF=%08x\n"),
|
|
getdevname(), unit, pdev->copytobuff, pdev->copyfrombuff, pdev->packetfilter);
|
|
m68k_dreg (regs, 0) = dev->td->mtu + ETH_HEADER_SIZE + 2;
|
|
m68k_dreg (regs, 1) = 1;
|
|
pdev->tempbuf = CallLib (context, get_long (4), -0xC6); /* AllocMem */
|
|
if (!pdev->tempbuf) {
|
|
if (dev->opencnt == 0) {
|
|
ethernet_close (pdev->td, dev->sysdata);
|
|
xfree (dev->sysdata);
|
|
dev->sysdata = NULL;
|
|
}
|
|
return openfail (ioreq, S2ERR_BAD_ARGUMENT);
|
|
}
|
|
/* buffermanagement */
|
|
put_long (ioreq + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2 + 4 + 4 + 4, pdev->tempbuf);
|
|
}
|
|
dev->exclusive = flags & SANA2OPF_MINE;
|
|
dev->opencnt++;
|
|
put_word (m68k_areg (regs, 6) + 32, get_word (m68k_areg (regs, 6) + 32) + 1);
|
|
put_byte (ioreq + 31, 0);
|
|
put_byte (ioreq + 8, 7);
|
|
return 0;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dev_open (TrapContext *context)
|
|
{
|
|
return dev_open_2 (context);
|
|
}
|
|
static uae_u32 REGPARAM2 dev_expunge (TrapContext *context)
|
|
{
|
|
return 0;
|
|
}
|
|
static uae_u32 REGPARAM2 diskdev_expunge (TrapContext *context)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void freepacket (struct s2packet *s2p)
|
|
{
|
|
xfree (s2p->data);
|
|
xfree (s2p);
|
|
}
|
|
|
|
static void add_async_packet (struct s2devstruct *dev, struct s2packet *s2p, uaecptr request)
|
|
{
|
|
struct asyncreq *ar, *ar2;
|
|
|
|
ar = xcalloc (struct asyncreq, 1);
|
|
ar->s2p = s2p;
|
|
if (!dev->s2p) {
|
|
dev->s2p = ar;
|
|
} else {
|
|
ar2 = dev->s2p;
|
|
while (ar2->next)
|
|
ar2 = ar2->next;
|
|
ar2->next = ar;
|
|
}
|
|
ar->request = request;
|
|
}
|
|
|
|
static void rem_async_packet (struct s2devstruct *dev, uaecptr request)
|
|
{
|
|
struct asyncreq *ar, *prevar;
|
|
|
|
uae_sem_wait (&async_sem);
|
|
ar = dev->s2p;
|
|
prevar = NULL;
|
|
while (ar) {
|
|
if (ar->request == request) {
|
|
if (prevar == NULL)
|
|
dev->s2p = ar->next;
|
|
else
|
|
prevar->next = ar->next;
|
|
uae_sem_post (&async_sem);
|
|
freepacket (ar->s2p);
|
|
xfree (ar);
|
|
return;
|
|
}
|
|
prevar = ar;
|
|
ar = ar->next;
|
|
}
|
|
uae_sem_post (&async_sem);
|
|
}
|
|
|
|
static struct asyncreq *get_async_request (struct s2devstruct *dev, uaecptr request, int ready)
|
|
{
|
|
struct asyncreq *ar;
|
|
int ret = 0;
|
|
|
|
uae_sem_wait (&async_sem);
|
|
ar = dev->ar;
|
|
while (ar) {
|
|
if (ar->request == request) {
|
|
if (ready)
|
|
ar->ready = 1;
|
|
break;
|
|
}
|
|
ar = ar->next;
|
|
}
|
|
uae_sem_post (&async_sem);
|
|
return ar;
|
|
}
|
|
|
|
static int add_async_request (struct s2devstruct *dev, uaecptr request)
|
|
{
|
|
struct asyncreq *ar, *ar2;
|
|
|
|
if (log_net)
|
|
write_log (_T("%s:%d async request %x added\n"), getdevname(), dev->unit, request);
|
|
|
|
uae_sem_wait (&async_sem);
|
|
ar = xcalloc (struct asyncreq, 1);
|
|
ar->request = request;
|
|
if (!dev->ar) {
|
|
dev->ar = ar;
|
|
} else {
|
|
ar2 = dev->ar;
|
|
while (ar2->next)
|
|
ar2 = ar2->next;
|
|
ar2->next = ar;
|
|
}
|
|
uae_sem_post (&async_sem);
|
|
return 1;
|
|
}
|
|
|
|
static int release_async_request (struct s2devstruct *dev, uaecptr request)
|
|
{
|
|
struct asyncreq *ar, *prevar;
|
|
|
|
uae_sem_wait (&async_sem);
|
|
ar = dev->ar;
|
|
prevar = NULL;
|
|
while (ar) {
|
|
if (ar->request == request) {
|
|
if (prevar == NULL)
|
|
dev->ar = ar->next;
|
|
else
|
|
prevar->next = ar->next;
|
|
uae_sem_post (&async_sem);
|
|
xfree (ar);
|
|
if (log_net)
|
|
write_log (_T("%s:%d async request %x removed\n"), getdevname(), dev->unit, request);
|
|
return 1;
|
|
}
|
|
prevar = ar;
|
|
ar = ar->next;
|
|
}
|
|
uae_sem_post (&async_sem);
|
|
write_log (_T("%s:%d async request %x not found for removal!\n"), getdevname(), dev->unit, request);
|
|
return 0;
|
|
}
|
|
|
|
static void do_abort_async (struct s2devstruct *dev, uaecptr request)
|
|
{
|
|
put_byte (request + 30, get_byte (request + 30) | 0x20);
|
|
put_byte (request + 31, IOERR_ABORTED);
|
|
put_long (request + 32, S2WERR_GENERIC_ERROR);
|
|
write_comm_pipe_u32 (&dev->requests, request, 1);
|
|
}
|
|
|
|
static void abort_async (struct s2devstruct *dev, uaecptr request)
|
|
{
|
|
struct asyncreq *ar = get_async_request (dev, request, 1);
|
|
if (!ar) {
|
|
write_log (_T("%s:%d: abort async but no request %x found!\n"), getdevname(), dev->unit, request);
|
|
return;
|
|
}
|
|
if (log_net)
|
|
write_log (_T("%s:%d asyncronous request=%08X aborted\n"), getdevname(), dev->unit, request);
|
|
do_abort_async (dev, request);
|
|
}
|
|
|
|
static void signalasync (struct s2devstruct *dev, struct asyncreq *ar, int actual, int err)
|
|
{
|
|
uaecptr request = ar->request;
|
|
int command = get_word (request + 28);
|
|
if (log_net)
|
|
write_log (_T("%s:%d CMD=%d async request %x completed\n"), getdevname(), dev->unit, command, request);
|
|
put_long (request + 32, actual);
|
|
put_byte (request + 31, err);
|
|
ar->ready = 1;
|
|
write_comm_pipe_u32 (&dev->requests, request, 1);
|
|
}
|
|
|
|
static uae_u32 copytobuff (TrapContext *ctx, uaecptr from, uaecptr to, uae_u32 len, uaecptr func)
|
|
{
|
|
m68k_areg (regs, 0) = to;
|
|
m68k_areg (regs, 1) = from;
|
|
m68k_dreg (regs, 0) = len;
|
|
return CallFunc (ctx, func);
|
|
}
|
|
static uae_u32 copyfrombuff (TrapContext *ctx, uaecptr from, uaecptr to, uae_u32 len, uaecptr func)
|
|
{
|
|
m68k_areg (regs, 0) = to;
|
|
m68k_areg (regs, 1) = from;
|
|
m68k_dreg (regs, 0) = len;
|
|
return CallFunc (ctx, func);
|
|
}
|
|
static uae_u32 packetfilter (TrapContext *ctx, uaecptr hook, uaecptr ios2, uaecptr data)
|
|
{
|
|
uae_u32 a2, v;
|
|
|
|
a2 = m68k_areg (regs, 2);
|
|
m68k_areg (regs, 0) = hook;
|
|
m68k_areg (regs, 2) = ios2;
|
|
m68k_areg (regs, 1) = data;
|
|
v = CallFunc (ctx, get_long (hook + 8));
|
|
m68k_areg (regs, 2) = a2;
|
|
return v;
|
|
}
|
|
|
|
static int isbroadcast (const uae_u8 *d)
|
|
{
|
|
if (d[0] == 0xff && d[1] == 0xff && d[2] == 0xff &&
|
|
d[3] == 0xff && d[4] == 0xff && d[5] == 0xff)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
static int ismulticast (const uae_u8 *d)
|
|
{
|
|
if (isbroadcast (d))
|
|
return 0;
|
|
if (d[0] & 1)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static uae_u64 addrto64 (const uae_u8 *d)
|
|
{
|
|
int i;
|
|
uae_u64 addr = 0;
|
|
for (i = 0; i < ADDR_SIZE; i++) {
|
|
addr <<= 8;
|
|
addr |= d[i];
|
|
}
|
|
return addr;
|
|
}
|
|
static uae_u64 amigaaddrto64 (uaecptr d)
|
|
{
|
|
int i;
|
|
uae_u64 addr = 0;
|
|
for (i = 0; i < ADDR_SIZE; i++) {
|
|
addr <<= 8;
|
|
addr |= get_byte (d + i);
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
static void addmulticastaddresses (struct s2devstruct *dev, uae_u64 start, uae_u64 end)
|
|
{
|
|
struct mcast *mc, *mc2;
|
|
|
|
if (!end)
|
|
end = start;
|
|
mc = dev->mc;
|
|
while (mc) {
|
|
if (start == mc->start && end == mc->end) {
|
|
mc->cnt++;
|
|
return;
|
|
}
|
|
mc = mc->next;
|
|
}
|
|
mc = xcalloc (struct mcast, 1);
|
|
mc->start = start;
|
|
mc->end = end;
|
|
mc->cnt = 1;
|
|
if (!dev->mc) {
|
|
dev->mc = mc;
|
|
} else {
|
|
mc2 = dev->mc;
|
|
while (mc2->next)
|
|
mc2 = mc2->next;
|
|
mc2->next = mc;
|
|
}
|
|
}
|
|
static int delmulticastaddresses (struct s2devstruct *dev, uae_u64 start, uae_u64 end)
|
|
{
|
|
struct mcast *mc, *prevmc;
|
|
|
|
if (!end)
|
|
end = start;
|
|
mc = dev->mc;
|
|
prevmc = NULL;
|
|
while (mc) {
|
|
if (start == mc->start && end == mc->end) {
|
|
mc->cnt--;
|
|
if (mc->cnt > 0)
|
|
return 1;
|
|
if (prevmc == NULL)
|
|
dev->mc = mc->next;
|
|
else
|
|
prevmc->next = mc->next;
|
|
xfree (mc);
|
|
return 1;
|
|
}
|
|
prevmc = mc;
|
|
mc = mc->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct s2packet *createreadpacket (struct s2devstruct *dev, const uae_u8 *d, int len)
|
|
{
|
|
struct s2packet *s2p = xcalloc (struct s2packet, 1);
|
|
s2p->data = xmalloc (uae_u8, dev->td->mtu + ETH_HEADER_SIZE + 2);
|
|
memcpy (s2p->data, d, len);
|
|
s2p->len = len;
|
|
return s2p;
|
|
}
|
|
|
|
static int handleread (TrapContext *ctx, struct priv_s2devstruct *pdev, uaecptr request, uae_u8 *d, int len, int cmd)
|
|
{
|
|
uae_u8 flags = get_byte (request + 30);
|
|
uaecptr data = get_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2 + 4);
|
|
uaecptr srcaddr = request + 32 + 4 + 4;
|
|
uaecptr dstaddr = request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES;
|
|
uae_u16 type = (d[2 * ADDR_SIZE] << 8) | d[2 * ADDR_SIZE + 1];
|
|
uae_u32 v = 0;
|
|
uaecptr data2;
|
|
|
|
memcpyha_safe (pdev->tempbuf, d, len);
|
|
memcpyha_safe (dstaddr, d, ADDR_SIZE);
|
|
memcpyha_safe (srcaddr, d + ADDR_SIZE, ADDR_SIZE);
|
|
put_long (request + 32 + 4, type);
|
|
if (pdev->tracks[type]) {
|
|
pdev->bytesreceived += len;
|
|
pdev->packetsreceived++;
|
|
}
|
|
flags &= ~(SANA2IOF_BCAST | SANA2IOF_MCAST);
|
|
if (isbroadcast (d))
|
|
flags |= SANA2IOF_BCAST;
|
|
else if (ismulticast (d))
|
|
flags |= SANA2IOF_MCAST;
|
|
put_byte (request + 30, flags);
|
|
|
|
data2 = pdev->tempbuf;
|
|
if (!(flags & SANA2IOF_RAW)) {
|
|
len -= ETH_HEADER_SIZE;
|
|
data2 += ETH_HEADER_SIZE;
|
|
}
|
|
put_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2, len);
|
|
|
|
if (pdev->packetfilter && cmd == CMD_READ && packetfilter (ctx, pdev->packetfilter, request, data2) == 0)
|
|
return 0;
|
|
if (!copytobuff (ctx, data2, data, len, pdev->copytobuff)) {
|
|
put_long (request + 32, S2WERR_BUFF_ERROR);
|
|
put_byte (request + 31, S2ERR_NO_RESOURCES);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void uaenet_gotdata (void *devv, const uae_u8 *d, int len)
|
|
{
|
|
uae_u16 type;
|
|
struct mcast *mc;
|
|
struct s2packet *s2p;
|
|
struct s2devstruct *dev = (struct s2devstruct*)devv;
|
|
|
|
if (!dev->online)
|
|
return;
|
|
/* drop if bogus size */
|
|
if (len < 20 || len >= dev->td->mtu + ETH_HEADER_SIZE + 2)
|
|
return;
|
|
/* drop if dst == broadcast and src == me */
|
|
if (isbroadcast (d) && !memcmp (d + 6, dev->td->mac, ADDR_SIZE))
|
|
return;
|
|
/* drop if not promiscuous and dst != broadcast and dst != me */
|
|
if (!dev->promiscuous && !isbroadcast (d) && memcmp (d, dev->td->mac, ADDR_SIZE))
|
|
return;
|
|
/* drop if multicast with unknown address */
|
|
if (ismulticast (d)) {
|
|
uae_u64 mac64 = addrto64 (d);
|
|
/* multicast */
|
|
mc = dev->mc;
|
|
while (mc) {
|
|
if (mac64 >= mc->start && mac64 <= mc->end)
|
|
break;
|
|
mc = mc->next;
|
|
}
|
|
if (!mc)
|
|
return;
|
|
}
|
|
|
|
type = (d[12] << 8) | d[13];
|
|
s2p = createreadpacket (dev, d, len);
|
|
if (log_net)
|
|
write_log (_T("<-DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X L=%d P=%p\n"),
|
|
d[0], d[1], d[2], d[3], d[4], d[5],
|
|
d[6], d[7], d[8], d[9], d[10], d[11],
|
|
type, len, s2p);
|
|
uae_sem_wait (&async_sem);
|
|
if (!dev->readqueue) {
|
|
dev->readqueue = s2p;
|
|
} else {
|
|
struct s2packet *s2p2 = dev->readqueue;
|
|
while (s2p2->next)
|
|
s2p2 = s2p2->next;
|
|
s2p2->next = s2p;
|
|
}
|
|
uaenet_int_requested = 1;
|
|
uae_sem_post (&async_sem);
|
|
}
|
|
|
|
static struct s2packet *createwritepacket (TrapContext *ctx, uaecptr request)
|
|
{
|
|
uae_u8 flags = get_byte (request + 30);
|
|
uae_u32 datalength = get_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2);
|
|
uaecptr data = get_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2 + 4);
|
|
uaecptr srcaddr = request + 32 + 4 + 4;
|
|
uaecptr dstaddr = request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES;
|
|
uae_u16 packettype = get_long (request + 32 + 4);
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
struct s2packet *s2p;
|
|
|
|
if (!pdev)
|
|
return NULL;
|
|
if (!copyfrombuff (ctx, data, pdev->tempbuf, datalength, pdev->copyfrombuff))
|
|
return NULL;
|
|
s2p = xcalloc (struct s2packet, 1);
|
|
s2p->data = xmalloc (uae_u8, pdev->td->mtu + ETH_HEADER_SIZE + 2);
|
|
if (flags & SANA2IOF_RAW) {
|
|
memcpyah_safe (s2p->data, pdev->tempbuf, datalength);
|
|
packettype = (s2p->data[2 * ADDR_SIZE + 0] << 8) | (s2p->data[2 * ADDR_SIZE + 1]);
|
|
s2p->len = datalength;
|
|
} else {
|
|
memcpyah_safe (s2p->data + ETH_HEADER_SIZE, pdev->tempbuf, datalength);
|
|
memcpy (s2p->data + ADDR_SIZE, pdev->td->mac, ADDR_SIZE);
|
|
memcpyah_safe (s2p->data, dstaddr, ADDR_SIZE);
|
|
s2p->data[2 * ADDR_SIZE + 0] = packettype >> 8;
|
|
s2p->data[2 * ADDR_SIZE + 1] = packettype;
|
|
s2p->len = datalength + ETH_HEADER_SIZE;
|
|
}
|
|
if (pdev->tracks[packettype]) {
|
|
pdev->packetssent++;
|
|
pdev->bytessent += datalength;
|
|
}
|
|
return s2p;
|
|
}
|
|
|
|
static int uaenet_getdata (void *devv, uae_u8 *d, int *len)
|
|
{
|
|
int gotit;
|
|
struct asyncreq *ar;
|
|
struct s2devstruct *dev = (struct s2devstruct*)devv;
|
|
|
|
uae_sem_wait (&async_sem);
|
|
ar = dev->ar;
|
|
gotit = 0;
|
|
while (ar && !gotit) {
|
|
if (!ar->ready) {
|
|
uaecptr request = ar->request;
|
|
int command = get_word (request + 28);
|
|
uae_u32 packettype = get_long (request + 32 + 4);
|
|
if (command == CMD_WRITE || command == S2_BROADCAST || command == S2_MULTICAST) {
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
struct asyncreq *ars2p = dev->s2p;
|
|
while (ars2p) {
|
|
if (ars2p->request == request) {
|
|
*len = ars2p->s2p->len;
|
|
memcpy (d, ars2p->s2p->data, *len);
|
|
if (log_net)
|
|
write_log (_T("->DST:%02X.%02X.%02X.%02X.%02X.%02X SRC:%02X.%02X.%02X.%02X.%02X.%02X E=%04X S=%d\n"),
|
|
d[0], d[1], d[2], d[3], d[4], d[5],
|
|
d[6], d[7], d[8], d[9], d[10], d[11],
|
|
packettype, *len);
|
|
gotit = 1;
|
|
dev->packetssent++;
|
|
signalasync (dev, ar, *len, 0);
|
|
break;
|
|
}
|
|
ars2p = ars2p->next;
|
|
}
|
|
}
|
|
}
|
|
ar = ar->next;
|
|
}
|
|
uae_sem_post (&async_sem);
|
|
return gotit;
|
|
}
|
|
|
|
void checkevents (struct s2devstruct *dev, int mask, int sem)
|
|
{
|
|
struct asyncreq *ar;
|
|
|
|
if (sem)
|
|
uae_sem_wait (&async_sem);
|
|
ar = dev->ar;
|
|
while (ar) {
|
|
if (!ar->ready) {
|
|
uaecptr request = ar->request;
|
|
int command = get_word (request + 28);
|
|
uae_u32 cmask = get_long (request + 32);
|
|
if (command == S2_ONEVENT && (mask & cmask))
|
|
signalasync (dev, ar, 0, 0);
|
|
}
|
|
ar = ar->next;
|
|
}
|
|
if (sem)
|
|
uae_sem_post (&async_sem);
|
|
}
|
|
|
|
static int checksize (uaecptr request, struct s2devstruct *dev)
|
|
{
|
|
uae_u32 datalength = get_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2);
|
|
|
|
if (datalength > dev->td->mtu)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static void flush (struct priv_s2devstruct *pdev)
|
|
{
|
|
struct asyncreq *ar;
|
|
struct s2devstruct *dev;
|
|
|
|
dev = gets2devstruct (pdev->unit);
|
|
ar = dev->ar;
|
|
while (ar) {
|
|
if (!ar->ready && getps2devstruct (ar->request) == pdev) {
|
|
ar->ready = 1;
|
|
do_abort_async (dev, ar->request);
|
|
}
|
|
ar = ar->next;
|
|
}
|
|
}
|
|
|
|
static int dev_do_io_2 (struct s2devstruct *dev, uaecptr request, int quick)
|
|
{
|
|
uae_u8 flags = get_byte (request + 30);
|
|
uae_u32 command = get_word (request + 28);
|
|
uae_u32 packettype = get_long (request + 32 + 4);
|
|
uaecptr data = get_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2 + 4);
|
|
uae_u32 datalength = get_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2);
|
|
uaecptr srcaddr = request + 32 + 4 + 4;
|
|
uaecptr dstaddr = request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES;
|
|
uaecptr statdata = get_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2 + 4 + 4);
|
|
uaecptr buffermgmt = get_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2 + 4 + 4 + 4);
|
|
uae_u32 io_error = 0;
|
|
uae_u32 wire_error = 0;
|
|
int i;
|
|
int async = 0;
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
|
|
if (log_net)
|
|
write_log (_T("S2: C=%02d T=%04X S=%02X%02X%02X%02X%02X%02X D=%02X%02X%02X%02X%02X%02X L=%d D=%08X SD=%08X BM=%08X\n"),
|
|
command, packettype,
|
|
get_byte (srcaddr + 0), get_byte (srcaddr + 1), get_byte (srcaddr + 2), get_byte (srcaddr + 3), get_byte (srcaddr + 4), get_byte (srcaddr + 5),
|
|
get_byte (dstaddr + 0), get_byte (dstaddr + 1), get_byte (dstaddr + 2), get_byte (dstaddr + 3), get_byte (dstaddr + 4), get_byte (dstaddr + 5),
|
|
datalength, data, statdata, buffermgmt);
|
|
|
|
if (command == CMD_READ || command == S2_READORPHAN || command == CMD_WRITE || command == S2_BROADCAST || command == S2_MULTICAST) {
|
|
if (!pdev->copyfrombuff || !pdev->copytobuff) {
|
|
io_error = S2ERR_BAD_ARGUMENT;
|
|
wire_error = S2WERR_BUFF_ERROR;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
switch (command)
|
|
{
|
|
case CMD_READ:
|
|
if (!dev->online)
|
|
goto offline;
|
|
async = 1;
|
|
break;
|
|
|
|
case S2_READORPHAN:
|
|
if (!dev->online)
|
|
goto offline;
|
|
async = 1;
|
|
break;
|
|
|
|
case S2_BROADCAST:
|
|
case CMD_WRITE:
|
|
if (!dev->online)
|
|
goto offline;
|
|
if (!checksize (request, dev))
|
|
goto toobig;
|
|
async = 1;
|
|
break;
|
|
|
|
case S2_MULTICAST:
|
|
if (!dev->online)
|
|
goto offline;
|
|
if ((get_byte (dstaddr + 0) & 1) == 0) {
|
|
io_error = S2ERR_BAD_ADDRESS;
|
|
wire_error = S2WERR_BAD_MULTICAST;
|
|
goto end;
|
|
}
|
|
if (!checksize (request, dev))
|
|
goto toobig;
|
|
async = 1;
|
|
break;
|
|
|
|
case CMD_FLUSH:
|
|
dev->flush_timeout_cnt = 0;
|
|
dev->flush_timeout = FLUSH_TIMEOUT;
|
|
if (log_net)
|
|
write_log (_T("CMD_FLUSH started %08x\n"), request);
|
|
uae_sem_wait (&async_sem);
|
|
flush (pdev);
|
|
uae_sem_post (&async_sem);
|
|
async = 1;
|
|
uaenet_vsync_requested++;
|
|
break;
|
|
|
|
case S2_ADDMULTICASTADDRESS:
|
|
addmulticastaddresses (dev, amigaaddrto64 (srcaddr), 0);
|
|
break;
|
|
case S2_DELMULTICASTADDRESS:
|
|
if (!delmulticastaddresses (dev, amigaaddrto64 (srcaddr), 0)) {
|
|
io_error = S2ERR_BAD_STATE;
|
|
wire_error = S2WERR_BAD_MULTICAST;
|
|
}
|
|
break;
|
|
case S2_ADDMULTICASTADDRESSES:
|
|
addmulticastaddresses (dev, amigaaddrto64 (srcaddr), amigaaddrto64 (dstaddr));
|
|
break;
|
|
case S2_DELMULTICASTADDRESSES:
|
|
if (!delmulticastaddresses (dev, amigaaddrto64 (srcaddr), amigaaddrto64 (dstaddr))) {
|
|
io_error = S2ERR_BAD_STATE;
|
|
wire_error = S2WERR_BAD_MULTICAST;
|
|
}
|
|
break;
|
|
|
|
case S2_DEVICEQUERY:
|
|
{
|
|
int size = get_long (statdata);
|
|
if (size > 30)
|
|
size = 30;
|
|
put_long (statdata + 4, size);
|
|
if (size >= 12)
|
|
put_long (statdata + 8, 0);
|
|
if (size >= 16)
|
|
put_long (statdata + 12, 0);
|
|
if (size >= 18)
|
|
put_word (statdata + 16, ADDR_SIZE * 8);
|
|
if (size >= 22)
|
|
put_long (statdata + 18, dev->td->mtu);
|
|
if (size >= 26)
|
|
put_long (statdata + 22, 10000000);
|
|
if (size >= 30)
|
|
put_long (statdata + 26, S2WireType_Ethernet);
|
|
}
|
|
break;
|
|
|
|
case S2_GETTYPESTATS:
|
|
if (pdev->trackcnt) {
|
|
put_long (statdata + 0, pdev->packetssent);
|
|
put_long (statdata + 4, pdev->packetsreceived);
|
|
put_long (statdata + 8, pdev->bytessent);
|
|
put_long (statdata + 12, pdev->bytesreceived);
|
|
put_long (statdata + 16, pdev->packetsdropped);
|
|
} else {
|
|
io_error = S2ERR_BAD_STATE;
|
|
wire_error = S2WERR_NOT_TRACKED;
|
|
}
|
|
break;
|
|
|
|
case S2_GETGLOBALSTATS:
|
|
put_long (statdata + 0, dev->packetsreceived);
|
|
put_long (statdata + 4, dev->packetssent);
|
|
put_long (statdata + 8, dev->baddata);
|
|
put_long (statdata + 12, dev->overruns);
|
|
put_long (statdata + 16, 0);
|
|
put_long (statdata + 20, dev->unknowntypesreceived);
|
|
put_long (statdata + 24, dev->reconfigurations);
|
|
put_long (statdata + 28, dev->online_secs);
|
|
put_long (statdata + 32, dev->online_micro);
|
|
break;
|
|
|
|
case S2_GETSPECIALSTATS:
|
|
put_long (statdata + 1, 0);
|
|
break;
|
|
|
|
case S2_GETSTATIONADDRESS:
|
|
for (i = 0; i < ADDR_SIZE; i++) {
|
|
put_byte (srcaddr + i, dev->td->mac[i]);
|
|
put_byte (dstaddr + i, dev->td->mac[i]);
|
|
}
|
|
break;
|
|
|
|
case S2_CONFIGINTERFACE:
|
|
if (dev->configured) {
|
|
io_error = S2ERR_BAD_STATE;
|
|
wire_error = S2WERR_IS_CONFIGURED;
|
|
} else {
|
|
dev->configured = TRUE;
|
|
}
|
|
break;
|
|
|
|
case S2_ONLINE:
|
|
if (!dev->configured) {
|
|
io_error = S2ERR_BAD_STATE;
|
|
wire_error = S2WERR_NOT_CONFIGURED;
|
|
}
|
|
if (!dev->adapter) {
|
|
io_error = S2ERR_OUTOFSERVICE;
|
|
wire_error = S2WERR_RCVREL_HDW_ERR;
|
|
}
|
|
if (!io_error) {
|
|
uaenet_vsync_requested++;
|
|
async = 1;
|
|
}
|
|
break;
|
|
|
|
case S2_TRACKTYPE:
|
|
if (packettype <= 65535) {
|
|
if (pdev->tracks[packettype]) {
|
|
io_error = S2ERR_BAD_STATE;
|
|
wire_error = S2WERR_ALREADY_TRACKED;
|
|
} else {
|
|
pdev->tracks[packettype] = 1;
|
|
pdev->trackcnt++;
|
|
}
|
|
} else {
|
|
io_error = S2ERR_BAD_ARGUMENT;
|
|
}
|
|
break;
|
|
case S2_UNTRACKTYPE:
|
|
if (packettype <= 65535) {
|
|
if (!pdev->tracks[packettype]) {
|
|
io_error = S2ERR_BAD_STATE;
|
|
wire_error = S2WERR_NOT_TRACKED;
|
|
} else {
|
|
pdev->tracks[packettype] = 0;
|
|
pdev->trackcnt--;
|
|
}
|
|
} else {
|
|
io_error = S2ERR_BAD_ARGUMENT;
|
|
}
|
|
break;
|
|
|
|
case S2_OFFLINE:
|
|
if (dev->online) {
|
|
dev->online = 0;
|
|
checkevents (dev, S2EVENT_OFFLINE, 1);
|
|
}
|
|
break;
|
|
|
|
case S2_ONEVENT:
|
|
{
|
|
uae_u32 events;
|
|
uae_u32 wanted_events = get_long (request + 32);
|
|
if (wanted_events & ~KNOWN_EVENTS) {
|
|
io_error = S2ERR_NOT_SUPPORTED;
|
|
events = S2WERR_BAD_EVENT;
|
|
} else {
|
|
if (dev->online)
|
|
events = S2EVENT_ONLINE;
|
|
else
|
|
events = S2EVENT_OFFLINE;
|
|
events &= wanted_events;
|
|
if (events) {
|
|
wire_error = events;
|
|
} else {
|
|
async = 1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
io_error = IOERR_NOCMD;
|
|
break;
|
|
|
|
offline:
|
|
io_error = S2ERR_OUTOFSERVICE;
|
|
wire_error = S2WERR_UNIT_OFFLINE;
|
|
break;
|
|
toobig:
|
|
io_error = S2ERR_MTU_EXCEEDED;
|
|
wire_error = S2WERR_GENERIC_ERROR;
|
|
break;
|
|
|
|
}
|
|
end:
|
|
if (log_net && (io_error || wire_error))
|
|
write_log (_T("-> %d (%d)\n"), io_error, wire_error);
|
|
put_long (request + 32, wire_error);
|
|
put_byte (request + 31, io_error);
|
|
return async;
|
|
}
|
|
|
|
static int dev_do_io (struct s2devstruct *dev, uaecptr request, int quick)
|
|
{
|
|
uae_u32 command = get_word (request + 28);
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
uaecptr data = get_long (request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES * 2 + 4);
|
|
|
|
put_byte (request + 31, 0);
|
|
if (!pdev) {
|
|
write_log (_T("%s unknown iorequest %08x\n"), getdevname (), request);
|
|
return 0;
|
|
}
|
|
if (command == NSCMD_DEVICEQUERY) {
|
|
uae_u32 data = get_long (request + 40); /* io_data */
|
|
put_long (data + 0, 0);
|
|
put_long (data + 4, 16); /* size */
|
|
put_word (data + 8, 7); /* NSDEVTYPE_SANA2 */
|
|
put_word (data + 10, 0);
|
|
put_long (data + 12, nscmd_cmd);
|
|
put_long (request + 32, 16); /* io_actual */
|
|
return 0;
|
|
} else if (get_word (request + 0x12) < SANA2_IOREQSIZE) {
|
|
put_byte (request + 31, IOERR_BADLENGTH);
|
|
return 0;
|
|
}
|
|
return dev_do_io_2 (dev, request, quick);
|
|
}
|
|
|
|
static int dev_can_quick (uae_u32 command)
|
|
{
|
|
switch (command)
|
|
{
|
|
case NSCMD_DEVICEQUERY:
|
|
case S2_DEVICEQUERY:
|
|
case S2_CONFIGINTERFACE:
|
|
case S2_GETSTATIONADDRESS:
|
|
case S2_TRACKTYPE:
|
|
case S2_UNTRACKTYPE:
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int dev_canquick (struct s2devstruct *dev, uaecptr request)
|
|
{
|
|
uae_u32 command = get_word (request + 28);
|
|
return dev_can_quick (command);
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dev_beginio (TrapContext *context)
|
|
{
|
|
uae_u32 request = m68k_areg (regs, 1);
|
|
uae_u8 flags = get_byte (request + 30);
|
|
int command = get_word (request + 28);
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
struct s2devstruct *dev;
|
|
|
|
put_byte (request + 8, NT_MESSAGE);
|
|
if (!pdev) {
|
|
write_log (_T("%s unknown iorequest (1) %08x\n"), getdevname (), request);
|
|
put_byte (request + 31, 32);
|
|
return get_byte (request + 31);
|
|
}
|
|
dev = gets2devstruct (pdev->unit);
|
|
if (!dev) {
|
|
write_log (_T("%s unknown iorequest (2) %08x\n"), getdevname (), request);
|
|
put_byte (request + 31, 32);
|
|
return get_byte (request + 31);
|
|
}
|
|
put_byte (request + 31, 0);
|
|
if ((flags & 1) && dev_canquick (dev, request)) {
|
|
if (dev_do_io (dev, request, 1))
|
|
write_log (_T("%s: command %d bug with IO_QUICK\n"), SANA2NAME, command);
|
|
return get_byte (request + 31);
|
|
} else {
|
|
if (command == CMD_WRITE || command == S2_BROADCAST || command == S2_MULTICAST) {
|
|
struct s2packet *s2p;
|
|
if (!pdev->copyfrombuff || !pdev->copytobuff) {
|
|
put_long (request + 32, S2ERR_BAD_ARGUMENT);
|
|
put_byte (request + 31, S2WERR_BUFF_ERROR);
|
|
} else {
|
|
if (command == S2_BROADCAST) {
|
|
uaecptr dstaddr = request + 32 + 4 + 4 + SANA2_MAX_ADDR_BYTES;
|
|
put_byte (dstaddr + 0, 0xff);
|
|
put_byte (dstaddr + 1, 0xff);
|
|
put_byte (dstaddr + 2, 0xff);
|
|
put_byte (dstaddr + 3, 0xff);
|
|
put_byte (dstaddr + 4, 0xff);
|
|
put_byte (dstaddr + 5, 0xff);
|
|
}
|
|
s2p = createwritepacket (context, request);
|
|
if (s2p) {
|
|
uae_sem_wait (&async_sem);
|
|
add_async_packet (dev, s2p, request);
|
|
uae_sem_post (&async_sem);
|
|
}
|
|
if (!s2p) {
|
|
put_long (request + 32, S2WERR_BUFF_ERROR);
|
|
put_byte (request + 31, S2ERR_NO_RESOURCES);
|
|
}
|
|
}
|
|
}
|
|
put_byte (request + 30, get_byte (request + 30) & ~1);
|
|
write_comm_pipe_u32 (&dev->requests, request, 1);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void *dev_thread (void *devs)
|
|
{
|
|
struct s2devstruct *dev = (struct s2devstruct*)devs;
|
|
|
|
uae_set_thread_priority (NULL, 1);
|
|
dev->thread_running = 1;
|
|
uae_sem_post (&dev->sync_sem);
|
|
for (;;) {
|
|
uaecptr request = (uaecptr)read_comm_pipe_u32_blocking (&dev->requests);
|
|
uae_sem_wait (&change_sem);
|
|
if (!request) {
|
|
dev->thread_running = 0;
|
|
uae_sem_post (&dev->sync_sem);
|
|
uae_sem_post (&change_sem);
|
|
write_log (_T("%s: dev_thread killed\n"), getdevname ());
|
|
return 0;
|
|
} else if (get_async_request (dev, request, 1)) {
|
|
uae_ReplyMsg (request);
|
|
release_async_request (dev, request);
|
|
rem_async_packet (dev, request);
|
|
} else if (dev_do_io (dev, request, 0) == 0) {
|
|
uae_ReplyMsg (request);
|
|
rem_async_packet (dev, request);
|
|
} else {
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
add_async_request (dev, request);
|
|
ethernet_trigger (pdev->td, dev->sysdata);
|
|
}
|
|
uae_sem_post (&change_sem);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dev_init_2 (TrapContext *context)
|
|
{
|
|
uae_u32 base = m68k_dreg (regs,0);
|
|
if (log_net)
|
|
write_log (_T("%s init\n"), SANA2NAME);
|
|
return base;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dev_init (TrapContext *context)
|
|
{
|
|
return dev_init_2 (context);
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 dev_abortio (TrapContext *context)
|
|
{
|
|
uae_u32 request = m68k_areg (regs, 1);
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
struct s2devstruct *dev;
|
|
|
|
if (!pdev) {
|
|
write_log (_T("%s abortio but no request %08x found!\n"), getdevname(), request);
|
|
put_byte (request + 31, 32);
|
|
return get_byte (request + 31);
|
|
}
|
|
dev = gets2devstruct (pdev->unit);
|
|
if (!dev) {
|
|
write_log (_T("%s (%d) abortio but no request %08x found!\n"), getdevname(), pdev->unit, request);
|
|
put_byte (request + 31, 32);
|
|
return get_byte (request + 31);
|
|
}
|
|
if (log_net)
|
|
write_log (_T("%s:%d abortio %08x\n"), getdevname(), dev->unit, request);
|
|
abort_async (dev, request);
|
|
return 0;
|
|
}
|
|
|
|
static uae_u32 REGPARAM2 uaenet_int_handler (TrapContext *ctx)
|
|
{
|
|
int i, j;
|
|
int gotit;
|
|
struct asyncreq *ar;
|
|
|
|
if (uae_sem_trywait (&async_sem)) {
|
|
uaenet_int_requested = 0;
|
|
uaenet_int_late = 1;
|
|
return 0;
|
|
}
|
|
for (i = 0; i < MAX_OPEN_DEVICES; i++)
|
|
pdevst[i].tmp = 0;
|
|
|
|
for (i = 0; i < MAX_TOTAL_NET_DEVICES; i++) {
|
|
struct s2devstruct *dev = &devst[i];
|
|
struct s2packet *p;
|
|
if (dev->online) {
|
|
while (dev->readqueue) {
|
|
uae_u16 type;
|
|
p = dev->readqueue;
|
|
type = (p->data[2 * ADDR_SIZE] << 8) | p->data[2 * ADDR_SIZE + 1];
|
|
ar = dev->ar;
|
|
gotit = 0;
|
|
while (ar) {
|
|
if (!ar->ready) {
|
|
uaecptr request = ar->request;
|
|
int command = get_word (request + 28);
|
|
uae_u32 packettype = get_long (request + 32 + 4);
|
|
if (command == CMD_READ && (packettype == type || (packettype <= 1500 && type <= 1500))) {
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
if (pdev && pdev->tmp == 0) {
|
|
if (handleread (ctx, pdev, request, p->data, p->len, command)) {
|
|
if (log_net)
|
|
write_log (_T("-> %p Accepted, CMD_READ, REQ=%08X LEN=%d\n"), p, request, p->len);
|
|
ar->ready = 1;
|
|
write_comm_pipe_u32 (&dev->requests, request, 1);
|
|
dev->packetsreceived++;
|
|
gotit = 1;
|
|
pdev->tmp = 1;
|
|
} else {
|
|
if (log_net)
|
|
write_log (_T("-> %p PacketFilter() rejected, CMD_READ, REQ=%08X LEN=%d\n"), p, request, p->len);
|
|
pdev->tmp = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ar = ar->next;
|
|
}
|
|
ar = dev->ar;
|
|
while (ar) {
|
|
if (!ar->ready) {
|
|
uaecptr request = ar->request;
|
|
int command = get_word (request + 28);
|
|
if (command == S2_READORPHAN) {
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
if (pdev && pdev->tmp <= 0) {
|
|
if (log_net)
|
|
write_log (_T("-> %p Accepted, S2_READORPHAN, REQ=%08X LEN=%d\n"), p, request, p->len);
|
|
handleread (ctx, pdev, request, p->data, p->len, command);
|
|
ar->ready = 1;
|
|
write_comm_pipe_u32 (&dev->requests, request, 1);
|
|
dev->packetsreceived++;
|
|
dev->unknowntypesreceived++;
|
|
gotit = 1;
|
|
pdev->tmp = 1;
|
|
}
|
|
}
|
|
}
|
|
ar = ar->next;
|
|
}
|
|
if (!gotit) {
|
|
if (log_net)
|
|
write_log (_T("-> %p packet dropped, LEN=%d\n"), p, p->len);
|
|
for (j = 0; j < MAX_OPEN_DEVICES; j++) {
|
|
if (pdevst[j].unit == dev->unit) {
|
|
if (pdevst[j].tracks[type])
|
|
pdevst[j].packetsdropped++;
|
|
}
|
|
}
|
|
}
|
|
dev->readqueue = dev->readqueue->next;
|
|
freepacket (p);
|
|
}
|
|
} else {
|
|
while (dev->readqueue) {
|
|
p = dev->readqueue;
|
|
dev->readqueue = dev->readqueue->next;
|
|
freepacket (p);
|
|
}
|
|
}
|
|
|
|
ar = dev->ar;
|
|
while (ar) {
|
|
if (!ar->ready) {
|
|
uaecptr request = ar->request;
|
|
int command = get_word (request + 28);
|
|
if (command == S2_ONLINE) {
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
dev->packetsreceived = 0;
|
|
dev->packetssent = 0;
|
|
dev->baddata = 0;
|
|
dev->overruns = 0;
|
|
dev->unknowntypesreceived = 0;
|
|
dev->reconfigurations = 0;
|
|
if (pdev && pdev->timerbase) {
|
|
m68k_areg (regs, 0) = pdev->tempbuf;
|
|
CallLib (ctx, pdev->timerbase, -0x42); /* GetSysTime() */
|
|
} else {
|
|
put_long (pdev->tempbuf + 0, 0);
|
|
put_long (pdev->tempbuf + 4, 0);
|
|
}
|
|
dev->online_secs = get_long (pdev->tempbuf + 0);
|
|
dev->online_micro = get_long (pdev->tempbuf + 4);
|
|
checkevents (dev, S2EVENT_ONLINE, 0);
|
|
dev->online = 1;
|
|
ar->ready = 1;
|
|
write_comm_pipe_u32 (&dev->requests, request, 1);
|
|
uaenet_vsync_requested--;
|
|
} else if (command == CMD_FLUSH) {
|
|
/* do not reply CMD_FLUSH until all other requests are gone */
|
|
if (dev->ar->next == NULL) {
|
|
if (log_net)
|
|
write_log (_T("CMD_FLUSH replied %08x\n"), request);
|
|
ar->ready = 1;
|
|
write_comm_pipe_u32 (&dev->requests, request, 1);
|
|
uaenet_vsync_requested--;
|
|
} else {
|
|
struct priv_s2devstruct *pdev = getps2devstruct (request);
|
|
if (pdev) {
|
|
dev->flush_timeout--;
|
|
if (dev->flush_timeout <= 0) {
|
|
dev->flush_timeout = FLUSH_TIMEOUT;
|
|
if (dev->flush_timeout_cnt > 1)
|
|
write_log (_T("WARNING: %s:%d CMD_FLUSH possibly frozen..\n"), getdevname(), pdev->unit);
|
|
dev->flush_timeout_cnt++;
|
|
flush (pdev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ar = ar->next;
|
|
}
|
|
}
|
|
if (uaenet_int_late)
|
|
uaenet_int_requested = 1;
|
|
else
|
|
uaenet_int_requested = 0;
|
|
uaenet_int_late = 0;
|
|
uae_sem_post (&async_sem);
|
|
return 0;
|
|
}
|
|
|
|
static void dev_reset (void)
|
|
{
|
|
int i;
|
|
struct s2devstruct *dev;
|
|
int unitnum = 0;
|
|
|
|
write_log (_T("%s reset\n"), getdevname());
|
|
for (i = 0; i < MAX_TOTAL_NET_DEVICES; i++) {
|
|
dev = &devst[i];
|
|
if (dev->opencnt) {
|
|
struct asyncreq *ar = dev->ar;
|
|
while (ar) {
|
|
if (!ar->ready) {
|
|
dev->ar->ready = 1;
|
|
do_abort_async (dev, ar->request);
|
|
}
|
|
ar = ar->next;
|
|
}
|
|
write_comm_pipe_u32 (&dev->requests, 0, 1);
|
|
uae_sem_wait (&dev->sync_sem);
|
|
}
|
|
while (dev->mc)
|
|
delmulticastaddresses (dev, dev->mc->start, dev->mc->end);
|
|
memset (dev, 0, sizeof (struct s2devstruct));
|
|
}
|
|
for (i = 0; i < MAX_OPEN_DEVICES; i++)
|
|
memset (&pdevst[i], 0, sizeof (struct priv_s2devstruct));
|
|
uaenet_vsync_requested = 0;
|
|
uaenet_int_requested = 0;
|
|
irq_init = 0;
|
|
|
|
}
|
|
|
|
uaecptr netdev_startup (uaecptr resaddr)
|
|
{
|
|
if (!currprefs.sana2)
|
|
return resaddr;
|
|
if (log_net)
|
|
write_log (_T("netdev_startup(0x%x)\n"), resaddr);
|
|
/* Build a struct Resident. This will set up and initialize
|
|
* the uaenet.device */
|
|
put_word (resaddr + 0x0, 0x4AFC);
|
|
put_long (resaddr + 0x2, resaddr);
|
|
put_long (resaddr + 0x6, resaddr + 0x1A); /* Continue scan here */
|
|
put_word (resaddr + 0xA, 0x8101); /* RTF_AUTOINIT|RTF_COLDSTART; Version 1 */
|
|
put_word (resaddr + 0xC, 0x0305); /* NT_DEVICE; pri 05 */
|
|
put_long (resaddr + 0xE, ROM_netdev_resname);
|
|
put_long (resaddr + 0x12, ROM_netdev_resid);
|
|
put_long (resaddr + 0x16, ROM_netdev_init); /* calls netdev_init */
|
|
resaddr += 0x1A;
|
|
return resaddr;
|
|
}
|
|
|
|
void netdev_install (void)
|
|
{
|
|
uae_u32 functable, datatable;
|
|
uae_u32 initcode, openfunc, closefunc, expungefunc;
|
|
uae_u32 beginiofunc, abortiofunc;
|
|
|
|
if (!currprefs.sana2)
|
|
return;
|
|
if (log_net)
|
|
write_log (_T("netdev_install(): 0x%x\n"), here ());
|
|
|
|
ethernet_enumerate_free ();
|
|
ethernet_enumerate (td, NULL);
|
|
|
|
ROM_netdev_resname = ds (getdevname());
|
|
ROM_netdev_resid = ds (_T("UAE net.device 0.2"));
|
|
timerdevname = ds (_T("timer.device"));
|
|
|
|
/* initcode */
|
|
initcode = here ();
|
|
calltrap (deftrap2 (dev_init, TRAPFLAG_EXTRA_STACK, _T("uaenet.init"))); dw (RTS);
|
|
|
|
/* Open */
|
|
openfunc = here ();
|
|
calltrap (deftrap2 (dev_open, TRAPFLAG_EXTRA_STACK, _T("uaenet.open"))); dw (RTS);
|
|
|
|
/* Close */
|
|
closefunc = here ();
|
|
calltrap (deftrap2 (dev_close, TRAPFLAG_EXTRA_STACK, _T("uaenet.close"))); dw (RTS);
|
|
|
|
/* Expunge */
|
|
expungefunc = here ();
|
|
calltrap (deftrap2 (dev_expunge, TRAPFLAG_EXTRA_STACK, _T("uaenet.expunge"))); dw (RTS);
|
|
|
|
/* BeginIO */
|
|
beginiofunc = here ();
|
|
calltrap (deftrap2 (dev_beginio, TRAPFLAG_EXTRA_STACK, _T("uaenet.beginio"))); dw (RTS);
|
|
|
|
/* AbortIO */
|
|
abortiofunc = here ();
|
|
calltrap (deftrap2 (dev_abortio, TRAPFLAG_EXTRA_STACK, _T("uaenet.abortio"))); dw (RTS);
|
|
|
|
/* FuncTable */
|
|
functable = here ();
|
|
dl (openfunc); /* Open */
|
|
dl (closefunc); /* Close */
|
|
dl (expungefunc); /* Expunge */
|
|
dl (EXPANSION_nullfunc); /* Null */
|
|
dl (beginiofunc); /* BeginIO */
|
|
dl (abortiofunc); /* AbortIO */
|
|
dl (0xFFFFFFFFul); /* end of table */
|
|
|
|
/* DataTable */
|
|
datatable = here ();
|
|
dw (0xE000); /* INITBYTE */
|
|
dw (0x0008); /* LN_TYPE */
|
|
dw (0x0300); /* NT_DEVICE */
|
|
dw (0xC000); /* INITLONG */
|
|
dw (0x000A); /* LN_NAME */
|
|
dl (ROM_netdev_resname);
|
|
dw (0xE000); /* INITBYTE */
|
|
dw (0x000E); /* LIB_FLAGS */
|
|
dw (0x0600); /* LIBF_SUMUSED | LIBF_CHANGED */
|
|
dw (0xD000); /* INITWORD */
|
|
dw (0x0014); /* LIB_VERSION */
|
|
dw (0x0004); /* 0.4 */
|
|
dw (0xD000); /* INITWORD */
|
|
dw (0x0016); /* LIB_REVISION */
|
|
dw (0x0000); /* end of table already ??? */
|
|
dw (0xC000); /* INITLONG */
|
|
dw (0x0018); /* LIB_IDSTRING */
|
|
dl (ROM_netdev_resid);
|
|
dw (0x0000); /* end of table */
|
|
|
|
ROM_netdev_init = here ();
|
|
dl (0x00000100); /* size of device base */
|
|
dl (functable);
|
|
dl (datatable);
|
|
dl (initcode);
|
|
|
|
nscmd_cmd = here ();
|
|
dw (CMD_READ);
|
|
dw (CMD_WRITE);
|
|
dw (CMD_FLUSH);
|
|
dw (S2_DEVICEQUERY);
|
|
dw (S2_GETSTATIONADDRESS);
|
|
dw (S2_CONFIGINTERFACE);
|
|
dw (S2_ADDMULTICASTADDRESS);
|
|
dw (S2_DELMULTICASTADDRESS);
|
|
dw (S2_MULTICAST);
|
|
dw (S2_BROADCAST);
|
|
dw (S2_TRACKTYPE);
|
|
dw (S2_UNTRACKTYPE);
|
|
dw (S2_GETTYPESTATS);
|
|
dw (S2_GETSPECIALSTATS);
|
|
dw (S2_GETGLOBALSTATS);
|
|
dw (S2_ONEVENT);
|
|
dw (S2_READORPHAN);
|
|
dw (S2_ONLINE);
|
|
dw (S2_OFFLINE);
|
|
dw (S2_ADDMULTICASTADDRESSES);
|
|
dw (S2_DELMULTICASTADDRESSES);
|
|
dw (NSCMD_DEVICEQUERY);
|
|
dw (0);
|
|
|
|
}
|
|
|
|
void netdev_start_threads (void)
|
|
{
|
|
if (!currprefs.sana2)
|
|
return;
|
|
if (log_net)
|
|
write_log (_T("netdev_start_threads()\n"));
|
|
uae_sem_init (&change_sem, 0, 1);
|
|
uae_sem_init (&async_sem, 0, 1);
|
|
}
|
|
|
|
void netdev_reset (void)
|
|
{
|
|
if (!currprefs.sana2)
|
|
return;
|
|
dev_reset ();
|
|
}
|