mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
558 lines
14 KiB
C++
558 lines
14 KiB
C++
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* Win32 uaenet emulation
|
|
*
|
|
* Copyright 2007 Toni Wilen
|
|
*/
|
|
|
|
#include <winsock2.h>
|
|
#include <Ws2tcpip.h>
|
|
#include <Iphlpapi.h>
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#define HAVE_REMOTE
|
|
#define WPCAP
|
|
#define PCAP_DONT_INCLUDE_PCAP_BPF_H
|
|
#include "pcap.h"
|
|
#include "packet32.h"
|
|
#include "pcap/dlt.h"
|
|
|
|
#include "ntddndis.h"
|
|
|
|
#include "options.h"
|
|
#include "traps.h"
|
|
#include "sana2.h"
|
|
|
|
#include "threaddep/thread.h"
|
|
#include "win32_uaenet.h"
|
|
#include "win32.h"
|
|
|
|
int log_ethernet;
|
|
static struct netdriverdata tds[MAX_TOTAL_NET_DEVICES];
|
|
static int enumerated;
|
|
static int ethernet_paused;
|
|
|
|
typedef int(_cdecl *PCAP_FINDALLDEVS_EX)(char *source, struct pcap_rmtauth *auth, pcap_if_t **alldevs, char *errbuf);
|
|
static PCAP_FINDALLDEVS_EX ppcap_findalldevs_ex;
|
|
typedef void(_cdecl *PCAP_FREEALLDEVS)(pcap_if_t *);
|
|
static PCAP_FREEALLDEVS ppcap_freealldevs;
|
|
typedef pcap_t *(_cdecl *PCAP_OPEN)(const char *source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth *auth, char *errbuf);
|
|
static PCAP_OPEN ppcap_open;
|
|
typedef void (_cdecl *PCAP_CLOSE)(pcap_t *);
|
|
static PCAP_CLOSE ppcap_close;
|
|
typedef int (_cdecl *PCAP_DATALINK)(pcap_t *);
|
|
static PCAP_DATALINK ppcap_datalink;
|
|
typedef int (_cdecl *PCAP_SENDPACKET)(pcap_t *, const u_char *, int);
|
|
static PCAP_SENDPACKET ppcap_sendpacket;
|
|
typedef int(_cdecl *PCAP_NEXT_EX)(pcap_t *, struct pcap_pkthdr **, const u_char **);
|
|
static PCAP_NEXT_EX ppcap_next_ex;
|
|
typedef const char *(_cdecl *PCAP_LIB_VERSION)(void);
|
|
static PCAP_LIB_VERSION ppcap_lib_version;
|
|
|
|
typedef LPADAPTER(_cdecl *PACKETOPENADAPTER)(PCHAR AdapterName);
|
|
static PACKETOPENADAPTER pPacketOpenAdapter;
|
|
typedef VOID(_cdecl *PACKETCLOSEADAPTER)(LPADAPTER lpAdapter);
|
|
static PACKETCLOSEADAPTER pPacketCloseAdapter;
|
|
typedef BOOLEAN (_cdecl *PACKETREQUEST)(LPADAPTER AdapterObject, BOOLEAN Set, PPACKET_OID_DATA OidData);
|
|
static PACKETREQUEST pPacketRequest;
|
|
|
|
static HMODULE wpcap, packet;
|
|
|
|
struct uaenetdatawin32
|
|
{
|
|
HANDLE evttw;
|
|
void *readdata, *writedata;
|
|
|
|
uae_sem_t change_sem;
|
|
|
|
volatile int threadactiver;
|
|
uae_thread_id tidr;
|
|
uae_sem_t sync_semr;
|
|
volatile int threadactivew;
|
|
uae_thread_id tidw;
|
|
uae_sem_t sync_semw;
|
|
|
|
void *user;
|
|
struct netdriverdata *tc;
|
|
uae_u8 *readbuffer;
|
|
uae_u8 *writebuffer;
|
|
int mtu;
|
|
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
pcap_t *fp;
|
|
uaenet_gotfunc *gotfunc;
|
|
uaenet_getfunc *getfunc;
|
|
};
|
|
|
|
int uaenet_getdatalenght (void)
|
|
{
|
|
return sizeof (struct uaenetdatawin32);
|
|
}
|
|
|
|
static void uaeser_initdata (struct uaenetdatawin32 *sd, void *user)
|
|
{
|
|
memset (sd, 0, sizeof (struct uaenetdatawin32));
|
|
sd->evttw = 0;
|
|
sd->user = user;
|
|
sd->fp = NULL;
|
|
}
|
|
|
|
#if 0
|
|
static void *uaenet_trap_thread (void *arg)
|
|
{
|
|
struct uaenetdatawin32 *sd = arg;
|
|
HANDLE handles[4];
|
|
int cnt, towrite;
|
|
int readactive, writeactive;
|
|
DWORD actual;
|
|
|
|
uae_set_thread_priority (NULL, 2);
|
|
sd->threadactive = 1;
|
|
uae_sem_post (&sd->sync_sem);
|
|
readactive = 0;
|
|
writeactive = 0;
|
|
while (sd->threadactive == 1) {
|
|
int donotwait = 0;
|
|
|
|
uae_sem_wait (&sd->change_sem);
|
|
|
|
if (readactive) {
|
|
if (GetOverlappedResult (sd->hCom, &sd->olr, &actual, FALSE)) {
|
|
readactive = 0;
|
|
uaenet_gotdata (sd->user, sd->readbuffer, actual);
|
|
donotwait = 1;
|
|
}
|
|
}
|
|
if (writeactive) {
|
|
if (GetOverlappedResult (sd->hCom, &sd->olw, &actual, FALSE)) {
|
|
writeactive = 0;
|
|
donotwait = 1;
|
|
}
|
|
}
|
|
|
|
if (!readactive) {
|
|
if (!ReadFile (sd->hCom, sd->readbuffer, sd->mtu, &actual, &sd->olr)) {
|
|
DWORD err = GetLastError();
|
|
if (err == ERROR_IO_PENDING)
|
|
readactive = 1;
|
|
} else {
|
|
uaenet_gotdata (sd->user, sd->readbuffer, actual);
|
|
donotwait = 1;
|
|
}
|
|
}
|
|
|
|
towrite = 0;
|
|
if (!writeactive && uaenet_getdata (sd->user, sd->writebuffer, &towrite)) {
|
|
donotwait = 1;
|
|
if (!WriteFile (sd->hCom, sd->writebuffer, towrite, &actual, &sd->olw)) {
|
|
DWORD err = GetLastError();
|
|
if (err == ERROR_IO_PENDING)
|
|
writeactive = 1;
|
|
}
|
|
}
|
|
|
|
uae_sem_post (&sd->change_sem);
|
|
|
|
if (!donotwait) {
|
|
cnt = 0;
|
|
handles[cnt++] = sd->evtt;
|
|
if (readactive)
|
|
handles[cnt++] = sd->olr.hEvent;
|
|
if (writeactive)
|
|
handles[cnt++] = sd->olw.hEvent;
|
|
WaitForMultipleObjects(cnt, handles, FALSE, INFINITE);
|
|
}
|
|
|
|
|
|
}
|
|
sd->threadactive = 0;
|
|
uae_sem_post (&sd->sync_sem);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void uaenet_trap_threadr (void *arg)
|
|
{
|
|
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)arg;
|
|
struct pcap_pkthdr *header;
|
|
const u_char *pkt_data;
|
|
|
|
uae_set_thread_priority (NULL, 1);
|
|
sd->threadactiver = 1;
|
|
uae_sem_post (&sd->sync_semr);
|
|
while (sd->threadactiver == 1) {
|
|
int r;
|
|
r = ppcap_next_ex(sd->fp, &header, &pkt_data);
|
|
if (r == 1 && !ethernet_paused) {
|
|
uae_sem_wait (&sd->change_sem);
|
|
sd->gotfunc ((struct s2devstruct*)sd->user, pkt_data, header->len);
|
|
uae_sem_post (&sd->change_sem);
|
|
}
|
|
if (r < 0) {
|
|
write_log (_T("pcap_next_ex failed, err=%d\n"), r);
|
|
break;
|
|
}
|
|
}
|
|
sd->threadactiver = 0;
|
|
uae_sem_post (&sd->sync_semr);
|
|
}
|
|
|
|
static void uaenet_trap_threadw (void *arg)
|
|
{
|
|
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)arg;
|
|
|
|
uae_set_thread_priority (NULL, 1);
|
|
sd->threadactivew = 1;
|
|
uae_sem_post (&sd->sync_semw);
|
|
while (sd->threadactivew == 1) {
|
|
int donotwait = 0;
|
|
int towrite = sd->mtu;
|
|
uae_sem_wait (&sd->change_sem);
|
|
if (sd->getfunc ((struct s2devstruct*)sd->user, sd->writebuffer, &towrite)) {
|
|
if (log_ethernet & 1) {
|
|
TCHAR out[1600 * 2], *p;
|
|
p = out;
|
|
for (int i = 0; i < towrite && i < 1600; i++) {
|
|
_stprintf(p, _T("%02x"), sd->writebuffer[i]);
|
|
p += 2;
|
|
*p = 0;
|
|
}
|
|
write_log(_T("OUT %4d: %s\n"), towrite, out);
|
|
}
|
|
ppcap_sendpacket(sd->fp, sd->writebuffer, towrite);
|
|
donotwait = 1;
|
|
}
|
|
uae_sem_post (&sd->change_sem);
|
|
if (!donotwait)
|
|
WaitForSingleObject (sd->evttw, INFINITE);
|
|
}
|
|
sd->threadactivew = 0;
|
|
uae_sem_post (&sd->sync_semw);
|
|
}
|
|
|
|
void uaenet_trigger (void *vsd)
|
|
{
|
|
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)vsd;
|
|
if (!sd)
|
|
return;
|
|
SetEvent (sd->evttw);
|
|
}
|
|
|
|
// locally administered unicast MAC: U<<1, A<<1, E<<1
|
|
static const uae_u8 uaemac[] = { 0xaa, 0x82, 0x8a, 0x00, 0x00, 0x00 };
|
|
|
|
int uaenet_open (void *vsd, struct netdriverdata *tc, void *user, uaenet_gotfunc *gotfunc, uaenet_getfunc *getfunc, int promiscuous, const uae_u8 *mac)
|
|
{
|
|
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)vsd;
|
|
char *s;
|
|
|
|
s = ua (tc->name);
|
|
if (mac)
|
|
memcpy(tc->mac, mac, 6);
|
|
if (memcmp(tc->mac, tc->originalmac, 6)) {
|
|
promiscuous = 1;
|
|
}
|
|
sd->fp = ppcap_open(s, 65536, (promiscuous ? PCAP_OPENFLAG_PROMISCUOUS : 0) | PCAP_OPENFLAG_MAX_RESPONSIVENESS, 100, NULL, sd->errbuf);
|
|
xfree (s);
|
|
if (sd->fp == NULL) {
|
|
TCHAR *ss = au (sd->errbuf);
|
|
write_log (_T("'%s' failed to open: %s\n"), tc->name, ss);
|
|
xfree (ss);
|
|
return 0;
|
|
}
|
|
sd->tc = tc;
|
|
sd->user = user;
|
|
sd->evttw = CreateEvent (NULL, FALSE, FALSE, NULL);
|
|
|
|
if (!sd->evttw)
|
|
goto end;
|
|
sd->mtu = tc->mtu;
|
|
sd->readbuffer = xmalloc (uae_u8, sd->mtu);
|
|
sd->writebuffer = xmalloc (uae_u8, sd->mtu);
|
|
sd->gotfunc = gotfunc;
|
|
sd->getfunc = getfunc;
|
|
|
|
uae_sem_init (&sd->change_sem, 0, 1);
|
|
uae_sem_init (&sd->sync_semr, 0, 0);
|
|
uae_start_thread (_T("uaenet_win32r"), uaenet_trap_threadr, sd, &sd->tidr);
|
|
uae_sem_wait (&sd->sync_semr);
|
|
uae_sem_init (&sd->sync_semw, 0, 0);
|
|
uae_start_thread (_T("uaenet_win32w"), uaenet_trap_threadw, sd, &sd->tidw);
|
|
uae_sem_wait (&sd->sync_semw);
|
|
write_log (_T("uaenet_win32 initialized\n"));
|
|
return 1;
|
|
|
|
end:
|
|
uaenet_close (sd);
|
|
return 0;
|
|
}
|
|
|
|
void uaenet_close (void *vsd)
|
|
{
|
|
struct uaenetdatawin32 *sd = (struct uaenetdatawin32*)vsd;
|
|
if (!sd)
|
|
return;
|
|
if (sd->threadactiver) {
|
|
sd->threadactiver = -1;
|
|
}
|
|
if (sd->threadactivew) {
|
|
sd->threadactivew = -1;
|
|
SetEvent (sd->evttw);
|
|
}
|
|
if (sd->threadactiver) {
|
|
while (sd->threadactiver)
|
|
Sleep(10);
|
|
write_log (_T("uaenet_win32 thread %d killed\n"), sd->tidr);
|
|
uae_end_thread (&sd->tidr);
|
|
}
|
|
if (sd->threadactivew) {
|
|
while (sd->threadactivew)
|
|
Sleep(10);
|
|
CloseHandle (sd->evttw);
|
|
write_log (_T("uaenet_win32 thread %d killed\n"), sd->tidw);
|
|
uae_end_thread (&sd->tidw);
|
|
}
|
|
xfree (sd->readbuffer);
|
|
xfree (sd->writebuffer);
|
|
if (sd->fp)
|
|
ppcap_close (sd->fp);
|
|
uaeser_initdata (sd, sd->user);
|
|
write_log (_T("uaenet_win32 closed\n"));
|
|
}
|
|
|
|
void uaenet_enumerate_free (void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_TOTAL_NET_DEVICES; i++) {
|
|
tds[i].active = 0;
|
|
}
|
|
enumerated = 0;
|
|
}
|
|
|
|
static struct netdriverdata *enumit (const TCHAR *name)
|
|
{
|
|
int cnt;
|
|
|
|
if (name == NULL)
|
|
return tds;
|
|
for (cnt = 0; cnt < MAX_TOTAL_NET_DEVICES; cnt++) {
|
|
TCHAR mac[20];
|
|
struct netdriverdata *tc = tds + cnt;
|
|
_stprintf (mac, _T("%02X:%02X:%02X:%02X:%02X:%02X"),
|
|
tc->mac[0], tc->mac[1], tc->mac[2], tc->mac[3], tc->mac[4], tc->mac[5]);
|
|
if (tc->active && name && (!_tcsicmp (name, tc->name) || !_tcsicmp (name, mac)))
|
|
return tc;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct netdriverdata *uaenet_enumerate (const TCHAR *name)
|
|
{
|
|
static int done;
|
|
char errbuf[PCAP_ERRBUF_SIZE];
|
|
pcap_if_t *alldevs, *d;
|
|
int cnt;
|
|
LPADAPTER lpAdapter = 0;
|
|
PPACKET_OID_DATA OidData;
|
|
struct netdriverdata *tc, *tcp;
|
|
pcap_t *fp;
|
|
int val;
|
|
TCHAR *ss;
|
|
bool npcap = true;
|
|
TCHAR sname[MAX_DPATH];
|
|
|
|
if (enumerated) {
|
|
return enumit (name);
|
|
}
|
|
tcp = tds;
|
|
|
|
int len = GetSystemDirectory(sname, MAX_DPATH);
|
|
if (len) {
|
|
_tcscat(sname, _T("\\Npcap"));
|
|
SetDllDirectory(sname);
|
|
}
|
|
wpcap = LoadLibrary(_T("wpcap.dll"));
|
|
packet = LoadLibrary(_T("packet.dll"));
|
|
SetDllDirectory(_T(""));
|
|
if (wpcap == NULL) {
|
|
FreeLibrary(packet);
|
|
int err = GetLastError();
|
|
wpcap = LoadLibrary (_T("wpcap.dll"));
|
|
packet = LoadLibrary(_T("packet.dll"));
|
|
if (wpcap == NULL) {
|
|
write_log (_T("uaenet: npcap/winpcap not installed (wpcap.dll)\n"));
|
|
return NULL;
|
|
}
|
|
npcap = false;
|
|
}
|
|
if (packet == NULL) {
|
|
write_log (_T("uaenet: npcap/winpcap not installed (packet.dll)\n"));
|
|
FreeLibrary(wpcap);
|
|
wpcap = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
ppcap_lib_version = (PCAP_LIB_VERSION)GetProcAddress(wpcap, "pcap_lib_version");
|
|
ppcap_findalldevs_ex = (PCAP_FINDALLDEVS_EX)GetProcAddress(wpcap, "pcap_findalldevs_ex");
|
|
ppcap_freealldevs = (PCAP_FREEALLDEVS)GetProcAddress(wpcap, "pcap_freealldevs");
|
|
ppcap_open = (PCAP_OPEN)GetProcAddress(wpcap, "pcap_open");
|
|
ppcap_close = (PCAP_CLOSE)GetProcAddress(wpcap, "pcap_close");
|
|
ppcap_datalink = (PCAP_DATALINK)GetProcAddress(wpcap, "pcap_datalink");
|
|
|
|
ppcap_sendpacket = (PCAP_SENDPACKET)GetProcAddress(wpcap, "pcap_sendpacket");
|
|
ppcap_next_ex = (PCAP_NEXT_EX)GetProcAddress(wpcap, "pcap_next_ex");
|
|
|
|
pPacketOpenAdapter = (PACKETOPENADAPTER)GetProcAddress(packet, "PacketOpenAdapter");
|
|
pPacketCloseAdapter = (PACKETCLOSEADAPTER)GetProcAddress(packet, "PacketCloseAdapter");
|
|
pPacketRequest = (PACKETREQUEST)GetProcAddress(packet, "PacketRequest");
|
|
|
|
if (!ppcap_lib_version || !ppcap_findalldevs_ex || !ppcap_freealldevs ||
|
|
!ppcap_open || !ppcap_close || !ppcap_datalink || !ppcap_sendpacket ||
|
|
!ppcap_next_ex || !pPacketOpenAdapter || !pPacketCloseAdapter || !pPacketRequest) {
|
|
write_log(_T("uaenet: too old npcap/winpcap, missing function(s).\n"));
|
|
return NULL;
|
|
}
|
|
|
|
ss = au (ppcap_lib_version());
|
|
if (!done)
|
|
write_log (_T("uaenet: %s\n"), ss);
|
|
xfree (ss);
|
|
|
|
if (ppcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) {
|
|
ss = au (errbuf);
|
|
write_log (_T("uaenet: failed to get interfaces: %s\n"), ss);
|
|
xfree (ss);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PIP_ADAPTER_ADDRESSES aa = NULL;
|
|
DWORD aasize = 0;
|
|
DWORD err = GetAdaptersAddresses(AF_UNSPEC,
|
|
GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
|
|
NULL, NULL, &aasize);
|
|
if (err == ERROR_BUFFER_OVERFLOW) {
|
|
aa = (IP_ADAPTER_ADDRESSES*)xcalloc(uae_u8, aasize);
|
|
if (GetAdaptersAddresses(AF_UNSPEC,
|
|
GAA_FLAG_SKIP_UNICAST | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER,
|
|
NULL, aa, &aasize) != ERROR_SUCCESS) {
|
|
xfree(aa);
|
|
aa = NULL;
|
|
}
|
|
}
|
|
|
|
OidData = (PPACKET_OID_DATA)xcalloc(uae_u8, 6 + sizeof(PACKET_OID_DATA));
|
|
OidData->Length = 6;
|
|
OidData->Oid = OID_802_3_CURRENT_ADDRESS;
|
|
|
|
if (!done)
|
|
write_log (_T("uaenet: detecting interfaces\n"));
|
|
for(cnt = 0, d = alldevs; d != NULL; d = d->next) {
|
|
char *n2;
|
|
TCHAR *ss2;
|
|
tc = tcp + cnt;
|
|
if (cnt >= MAX_TOTAL_NET_DEVICES) {
|
|
write_log (_T("buffer overflow\n"));
|
|
break;
|
|
}
|
|
ss = au (d->name);
|
|
ss2 = d->description ? au (d->description) : _T("(no description)");
|
|
write_log (_T("%s\n- %s\n"), ss, ss2);
|
|
xfree (ss2);
|
|
xfree (ss);
|
|
n2 = d->name;
|
|
if (strlen (n2) <= strlen (PCAP_SRC_IF_STRING)) {
|
|
write_log (_T("- corrupt name\n"));
|
|
continue;
|
|
}
|
|
fp = ppcap_open(d->name, 65536, 0, 0, NULL, errbuf);
|
|
if (!fp) {
|
|
ss = au (errbuf);
|
|
write_log (_T("- pcap_open() failed: %s\n"), ss);
|
|
xfree (ss);
|
|
continue;
|
|
}
|
|
val = ppcap_datalink(fp);
|
|
ppcap_close (fp);
|
|
if (val != DLT_EN10MB) {
|
|
if (!done)
|
|
write_log (_T("- not an ethernet adapter (%d)\n"), val);
|
|
continue;
|
|
}
|
|
|
|
lpAdapter = pPacketOpenAdapter(n2 + strlen (PCAP_SRC_IF_STRING));
|
|
if (lpAdapter == NULL) {
|
|
if (!done)
|
|
write_log (_T("- PacketOpenAdapter() failed\n"));
|
|
continue;
|
|
}
|
|
memset(tc->originalmac, 0, sizeof tc->originalmac);
|
|
memcpy(tc->mac, uaemac, 6);
|
|
if (pPacketRequest (lpAdapter, FALSE, OidData)) {
|
|
memcpy(tc->mac, OidData->Data, 6);
|
|
memcpy(tc->originalmac, tc->mac, 6);
|
|
} else {
|
|
PIP_ADAPTER_ADDRESSES aap = aa;
|
|
while (aap) {
|
|
char *name1 = aap->AdapterName;
|
|
char *name2 = d->name;
|
|
while (*name2 && *name2 != '{')
|
|
name2++;
|
|
if (*name2 && !stricmp(name1, name2)) {
|
|
memcpy(tc->mac, aap->PhysicalAddress, 6);
|
|
memcpy(tc->originalmac, tc->mac, 6);
|
|
break;
|
|
}
|
|
aap = aap->Next;
|
|
}
|
|
}
|
|
if (!done)
|
|
write_log(_T("- MAC %02X:%02X:%02X:%02X:%02X:%02X -> %02X:%02X:%02X:%02X:%02X:%02X\n"),
|
|
tc->mac[0], tc->mac[1], tc->mac[2], tc->mac[3], tc->mac[4], tc->mac[5],
|
|
uaemac[0], uaemac[1], uaemac[2], tc->mac[3], tc->mac[4], tc->mac[5]);
|
|
memcpy(tc->mac, uaemac, 3);
|
|
tc->type = UAENET_PCAP;
|
|
tc->active = 1;
|
|
tc->mtu = 1522;
|
|
tc->name = au(d->name);
|
|
tc->desc = au(d->description);
|
|
cnt++;
|
|
pPacketCloseAdapter(lpAdapter);
|
|
}
|
|
if (!done)
|
|
write_log (_T("uaenet: end of detection, %d devices found.\n"), cnt);
|
|
done = 1;
|
|
ppcap_freealldevs(alldevs);
|
|
xfree(OidData);
|
|
xfree(aa);
|
|
enumerated = 1;
|
|
return enumit (name);
|
|
}
|
|
|
|
void uaenet_close_driver (struct netdriverdata *tc)
|
|
{
|
|
int i;
|
|
|
|
if (!tc)
|
|
return;
|
|
for (i = 0; i < MAX_TOTAL_NET_DEVICES; i++) {
|
|
tc[i].active = 0;
|
|
}
|
|
}
|
|
|
|
void ethernet_pause(int pause)
|
|
{
|
|
ethernet_paused = pause;
|
|
}
|
|
|
|
void ethernet_reset(void)
|
|
{
|
|
ethernet_paused = 0;
|
|
}
|