V8/usr/sys/dev/il.c

Compare this file to the similar file:
Show the results in this format:

#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);
}