simh-testsetgenerator/PDP10/kl10_nia.c
2022-03-10 19:12:46 -05:00

1677 lines
61 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* kl10_NIA.c: NIA 20 Network interface.
Copyright (c) 2020, Richard Cornwell.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This emulates the MIT-AI/ML/MC Host/IMP interface.
*/
#include "kx10_defs.h"
#include "sim_ether.h"
#if NUM_DEVS_NIA > 0
#define NIA_DEVNUM (0540 + (5 * 4))
/* NIA Bits */
/* CONI */
#define NIA_PPT 0400000000000LL /* Port present */
#define NIA_DCC 0100000000000LL /* Diag CSR */
#define NIA_CPE 0004000000000LL /* CRAM Parity error */
#define NIA_MBE 0002000000000LL /* MBUS error */
#define NIA_ILD 0000100000000LL /* Idle */
#define NIA_DCP 0000040000000LL /* Disable complete */
#define NIA_ECP 0000020000000LL /* Enable complete */
#define NIA_PID 0000007000000LL /* Port ID */
/* CONO/ CONI */
#define NIA_CPT 0000000400000LL /* Clear Port */
#define NIA_SEB 0000000200000LL /* Diag Select EBUF */
#define NIA_GEB 0000000100000LL /* Diag Gen Ebus PE */
#define NIA_LAR 0000000040000LL /* Diag select LAR */
#define NIA_SSC 0000000020000LL /* Diag Single Cycle */
#define NIA_EPE 0000000004000LL /* Ebus parity error */
#define NIA_FQE 0000000002000LL /* Free Queue Error */
#define NIA_DME 0000000001000LL /* Data mover error */
#define NIA_CQA 0000000000400LL /* Command Queue Available */
#define NIA_RQA 0000000000200LL /* Response Queue Available */
#define NIA_DIS 0000000000040LL /* Disable */
#define NIA_ENB 0000000000020LL /* Enable */
#define NIA_MRN 0000000000010LL /* RUN */
#define NIA_PIA 0000000000007LL /* PIA */
#define NIA_LRA 0400000000000LL /* Load Ram address */
#define NIA_RAR 0377760000000LL /* Microcode address mask */
#define NIA_MSB 0000020000000LL /* Half word select */
/* PCB Offsets */
#define PCB_CQI 0 /* Command queue interlock */
#define PCB_CQF 1 /* Command queue flink */
#define PCB_CQB 2 /* Command queue blink */
#define PCB_RS0 3 /* Reserved */
#define PCB_RSI 4 /* Response queue interlock */
#define PCB_RSF 5 /* Response queue flink */
#define PCB_RSB 6 /* Response queue blink */
#define PCB_RS1 7 /* Reserved */
#define PCB_UPI 010 /* Unknown protocol queue interlock */
#define PCB_UPF 011 /* Unknown protocol queue flink */
#define PCB_UPB 012 /* Unknown protocol queue blink */
#define PCB_UPL 013 /* Unknown protocol queue length */
#define PCB_RS2 014 /* Reserved */
#define PCB_PTT 015 /* Protocol Type Table */
#define PCB_MCT 016 /* Multicast Table */
#define PCB_RS3 017 /* Reserved */
#define PCB_ER0 020 /* Error Log out 0 */
#define PCB_ER1 021 /* Error Log out 1 */
#define PCB_EPA 022 /* EPT Channel logout word 1 address */
#define PCB_EPW 023 /* EPT Channel logout word 1 contents */
#define PCB_PCB 024 /* PCB Base Address */
#define PCB_PIA 025 /* PIA */
#define PCB_RS4 026 /* Reserved */
#define PCB_CCW 027 /* Channel command word */
#define PCB_RCB 030 /* Counters base address */
#define CHNERR 07762
#define SLFTST 07751
#define INTERR 07750
#define EBSERR 07752
/* 12 Bit Shift */
#define NIA_CMD_SND 0001 /* Send a datagram */
#define NIA_CMD_LMAC 0002 /* Load Multicast table */
#define NIA_CMD_LPTT 0003 /* Load Protocol Type table */
#define NIA_CMD_RCNT 0004 /* Read counts */
#define NIA_CMD_RCV 0005 /* Received datagram */
#define NIA_CMD_WPLI 0006 /* Write PLI */
#define NIA_CMD_RPLI 0007 /* Read PLI */
#define NIA_CMD_RNSA 0010 /* Read Station Address */
#define NIA_CMD_WNSA 0011 /* Write Station Address */
/* 20 Bit shift */
#define NIA_FLG_RESP 0001 /* Command wants a response */
#define NIA_FLG_CLRC 0002 /* Clear counters (Read counters) */
#define NIA_FLG_BSD 0010 /* Send BSD packet */
#define NIA_FLG_PAD 0040 /* Send pad */
#define NIA_FLG_ICRC 0100 /* Send use host CRC */
#define NIA_FLG_PACK 0200 /* Send Pack */
#define NIA_STS_CPE 0200 /* CRAM PE */
#define NIA_STS_SR 0100 /* Send receive */
#define NIA_STS_ERR 0001 /* Error bits valid */
/* 28 bit shift ERR +1 */
#define NIA_ERR_ECL 000 /* Excessive collisions */
#define NIA_ERR_CAR 001 /* Carrier check failed */
#define NIA_ERR_COL 002 /* Collision detect failed */
#define NIA_ERR_SHT 003 /* Short circuit */
#define NIA_ERR_OPN 004 /* Open circuit */
#define NIA_ERR_LNG 005 /* Frame to long */
#define NIA_ERR_RMT 006 /* Remote failure */
#define NIA_ERR_BLK 007 /* Block check error */
#define NIA_ERR_FRM 010 /* Framing error */
#define NIA_ERR_OVR 011 /* Data Overrun */
#define NIA_ERR_PRO 012 /* Unrecongized protocol */
#define NIA_ERR_RUN 013 /* Frame too short */
#define NIA_ERR_WCZ 030 /* Word count not zero */
#define NIA_ERR_QLV 031 /* Queue length violation */
#define NIA_ERR_PLI 032 /* Illegal PLI function */
#define NIA_ERR_UNK 033 /* Unknown command */
#define NIA_ERR_BLV 034 /* Buffer length violation */
#define NIA_ERR_PAR 036 /* Parity error */
#define NIA_ERR_INT 037 /* Internal error */
/* Counters */
#define NIA_CNT_BR 000 /* Bytes received */
#define NIA_CNT_BX 001 /* Bytes transmitted */
#define NIA_CNT_FR 002 /* Frames received */
#define NIA_CNT_FX 003 /* Frames transmitted */
#define NIA_CNT_MCB 004 /* Multicast bytes received */
#define NIA_CNT_MCF 005 /* Multicast frames received */
#define NIA_CNT_FXD 006 /* Frames xmitted, initially deferred */
#define NIA_CNT_FXS 007 /* Frames xmitted, single collision */
#define NIA_CNT_FXM 010 /* Frames xmitted, multiple collisions */
#define NIA_CNT_XF 011 /* Transmit failures */
#define NIA_CNT_XFM 012 /* Transmit failure bit mask */
#define NIA_XFM_LOC 04000 /* B24 - Loss of carrier */
#define NIA_XFM_XBP 02000 /* B25 - Xmit buffer parity error */
#define NIA_XFM_RFD 01000 /* B26 - Remote failure to defer */
#define NIA_XFM_XFL 00400 /* B27 - Xmitted frame too long */
#define NIA_XFM_OC 00200 /* B28 - Open circuit */
#define NIA_XFM_SC 00100 /* B29 - Short circuit */
#define NIA_XFM_CCF 00040 /* B30 - Collision detect check failed */
#define NIA_XFM_EXC 00020 /* B31 - Excessive collisions */
#define NIA_CNT_CDF 013 /* Carrier detect check failed */
#define NIA_CNT_RF 014 /* Receive failures */
#define NIA_CNT_RFM 015 /* Receive failure bit mask */
#define NIA_RFM_FLE 0400 /* B27 - free list parity error */
#define NIA_RFM_NFB 0200 /* B28 - no free buffers */
#define NIA_RFM_FTL 0100 /* B29 - frame too long */
#define NIA_RFM_FER 0040 /* B30 - framing error */
#define NIA_RFM_BCE 0020 /* B31 - block check error */
#define NIA_CNT_DUN 016 /* Discarded unknown */
#define NIA_CNT_D01 017 /* Discarded position 1 */
#define NIA_CNT_D02 020 /* Discarded position 2 */
#define NIA_CNT_D03 021 /* Discarded position 3 */
#define NIA_CNT_D04 022 /* Discarded position 4 */
#define NIA_CNT_D05 023 /* Discarded position 5 */
#define NIA_CNT_D06 024 /* Discarded position 6 */
#define NIA_CNT_D07 025 /* Discarded position 7 */
#define NIA_CNT_D08 026 /* Discarded position 8 */
#define NIA_CNT_D09 027 /* Discarded position 9 */
#define NIA_CNT_D10 030 /* Discarded position 10 */
#define NIA_CNT_D11 031 /* Discarded position 11 */
#define NIA_CNT_D12 032 /* Discarded position 12 */
#define NIA_CNT_D13 033 /* Discarded position 13 */
#define NIA_CNT_D14 034 /* Discarded position 14 */
#define NIA_CNT_D15 035 /* Discarded position 15 */
#define NIA_CNT_D16 036 /* Discarded position 16 */
#define NIA_CNT_UFD 037 /* Unrecognized frame dest */
#define NIA_CNT_DOV 040 /* Data overrun */
#define NIA_CNT_SBU 041 /* System buffer unavailable */
#define NIA_CNT_UBU 042 /* User buffer unavailable */
#define NIA_CNT_RS0 043 /* Pli reg rd par error,,pli parity error */
#define NIA_CNT_RS1 044 /* Mover parity error,,cbus parity error */
#define NIA_CNT_RS2 045 /* Ebus parity error,,ebus que parity error */
#define NIA_CNT_RS3 046 /* Channel error,,spur channel error */
#define NIA_CNT_RS4 047 /* Spur xmit attn error,,cbus req timout error */
#define NIA_CNT_RS5 050 /* Ebus req timeout error,,csr grnt timeout error */
#define NIA_CNT_RS6 051 /* Used buff parity error,,xmit buff parity error */
#define NIA_CNT_RS7 052 /* Reserved for ucode */
#define NIA_CNT_RS8 053 /* Reserved for ucode */
#define NIA_CNT_LEN 054 /* # of counters */
typedef uint32 in_addr_T;
#define ETHTYPE_ARP 0x0806
#define ETHTYPE_IP 0x0800
PACKED_BEGIN
struct nia_eth_hdr {
ETH_MAC dest;
ETH_MAC src;
uint16 type;
} PACKED_END;
/*
* Structure of an internet header, naked of options.
*/
PACKED_BEGIN
struct ip {
uint8 ip_v_hl; /* version,header length */
uint8 ip_tos; /* type of service */
uint16 ip_len; /* total length */
uint16 ip_id; /* identification */
uint16 ip_off; /* fragment offset field */
#define IP_DF 0x4000 /* don't fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
uint8 ip_ttl; /* time to live */
uint8 ip_p; /* protocol */
uint16 ip_sum; /* checksum */
in_addr_T ip_src;
in_addr_T ip_dst; /* source and dest address */
} PACKED_END;
#define TCP_PROTO 6
PACKED_BEGIN
struct tcp {
uint16 tcp_sport; /* Source port */
uint16 tcp_dport; /* Destination port */
uint32 seq; /* Sequence number */
uint32 ack; /* Ack number */
uint16 flags; /* Flags */
#define TCP_FL_FIN 0x01
#define TCP_FL_SYN 0x02
#define TCP_FL_RST 0x04
#define TCP_FL_PSH 0x08
#define TCP_FL_ACK 0x10
#define TCP_FL_URG 0x20
uint16 window; /* Window size */
uint16 chksum; /* packet checksum */
uint16 urgent; /* Urgent pointer */
} PACKED_END;
#define UDP_PROTO 17
PACKED_BEGIN
struct udp {
uint16 udp_sport; /* Source port */
uint16 udp_dport; /* Destination port */
uint16 len; /* Length */
uint16 chksum; /* packet checksum */
} PACKED_END;
PACKED_BEGIN
struct udp_hdr {
in_addr_T ip_src;
in_addr_T ip_dst; /* source and dest address */
uint8 zero;
uint8 proto; /* Protocol */
uint16 hlen; /* Length of header and data */
} PACKED_END;
#define ICMP_PROTO 1
PACKED_BEGIN
struct icmp {
uint8 type; /* Type of packet */
uint8 code; /* Code */
uint16 chksum; /* packet checksum */
} PACKED_END;
PACKED_BEGIN
struct ip_hdr {
struct nia_eth_hdr ethhdr;
struct ip iphdr;
} PACKED_END;
#define ARP_REQUEST 1
#define ARP_REPLY 2
#define ARP_HWTYPE_ETH 1
PACKED_BEGIN
struct arp_hdr {
struct nia_eth_hdr ethhdr;
uint16 hwtype;
uint16 protocol;
uint8 hwlen;
uint8 protolen;
uint16 opcode;
ETH_MAC shwaddr;
in_addr_T sipaddr;
ETH_MAC dhwaddr;
in_addr_T dipaddr;
uint8 padding[18];
} PACKED_END;
struct nia_device {
ETH_PCALLBACK rcallback; /* read callback routine */
ETH_PCALLBACK wcallback; /* write callback routine */
ETH_MAC mac; /* Hardware MAC addresses */
ETH_DEV etherface;
ETH_QUE ReadQ;
ETH_PACK rec_buff; /* Buffer for recieved packet */
ETH_PACK snd_buff; /* Buffer for sending packet */
t_addr cmd_entry; /* Pointer to current command entry */
t_addr cmd_rply; /* Pointer to reply entry */
uint8 cmd_status; /* Status feild of current command */
t_addr rec_entry; /* Pointer to current recieve entry */
t_addr pcb; /* Address of PCB */
t_addr rcb; /* Read count buffer address */
#define cmd_hdr pcb /* Command queue is at PCB */
t_addr resp_hdr; /* Head of response queue */
t_addr unk_hdr; /* Unknown protocol free queue */
int unk_len; /* Length of Unknown entries */
t_addr ptt_addr; /* Address of Protocol table */
t_addr mcast_addr; /* Address of Multicast table */
int pia; /* Interrupt channel */
t_addr cnt_addr; /* Address of counters */
uint64 pcnt[NIA_CNT_LEN]; /* Counters */
int ptt_n; /* Number of Protocol entries */
uint16 ptt_proto[17]; /* Protocol for entry */
t_addr ptt_head[17]; /* Head of protocol queue */
int macs_n; /* Number of multi-cast addresses */
ETH_MAC macs[20]; /* Watched Multi-cast addresses */
int amc; /* Recieve all multicast packets */
int prmsc; /* Recieve all packets */
int h4000; /* Heart beat detection */
int rar;
uint64 ebuf;
uint64 status; /* Status of device. */
uint32 uver[4]; /* Version information */
int r_pkt; /* Packet pending */
int poll; /* Need to poll receiver */
} nia_data;
extern int32 tmxr_poll;
static CONST ETH_MAC broadcast_ethaddr = {0xff,0xff,0xff,0xff,0xff,0xff};
t_stat nia_devio(uint32 dev, uint64 *data);
void nia_start();
void nia_stop();
void nia_enable();
void nia_disable();
void nia_load_ptt();
void nia_load_mcast();
void nia_error(int);
t_stat nia_eth_srv(UNIT *);
t_stat nia_rec_srv(UNIT *);
t_stat nia_cmd_srv(UNIT *);
t_stat nia_reset (DEVICE *dptr);
t_stat nia_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
t_stat nia_set_mac (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
t_stat nia_attach (UNIT * uptr, CONST char * cptr);
t_stat nia_detach (UNIT * uptr);
t_stat nia_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);
const char *nia_description (DEVICE *dptr);
struct rh_if nia_rh = { NULL, NULL, NULL};
UNIT nia_unit[] = {
{UDATA(nia_eth_srv, UNIT_IDLE+UNIT_ATTABLE, 0)}, /* 0 */
{UDATA(nia_rec_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 1 */
{UDATA(nia_cmd_srv, UNIT_IDLE+UNIT_DIS, 0)}, /* 2 */
};
REG nia_reg[] = {
{SAVEDATA(DATA, nia_data) },
{0}
};
#define nia_cmd_uptr (&nia_unit[2]) /* Unit for processing commands */
#define nia_recv_uptr (&nia_unit[0]) /* Unit doing receive digestion */
#define nia_proc_uptr (&nia_unit[1]) /* Unit doing receive dispatching */
DIB nia_dib = {NIA_DEVNUM | RH20_DEV, 1, &nia_devio, NULL, &nia_rh };
MTAB nia_mod[] = {
{ MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NC, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx",
&nia_set_mac, &nia_show_mac, NULL, "MAC address" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "ETH", NULL, NULL,
&eth_show, NULL, "Display attachedable devices" },
{ 0 }
};
/* Simulator debug controls */
DEBTAB nia_debug[] = {
{"CMD", DEBUG_CMD, "Show command execution to devices"},
{"DATA", DEBUG_DATA, "Show data transfers"},
{"DETAIL", DEBUG_DETAIL, "Show details about device"},
{"EXP", DEBUG_EXP, "Show exception information"},
{"CONI", DEBUG_CONI, "Show coni instructions"},
{"CONO", DEBUG_CONO, "Show cono instructions"},
{"DATAIO", DEBUG_DATAIO, "Show datai and datao instructions"},
{"IRQ", DEBUG_IRQ, "Show IRQ requests"},
#define DEBUG_ARP (DEBUG_IRQ<<1)
{"ARP", DEBUG_ARP, "Show ARP activities"},
#define DEBUG_TCP (DEBUG_ARP<<1)
{"TCP", DEBUG_TCP, "Show TCP packet activities"},
#define DEBUG_UDP (DEBUG_TCP<<1)
{"UDP", DEBUG_UDP, "Show UDP packet activities"},
#define DEBUG_ICMP (DEBUG_UDP<<1)
{"ICMP", DEBUG_ICMP, "Show ICMP packet activities"},
#define DEBUG_ETHER (DEBUG_ICMP<<1)
{"ETHER", DEBUG_ETHER, "Show ETHER activities"},
{0, 0}
};
DEVICE nia_dev = {
"NI", nia_unit, nia_reg, nia_mod,
3, 8, 0, 1, 8, 36,
NULL, NULL, &nia_reset, NULL, &nia_attach, &nia_detach,
&nia_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_ETHER, 0, nia_debug,
NULL, NULL, &nia_help, NULL, NULL, &nia_description
};
t_stat nia_devio(uint32 dev, uint64 *data)
{
DEVICE *dptr = &nia_dev;
UNIT *uptr = nia_cmd_uptr;
switch(dev & 03) {
case CONO:
if (*data & NIA_CPT)
nia_reset(dptr);
nia_data.status &= ~(NIA_SEB|NIA_LAR|NIA_SSC|NIA_DIS|NIA_ENB|NIA_PIA);
nia_data.status |= *data & (NIA_SEB|NIA_LAR|NIA_SSC|NIA_DIS|NIA_ENB|NIA_PIA);
nia_data.status &= ~(*data & (NIA_EPE|NIA_FQE|NIA_DME|NIA_RQA));
clr_interrupt(NIA_DEVNUM);
if (*data & NIA_MRN) {
if ((nia_data.status & NIA_MRN) == 0)
nia_start();
} else {
if ((nia_data.status & NIA_MRN) != 0)
nia_stop();
}
if (*data & NIA_ENB) {
if ((nia_data.status & NIA_MRN) != 0 &&
(nia_data.status & NIA_ECP) == 0)
nia_enable();
else
nia_data.status |= NIA_ECP;
} else
nia_data.status &= ~NIA_ECP;
if (*data & NIA_DIS) {
if ((nia_data.status & NIA_MRN) != 0 &&
(nia_data.status & NIA_DCP) == 0)
nia_disable();
else
nia_data.status |= NIA_DCP;
} else
nia_data.status &= ~NIA_DCP;
if (*data & NIA_CQA &&
(nia_data.status & NIA_CQA) == 0 &&
(nia_data.status & NIA_MRN) != 0) {
nia_data.status |= NIA_CQA;
sim_activate(uptr, 200);
}
if (nia_data.status & (NIA_CPE|NIA_RQA))
set_interrupt(NIA_DEVNUM, nia_data.status & NIA_PIA);
sim_debug(DEBUG_CONO, dptr, "NIA %03o CONO %06o PC=%06o %012llo\n", dev,
(uint32)(*data & RMASK), PC, nia_data.status);
break;
case CONI:
*data = nia_data.status|NIA_PPT|NIA_PID;
sim_debug(DEBUG_CONI, dptr, "NIA %03o CONI %012llo PC=%o\n", dev,
*data, PC);
break;
case DATAO:
if (nia_data.status & NIA_SEB) {
nia_data.ebuf = *data;
} else {
if (*data & NIA_LRA) {
nia_data.rar = (uint32)((*data & NIA_RAR) >> 22);
sim_debug(DEBUG_DETAIL, dptr, "NIA %03o set RAR=%o\n",
dev, nia_data.rar);
} else {
if (nia_data.rar >= 0274 && nia_data.rar <= 0277)
nia_data.uver[nia_data.rar - 0274] = (uint32)(*data & RMASK);
sim_debug(DEBUG_DETAIL, dptr, "NIA %03o set data=%o %06o\n",
dev, nia_data.rar, (uint32)(*data & RMASK));
}
}
sim_debug(DEBUG_DATAIO, dptr, "NIA %03o DATO %012llo PC=%o\n",
dev, *data, PC);
break;
case DATAI:
if (nia_data.status & NIA_SEB) {
*data = nia_data.ebuf;
} else {
if (nia_data.status & NIA_LAR) {
*data = ((uint64)nia_data.rar) << 20;
*data &= ~NIA_MSB;
*data |= NIA_LRA;
} else if (nia_data.rar >= 0274 && nia_data.rar <= 0277) {
*data = (uint64)nia_data.uver[nia_data.rar - 0274];
}
}
sim_debug(DEBUG_DATAIO, dptr, "NIA %03o DATI %012llo PC=%o\n",
dev, *data, PC);
break;
}
return SCPE_OK;
}
static char *
ipv4_inet_ntoa(struct in_addr ip)
{
static char str[20];
if (sim_end)
sprintf (str, "%d.%d.%d.%d", ip.s_addr & 0xFF,
(ip.s_addr >> 8) & 0xFF,
(ip.s_addr >> 16) & 0xFF,
(ip.s_addr >> 24) & 0xFF);
else
sprintf (str, "%d.%d.%d.%d", (ip.s_addr >> 24) & 0xFF,
(ip.s_addr >> 16) & 0xFF,
(ip.s_addr >> 8) & 0xFF,
ip.s_addr & 0xFF);
return str;
}
/*
* Set error code and stop.
*/
void nia_error(int err)
{
nia_data.rar = err;
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA error %03o\n", err);
nia_data.status |= NIA_CPE;
set_interrupt(NIA_DEVNUM, nia_data.status & NIA_PIA);
nia_stop();
}
/*
* Start NIA device, load in 2 words using RH20 mode.
*/
void nia_start()
{
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA start\n");
/* Set up RH20 to read 2 words */
nia_rh.stcr = BIT7;
nia_rh.imode = 2;
rh20_setup(&nia_rh);
/* Read PCB address */
if (!rh_read(&nia_rh)) {
nia_error(CHNERR);
return;
}
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA PCB %012llo %o\n", nia_rh.buf,
nia_rh.wcr);
nia_data.pcb = (t_addr)(nia_rh.buf & AMASK);
nia_data.resp_hdr = (t_addr)((nia_rh.buf + 4) & AMASK);
nia_data.unk_hdr = (t_addr)((nia_rh.buf + 8) & AMASK);
/* Read PIA value */
if (!rh_read(&nia_rh)) {
nia_error(CHNERR);
return;
}
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA PIA %012llo %o\n", nia_rh.buf,
nia_rh.wcr);
nia_data.pia = (int)(nia_rh.buf & 7);
nia_data.status |= NIA_MRN;
memcpy(&nia_data.macs[0], &nia_data.mac, sizeof (ETH_MAC));
memcpy(&nia_data.macs[1], &broadcast_ethaddr, sizeof(ETH_MAC));
}
void nia_stop()
{
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA stop\n");
nia_data.status &= ~NIA_MRN;
}
/*
* Enable NIA 20.
*
* Read in PTT and MACS table.
*/
void nia_enable()
{
uint64 word;
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA enable\n");
/* Load pointers to various table */
if (Mem_read_word(nia_data.unk_hdr + PCB_UPL, &word, 0)) {
nia_error(EBSERR);
return;
}
nia_data.unk_len = (int)(word & AMASK);
/* Load PTT */
if (Mem_read_word(nia_data.pcb + PCB_PTT, &word, 0)) {
nia_error(EBSERR);
return;
}
nia_data.ptt_addr = (t_addr)(word & AMASK);
nia_load_ptt();
/* Load MCT */
if (Mem_read_word(nia_data.pcb + PCB_MCT, &word, 0)) {
nia_error(EBSERR);
return;
}
nia_data.mcast_addr = (t_addr)(word & AMASK);
nia_load_mcast();
/* Load read count buffer address */
if (Mem_read_word(nia_data.pcb + PCB_RCB, &word, 0)) {
nia_error(EBSERR);
return;
}
nia_data.rcb = (t_addr)(word & AMASK);
nia_data.status |= NIA_ECP;
nia_data.status &= ~NIA_DCP;
}
/*
* Disable NIA 20.
*/
void nia_disable()
{
nia_data.status |= NIA_DCP;
nia_data.status &= ~NIA_ECP;
}
/*
* Copy a MAC address from string to memory word.
*/
void nia_cpy_mac(uint64 word1, uint64 word2, ETH_MAC *mac)
{
ETH_MAC m;
m[0] = (unsigned char)((word1 >> 28) & 0xff);
m[1] = (unsigned char)((word1 >> 20) & 0xff);
m[2] = (unsigned char)((word1 >> 12) & 0xff);
m[3] = (unsigned char)((word1 >> 4) & 0xff);
m[4] = (unsigned char)((word2 >> 28) & 0xff);
m[5] = (unsigned char)((word2 >> 20) & 0xff);
memcpy(mac, &m, sizeof(ETH_MAC));
}
/*
* Copy memory to a packet.
*/
uint8 *nia_cpy_to(t_addr addr, uint8 *data, int len)
{
uint64 word;
/* Copy full words */
while (len > 3) {
word = M[addr++];
*data++ = (uint8)((word >> 28) & 0xff);
*data++ = (uint8)((word >> 20) & 0xff);
*data++ = (uint8)((word >> 12) & 0xff);
*data++ = (uint8)((word >> 4) & 0xff);
len -= 4;
}
/* Grab last partial word */
if (len) {
word = M[addr++];
switch (len) {
case 3:
*data++ = (uint8)((word >> 28) & 0xff);
*data++ = (uint8)((word >> 20) & 0xff);
*data++ = (uint8)((word >> 12) & 0xff);
break;
case 2:
*data++ = (uint8)((word >> 28) & 0xff);
*data++ = (uint8)((word >> 20) & 0xff);
break;
case 1:
*data++ = (uint8)((word >> 28) & 0xff);
break;
}
}
return data;
}
/*
* Copy a packet to memory.
*/
uint8 *nia_cpy_from(t_addr addr, uint8 *data, int len)
{
uint64 word;
/* Copy full words */
while (len > 3) {
word = (uint64)(*data++) << 28;
word |= (uint64)(*data++) << 20;
word |= (uint64)(*data++) << 12;
word |= (uint64)(*data++) << 4;
M[addr++] = word;
len -= 4;
}
/* Copy last partial word */
if (len) {
switch (len) {
case 3:
word = (uint64)(*data++) << 28;
word |= (uint64)(*data++) << 20;
word |= (uint64)(*data++) << 12;
break;
case 2:
word = (uint64)(*data++) << 28;
word |= (uint64)(*data++) << 20;
break;
case 1:
word = (uint64)(*data++) << 28;
break;
default:
word = 0;
break;
}
M[addr++] = word;
}
return data;
}
/*
* Get next entry off a queue.
* Return entry in *entry and 1 if successfull.
* Returns 0 if fail, queue locked or memory out of bounds.
*/
int nia_getq(t_addr head, t_addr *entry)
{
uint64 temp;
t_addr flink;
t_addr nlink;
*entry = 0; /* For safty */
/* Try and get lock */
if (Mem_read_word(head, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
/* Check if entry locked */
if ((temp & SMASK) == 0)
return 0;
/* Increment lock here */
/* Get head of queue */
if (Mem_read_word(head + 1, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
flink = (t_addr)(temp & AMASK);
/* Check if queue empty */
if (flink == (head+1)) {
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA empty %08o\n", head);
/* Decrement lock here */
return 1;
}
/* Get link to next entry */
if (Mem_read_word(flink + 1, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
nlink = (t_addr)(temp & AMASK);
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA head: q=%08o f=%08o n=%08o\n",
head, flink, nlink);
/* Set Head Flink to point to next */
temp = (uint64)nlink;
if (Mem_write_word(head+1, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
/* Set Next Blink to head */
temp = (uint64)(head + 1);
if (Mem_write_word(nlink+1, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
/* Return entry */
*entry = flink;
/* Decrement lock here */
return 1;
}
/*
* Put entry on head of queue.
* Return entry in *entry and 1 if successfull.
* Returns 0 if fail, queue locked or memory out of bounds.
*/
int nia_putq(t_addr head, t_addr *entry)
{
uint64 temp;
t_addr blink;
/* Try and get lock */
if (Mem_read_word(head, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
/* Check if entry locked */
if ((temp & SMASK) == 0)
return 0;
/* Increment lock here */
/* Hook entry into tail of queue */
if (Mem_read_word(head + 2, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
blink = (t_addr)(temp & AMASK); /* Get back link */
/* Get link to previous entry */
temp = (uint64)*entry;
if (Mem_write_word(blink, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
/* Old forward is new */
if (Mem_write_word(head+2, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
/* Flink is head of queue */
temp = (uint64)(head+1);
if (Mem_write_word(*entry, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
/* Back link points to previous */
temp = (uint64)blink;
if (Mem_write_word(*entry+1, &temp, 0)) {
nia_error(EBSERR);
return 0;
}
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA put: q=%08o i=%08o b=%08o\n",
head, *entry, blink);
*entry = 0;
/* Decement lock here */
/* Check if Queue was empty, and response queue */
if (blink == (head+1) && head == nia_data.resp_hdr) {
nia_data.status |= NIA_RQA;
set_interrupt(NIA_DEVNUM, nia_data.pia);
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA set response\n");
}
return 1;
}
/*
* Load in the protocol type table
*/
void nia_load_ptt()
{
int i;
int n = 0;
t_addr addr = nia_data.ptt_addr;
for (i = 0; i < 17; i++) {
uint64 word1, word2;
/* Get entry */
if (Mem_read_word(addr++, &word1, 0)) {
nia_error(EBSERR);
return;
}
if (Mem_read_word(addr++, &word2, 0)) {
nia_error(EBSERR);
return;
}
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load ptt%d: %012llo %012llo\n",
n, word1, word2);
if (word1 & SMASK) {
uint16 type;
type = (uint16)(word1 >> 12) & 0xff;
type |= (uint16)(word1 << 4) & 0xff00;
nia_data.ptt_proto[n] = type;
nia_data.ptt_head[n] = (t_addr)(word2 &AMASK) - 1;
n++;
}
addr++;
}
for (i = 0; i < n; i++)
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load ptt%d: %04x %010o\n",
n, nia_data.ptt_proto[i], nia_data.ptt_head[i]);
nia_data.ptt_n = n;
}
/*
* Load in the multi-cast table
*/
void nia_load_mcast()
{
int i;
int n = 0;
char buffer[20];
t_addr addr = nia_data.mcast_addr;
/* Start with our own address. */
memcpy(&nia_data.macs[n], &nia_data.mac, sizeof (ETH_MAC));
n++;
memcpy(&nia_data.macs[n], &broadcast_ethaddr, sizeof (ETH_MAC));
n++;
for (i = 0; i < 17; i++) {
uint64 word1, word2;
if (Mem_read_word(addr++, &word1, 0)) {
nia_error(EBSERR);
return;
}
if (Mem_read_word(addr++, &word2, 0)) {
nia_error(EBSERR);
return;
}
if (word2 & 1) {
nia_cpy_mac(word1, word2, &nia_data.macs[n]);
n++;
}
}
for(i = 0; i< n; i++) {
eth_mac_fmt(&nia_data.macs[i], buffer);
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA load mcast%d: %s\n",i,buffer);
}
nia_data.macs_n = n - 2;
if (nia_recv_uptr->flags & UNIT_ATT)
eth_filter (&nia_data.etherface, n, nia_data.macs, nia_data.amc,
nia_data.prmsc);
}
/*
* Pretty print a packet for debugging.
*/
void nia_packet_debug(struct nia_device *nia, const char *action,
ETH_PACK *packet) {
struct nia_eth_hdr *eth = (struct nia_eth_hdr *)&packet->msg[0];
struct arp_hdr *arp = (struct arp_hdr *)eth;
struct ip *ip = (struct ip *)&packet->msg[sizeof(struct nia_eth_hdr)];
struct udp *udp;
struct tcp *tcp;
struct icmp *icmp;
uint8 *payload;
struct in_addr ipaddr;
size_t len;
int flag;
char src_ip[20];
char dst_ip[20];
char src_port[8];
char dst_port[8];
char flags[64];
static struct tcp_flag_bits {
const char *name;
uint16 bitmask;
} bits[] = {
{"FIN", TCP_FL_FIN},
{"SYN", TCP_FL_SYN},
{"RST", TCP_FL_RST},
{"PSH", TCP_FL_PSH},
{"ACK", TCP_FL_ACK},
{"URG", TCP_FL_URG},
{NULL, 0}
};
static const char *icmp_types[] = {
"Echo Reply", // Type 0
"Type 1 - Unassigned",
"Type 2 - Unassigned",
"Destination Unreachable", // Type 3
"Source Quench (Deprecated)", // Type 4
"Redirect", // Type 5
"Type 6 - Alternate Host Address (Deprecated)",
"Type 7 - Unassigned",
"Echo Request", // Type 8
"Router Advertisement", // Type 9
"Router Selection", // Type 10
"Time Exceeded", // Type 11
"Type 12 - Parameter Problem",
"Type 13 - Timestamp",
"Type 14 - Timestamp Reply",
"Type 15 - Information Request (Deprecated)",
"Type 16 - Information Reply (Deprecated)",
"Type 17 - Address Mask Request (Deprecated)",
"Type 18 - Address Mask Reply (Deprecated)",
"Type 19 - Reserved (for Security)",
"Type 20 - Reserved (for Robustness Experiment)",
"Type 21 - Reserved (for Robustness Experiment)",
"Type 22 - Reserved (for Robustness Experiment)",
"Type 23 - Reserved (for Robustness Experiment)",
"Type 24 - Reserved (for Robustness Experiment)",
"Type 25 - Reserved (for Robustness Experiment)",
"Type 26 - Reserved (for Robustness Experiment)",
"Type 27 - Reserved (for Robustness Experiment)",
"Type 28 - Reserved (for Robustness Experiment)",
"Type 29 - Reserved (for Robustness Experiment)",
"Type 30 - Traceroute (Deprecated)",
"Type 31 - Datagram Conversion Error (Deprecated)",
"Type 32 - Mobile Host Redirect (Deprecated)",
"Type 33 - IPv6 Where-Are-You (Deprecated)",
"Type 34 - IPv6 I-Am-Here (Deprecated)",
"Type 35 - Mobile Registration Request (Deprecated)",
"Type 36 - Mobile Registration Reply (Deprecated)",
"Type 37 - Domain Name Request (Deprecated)",
"Type 38 - Domain Name Reply (Deprecated)",
"Type 39 - SKIP (Deprecated)",
"Type 40 - Photuris",
"Type 41 - ICMP messages utilized by experimental mobility protocols such as Seamoby",
"Type 42 - Extended Echo Request",
"Type 43 - Extended Echo Reply"
};
if (ntohs(eth->type) == ETHTYPE_ARP) {
struct in_addr in_addr;
const char *arp_op = (ARP_REQUEST == ntohs(arp->opcode)) ? "REQUEST" : ((ARP_REPLY == ntohs(arp->opcode)) ? "REPLY" : "Unknown");
char eth_src[20], eth_dst[20];
char arp_shwaddr[20], arp_dhwaddr[20];
char arp_sipaddr[20], arp_dipaddr[20];
if (!(nia_dev.dctrl & DEBUG_ARP))
return;
eth_mac_fmt(&arp->ethhdr.src, eth_src);
eth_mac_fmt(&arp->ethhdr.dest, eth_dst);
eth_mac_fmt(&arp->shwaddr, arp_shwaddr);
memcpy(&in_addr, &arp->sipaddr, sizeof(in_addr));
strlcpy(arp_sipaddr, ipv4_inet_ntoa(in_addr), sizeof(arp_sipaddr));
eth_mac_fmt(&arp->dhwaddr, arp_dhwaddr);
memcpy(&in_addr, &arp->dipaddr, sizeof(in_addr));
strlcpy(arp_dipaddr, ipv4_inet_ntoa(in_addr), sizeof(arp_dipaddr));
sim_debug(DEBUG_ARP, &nia_dev,
"%s %s EthDst=%s EthSrc=%s shwaddr=%s sipaddr=%s dhwaddr=%s dipaddr=%s\n",
action, arp_op, eth_dst, eth_src, arp_shwaddr, arp_sipaddr, arp_dhwaddr, arp_dipaddr);
return;
}
if (ntohs(eth->type) != ETHTYPE_IP) {
payload = (uint8 *)&packet->msg[sizeof(struct nia_eth_hdr)];
len = packet->len - sizeof(struct nia_eth_hdr);
sim_data_trace(&nia_dev, nia_unit, payload, "", len, "", DEBUG_DATA);
return;
}
if (!(nia_dev.dctrl & (DEBUG_TCP|DEBUG_UDP|DEBUG_ICMP)))
return;
memcpy(&ipaddr, &ip->ip_src, sizeof(ipaddr));
strlcpy(src_ip, ipv4_inet_ntoa(ipaddr), sizeof(src_ip));
memcpy(&ipaddr, &ip->ip_dst, sizeof(ipaddr));
strlcpy(dst_ip, ipv4_inet_ntoa(ipaddr), sizeof(dst_ip));
payload = (uint8 *)&packet->msg[sizeof(struct nia_eth_hdr) + (ip->ip_v_hl & 0xf) * 4];
switch (ip->ip_p) {
case UDP_PROTO:
udp = (struct udp *)payload;
snprintf(src_port, sizeof(src_port), "%d", ntohs(udp->udp_sport));
snprintf(dst_port, sizeof(dst_port), "%d", ntohs(udp->udp_dport));
sim_debug(DEBUG_UDP, &nia_dev, "%s %d byte packet from %s:%s to %s:%s\n", action,
ntohs(udp->len), src_ip, src_port, dst_ip, dst_port);
if (udp->len && (nia_dev.dctrl & DEBUG_UDP))
sim_data_trace(&nia_dev, nia_unit, payload + sizeof(struct udp), "",
ntohs(udp->len), "", DEBUG_DATA);
break;
case TCP_PROTO:
tcp = (struct tcp *)payload;
snprintf(src_port, sizeof(src_port), "%d", ntohs(tcp->tcp_sport));
snprintf(dst_port, sizeof(dst_port), "%d", ntohs(tcp->tcp_dport));
strlcpy(flags, "", sizeof(flags));
for (flag=0; bits[flag].name; flag++) {
if (ntohs(tcp->flags) & bits[flag].bitmask) {
if (*flags)
strlcat(flags, ",", sizeof(flags));
strlcat(flags, bits[flag].name, sizeof(flags));
}
}
len = ntohs(ip->ip_len) - ((ip->ip_v_hl & 0xf) * 4 + (ntohs(tcp->flags) >> 12) * 4);
sim_debug(DEBUG_TCP, &nia_dev, "%s %s%s %d byte packet from %s:%s to %s:%s\n", action,
flags, *flags ? ":" : "", (int)len, src_ip, src_port, dst_ip, dst_port);
if (len && (nia_dev.dctrl & DEBUG_TCP))
sim_data_trace(&nia_dev, nia_unit, payload + 4 * (ntohs(tcp->flags) >> 12), "", len, "", DEBUG_DATA);
break;
case ICMP_PROTO:
icmp = (struct icmp *)payload;
len = ntohs(ip->ip_len) - (ip->ip_v_hl & 0xf) * 4;
if (len > 1500)
len = 1500;
sim_debug(DEBUG_ICMP, &nia_dev, "%s %s %d byte packet from %s to %s\n", action,
(icmp->type < sizeof(icmp_types)/sizeof(icmp_types[0])) ? icmp_types[icmp->type] : "", (int)len, src_ip, dst_ip);
if (len && (nia_dev.dctrl & DEBUG_ICMP))
sim_data_trace(&nia_dev, nia_unit, payload + sizeof(struct icmp), "", len, "", DEBUG_DATA);
break;
}
}
/*
* Send out a packet.
*/
int nia_send_pkt(uint64 cmd)
{
uint64 word1, word2;
struct nia_eth_hdr *hdr = (struct nia_eth_hdr *)(&nia_data.snd_buff.msg[0]);
uint8 *data = &nia_data.snd_buff.msg[sizeof(struct nia_eth_hdr)];
ETH_MAC dest;
uint16 type;
int len;
int blen;
/* Read packet length */
if (Mem_read_word(nia_data.cmd_entry + 4, &word1, 0)) {
nia_error(EBSERR);
return 0;
}
len = (int)(word1 & 0177777);
blen = len + sizeof(struct nia_eth_hdr);
/* Check for runt packet */
if (blen < ETH_MIN_PACKET && (cmd & (NIA_FLG_PAD << 8)) == 0) {
return NIA_ERR_RUN;
}
/* Check for too long of a packet */
if (blen > ETH_MAX_PACKET) {
nia_data.pcnt[NIA_CNT_XF]++;
nia_data.pcnt[NIA_CNT_XFM] |= NIA_XFM_XFL;
return NIA_ERR_LNG;
}
/* If unit not attached, nothing more we can do */
if ((nia_recv_uptr->flags & UNIT_ATT) == 0)
return 0;
/* Read protocol type */
if (Mem_read_word(nia_data.cmd_entry + 5, &word1, 0)) {
nia_error(EBSERR);
return 0;
}
type = (uint16)(word1 >> 12) & 0xff;
type |= (uint16)(word1 << 4) & 0xff00;
hdr->type = htons(type);
/* Load destination address */
if (Mem_read_word(nia_data.cmd_entry + 7, &word1, 0)) {
nia_error(EBSERR);
return 0;
}
if (Mem_read_word(nia_data.cmd_entry + 8, &word2, 0)) {
nia_error(EBSERR);
return 0;
}
nia_cpy_mac(word1, word2, &dest);
memcpy(hdr->dest, dest, sizeof(ETH_MAC));
/* Copy our address over */
memcpy(hdr->src, nia_data.mac, sizeof(ETH_MAC));
/* Set packet length */
nia_data.snd_buff.len = len + sizeof(struct nia_eth_hdr);
/* Preappend length if asking for pad */
if ((cmd & (NIA_FLG_PAD << 8)) != 0) {
*data++ = len & 0377;
*data++ = (len >> 8) & 0377;
nia_data.snd_buff.len += 2;
}
/* Copy over rest of packet */
if (cmd & (NIA_FLG_BSD << 8)) {
if (Mem_read_word(nia_data.cmd_entry + 9, &word1, 0)) {
nia_error(EBSERR);
return 0;
}
while (len > 0) {
uint64 tlen;
/* Read pointer to buffer */
if (Mem_read_word((t_addr)(word1 & AMASK), &word2, 0)) {
nia_error(EBSERR);
return 0;
}
/* Read length */
if (Mem_read_word((t_addr)((word1+2) & AMASK), &tlen, 0)) {
nia_error(EBSERR);
return 0;
}
blen = (int)(tlen & 0177777);
data = nia_cpy_to((t_addr)(word2 & AMASK), data, blen);
len -= blen;
if (Mem_read_word((t_addr)((word1 + 1) & AMASK), &word1, 0)) {
nia_error(EBSERR);
return 0;
}
}
} else {
data = nia_cpy_to(nia_data.cmd_entry + 9, data, len);
}
if (((cmd & (NIA_FLG_PAD << 8)) != 0) &&
nia_data.snd_buff.len < ETH_MIN_PACKET) {
while (nia_data.snd_buff.len < ETH_MIN_PACKET) {
*data++ = 0;
nia_data.snd_buff.len++;
}
}
nia_packet_debug(&nia_data, "send", &nia_data.snd_buff);
if (eth_write(&nia_data.etherface, &nia_data.snd_buff, NULL) != SCPE_OK) {
nia_data.pcnt[NIA_CNT_XF]++;
nia_data.pcnt[NIA_CNT_XFM] |= NIA_XFM_LOC;
}
nia_data.pcnt[NIA_CNT_BX] += nia_data.snd_buff.len;
nia_data.pcnt[NIA_CNT_FX] ++;
return 0;
}
/*
* Process commands.
*/
t_stat nia_cmd_srv(UNIT * uptr)
{
uint64 word1, word2;
uint32 cmd;
int len, i;
int err;
/* See if we have command that we could not respond too */
if (nia_data.cmd_entry != 0) {
/* Have to put this either on response queue or free queue */
if (nia_putq(nia_data.cmd_rply, &nia_data.cmd_entry) == 0){
sim_activate(uptr, 200); /* Reschedule ourselves to deal with it */
return SCPE_OK;
}
nia_data.cmd_rply = 0;
}
/* Check if we are running */
if ((nia_data.status & NIA_MRN) == 0 || (nia_data.status & NIA_CQA) == 0) {
return SCPE_OK;
}
/* or no commands pending, just idle out */
/* Try to get command off queue */
if (nia_getq(nia_data.cmd_hdr, &nia_data.cmd_entry) == 0) {
sim_activate(uptr, 200); /* Reschedule ourselves to deal with it */
return SCPE_OK;
}
/* Check if we got one */
if (nia_data.cmd_entry == 0) {
/* Nothing to do */
nia_data.status &= ~NIA_CQA;
return SCPE_OK;
}
/* Get command */
if (Mem_read_word(nia_data.cmd_entry + 3, &word1, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
cmd = (uint32)(word1 >> 12);
/* Save initial status */
nia_data.cmd_status = ((uint8)(cmd >> 16)) & 0xff;
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA cmd: %08x\n", cmd);
cmd &= 0xffff;
len = 5;
/* Preform function of command */
switch(cmd & 0xff) {
case NIA_CMD_SND: /* Send a datagram */
err = nia_send_pkt(cmd);
if (err != 0)
cmd |= ((err<<1)|1) << 16;
cmd |= NIA_STS_SR << 16;
len = 10;
break;
case NIA_CMD_LPTT: /* Load Protocol Type table */
nia_load_ptt();
break;
case NIA_CMD_LMAC: /* Load Multicast table */
nia_load_mcast();
break;
case NIA_CMD_RCNT: /* Read counts */
for (i = 0; i < NIA_CNT_LEN; i++) {
word1 = nia_data.pcnt[i];
if (Mem_write_word(nia_data.cnt_addr + i, &word1, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
if ((cmd & (NIA_FLG_CLRC << 20)) != 0)
nia_data.pcnt[i] = 0;
}
break;
case NIA_CMD_WPLI: /* Write PLI */
break;
case NIA_CMD_RPLI: /* Read PLI */
break;
case NIA_CMD_RNSA: /* Read Station Address */
len = 8;
word1 = ((uint64)nia_data.mac[0]) << 28;
word1 |= ((uint64)nia_data.mac[1]) << 20;
word1 |= ((uint64)nia_data.mac[2]) << 12;
word1 |= ((uint64)nia_data.mac[3]) << 4;
word2 = ((uint64)nia_data.mac[4]) << 28;
word2 |= ((uint64)nia_data.mac[5]) << 20;
if (Mem_write_word(nia_data.cmd_entry + 4, &word1, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
if (Mem_write_word(nia_data.cmd_entry + 5, &word2, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
word1 = (uint64)((nia_data.amc << 2)| (nia_data.h4000 << 1)
| nia_data.prmsc);
/* Micro code version, PTT len MACS len */
word2 = (nia_data.uver[3] << 12) |(0xF << 6)|0xF;
if (Mem_write_word(nia_data.cmd_entry + 6, &word1, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
if (Mem_write_word(nia_data.cmd_entry + 7, &word2, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
break;
case NIA_CMD_WNSA: /* Write Station Address */
len = 8;
if (Mem_read_word(nia_data.cmd_entry + 4, &word1, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
if (Mem_read_word(nia_data.cmd_entry + 5, &word2, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
nia_cpy_mac(word1, word2, &nia_data.mac);
if (Mem_read_word(nia_data.cmd_entry + 6, &word1, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
if (Mem_read_word(nia_data.cmd_entry + 7, &word2, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
nia_data.prmsc = (int)(word1 & 1);
nia_data.h4000 = (int)((word1 & 2) != 0);
nia_data.amc = (int)((word1 & 4) != 0);
memcpy(&nia_data.macs[0], &nia_data.mac, sizeof (ETH_MAC));
if (nia_recv_uptr->flags & UNIT_ATT)
eth_filter (&nia_data.etherface, nia_data.macs_n + 2,
nia_data.macs, 0, 0);
break;
case NIA_CMD_RCV: /* Received datagram */
default: /* Invalid command */
cmd |= ((NIA_ERR_UNK<<1)|1) << 16;
break;
}
nia_data.cmd_rply = nia_data.unk_hdr;
word1 = ((uint64)cmd) << 12;
if (Mem_write_word(nia_data.cmd_entry + 3, &word1, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
if (((cmd >> 16) & 1) != 0 || (cmd & (NIA_FLG_RESP << 8)) != 0) {
nia_data.cmd_rply = nia_data.resp_hdr;
} else if ((cmd & 0xff) == NIA_CMD_SND) {
if (Mem_read_word(nia_data.cmd_entry + 5, &word1, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
nia_data.cmd_rply = (t_addr)(word1 & AMASK);
}
for(i = 0; i < len; i++)
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA rcmd: %d %09llx %012llo\n",
i, M[nia_data.cmd_entry + i], M[nia_data.cmd_entry + i]);
(void)nia_putq(nia_data.cmd_rply, &nia_data.cmd_entry);
sim_activate(uptr, 500);
return SCPE_OK;
}
int
nia_rec_pkt()
{
struct nia_eth_hdr *hdr;
uint16 type;
int i;
int len;
t_addr queue;
t_addr bsd;
uint64 word;
uint8 *data;
/* See if we have received packet to process */
if (nia_data.rec_entry != 0) {
/* Have to put this response queue */
if (nia_putq(nia_data.resp_hdr, &nia_data.rec_entry) == 0)
return 0;
}
/* If no pending packet, return success */
if (nia_data.r_pkt == 0)
return 1;
/* Determine which queue to get free packet from */
hdr = (struct nia_eth_hdr *)(&nia_data.rec_buff.msg[0]);
type = ntohs(hdr->type);
queue = nia_data.unk_hdr;
for (i = 0; i < nia_data.ptt_n; i++) {
if (nia_data.ptt_proto[i] == type) {
queue = nia_data.ptt_head[i];
break;
}
}
/* Try to grab place to save packet */
if (nia_getq(queue, &nia_data.rec_entry) == 0)
return 0; /* Indicate packet not processed */
/* If we queue empty, just drop packet */
if (nia_data.rec_entry == 0) {
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA drop packet\n");
nia_data.r_pkt = 0; /* Drop packet it queue empty */
if (queue == nia_data.unk_hdr)
nia_data.pcnt[NIA_CNT_DUN]++;
else
nia_data.pcnt[NIA_CNT_D01 + i]++;
nia_data.pcnt[NIA_CNT_UBU] += nia_data.rec_buff.len;
nia_data.status |= NIA_FQE;
set_interrupt(NIA_DEVNUM, nia_data.status & NIA_PIA);
return 1; /* We did what we could with it. */
}
/* Get some information about packet */
len = nia_data.rec_buff.len - sizeof(struct nia_eth_hdr);
data = &nia_data.rec_buff.msg[sizeof(struct nia_eth_hdr)];
/* Got one, now fill in data */
word = (uint64)(NIA_CMD_RCV << 12);
if (Mem_write_word(nia_data.rec_entry + 3, &word, 0)) {
nia_error(EBSERR);
return SCPE_OK;
}
word = (uint64)len;
if (Mem_write_word(nia_data.rec_entry + 4, &word, 0)) {
nia_error(EBSERR);
return 0;
}
(void)nia_cpy_from(nia_data.rec_entry + 5,
(uint8 *)&hdr->dest, sizeof(ETH_MAC));
(void)nia_cpy_from(nia_data.rec_entry + 7,
(uint8 *)&hdr->src, sizeof(ETH_MAC));
word = (uint64)(((type & 0xff00) >> 4) |
((type & 0xff) << 12));
if (Mem_write_word(nia_data.rec_entry + 9, &word, 0)) {
nia_error(EBSERR);
return 0;
}
if (Mem_read_word(nia_data.rec_entry + 10, &word, 0)) {
nia_error(EBSERR);
return 0;
}
bsd = (t_addr)(word & AMASK);
while (len > 0) {
int blen;
/* Get length of segment */
if (Mem_read_word(bsd+2, &word, 0)) {
nia_error(EBSERR);
return 0;
}
blen = (int)(word & 0177777);
if (blen > len)
blen = len;
/* Get address of where to put data */
if (Mem_read_word(bsd, &word, 0)) {
nia_error(EBSERR);
return 0;
}
data = nia_cpy_from((t_addr)(word & AMASK), data, blen);
len -= blen;
/* Get pointer to next segment */
if (Mem_read_word(bsd+1, &word, 0)) {
nia_error(EBSERR);
return 0;
}
bsd = (t_addr)(word & AMASK);
}
for(i = 0; i < 10; i++)
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA recv: %d %09llx %012llo\n",
i, M[nia_data.rec_entry + i], M[nia_data.rec_entry + i]);
/* All done with packet */
nia_data.r_pkt = 0;
/* Put on response queue */
return nia_putq(nia_data.resp_hdr, &nia_data.rec_entry);
}
/*
* Receive ether net packets.
*/
t_stat nia_eth_srv(UNIT * uptr)
{
struct nia_eth_hdr *hdr;
uint16 type;
if (nia_data.poll)
sim_clock_coschedule(uptr, 1000); /* continue poll */
/* Check if we need to get a packet */
while (nia_data.r_pkt == 0) {
if (eth_read(&nia_data.etherface, &nia_data.rec_buff, NULL) <= 0)
return SCPE_OK;
nia_packet_debug(&nia_data, "recv", &nia_data.rec_buff);
hdr = (struct nia_eth_hdr *)(&nia_data.rec_buff.msg[0]);
type = ntohs(hdr->type);
/* Check if we are running */
if ((nia_data.status & NIA_MRN) == 0) {
sim_debug(DEBUG_DETAIL, &nia_dev,
"NIA read packet - not running: %d %04x\n",
nia_data.rec_buff.len, type);
return SCPE_OK;
}
sim_debug(DEBUG_DETAIL, &nia_dev, "NIA read packet: %d %04x\n",
nia_data.rec_buff.len, type);
nia_data.r_pkt = 1; /* Mark packet buffer full */
nia_data.pcnt[NIA_CNT_BR] += nia_data.rec_buff.len;
nia_data.pcnt[NIA_CNT_FR] ++;
if (hdr->dest[0] & 1) {
nia_data.pcnt[NIA_CNT_MCB] += nia_data.rec_buff.len;
nia_data.pcnt[NIA_CNT_MCF] ++;
}
/* Try to process the packet */
if (nia_rec_pkt() == 0) {
sim_activate(nia_proc_uptr, 100);
return SCPE_OK;
}
}
return SCPE_OK;
}
/*
* Handle delayed packets.
*/
t_stat nia_rec_srv(UNIT * uptr)
{
/* Process what we have */
if (nia_rec_pkt() == 0) {
sim_activate(uptr, 100);
return SCPE_OK;
}
/* See if we can pick up any more packets */
return nia_eth_srv(nia_recv_uptr);
}
t_stat nia_show_mac (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
{
char buffer[20];
eth_mac_fmt(&nia_data.mac, buffer);
fprintf(st, "MAC=%s", buffer);
return SCPE_OK;
}
t_stat nia_set_mac (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
{
t_stat status;
if (!cptr) return SCPE_IERR;
if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
status = eth_mac_scan_ex(&nia_data.mac, cptr, uptr);
if (status != SCPE_OK)
return status;
return SCPE_OK;
}
t_stat nia_reset (DEVICE *dptr)
{
int i;
for (i = 0; i < 6; i++) {
if (nia_data.mac[i] != 0)
break;
}
if (i == 6) { /* First call to reset? */
/* Set a default MAC address in a BBN assigned OID range no longer in use */
nia_set_mac (dptr->units, 0, "00:00:02:00:00:00/24", NULL);
}
return SCPE_OK;
}
/* attach device: */
t_stat nia_attach(UNIT* uptr, CONST char* cptr)
{
t_stat status;
char* tptr;
char buf[32];
tptr = (char *) malloc(strlen(cptr) + 1);
if (tptr == NULL) return SCPE_MEM;
strcpy(tptr, cptr);
memcpy(&nia_data.macs[0], &nia_data.mac, sizeof (ETH_MAC));
memcpy(&nia_data.macs[1], &broadcast_ethaddr, 6);
status = eth_open(&nia_data.etherface, cptr, &nia_dev, DEBUG_ETHER);
if (status != SCPE_OK) {
free(tptr);
return status;
}
eth_mac_fmt(&nia_data.mac, buf); /* format ethernet mac address */
if (SCPE_OK != eth_check_address_conflict (&nia_data.etherface,
&nia_data.mac)) {
eth_close(&nia_data.etherface);
free(tptr);
return sim_messagef (SCPE_NOATT,
"%s: MAC Address Conflict on LAN for address %s\n",
nia_dev.name, buf);
}
if (SCPE_OK != eth_filter(&nia_data.etherface, 2, nia_data.macs, 0, 0)) {
eth_close(&nia_data.etherface);
free(tptr);
return sim_messagef (SCPE_NOATT,
"%s: Can't set packet filter for MAC Address %s\n",
nia_dev.name, buf);
}
uptr->filename = tptr;
uptr->flags |= UNIT_ATT;
eth_setcrc(&nia_data.etherface, 1); /* Enable CRC */
/* init read queue (first time only) */
status = ethq_init(&nia_data.ReadQ, 8);
if (status != SCPE_OK) {
eth_close(&nia_data.etherface);
uptr->filename = NULL;
free(tptr);
return sim_messagef (status, "%s: Can't initialize receive queue\n",
nia_dev.name);
}
/* Allow Asynchronous inbound packets */
if (eth_set_async (&nia_data.etherface, 0) == SCPE_OK)
nia_data.poll = 0;
else {
nia_data.poll = 1;
sim_activate (&nia_unit[0], 100);
}
return SCPE_OK;
}
/* detach device: */
t_stat nia_detach(UNIT* uptr)
{
if (uptr->flags & UNIT_ATT) {
sim_cancel(&nia_unit[1]);
sim_cancel(&nia_unit[2]);
eth_close (&nia_data.etherface);
free(uptr->filename);
uptr->filename = NULL;
uptr->flags &= ~UNIT_ATT;
}
return SCPE_OK;
}
t_stat nia_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "NIA interface\n\n");
fprintf (st, "The NIA interfaces to the network. Setting MAC defines default MAC address\n");
fprint_set_help (st, dptr);
fprint_show_help (st, dptr);
eth_attach_help(st, dptr, uptr, flag, cptr);
return SCPE_OK;
}
const char *nia_description (DEVICE *dptr)
{
return "KL NIA interface";
}
#endif