V8/usr/sys/dev/il.c
#include "il.h"
#if NIL > 0
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/map.h"
#include "../h/pte.h"
#include "../h/buf.h"
#include "../h/ubareg.h"
#include "../h/ubavar.h"
#include "../h/proc.h"
#include "../h/tty.h"
/*#include "../h/queue.h"*/
#include "../h/ilreg.h"
#include "../h/conf.h"
#define PRINET 26 /* sleep priority: interruptible */
struct ilcb ilcb[NIL];
extern int ilprobe(), ilattach(), ilrint(), ilcint();
struct uba_device *ildinfo[NIL];
u_short ilstd[] = {0164040};
struct uba_driver ildriver =
{ ilprobe, 0, ilattach, 0, ilstd, "il", ildinfo };
/*++*
* Macro: NM10command
* Abstract: pass the NM10 board an inline command
* Parameters:
* command: command to process
* size: size of data buffer
* buf: data buffer
* addr: NM10 device addr
* Returns: nothing
* Affects: nothing
* Errors: none
* Design: trivial
*
*--*/
#define NM10command(command, size, buf) \
{ \
register unsigned int map = ((unsigned int) buf); \
reg->bcr = size; \
reg->bar = map & 0xffff; \
reg->csr = ((map & 0x30000) >> 2) | command; \
while ((reg->csr & IL_CMD_DONE) == 0); \
}
/*++*
* Routine: ilinit
* Abstract: Initialize the Interlan ethernet driver.
* Parameters:
* dev: number device to initialize;
* Returns: nothing
* Affects: nothing
* Errors:
* Design: trivial
*
*--*/
ilinit (dev)
{
register struct ilcb *ib = &ilcb[dev];
register struct uba_device *ui = ildinfo[dev];
register struct ilreg *reg = (struct ilreg *)ui->ui_addr;
if (ib->avail)
{
u.u_error = EBUSY;
return;
}
ib->in_map = uballoc(ui->ui_ubanum, (caddr_t) 0x80000000,
ILUBASIZE, UBA_NEEDBDP|UBA_CANTWAIT);
ib->cmd_map = uballoc(ui->ui_ubanum, (caddr_t) 0x80000000,
ILUBASIZE, UBA_NEEDBDP|UBA_CANTWAIT);
if ((ib->in_map == 0) || (ib->cmd_map == 0))
{
if (ib->in_map)
ubarelse (ui->ui_ubanum, &ib->in_map);
if (ib->cmd_map)
ubarelse (ui->ui_ubanum, &ib->cmd_map);
u.u_error = ENOMEM;
return;
}
/*++*
* initialize cummulative statistics
*
*--*/
{
struct ilstats stats;
register unsigned short *st = (unsigned short *) &stats.rframes;
register unsigned int *in = (unsigned int *) &ib->info.rframes;
NM10command (IL_RPT_STATS, 66,
ubaremap(ui->ui_ubanum, ib->in_map, stats));
/* we do this remap to cause a ubapurge */
ubaremap(ui->ui_ubanum, ib->in_map, stats);
while (st > (unsigned short *) &stats.modid)
*in++ = *st++;
ib->info.iladdr = stats.iladdr;
ib->info.modid = stats.modid;
ib->info.firmid = stats.firmid;
}
ib->in_ptr = 0;
ib->in_packet = NULL;
ib->cmd_packet = NULL;
ib->in_next = NULL;
initqueue((struct Queue *)&ib->cmd_q);
ilrstart (ui);
NM10command (IL_ON_LINE, 0, 0);
}
ilopen (dev, flag)
{
register struct ilcb *ib = &ilcb[dev];
if (ib > &ilcb[NIL] || !ib->attached)
{
u.u_error = ENXIO;
return;
}
}
ilclose (dev)
{
return;
}
/*++*
* Procedure: ilcommand
* Abstract:
* send command to net.
* Parameters:
* dev = device number
* p = pointer to packet that is to be given to Interland device.
* Design:
*
*--*/
ilcommand(dev, p)
register struct ilpacket *p;
{
register struct uba_device *ui = ildinfo [dev];
register struct ilcb *ib = &ilcb [dev];
int ipl;
extern ilcmdstart();
ipl = spl6();
if (ib->cmd_packet == NULL)
{
ib->cmd_packet = p;
ilcmdstart (ui);
}
else enqueue (&ib->cmd_q, p);
splx(ipl);
}
ilioctl (dev, cmd, arg) int *arg;
{
switch (cmd)
{
case IL_INIT:
{
if (suser())
ilinit (dev);
else
{
u.u_error = EPERM;
return;
}
}
break;
case IL_DISABLE:
{
/* ildisable (dev); */
}
break;
case IL_STATS:
{
register struct ilcb *ib = &ilcb[dev];
unsigned short buf[ILPHEAD + 66];
register struct ilpacket *p = (struct ilpacket *)buf;
extern ilstats ();
p->command = IL_RPT_STATS;
p->function = ilstats;
p->count = 66;
ilcommand (dev, p);
while (p->command == IL_RPT_STATS)
sleep((caddr_t) p, PRINET);
copyout(&ib->info, arg, sizeof ib->info);
}
break;
default:
{
u.u_error = EINVAL;
}
break;
}
}
/*++*
* Routine: ilfind
* Abstract: find a type filter that matched a specified type.
* Parameters:
* ib = pointer to the ethernet device ilcb.
* type = type that filter will match.
* Returns:
* SUCCESS: a descriptor with the matched type.
* FAILURE: NULL
* Affects: nothing
* Errors: none
* Design: trivial
*
*--*/
struct iltype *ilfind(ib, type) register unsigned short type;
register struct ilcb *ib;
{
register struct iltype *d;
for ( d = (struct iltype *)ib->type_q.F;
d != (struct iltype *)&ib->type_q;
d = (struct iltype *) d->head.F )
if (d->type == type)
return (d);
return (NULL);
}
/*++*
* Routine: ilconnect
* Abstract: create and ethernet type filter entry
* Parameters:
* unit = ethernet device unit number.
* type = ethernet type
* funct = function to call if we find a match
* Returns:
* SUCCESS: 1
* FAILURE: 0
* Affects:
* ilcb[unit].type_q: entry placed in queue
* ilcb[unit].free_q: entry removed from queue
* Errors: none
* Design: ...
*
*--*/
ilconnect (unit, type, function) register funct *function;
{
register struct iltype *d;
register struct ilcb *ib = &ilcb[unit];
register int ipl;
ipl = spl6();
if (ilfind (ib, type) == NULL)
if (d = (struct iltype *) dequeue (&ib->free_q))
{
ib->num_free--;
d->type = type;
d->funct = function;
enqueue (&ib->type_q, d);
ib->num_types++;
splx (ipl);
return (1);
}
splx (ipl);
return (0);
}
/*++*
* Routine: ildisconnect
* Abstract: remove an ethernet type filter match entry
* Parameters:
* unit = ethernet device unit number
* type = ethernet type
* Returns:
* SUCCESS: 1
* FAILURE: 0
* Affects:
* ilcb[unit].type_q: entry removed
* ilcb[unit].free_q: entry entered
* Errors: none
* Design: ...
*
*--*/
ildisconnect (unit, type) register int type;
{
struct iltype *d;
register struct ilcb *ib = &ilcb[unit];
register int ipl;
if (d = ilfind (ib, type))
{
ipl = spl6 ();
enqueue (&ib->free_q, dequeue (&d));
ib->num_types--;
ib->num_free++;
splx(ipl);
}
}
/*++*
* Routine: iladdress
* Abstract: return (by reference) the device net address
* Parameters:
* addr = the network address.
* Returns: nothing
* Affects: nothing
* Errors: none
* Design: trivial
*
*--*/
iladdress (dev, addr) register struct iladdr *addr;
{
register struct ilcb *ib = &ilcb[dev];
*addr = ib->info.iladdr;
}
/*++*
* Procedure: ilrstart
* Abstract: Start read operation on net.
* Parameters:
* 'ui' type "uba_device *": pointer to ethernet device to be started.
*
*--*/
ilrstart(ui)
register struct uba_device *ui;
{
register struct ilreg *reg = (struct ilreg *)ui->ui_addr;
register struct ilcb *ib = &ilcb[ui->ui_unit];
register struct ilpacket *p;
int ipl;
if (ib->in_packet == NULL)
{
ipl = spl6();
ib->in_packet = &ib->in_pool[ib->in_ptr];
ib->in_ptr ^= 1;
ib->in_next = ubaremap (ui->ui_ubanum, ib->in_map,
ib->in_packet->data);
if (ib->cmd_packet == NULL)
{
NM10command (IL_RCVR_BUFFER | IL_RCV_INTR,
ILPACKETSIZE,
ib->in_next);
ib->in_next = NULL;
}
splx (ipl);
}
}
/*++*
* Procedure: ilcmdstart
* Abstract: Start packet transmission on net.
* Parameters:
* ui pointer to ethernet device structure.
* Design:
* We give the Interland device the correct command
*
* The UNIBUS address is derived by calculating the offset of the
* packet from the packet pool buffer base address and adding it to the
* allocated unibus buffer base address.
*
* We must do a purge to make sure the uba device has no data words
* lying around that might get sent on the next DMA.
*
*--*/
ilcmdstart(ui)
register struct uba_device *ui;
{
register struct ilcb *ib = &ilcb[ui->ui_unit];
register struct ilreg *ilreg = (struct ilreg *)ui->ui_addr;
register struct ilpacket *p;
register unsigned int buffer;
if (ib->cmd_packet == NULL)
if ((ib->cmd_packet = (struct ilpacket *)dequeue(&ib->cmd_q)) == NULL)
return;
/* we dont use NM10command here since we are not interested
in waiting for the command to complete synchronously */
p = ib->cmd_packet;
buffer = (unsigned int) ubaremap (ui->ui_ubanum, ib->cmd_map,
p->data);
ilreg->bcr = p->count;
ilreg->bar = buffer & 0xffff;
ilreg->csr = ((buffer & 0x30000) >> 2)
| p->command | IL_CMD_INTR | IL_RCV_INTR;
}
/*++*
* Procedure: ilrint
* Abstract: Net read interrupt handler.
* Parameters:
* dev = number of ethernet device.
*
*--*/
ilrint(dev)
{
register struct ilcb *ib = &ilcb[dev];
register struct uba_device *ui = ildinfo[dev];
register struct il_rheader *l;
register struct iltype *d;
l = (struct il_rheader *)ib->in_packet;
ilrstart(ui);
if (l->status & IL_RCV_STATUS)
printf ("il%d: received bad packet; status %x\n", dev, l);
else if (d = ilfind(ib, l->type))
(*d->funct)(dev, l);
}
/*++*
* Procedure: ilcint
* Abstract: Net transmit interrupt handler.
* Parameters:
* 'en' number of device we interrupted on.
*
*--*/
ilcint(dev)
{
register struct ilcb *ib = &ilcb[dev];
register struct uba_device *ui = ildinfo[dev];
register unsigned short status;
register struct ilpacket *p = ib->cmd_packet;
register struct ilreg *reg = (struct ilreg *) ui->ui_addr;
if (p == NULL)
{
printf ("il%d: spurious transmit interrupt\n", dev);
return;
}
status = reg->csr & IL_CMD_STATUS;
if (p->command == IL_XMIT_SEND)
{
if ((status) > 1)
printf ("il%d: dropped xmit packet\n", dev);
}
else if (status)
printf ("il%d: command %x bad status packet: %x\n",
dev, p->command, p);
if (p->function != NULL)
(*(p->function)) (p, dev);
if (ib->in_next)
{
NM10command (IL_RCVR_BUFFER | IL_RCV_INTR,
ILPACKETSIZE,
ib->in_next);
}
ib->in_next = NULL;
ib->cmd_packet = NULL;
ilcmdstart (ui);
}
/*++*
* Procedure: ilstats
* Abstract:
* Merge currently collected data into the device statistics
* table. This routine assumes HIGH PRIORITY.
*
*--*/
ilstats (p, dev) struct ilpacket *p;
{
register struct ilcb *ib = &ilcb[dev];
register struct ilstats *stats = (struct ilstats *)p->data;
register unsigned int *in = (unsigned int *) &ib->info.rframes;
register unsigned short *st = (unsigned short *) &stats->rframes;
while (st < (unsigned short *) stats)
*in++ += *st++;
ib->info.rfifo = stats->rfifo;
p->command = IL_NULL_CMD;
wakeup((caddr_t) p);
}
/*++*
* Procedure: ilattach
* Abstract:
* This routine is called at system startup and initializes
* the device information structures.
* Parameters:
* 'ui' type "uba_device *": pointer to the device information
* structure.
*--*/
ilattach(ui)
register struct uba_device *ui;
{
register struct ilreg *reg = (struct ilreg *) ui->ui_addr;
register struct ilcb *ib = &ilcb[ui->ui_unit];
register unsigned short status;
register int i;
if (&ilcb[ui->ui_unit] > &ilcb[NIL])
{
printf ("il%d: unit number to large. Max = %d\n",
ui->ui_unit, NIL);
return;
}
if (status = (reg->csr & IL_CMD_STATUS))
{
printf ("il%d: bad reset status %x\n");
return;
}
ib->attached = 1;
initqueue((struct Queue *)&ib->type_q);
initqueue((struct Queue *)&ib->free_q);
ib->num_types = 0;
ib->num_free = ILMAXTYPES;
for (i = 0; i < ILMAXTYPES; i++)
enqueue (&ib->free_q, ib->type[i]);
#ifdef TCP_IP
#if NBBNNET > 0
il_attach();
#endif NBBNNET
#endif TCP_IP
}
/*++*
* Procedure: ilprobe
* Abstract:
* This routine is called by the system auto configurer
* at boot time. It locates the interrupt vectors of the device
* by doing FM (f___ing Magic). Cvec is used as a global register
* in which the interrupt routine places the interrupt vector
* that it found when the device interrupted.
* We can use this routine to do some boot time testing if
* we want.
* Parameters:
* 'reg' the base address of the ethernet device.
*
*--*/
ilprobe(reg)
struct ilreg *reg;
{
register int br,cvec;
#ifdef lint
br = 0; cvec = br; br = cvec;
#endif
reg->bcr = 0;
reg->bar = 0;
reg->csr = IL_OFF_LINE | IL_CMD_INTR;
DELAY(10000);
br = reg->csr; /* clear off status from il board */
cvec &= ~0xf; /* make sure we have the first intr vector */
return(1);
}
/*++*
* Procedure: ilreset
* Abstract:
* reset the interlan ethernet device
* Called on UBA reset to restart pending I/O.
* Just prints the device name and restarts the receiver and
* transmitter. No other state that we care about is lost by
* the reset.
* Parameters:
* 'uban' number of the uba that was reset.
*
*--*/
ilreset(uban)
{
register int dev;
register struct uba_device *ui;
register struct ilreg *reg = (struct ilreg *) ui->ui_addr;
register struct ilcb *ib;
for (dev = 0; dev < NIL; dev++)
{
if ((ui = ildinfo[dev]) ||
ui->ui_alive == 0 ||
ui->ui_ubanum != uban)
continue;
ib = &ilcb[dev];
/*++*
* do a reset command so we are sure of the state
*
*--*/
NM10command (IL_RESET, 0, 0);
printf("[reset] ");
/*++*
* put us back on line if we are active
* and start the command and receive processes.
*
*--*/
if (ib->avail)
{
NM10command (IL_ON_LINE, 0, 0);
printf ("[on line]");
}
else printf ("[off line]");
}
if (ib->cmd_packet)
(*ib->cmd_packet->function) (ib->cmd_packet, dev);
ib->in_ptr = 0;
ib->in_packet = NULL;
ib->in_next = NULL;
ib->cmd_packet = NULL;
ilcmdstart(ui);
ilrstart (ui);
}