|
|
GNU Mach 1.0
/*
* Mach Operating System
* Copyright (c) 1991,1990,1989 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or [email protected]
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/*
* Olivetti PC586 Mach Ethernet driver v1.0
* Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989
* All rights reserved.
*
*/
/*
Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
Cupertino, California.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appears in all
copies and that both the copyright notice and this permission notice
appear in supporting documentation, and that the name of Olivetti
not be used in advertising or publicity pertaining to distribution
of the software without specific, written prior permission.
OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appears in all
copies and that both the copyright notice and this permission notice
appear in supporting documentation, and that the name of Intel
not be used in advertising or publicity pertaining to distribution
of the software without specific, written prior permission.
INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* NOTE:
* by rvb:
* 1. The best book on the 82586 is:
* LAN Components User's Manual by Intel
* The copy I found was dated 1984. This really tells you
* what the state machines are doing
* 2. In the current design, we only do one write at a time,
* though the hardware is capable of chaining and possibly
* even batching. The problem is that we only make one
* transmit buffer available in sram space.
* 3.
* n. Board Memory Map
RFA/FD 0 - 227 0x228 bytes
226 = 0x19 * 0x16 bytes
RBD 228 - 3813 0x35ec bytes
35e8 = 0x19 * 0x228 bytes
== 0x0a bytes (bd) + 2 bytes + 21c bytes
CU 3814 - 3913 0x100 bytes
TBD 3914 - 39a3 0x90 bytes
90 = No 18 * 0x08 bytes
TBUF 39a4 - 3fdd 0x63a bytes (= 1594(10))
SCB 3fde - 3fed 0x10 bytes
ISCP 3fee - 3ff5 0x08 bytes
SCP 3ff6 - 3fff 0x0a bytes
*
*/
/*
* NOTE:
*
* Currently this driver doesn't support trailer protocols for
* packets. Once that is added, please remove this comment.
*
* Also, some lacking material includes the DLI code. If you
* are compiling this driver with DLI set, lookout, that code
* has not been looked at.
*
*/
#define DEBUG
#define IF_CNTRS MACH
#define NDLI 0
#include <pc586.h>
#ifdef MACH_KERNEL
#include <kern/time_out.h>
#include <device/device_types.h>
#include <device/errno.h>
#include <device/io_req.h>
#include <device/if_hdr.h>
#include <device/if_ether.h>
#include <device/net_status.h>
#include <device/net_io.h>
#else MACH_KERNEL
#include <sys/param.h>
#include <mach/machine/vm_param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/buf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/vmmac.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/netisr.h>
#include <net/route.h>
#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#endif
#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif
#if DLI
#include <net/dli_var.h>
struct dli_var de_dlv[NDE];
#endif DLI
#endif MACH_KERNEL
#include <i386/ipl.h>
#include <mach/vm_param.h>
#include <vm/vm_kern.h>
#include <chips/busses.h>
#include <i386at/if_pc586.h>
#define SPLNET spl6
#if __STDC__
#define CMD(x, y, unit) *(u_short *)(pc_softc[unit].prom + OFFSET_ ## x) = (u_short) (y)
#else __STDC__
#define CMD(x, y, unit) *(u_short *)(pc_softc[unit].prom + OFFSET_/**/x) = (u_short) (y)
#endif __STDC__
#define pc586chatt(unit) CMD(CHANATT, 0x0001, unit)
#define pc586inton(unit) CMD(INTENAB, CMD_1, unit)
#define pc586intoff(unit) CMD(INTENAB, CMD_0, unit)
int pc586probe();
void pc586attach();
int pc586intr(), pc586init(), pc586output(), pc586ioctl(), pc586reset();
int pc586watch(), pc586rcv(), pc586xmt(), pc586bldcu();
int pc586diag(), pc586config();
char *pc586bldru();
char *ram_to_ptr();
u_short ptr_to_ram();
static vm_offset_t pc586_std[NPC586] = { 0 };
static struct bus_device *pc586_info[NPC586];
struct bus_driver pcdriver =
{pc586probe, 0, pc586attach, 0, pc586_std, "pc", pc586_info, 0, 0, 0};
char t_packet[ETHERMTU + sizeof(struct ether_header) + sizeof(long)];
int xmt_watch = 0;
typedef struct {
#ifdef MACH_KERNEL
struct ifnet ds_if; /* generic interface header */
u_char ds_addr[6]; /* Ethernet hardware address */
#else MACH_KERNEL
struct arpcom pc586_ac;
#define ds_if pc586_ac.ac_if
#define ds_addr pc586_ac.ac_enaddr
#endif MACH_KERNEL
int flags;
int seated;
int timer;
int open;
fd_t *begin_fd;
fd_t *end_fd;
rbd_t *end_rbd;
char *prom;
char *sram;
int tbusy;
short mode;
} pc_softc_t;
pc_softc_t pc_softc[NPC586];
struct pc586_cntrs {
struct {
u_int xmt, xmti;
u_int defer;
u_int busy;
u_int sleaze, intrinsic, intrinsic_count;
u_int chain;
} xmt;
struct {
u_int rcv;
u_int ovw;
u_int crc;
u_int frame;
u_int rscerrs, ovrnerrs;
u_int partial, bad_chain, fill;
} rcv;
u_int watch;
} pc586_cntrs[NPC586];
#ifdef IF_CNTRS
int pc586_narp = 1, pc586_arp = 0;
int pc586_ein[32], pc586_eout[32];
int pc586_lin[128/8], pc586_lout[128/8];
static
log_2(no)
unsigned long no;
{
return ({ unsigned long _temp__;
asm("bsr %1, %0; jne 0f; xorl %0, %0; 0:" :
"=r" (_temp__) : "a" (no));
_temp__;});
}
#endif IF_CNTRS
/*
* pc586probe:
*
* This function "probes" or checks for the pc586 board on the bus to see
* if it is there. As far as I can tell, the best break between this
* routine and the attach code is to simply determine whether the board
* is configured in properly. Currently my approach to this is to write
* and read a word from the SRAM on the board being probed. If the word
* comes back properly then we assume the board is there. The config
* code expects to see a successful return from the probe routine before
* attach will be called.
*
* input : address device is mapped to, and unit # being checked
* output : a '1' is returned if the board exists, and a 0 otherwise
*
*/
pc586probe(port, dev)
struct bus_device *dev;
{
caddr_t addr = (caddr_t)dev->address;
int unit = dev->unit;
int len = round_page(0x4000);
int sram_len = round_page(0x4000);
extern vm_offset_t phys_last_addr;
int i;
volatile char *b_prom;
volatile char *b_sram;
volatile u_short*t_ps;
if ((unit < 0) || (unit > NPC586)) {
printf("pc%d: board out of range [0..%d]\n",
unit, NPC586);
return(0);
}
if ((addr > (caddr_t)0x100000) && (addr < (caddr_t)phys_last_addr))
return 0;
if (kmem_alloc_pageable(kernel_map, (vm_offset_t *) &b_prom, len)
!= KERN_SUCCESS) {
printf("pc%d: can not allocate memory for prom.\n", unit);
return 0;
}
if (kmem_alloc_pageable(kernel_map, (vm_offset_t *) &b_sram, sram_len)
!= KERN_SUCCESS) {
printf("pc%d: can not allocate memory for sram.\n", unit);
return 0;
}
(void)pmap_map(b_prom, (vm_offset_t)addr,
(vm_offset_t)addr+len,
VM_PROT_READ | VM_PROT_WRITE);
if ((int)addr > 0x100000) /* stupid hardware */
addr += EXTENDED_ADDR;
addr += 0x4000; /* sram space */
(void)pmap_map(b_sram, (vm_offset_t)addr,
(vm_offset_t)addr+sram_len,
VM_PROT_READ | VM_PROT_WRITE);
*(b_prom + OFFSET_RESET) = 1;
{ int i; for (i = 0; i < 1000; i++); /* 4 clocks at 6Mhz */}
*(b_prom + OFFSET_RESET) = 0;
t_ps = (u_short *)(b_sram + OFFSET_SCB);
*(t_ps) = (u_short)0x5a5a;
if (*(t_ps) != (u_short)0x5a5a) {
kmem_free(kernel_map, b_prom, len);
kmem_free(kernel_map, b_sram, sram_len);
return(0);
}
t_ps = (u_short *)(b_prom + + OFFSET_PROM);
#define ETHER0 0x00
#define ETHER1 0xaa
#define ETHER2 0x00
if ((t_ps[0]&0xff) == ETHER0 &&
(t_ps[1]&0xff) == ETHER1 &&
(t_ps[2]&0xff) == ETHER2)
pc_softc[unit].seated = TRUE;
#undef ETHER0
#undef ETHER1
#undef ETHER2
#define ETHER0 0x00
#define ETHER1 0x00
#define ETHER2 0x1c
if ((t_ps[0]&0xff) == ETHER0 ||
(t_ps[1]&0xff) == ETHER1 ||
(t_ps[2]&0xff) == ETHER2)
pc_softc[unit].seated = TRUE;
#undef ETHER0
#undef ETHER1
#undef ETHER2
if (pc_softc[unit].seated != TRUE) {
kmem_free(kernel_map, b_prom, len);
kmem_free(kernel_map, b_sram, sram_len);
return(0);
}
(volatile char *)pc_softc[unit].prom = (volatile char *)b_prom;
(volatile char *)pc_softc[unit].sram = (volatile char *)b_sram;
return(1);
}
/*
* pc586attach:
*
* This function attaches a PC586 board to the "system". The rest of
* runtime structures are initialized here (this routine is called after
* a successful probe of the board). Once the ethernet address is read
* and stored, the board's ifnet structure is attached and readied.
*
* input : bus_device structure setup in autoconfig
* output : board structs and ifnet is setup
*
*/
void pc586attach(dev)
struct bus_device *dev;
{
struct ifnet *ifp;
u_char *addr_p;
u_short *b_addr;
u_char unit = (u_char)dev->unit;
pc_softc_t *sp = &pc_softc[unit];
volatile scb_t *scb_p;
take_dev_irq(dev);
printf(", port = %x, spl = %d, pic = %d. ",
dev->address, dev->sysdep, dev->sysdep1);
sp->timer = -1;
sp->flags = 0;
sp->mode = 0;
sp->open = 0;
CMD(RESET, CMD_1, unit);
{ int i; for (i = 0; i < 1000; i++); /* 4 clocks at 6Mhz */}
CMD(RESET, CMD_0, unit);
b_addr = (u_short *)(sp->prom + OFFSET_PROM);
addr_p = (u_char *)sp->ds_addr;
addr_p[0] = b_addr[0];
addr_p[1] = b_addr[1];
addr_p[2] = b_addr[2];
addr_p[3] = b_addr[3];
addr_p[4] = b_addr[4];
addr_p[5] = b_addr[5];
printf("ethernet id [%x:%x:%x:%x:%x:%x]",
addr_p[0], addr_p[1], addr_p[2],
addr_p[3], addr_p[4], addr_p[5]);
scb_p = (volatile scb_t *)(sp->sram + OFFSET_SCB);
scb_p->scb_crcerrs = 0; /* initialize counters */
scb_p->scb_alnerrs = 0;
scb_p->scb_rscerrs = 0;
scb_p->scb_ovrnerrs = 0;
ifp = &(sp->ds_if);
ifp->if_unit = unit;
ifp->if_mtu = ETHERMTU;
ifp->if_flags = IFF_BROADCAST;
#ifdef MACH_KERNEL
ifp->if_header_size = sizeof(struct ether_header);
ifp->if_header_format = HDR_ETHERNET;
ifp->if_address_size = 6;
ifp->if_address = (char *)&sp->ds_addr[0];
if_init_queues(ifp);
#else MACH_KERNEL
ifp->if_name = "pc";
ifp->if_init = pc586init;
ifp->if_output = pc586output;
ifp->if_ioctl = pc586ioctl;
ifp->if_reset = pc586reset;
ifp->if_next = NULL;
if_attach(ifp);
#endif MACH_KERNEL
}
/*
* pc586reset:
*
* This routine is in part an entry point for the "if" code. Since most
* of the actual initialization has already (we hope already) been done
* by calling pc586attach().
*
* input : unit number or board number to reset
* output : board is reset
*
*/
pc586reset(unit)
int unit;
{
pc_softc[unit].ds_if.if_flags &= ~IFF_RUNNING;
pc_softc[unit].flags &= ~(DSF_LOCK|DSF_RUNNING);
return(pc586init(unit));
}
/*
* pc586init:
*
* Another routine that interfaces the "if" layer to this driver.
* Simply resets the structures that are used by "upper layers".
* As well as calling pc586hwrst that does reset the pc586 board.
*
* input : board number
* output : structures (if structs) and board are reset
*
*/
pc586init(unit)
int unit;
{
struct ifnet *ifp;
int stat;
spl_t oldpri;
ifp = &(pc_softc[unit].ds_if);
#ifdef MACH_KERNEL
#else MACH_KERNEL
if (ifp->if_addrlist == (struct ifaddr *)0) {
return;
}
#endif MACH_KERNEL
oldpri = SPLNET();
if ((stat = pc586hwrst(unit)) == TRUE) {
#ifdef MACH_KERNEL
#undef HZ
#define HZ hz
#endif MACH_KERNEL
timeout(pc586watch, &(ifp->if_unit), 5*HZ);
pc_softc[unit].timer = 5;
pc_softc[unit].ds_if.if_flags |= IFF_RUNNING;
pc_softc[unit].flags |= DSF_RUNNING;
pc_softc[unit].tbusy = 0;
pc586start(unit);
#if DLI
dli_init();
#endif DLI
} else
printf("pc%d init(): trouble resetting board.\n", unit);
splx(oldpri);
return(stat);
}
#ifdef MACH_KERNEL
/*ARGSUSED*/
pc586open(dev, flag)
dev_t dev;
int flag;
{
register int unit;
pc_softc_t *sp;
unit = minor(dev); /* XXX */
if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
return (ENXIO);
pc_softc[unit].ds_if.if_flags |= IFF_UP;
pc586init(unit);
return (0);
}
#endif MACH_KERNEL
/*
* pc586start:
*
* This is yet another interface routine that simply tries to output a
* in an mbuf after a reset.
*
* input : board number
* output : stuff sent to board if any there
*
*/
pc586start(unit)
int unit;
{
#ifdef MACH_KERNEL
io_req_t m;
#else MACH_KERNEL
struct mbuf *m;
#endif MACH_KERNEL
struct ifnet *ifp;
register pc_softc_t *is = &pc_softc[unit];
volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
if (is->tbusy) {
if (!(scb_p->scb_status & 0x0700)) { /* ! IDLE */
is->tbusy = 0;
pc586_cntrs[unit].xmt.busy++;
/*
* This is probably just a race. The xmt'r is just
* became idle but WE have masked interrupts so ...
*/
if (xmt_watch) printf("!!");
} else
return;
}
ifp = &(pc_softc[unit].ds_if);
IF_DEQUEUE(&ifp->if_snd, m);
#ifdef MACH_KERNEL
if (m != 0)
#else MACH_KERNEL
if (m != (struct mbuf *)0)
#endif MACH_KERNEL
{
is->tbusy++;
pc586_cntrs[unit].xmt.xmt++;
pc586xmt(unit, m);
}
return;
}
/*
* pc586read:
*
* This routine does the actual copy of data (including ethernet header
* structure) from the pc586 to an mbuf chain that will be passed up
* to the "if" (network interface) layer. NOTE: we currently
* don't handle trailer protocols, so if that is needed, it will
* (at least in part) be added here. For simplicities sake, this
* routine copies the receive buffers from the board into a local (stack)
* buffer until the frame has been copied from the board. Once in
* the local buffer, the contents are copied to an mbuf chain that
* is then enqueued onto the appropriate "if" queue.
*
* input : board number, and an frame descriptor pointer
* output : the packet is put into an mbuf chain, and passed up
* assumes : if any errors occur, packet is "dropped on the floor"
*
*/
pc586read(unit, fd_p)
int unit;
fd_t *fd_p;
{
register pc_softc_t *is = &pc_softc[unit];
register struct ifnet *ifp = &is->ds_if;
struct ether_header eh;
#ifdef MACH_KERNEL
ipc_kmsg_t new_kmsg;
struct ether_header *ehp;
struct packet_header *pkt;
char *dp;
#else MACH_KERNEL
struct mbuf *m, *tm;
#endif MACH_KERNEL
rbd_t *rbd_p;
u_char *buffer_p;
u_char *mb_p;
u_short mlen, len, clen;
u_short bytes_in_msg, bytes_in_mbuf, bytes;
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
printf("pc%d read(): board is not running.\n", ifp->if_unit);
pc586intoff(ifp->if_unit);
}
pc586_cntrs[unit].rcv.rcv++;
#ifdef MACH_KERNEL
new_kmsg = net_kmsg_get();
if (new_kmsg == IKM_NULL) {
/*
* Drop the received packet.
*/
is->ds_if.if_rcvdrops++;
/*
* not only do we want to return, we need to drop the packet on
* the floor to clear the interrupt.
*/
return 1;
}
ehp = (struct ether_header *) (&net_kmsg(new_kmsg)->header[0]);
pkt = (struct packet_header *)(&net_kmsg(new_kmsg)->packet[0]);
/*
* Get ether header.
*/
ehp->ether_type = fd_p->length;
len = sizeof(struct ether_header);
bcopy16(fd_p->source, ehp->ether_shost, ETHER_ADD_SIZE);
bcopy16(fd_p->destination, ehp->ether_dhost, ETHER_ADD_SIZE);
/*
* Get packet body.
*/
dp = (char *)(pkt + 1);
rbd_p = (rbd_t *)ram_to_ptr(fd_p->rbd_offset, unit);
if (rbd_p == 0) {
printf("pc%d read(): Invalid buffer\n", unit);
if (pc586hwrst(unit) != TRUE) {
printf("pc%d read(): hwrst trouble.\n", unit);
}
net_kmsg_put(new_kmsg);
return 0;
}
do {
buffer_p = (u_char *)(pc_softc[unit].sram + rbd_p->buffer_addr);
bytes_in_msg = rbd_p->status & RBD_SW_COUNT;
bcopy16((u_short *)buffer_p,
(u_short *)dp,
(bytes_in_msg + 1) & ~1); /* but we know it's even */
len += bytes_in_msg;
dp += bytes_in_msg;
if (rbd_p->status & RBD_SW_EOF)
break;
rbd_p = (rbd_t *)ram_to_ptr(rbd_p->next_rbd_offset, unit);
} while ((int) rbd_p);
pkt->type = ehp->ether_type;
pkt->length =
len - sizeof(struct ether_header)
+ sizeof(struct packet_header);
/*
* Send the packet to the network module.
*/
net_packet(ifp, new_kmsg, pkt->length, ethernet_priority(new_kmsg));
return 1;
#else MACH_KERNEL
eh.ether_type = ntohs(fd_p->length);
bcopy16(fd_p->source, eh.ether_shost, ETHER_ADD_SIZE);
bcopy16(fd_p->destination, eh.ether_dhost, ETHER_ADD_SIZE);
if ((rbd_p =(rbd_t *)ram_to_ptr(fd_p->rbd_offset, unit))== (rbd_t *)NULL) {
printf("pc%d read(): Invalid buffer\n", unit);
if (pc586hwrst(unit) != TRUE) {
printf("pc%d read(): hwrst trouble.\n", unit);
}
return 0;
}
bytes_in_msg = rbd_p->status & RBD_SW_COUNT;
buffer_p = (u_char *)(pc_softc[unit].sram + rbd_p->buffer_addr);
MGET(m, M_DONTWAIT, MT_DATA);
tm = m;
if (m == (struct mbuf *)0) {
/*
* not only do we want to return, we need to drop the packet on
* the floor to clear the interrupt.
*
*/
printf("pc%d read(): No mbuf 1st\n", unit);
if (pc586hwrst(unit) != TRUE) {
pc586intoff(unit);
printf("pc%d read(): hwrst trouble.\n", unit);
pc_softc[unit].timer = 0;
}
return 0;
}
m->m_next = (struct mbuf *) 0;
m->m_len = MLEN;
if (bytes_in_msg > 2 * MLEN - sizeof (struct ifnet **)) {
MCLGET(m);
}
/*
* first mbuf in the packet must contain a pointer to the
* ifnet structure. other mbufs that follow and make up
* the packet do not need this pointer in the mbuf.
*
*/
*(mtod(tm, struct ifnet **)) = ifp;
mlen = sizeof (struct ifnet **);
clen = mlen;
bytes_in_mbuf = m->m_len - sizeof(struct ifnet **);
mb_p = mtod(tm, u_char *) + sizeof (struct ifnet **);
bytes = min(bytes_in_mbuf, bytes_in_msg);
do {
if (bytes & 1)
len = bytes + 1;
else
len = bytes;
bcopy16(buffer_p, mb_p, len);
clen += bytes;
mlen += bytes;
if (!(bytes_in_mbuf -= bytes)) {
MGET(tm->m_next, M_DONTWAIT, MT_DATA);
tm = tm->m_next;
if (tm == (struct mbuf *)0) {
m_freem(m);
printf("pc%d read(): No mbuf nth\n", unit);
if (pc586hwrst(unit) != TRUE) {
pc586intoff(unit);
printf("pc%d read(): hwrst trouble.\n", unit);
pc_softc[unit].timer = 0;
}
return 0;
}
mlen = 0;
tm->m_len = MLEN;
bytes_in_mbuf = MLEN;
mb_p = mtod(tm, u_char *);
} else
mb_p += bytes;
if (!(bytes_in_msg -= bytes)) {
if (rbd_p->status & RBD_SW_EOF ||
(rbd_p = (rbd_t *)ram_to_ptr(rbd_p->next_rbd_offset, unit)) ==
NULL) {
tm->m_len = mlen;
break;
} else {
bytes_in_msg = rbd_p->status & RBD_SW_COUNT;
buffer_p = (u_char *)(pc_softc[unit].sram + rbd_p->buffer_addr);
}
} else
buffer_p += bytes;
bytes = min(bytes_in_mbuf, bytes_in_msg);
} while(1);
#ifdef IF_CNTRS
/* clen -= (sizeof (struct ifnet **)
clen += 4 /* crc */;
clen += sizeof (struct ether_header);
pc586_ein[log_2(clen)]++;
if (clen < 128) pc586_lin[clen>>3]++;
if (eh.ether_type == ETHERTYPE_ARP) {
pc586_arp++;
if (pc586_narp) {
pc586_ein[log_2(clen)]--;
if (clen < 128) pc586_lin[clen>>3]--;
}
}
#endif IF_CNTRS
/*
* received packet is now in a chain of mbuf's. next step is
* to pass the packet upwards.
*
*/
pc586send_packet_up(m, &eh, is);
return 1;
#endif MACH_KERNEL
}
/*
* Send a packet composed of an mbuf chain to the higher levels
*
*/
#ifndef MACH_KERNEL
pc586send_packet_up(m, eh, is)
struct mbuf *m;
struct ether_header *eh;
pc_softc_t *is;
{
register struct ifqueue *inq;
spl_t opri;
switch (eh->ether_type) {
#ifdef INET
case ETHERTYPE_IP:
schednetisr(NETISR_IP);
inq = &ipintrq;
break;
case ETHERTYPE_ARP:
arpinput(&is->pc586_ac, m);
return;
#endif
#ifdef NS
case ETHERTYPE_NS:
schednetisr(NETISR_NS);
inq = &nsintrq;
break;
#endif
default:
#if DLI
{
eh.ether_type = htons(eh.ether_type);
dli_input(m,eh.ether_type,&eh.ether_shost[0],
&de_dlv[ds->ds_if.if_unit], &eh);
}
#else DLI
m_freem(m);
#endif DLI
return;
}
opri = SPLNET();
if (IF_QFULL(inq)) {
IF_DROP(inq);
splx(opri);
m_freem(m);
return;
}
IF_ENQUEUE(inq, m);
splx(opri);
return;
}
#endif MACH_KERNEL
#ifdef MACH_KERNEL
pc586output(dev, ior)
dev_t dev;
io_req_t ior;
{
register int unit;
unit = minor(dev); /* XXX */
if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
return (ENXIO);
return (net_write(&pc_softc[unit].ds_if, pc586start, ior));
}
pc586setinput(dev, receive_port, priority, filter, filter_count)
dev_t dev;
mach_port_t receive_port;
int priority;
filter_t filter[];
unsigned int filter_count;
{
register int unit = minor(dev);
if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
return (ENXIO);
return (net_set_filter(&pc_softc[unit].ds_if,
receive_port, priority,
filter, filter_count));
}
#else MACH_KERNEL
/*
* pc586output:
*
* This routine is called by the "if" layer to output a packet to
* the network. This code resolves the local ethernet address, and
* puts it into the mbuf if there is room. If not, then a new mbuf
* is allocated with the header information and precedes the data
* to be transmitted. The routines that actually transmit the
* data (pc586xmt()) expect the ethernet structure to precede
* the data in the mbuf. This information is required by the
* 82586's transfer command segment, and thus mbuf's cannot
* be simply "slammed" out onto the network.
*
* input: ifnet structure pointer, an mbuf with data, and address
* to be resolved
* output: mbuf is updated to hold enet address, or a new mbuf
* with the address is added
*
*/
pc586output(ifp, m0, dst)
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
{
register pc_softc_t *is = &pc_softc[ifp->if_unit];
register struct mbuf *m = m0;
int type, error;
spl_t opri;
u_char edst[6];
struct in_addr idst;
register struct ether_header *eh;
register int off;
int usetrailers;
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
printf("pc%d output(): board is not running.\n", ifp->if_unit);
pc586intoff(ifp->if_unit);
error = ENETDOWN;
goto bad;
}
switch (dst->sa_family) {
#ifdef INET
case AF_INET:
idst = ((struct sockaddr_in *)dst)->sin_addr;
if (!arpresolve(&is->pc586_ac, m, &idst, edst, &usetrailers)){
return (0); /* if not yet resolved */
}
off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
type = ETHERTYPE_TRAIL + (off>>9);
m->m_off -= 2 * sizeof (u_short);
m->m_len += 2 * sizeof (u_short);
*mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
goto gottrailertype;
}
type = ETHERTYPE_IP;
off = 0;
goto gottype;
#endif
#ifdef NS
case AF_NS:
type = ETHERTYPE_NS;
bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
(caddr_t)edst, sizeof (edst));
off = 0;
goto gottype;
#endif
#if DLI
case AF_DLI:
if (m->m_len < sizeof(struct ether_header))
{
error = EMSGSIZE;
goto bad;
}
eh = mtod(m, struct ether_header *);
bcopy(dst->sa_data, (caddr_t)eh->ether_dhost,
sizeof (eh->ether_dhost));
goto gotheader;
#endif DLI
case AF_UNSPEC:
eh = (struct ether_header *)dst->sa_data;
bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
type = eh->ether_type;
goto gottype;
default:
printf("pc%d output(): can't handle af%d\n",
ifp->if_unit, dst->sa_family);
error = EAFNOSUPPORT;
goto bad;
}
gottrailertype:
/*
* Packet to be sent as trailer: move first packet
* (control information) to end of chain.
*/
while (m->m_next)
m = m->m_next;
m->m_next = m0;
m = m0->m_next;
m0->m_next = 0;
m0 = m;
gottype:
/*
* Add local net header. If no space in first mbuf,
* allocate another.
*/
if (m->m_off > MMAXOFF ||
MMINOFF + sizeof (struct ether_header) > m->m_off) {
m = m_get(M_DONTWAIT, MT_HEADER);
if (m == 0) {
error = ENOBUFS;
goto bad;
}
m->m_next = m0;
m->m_off = MMINOFF;
m->m_len = sizeof (struct ether_header);
} else {
m->m_off -= sizeof (struct ether_header);
m->m_len += sizeof (struct ether_header);
}
eh = mtod(m, struct ether_header *);
eh->ether_type = htons((u_short)type);
bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
bcopy((caddr_t)is->ds_addr,(caddr_t)eh->ether_shost, sizeof(edst));
#if DLI
gotheader:
#endif DLI
/*
* Queue message on interface, and start output if interface
* not yet active.
*/
opri = SPLNET();
if (IF_QFULL(&ifp->if_snd)) {
IF_DROP(&ifp->if_snd);
splx(opri);
m_freem(m);
return (ENOBUFS);
}
IF_ENQUEUE(&ifp->if_snd, m);
/*
* Some action needs to be added here for checking whether the
* board is already transmitting. If it is, we don't want to
* start it up (ie call pc586start()). We will attempt to send
* packets that are queued up after an interrupt occurs. Some
* flag checking action has to happen here and/or in the start
* routine. This note is here to remind me that some thought
* is needed and there is a potential problem here.
*
*/
pc586start(ifp->if_unit);
splx(opri);
return (0);
bad:
m_freem(m0);
return (error);
}
#endif MACH_KERNEL
#ifdef MACH_KERNEL
pc586getstat(dev, flavor, status, count)
dev_t dev;
int flavor;
dev_status_t status; /* pointer to OUT array */
unsigned int *count; /* out */
{
register int unit = minor(dev);
register pc_softc_t *sp;
if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
return (ENXIO);
sp = &pc_softc[unit];
return (net_getstat(&sp->ds_if, flavor, status, count));
}
pc586setstat(dev, flavor, status, count)
dev_t dev;
int flavor;
dev_status_t status;
unsigned int count;
{
register int unit = minor(dev);
register pc_softc_t *sp;
if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
return (ENXIO);
sp = &pc_softc[unit];
switch (flavor) {
case NET_STATUS:
{
/*
* All we can change are flags, and not many of those.
*/
register struct net_status *ns = (struct net_status *)status;
int mode = 0;
if (count < NET_STATUS_COUNT)
return (D_INVALID_OPERATION);
if (ns->flags & IFF_ALLMULTI)
mode |= MOD_ENAL;
if (ns->flags & IFF_PROMISC)
mode |= MOD_PROM;
/*
* Force a complete reset if the receive mode changes
* so that these take effect immediately.
*/
if (sp->mode != mode) {
sp->mode = mode;
if (sp->flags & DSF_RUNNING) {
sp->flags &= ~(DSF_LOCK|DSF_RUNNING);
pc586init(unit);
}
}
break;
}
default:
return (D_INVALID_OPERATION);
}
return (D_SUCCESS);
}
#else MACH_KERNEL
/*
* pc586ioctl:
*
* This routine processes an ioctl request from the "if" layer
* above.
*
* input : pointer the appropriate "if" struct, command, and data
* output : based on command appropriate action is taken on the
* pc586 board(s) or related structures
* return : error is returned containing exit conditions
*
*/
pc586ioctl(ifp, cmd, data)
struct ifnet *ifp;
int cmd;
caddr_t data;
{
register struct ifaddr *ifa = (struct ifaddr *)data;
int unit = ifp->if_unit;
register pc_softc_t *is = &pc_softc[unit];
short mode = 0;
int error = 0;
spl_t opri;
opri = SPLNET();
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
pc586init(unit);
switch (ifa->ifa_addr.sa_family) {
#ifdef INET
case AF_INET:
((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
break;
#endif
#ifdef NS
case AF_NS:
{
register struct ns_addr *ina =
&(IA_SNS(ifa)->sns_addr);
if (ns_nullhost(*ina))
ina->x_host = *(union ns_host *)(ds->ds_addr);
else
pc586setaddr(ina->x_host.c_host, unit);
break;
}
#endif
}
break;
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_ALLMULTI)
mode |= MOD_ENAL;
if (ifp->if_flags & IFF_PROMISC)
mode |= MOD_PROM;
/*
* force a complete reset if the receive multicast/
* promiscuous mode changes so that these take
* effect immediately.
*
*/
if (is->mode != mode) {
is->mode = mode;
if (is->flags & DSF_RUNNING) {
is->flags &= ~(DSF_LOCK|DSF_RUNNING);
pc586init(unit);
}
}
if ((ifp->if_flags & IFF_UP) == 0 && is->flags & DSF_RUNNING) {
printf("pc%d ioctl(): board is not running\n", unit);
is->flags &= ~(DSF_LOCK | DSF_RUNNING);
is->timer = -1;
pc586intoff(unit);
} else if (ifp->if_flags & IFF_UP && (is->flags & DSF_RUNNING) == 0) {
pc586init(unit);
}
break;
#ifdef IF_CNTRS
case SIOCCIFCNTRS:
if (!suser()) {
error = EPERM;
break;
}
bzero((caddr_t)pc586_ein, sizeof (pc586_ein));
bzero((caddr_t)pc586_eout, sizeof (pc586_eout));
bzero((caddr_t)pc586_lin, sizeof (pc586_lin));
bzero((caddr_t)pc586_lout, sizeof (pc586_lout));
bzero((caddr_t)&pc586_arp, sizeof (int));
bzero((caddr_t)&pc586_cntrs, sizeof (pc586_cntrs));
break;
#endif IF_CNTRS
default:
error = EINVAL;
}
splx(opri);
return (error);
}
#endif MACH_KERNEL
/*
* pc586hwrst:
*
* This routine resets the pc586 board that corresponds to the
* board number passed in.
*
* input : board number to do a hardware reset
* output : board is reset
*
*/
pc586hwrst(unit)
int unit;
{
CMD(CHANATT, CMD_0, unit);
CMD(RESET, CMD_1, unit);
{ int i; for (i = 0; i < 1000; i++); /* 4 clocks at 6Mhz */}
CMD(RESET,CMD_0, unit);
/*
* for (i = 0; i < 1000000; i++);
* with this loop above and with the reset toggle also looping to
* 1000000. We don't see the reset behaving as advertised. DOES
* IT HAPPEN AT ALL. In particular, NORMODE, ENABLE, and XFER
* should all be zero and they have not changed at all.
*/
CMD(INTENAB, CMD_0, unit);
CMD(NORMMODE, CMD_0, unit);
CMD(XFERMODE, CMD_1, unit);
pc586bldcu(unit);
if (pc586diag(unit) == FALSE)
return(FALSE);
if (pc586config(unit) == FALSE)
return(FALSE);
/*
* insert code for loopback test here
*
*/
pc586rustrt(unit);
pc586inton(unit);
CMD(NORMMODE, CMD_1, unit);
return(TRUE);
}
/*
* pc586watch():
*
* This routine is the watchdog timer routine for the pc586 chip. If
* chip wedges, this routine will fire and cause a board reset and
* begin again.
*
* input : which board is timing out
* output : potential board reset if wedged
*
*/
int watch_dead = 0;
pc586watch(b_ptr)
caddr_t b_ptr;
{
spl_t opri;
int unit = *b_ptr;
if ((pc_softc[unit].ds_if.if_flags & IFF_UP) == 0) {
return;
}
if (pc_softc[unit].timer == -1) {
timeout(pc586watch, b_ptr, 5*HZ);
return;
}
if (--pc_softc[unit].timer != -1) {
timeout(pc586watch, b_ptr, 1*HZ);
return;
}
opri = SPLNET();
#ifdef notdef
printf("pc%d watch(): 6sec timeout no %d\n", unit, ++watch_dead);
#endif notdef
pc586_cntrs[unit].watch++;
if (pc586hwrst(unit) != TRUE) {
printf("pc%d watch(): hwrst trouble.\n", unit);
pc_softc[unit].timer = 0;
} else {
timeout(pc586watch, b_ptr, 1*HZ);
pc_softc[unit].timer = 5;
}
splx(opri);
}
/*
* pc586intr:
*
* This function is the interrupt handler for the pc586 ethernet
* board. This routine will be called whenever either a packet
* is received, or a packet has successfully been transfered and
* the unit is ready to transmit another packet.
*
* input : board number that interrupted
* output : either a packet is received, or a packet is transfered
*
*/
pc586intr(unit)
int unit;
{
volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
volatile ac_t *cb_p = (volatile ac_t *)(pc_softc[unit].sram + OFFSET_CU);
int next, x;
int i;
u_short int_type;
if (pc_softc[unit].seated == FALSE) {
printf("pc%d intr(): board not seated\n", unit);
return(-1);
}
while ((int_type = (scb_p->scb_status & SCB_SW_INT)) != 0) {
pc586ack(unit);
if (int_type & SCB_SW_FR) {
pc586rcv(unit);
watch_dead=0;
}
if (int_type & SCB_SW_RNR) {
pc586_cntrs[unit].rcv.ovw++;
#ifdef notdef
printf("pc%d intr(): receiver overrun! begin_fd = %x\n",
unit, pc_softc[unit].begin_fd);
#endif notdef
pc586rustrt(unit);
}
if (int_type & SCB_SW_CNA) {
/*
* At present, we don't care about CNA's. We
* believe they are a side effect of XMT.
*/
}
if (int_type & SCB_SW_CX) {
/*
* At present, we only request Interrupt for
* XMT.
*/
if ((!(cb_p->ac_status & AC_SW_OK)) ||
(cb_p->ac_status & (0xfff^TC_SQE))) {
if (cb_p->ac_status & TC_DEFER) {
if (xmt_watch) printf("DF");
pc586_cntrs[unit].xmt.defer++;
} else if (cb_p->ac_status & (TC_COLLISION|0xf)) {
if (xmt_watch) printf("%x",cb_p->ac_status & 0xf);
} else if (xmt_watch)
printf("pc%d XMT: %x %x\n",
unit, cb_p->ac_status, cb_p->ac_command);
}
pc586_cntrs[unit].xmt.xmti++;
pc_softc[unit].tbusy = 0;
pc586start(unit);
}
pc_softc[unit].timer = 5;
}
return(0);
}
/*
* pc586rcv:
*
* This routine is called by the interrupt handler to initiate a
* packet transfer from the board to the "if" layer above this
* driver. This routine checks if a buffer has been successfully
* received by the pc586. If so, the routine pc586read is called
* to do the actual transfer of the board data (including the
* ethernet header) into a packet (consisting of an mbuf chain).
*
* input : number of the board to check
* output : if a packet is available, it is "sent up"
*
*/
pc586rcv(unit)
int unit;
{
fd_t *fd_p;
for (fd_p = pc_softc[unit].begin_fd; fd_p != (fd_t *)NULL;
fd_p = pc_softc[unit].begin_fd) {
if (fd_p->status == 0xffff || fd_p->rbd_offset == 0xffff) {
if (pc586hwrst(unit) != TRUE)
printf("pc%d rcv(): hwrst ffff trouble.\n",
unit);
return;
} else if (fd_p->status & AC_SW_C) {
fd_t *bfd = (fd_t *)ram_to_ptr(fd_p->link_offset, unit);
if (fd_p->status == (RFD_DONE|RFD_RSC)) {
/* lost one */;
#ifdef notdef
printf("pc%d RCV: RSC %x\n",
unit, fd_p->status);
#endif notdef
pc586_cntrs[unit].rcv.partial++;
} else if (!(fd_p->status & RFD_OK))
printf("pc%d RCV: !OK %x\n",
unit, fd_p->status);
else if (fd_p->status & 0xfff)
printf("pc%d RCV: ERRs %x\n",
unit, fd_p->status);
else
if (!pc586read(unit, fd_p))
return;
if (!pc586requeue(unit, fd_p)) { /* abort on chain error */
if (pc586hwrst(unit) != TRUE)
printf("pc%d rcv(): hwrst trouble.\n", unit);
return;
}
pc_softc[unit].begin_fd = bfd;
} else
break;
}
return;
}
/*
* pc586requeue:
*
* This routine puts rbd's used in the last receive back onto the
* free list for the next receive.
*
*/
pc586requeue(unit, fd_p)
int unit;
fd_t *fd_p;
{
rbd_t *l_rbdp;
rbd_t *f_rbdp;
#ifndef REQUEUE_DBG
if (bad_rbd_chain(fd_p->rbd_offset, unit))
return 0;
#endif REQUEUE_DBG
f_rbdp = (rbd_t *)ram_to_ptr(fd_p->rbd_offset, unit);
if (f_rbdp != NULL) {
l_rbdp = f_rbdp;
while ( (!(l_rbdp->status & RBD_SW_EOF)) &&
(l_rbdp->next_rbd_offset != 0xffff))
{
l_rbdp->status = 0;
l_rbdp = (rbd_t *)ram_to_ptr(l_rbdp->next_rbd_offset,
unit);
}
l_rbdp->next_rbd_offset = PC586NULL;
l_rbdp->status = 0;
l_rbdp->size |= AC_CW_EL;
pc_softc[unit].end_rbd->next_rbd_offset =
ptr_to_ram((char *)f_rbdp, unit);
pc_softc[unit].end_rbd->size &= ~AC_CW_EL;
pc_softc[unit].end_rbd= l_rbdp;
}
fd_p->status = 0;
fd_p->command = AC_CW_EL;
fd_p->link_offset = PC586NULL;
fd_p->rbd_offset = PC586NULL;
pc_softc[unit].end_fd->link_offset = ptr_to_ram((char *)fd_p, unit);
pc_softc[unit].end_fd->command = 0;
pc_softc[unit].end_fd = fd_p;
return 1;
}
/*
* pc586xmt:
*
* This routine fills in the appropriate registers and memory
* locations on the PC586 board and starts the board off on
* the transmit.
*
* input : board number of interest, and a pointer to the mbuf
* output : board memory and registers are set for xfer and attention
*
*/
#ifdef DEBUG
int xmt_debug = 0;
#endif DEBUG
pc586xmt(unit, m)
int unit;
#ifdef MACH_KERNEL
io_req_t m;
#else MACH_KERNEL
struct mbuf *m;
#endif MACH_KERNEL
{
pc_softc_t *is = &pc_softc[unit];
register u_char *xmtdata_p = (u_char *)(is->sram + OFFSET_TBUF);
register u_short *xmtshort_p;
#ifdef MACH_KERNEL
register struct ether_header *eh_p = (struct ether_header *)m->io_data;
#else MACH_KERNEL
struct mbuf *tm_p = m;
register struct ether_header *eh_p = mtod(m, struct ether_header *);
u_char *mb_p = mtod(m, u_char *) + sizeof(struct ether_header);
u_short count = m->m_len - sizeof(struct ether_header);
#endif MACH_KERNEL
volatile scb_t *scb_p = (volatile scb_t *)(is->sram + OFFSET_SCB);
volatile ac_t *cb_p = (volatile ac_t *)(is->sram + OFFSET_CU);
tbd_t *tbd_p = (tbd_t *)(is->sram + OFFSET_TBD);
u_short tbd = OFFSET_TBD;
u_short len, clen = 0;
cb_p->ac_status = 0;
cb_p->ac_command = (AC_CW_EL|AC_TRANSMIT|AC_CW_I);
cb_p->ac_link_offset = PC586NULL;
cb_p->cmd.transmit.tbd_offset = OFFSET_TBD;
bcopy16(eh_p->ether_dhost, cb_p->cmd.transmit.dest_addr, ETHER_ADD_SIZE);
cb_p->cmd.transmit.length = (u_short)(eh_p->ether_type);
#ifndef MACH_KERNEL
#ifdef DEBUG
if (xmt_debug)
printf("XMT mbuf: L%d @%x ", count, mb_p);
#endif DEBUG
#endif MACH_KERNEL
tbd_p->act_count = 0;
tbd_p->buffer_base = 0;
tbd_p->buffer_addr = ptr_to_ram(xmtdata_p, unit);
#ifdef MACH_KERNEL
{ int Rlen, Llen;
clen = m->io_count - sizeof(struct ether_header);
Llen = clen & 1;
Rlen = ((int)(m->io_data + sizeof(struct ether_header))) & 1;
bcopy16(m->io_data + sizeof(struct ether_header) - Rlen,
xmtdata_p,
clen + (Rlen + Llen) );
xmtdata_p += clen + Llen;
tbd_p->act_count = clen;
tbd_p->buffer_addr += Rlen;
}
#else MACH_KERNEL
do {
if (count) {
if (clen + count > ETHERMTU)
break;
if (count & 1)
len = count + 1;
else
len = count;
bcopy16(mb_p, xmtdata_p, len);
clen += count;
tbd_p->act_count += count;
xmtdata_p += len;
if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
break;
if (count & 1) {
/* go to the next descriptor */
tbd_p++->next_tbd_offset = (tbd += sizeof (tbd_t));
tbd_p->act_count = 0;
tbd_p->buffer_base = 0;
tbd_p->buffer_addr = ptr_to_ram(xmtdata_p, unit);
/* at the end -> coallesce remaining mbufs */
if (tbd == OFFSET_TBD + (N_TBD-1) * sizeof (tbd_t)) {
pc586sftwsleaze(&count, &mb_p, &tm_p, unit);
continue;
}
/* next mbuf short -> coallesce as needed */
if ( (tm_p->m_next == (struct mbuf *) 0) ||
#define HDW_THRESHOLD 55
tm_p->m_len > HDW_THRESHOLD)
/* ok */;
else {
pc586hdwsleaze(&count, &mb_p, &tm_p, unit);
continue;
}
}
} else if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
break;
count = tm_p->m_len;
mb_p = mtod(tm_p, u_char *);
#ifdef DEBUG
if (xmt_debug)
printf("mbuf+ L%d @%x ", count, mb_p);
#endif DEBUG
} while (1);
#endif MACH_KERNEL
#ifdef DEBUG
if (xmt_debug)
printf("CLEN = %d\n", clen);
#endif DEBUG
if (clen < ETHERMIN) {
tbd_p->act_count += ETHERMIN - clen;
for (xmtshort_p = (u_short *)xmtdata_p;
clen < ETHERMIN;
clen += 2) *xmtshort_p++ = 0;
}
tbd_p->act_count |= TBD_SW_EOF;
tbd_p->next_tbd_offset = PC586NULL;
#ifdef IF_CNTRS
clen += sizeof (struct ether_header) + 4 /* crc */;
pc586_eout[log_2(clen)]++;
if (clen < 128) pc586_lout[clen>>3]++;
#endif IF_CNTRS
#ifdef DEBUG
if (xmt_debug) {
pc586tbd(unit);
printf("\n");
}
#endif DEBUG
while (scb_p->scb_command) ;
scb_p->scb_command = SCB_CU_STRT;
pc586chatt(unit);
#ifdef MACH_KERNEL
iodone(m);
#else MACH_KERNEL
for (count=0; ((count < 6) && (eh_p->ether_dhost[count] == 0xff)); count++) ;
if (count == 6) {
pc586send_packet_up(m, eh_p, is);
} else
m_freem(m);
#endif MACH_KERNEL
return;
}
/*
* pc586bldcu:
*
* This function builds up the command unit structures. It inits
* the scp, iscp, scb, cb, tbd, and tbuf.
*
*/
pc586bldcu(unit)
{
char *sram = pc_softc[unit].sram;
scp_t *scp_p = (scp_t *)(sram + OFFSET_SCP);
iscp_t *iscp_p = (iscp_t *)(sram + OFFSET_ISCP);
volatile scb_t *scb_p = (volatile scb_t *)(sram + OFFSET_SCB);
volatile ac_t *cb_p = (volatile ac_t *)(sram + OFFSET_CU);
tbd_t *tbd_p = (tbd_t *)(sram + OFFSET_TBD);
int i;
scp_p->scp_sysbus = 0;
scp_p->scp_iscp = OFFSET_ISCP;
scp_p->scp_iscp_base = 0;
iscp_p->iscp_busy = 1;
iscp_p->iscp_scb_offset = OFFSET_SCB;
iscp_p->iscp_scb = 0;
iscp_p->iscp_scb_base = 0;
pc586_cntrs[unit].rcv.crc += scb_p->scb_crcerrs;
pc586_cntrs[unit].rcv.frame += scb_p->scb_alnerrs;
pc586_cntrs[unit].rcv.rscerrs += scb_p->scb_rscerrs;
pc586_cntrs[unit].rcv.ovrnerrs += scb_p->scb_ovrnerrs;
scb_p->scb_status = 0;
scb_p->scb_command = 0;
scb_p->scb_cbl_offset = OFFSET_CU;
scb_p->scb_rfa_offset = OFFSET_RU;
scb_p->scb_crcerrs = 0;
scb_p->scb_alnerrs = 0;
scb_p->scb_rscerrs = 0;
scb_p->scb_ovrnerrs = 0;
scb_p->scb_command = SCB_RESET;
pc586chatt(unit);
for (i = 1000000; iscp_p->iscp_busy && (i-- > 0); );
if (!i) printf("pc%d bldcu(): iscp_busy timeout.\n", unit);
for (i = STATUS_TRIES; i-- > 0; ) {
if (scb_p->scb_status == (SCB_SW_CX|SCB_SW_CNA))
break;
}
if (!i)
printf("pc%d bldcu(): not ready after reset.\n", unit);
pc586ack(unit);
cb_p->ac_status = 0;
cb_p->ac_command = AC_CW_EL;
cb_p->ac_link_offset = OFFSET_CU;
tbd_p->act_count = 0;
tbd_p->next_tbd_offset = PC586NULL;
tbd_p->buffer_addr = 0;
tbd_p->buffer_base = 0;
return;
}
/*
* pc586bldru:
*
* This function builds the linear linked lists of fd's and
* rbd's. Based on page 4-32 of 1986 Intel microcom handbook.
*
*/
char *
pc586bldru(unit)
int unit;
{
fd_t *fd_p = (fd_t *)(pc_softc[unit].sram + OFFSET_RU);
ru_t *rbd_p = (ru_t *)(pc_softc[unit].sram + OFFSET_RBD);
int i;
pc_softc[unit].begin_fd = fd_p;
for(i = 0; i < N_FD; i++, fd_p++) {
fd_p->status = 0;
fd_p->command = 0;
fd_p->link_offset = ptr_to_ram((char *)(fd_p + 1), unit);
fd_p->rbd_offset = PC586NULL;
}
pc_softc[unit].end_fd = --fd_p;
fd_p->link_offset = PC586NULL;
fd_p->command = AC_CW_EL;
fd_p = (fd_t *)(pc_softc[unit].sram + OFFSET_RU);
fd_p->rbd_offset = ptr_to_ram((char *)rbd_p, unit);
for(i = 0; i < N_RBD; i++, rbd_p = (ru_t *) &(rbd_p->rbuffer[RCVBUFSIZE])) {
rbd_p->r.status = 0;
rbd_p->r.buffer_addr = ptr_to_ram((char *)(rbd_p->rbuffer),
unit);
rbd_p->r.buffer_base = 0;
rbd_p->r.size = RCVBUFSIZE;
if (i != N_RBD-1) {
rbd_p->r.next_rbd_offset=ptr_to_ram(&(rbd_p->rbuffer[RCVBUFSIZE]),
unit);
} else {
rbd_p->r.next_rbd_offset = PC586NULL;
rbd_p->r.size |= AC_CW_EL;
pc_softc[unit].end_rbd = (rbd_t *)rbd_p;
}
}
return (char *)pc_softc[unit].begin_fd;
}
/*
* pc586rustrt:
*
* This routine starts the receive unit running. First checks if the
* board is actually ready, then the board is instructed to receive
* packets again.
*
*/
pc586rustrt(unit)
int unit;
{
volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
char *strt;
if ((scb_p->scb_status & SCB_RUS_READY) == SCB_RUS_READY)
return;
strt = pc586bldru(unit);
scb_p->scb_command = SCB_RU_STRT;
scb_p->scb_rfa_offset = ptr_to_ram(strt, unit);
pc586chatt(unit);
return;
}
/*
* pc586diag:
*
* This routine does a 586 op-code number 7, and obtains the
* diagnose status for the pc586.
*
*/
pc586diag(unit)
int unit;
{
volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
volatile ac_t *cb_p = (volatile ac_t *)(pc_softc[unit].sram + OFFSET_CU);
int i;
if (scb_p->scb_status & SCB_SW_INT) {
printf("pc%d diag(): bad initial state %\n",
unit, scb_p->scb_status);
pc586ack(unit);
}
cb_p->ac_status = 0;
cb_p->ac_command = (AC_DIAGNOSE|AC_CW_EL);
scb_p->scb_command = SCB_CU_STRT;
pc586chatt(unit);
for(i = 0; i < 0xffff; i++)
if ((cb_p->ac_status & AC_SW_C))
break;
if (i == 0xffff || !(cb_p->ac_status & AC_SW_OK)) {
printf("pc%d: diag failed; status = %x\n",
unit, cb_p->ac_status);
return(FALSE);
}
if ( (scb_p->scb_status & SCB_SW_INT) && (scb_p->scb_status != SCB_SW_CNA) ) {
printf("pc%d diag(): bad final state %x\n",
unit, scb_p->scb_status);
pc586ack(unit);
}
return(TRUE);
}
/*
* pc586config:
*
* This routine does a standard config of the pc586 board.
*
*/
pc586config(unit)
int unit;
{
volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
volatile ac_t *cb_p = (volatile ac_t *)(pc_softc[unit].sram + OFFSET_CU);
int i;
/*
if ((scb_p->scb_status != SCB_SW_CNA) && (scb_p->scb_status & SCB_SW_INT) ) {
printf("pc%d config(): unexpected initial state %x\n",
unit, scb_p->scb_status);
}
*/
pc586ack(unit);
cb_p->ac_status = 0;
cb_p->ac_command = (AC_CONFIGURE|AC_CW_EL);
/*
* below is the default board configuration from p2-28 from 586 book
*/
cb_p->cmd.configure.fifolim_bytecnt = 0x080c;
cb_p->cmd.configure.addrlen_mode = 0x2600;
cb_p->cmd.configure.linprio_interframe = 0x6000;
cb_p->cmd.configure.slot_time = 0xf200;
cb_p->cmd.configure.hardware = 0x0000;
cb_p->cmd.configure.min_frame_len = 0x0040;
scb_p->scb_command = SCB_CU_STRT;
pc586chatt(unit);
for(i = 0; i < 0xffff; i++)
if ((cb_p->ac_status & AC_SW_C))
break;
if (i == 0xffff || !(cb_p->ac_status & AC_SW_OK)) {
printf("pc%d: config-configure failed; status = %x\n",
unit, cb_p->ac_status);
return(FALSE);
}
/*
if (scb_p->scb_status & SCB_SW_INT) {
printf("pc%d configure(): bad configure state %x\n",
unit, scb_p->scb_status);
pc586ack(unit);
}
*/
cb_p->ac_status = 0;
cb_p->ac_command = (AC_IASETUP|AC_CW_EL);
bcopy16(pc_softc[unit].ds_addr, cb_p->cmd.iasetup, ETHER_ADD_SIZE);
scb_p->scb_command = SCB_CU_STRT;
pc586chatt(unit);
for (i = 0; i < 0xffff; i++)
if ((cb_p->ac_status & AC_SW_C))
break;
if (i == 0xffff || !(cb_p->ac_status & AC_SW_OK)) {
printf("pc%d: config-address failed; status = %x\n",
unit, cb_p->ac_status);
return(FALSE);
}
/*
if ((scb_p->scb_status & SCB_SW_INT) != SCB_SW_CNA) {
printf("pc%d configure(): unexpected final state %x\n",
unit, scb_p->scb_status);
}
*/
pc586ack(unit);
return(TRUE);
}
/*
* pc586ack:
*/
pc586ack(unit)
{
volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
int i;
if (!(scb_p->scb_command = scb_p->scb_status & SCB_SW_INT))
return;
CMD(CHANATT, 0x0001, unit);
for (i = 1000000; scb_p->scb_command && (i-- > 0); );
if (!i)
printf("pc%d pc586ack(): board not accepting command.\n", unit);
}
char *
ram_to_ptr(offset, unit)
int unit;
u_short offset;
{
if (offset == PC586NULL)
return(NULL);
if (offset > 0x3fff) {
printf("ram_to_ptr(%x, %d)\n", offset, unit);
panic("range");
return(NULL);
}
return(pc_softc[unit].sram + offset);
}
#ifndef REQUEUE_DBG
bad_rbd_chain(offset, unit)
{
rbd_t *rbdp;
char *sram = pc_softc[unit].sram;
for (;;) {
if (offset == PC586NULL)
return 0;
if (offset > 0x3fff) {
printf("pc%d: bad_rbd_chain offset = %x\n",
unit, offset);
pc586_cntrs[unit].rcv.bad_chain++;
return 1;
}
rbdp = (rbd_t *)(sram + offset);
offset = rbdp->next_rbd_offset;
}
}
#endif REQUEUE_DBG
u_short
ptr_to_ram(k_va, unit)
char *k_va;
int unit;
{
return((u_short)(k_va - pc_softc[unit].sram));
}
pc586scb(unit)
{
volatile scb_t *scb = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
volatile u_short*cmd = (volatile u_short *)(pc_softc[unit].prom + OFFSET_NORMMODE);
u_short i;
i = scb->scb_status;
printf("stat: stat %x, cus %x, rus %x //",
(i&0xf000)>>12, (i&0x0700)>>8, (i&0x0070)>>4);
i = scb->scb_command;
printf(" cmd: ack %x, cuc %x, ruc %x\n",
(i&0xf000)>>12, (i&0x0700)>>8, (i&0x0070)>>4);
printf("crc %d[%d], align %d[%d], rsc %d[%d], ovr %d[%d]\n",
scb->scb_crcerrs, pc586_cntrs[unit].rcv.crc,
scb->scb_alnerrs, pc586_cntrs[unit].rcv.frame,
scb->scb_rscerrs, pc586_cntrs[unit].rcv.rscerrs,
scb->scb_ovrnerrs, pc586_cntrs[unit].rcv.ovrnerrs);
printf("cbl %x, rfa %x //", scb->scb_cbl_offset, scb->scb_rfa_offset);
printf(" norm %x, ena %x, xfer %x //",
cmd[0] & 1, cmd[3] & 1, cmd[4] & 1);
printf(" atn %x, reset %x, type %x, stat %x\n",
cmd[1] & 1, cmd[2] & 1, cmd[5] & 1, cmd[6] & 1);
}
pc586tbd(unit)
{
pc_softc_t *is = &pc_softc[unit];
tbd_t *tbd_p = (tbd_t *)(is->sram + OFFSET_TBD);
int i = 0;
int sum = 0;
do {
sum += (tbd_p->act_count & ~TBD_SW_EOF);
printf("%d: addr %x, count %d (%d), next %x, base %x\n",
i++, tbd_p->buffer_addr,
(tbd_p->act_count & ~TBD_SW_EOF), sum,
tbd_p->next_tbd_offset,
tbd_p->buffer_base);
if (tbd_p->act_count & TBD_SW_EOF)
break;
tbd_p = (tbd_t *)(is->sram + tbd_p->next_tbd_offset);
} while (1);
}
#ifndef MACH_KERNEL
pc586hdwsleaze(countp, mb_pp, tm_pp, unit)
struct mbuf **tm_pp;
u_char **mb_pp;
u_short *countp;
{
struct mbuf *tm_p = *tm_pp;
u_char *mb_p = *mb_pp;
u_short count = 0;
u_char *cp;
int len;
pc586_cntrs[unit].xmt.sleaze++;
/*
* can we get a run that will be coallesced or
* that terminates before breaking
*/
do {
count += tm_p->m_len;
if (tm_p->m_len & 1)
break;
} while ((tm_p = tm_p->m_next) != (struct mbuf *)0);
if ( (tm_p == (struct mbuf *)0) ||
count > HDW_THRESHOLD) {
*countp = (*tm_pp)->m_len;
*mb_pp = mtod((*tm_pp), u_char *);
printf("\n");
return;
}
/* we need to copy */
pc586_cntrs[unit].xmt.intrinsic++;
tm_p = *tm_pp;
mb_p = *mb_pp;
count = 0;
cp = (u_char *) t_packet;
do {
bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
count += len;
if (count > HDW_THRESHOLD)
break;
cp += len;
if (tm_p->m_next == (struct mbuf *)0)
break;
tm_p = tm_p->m_next;
} while (1);
pc586_cntrs[unit].xmt.intrinsic_count += count;
*countp = count;
*mb_pp = (u_char *) t_packet;
*tm_pp = tm_p;
return;
}
pc586sftwsleaze(countp, mb_pp, tm_pp, unit)
struct mbuf **tm_pp;
u_char **mb_pp;
u_short *countp;
{
struct mbuf *tm_p = *tm_pp;
u_char *mb_p = *mb_pp;
u_short count = 0;
u_char *cp = (u_char *) t_packet;
int len;
pc586_cntrs[unit].xmt.chain++;
/* we need to copy */
do {
bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
count += len;
cp += len;
if (tm_p->m_next == (struct mbuf *)0)
break;
tm_p = tm_p->m_next;
} while (1);
*countp = count;
*mb_pp = (u_char *) t_packet;
*tm_pp = tm_p;
return;
}
#endif MACH_KERNEL
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.