459 lines
11 KiB
C
459 lines
11 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)if_gn.c 1.1 92/07/30 SMI";
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1988 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* if_gn.c
|
|
*
|
|
* Sun Generic Network Controller Interface
|
|
*
|
|
* This generic network standalone driver adheres to the
|
|
* llcp specification, and includes the additional llcp
|
|
* files:
|
|
* hllcputils.c
|
|
* hportllcp.c
|
|
*/
|
|
|
|
|
|
#ifdef PROMCODE
|
|
#include <strings.h>
|
|
#include <signal.h>
|
|
#include "../h/types.h"
|
|
#include "../h/sunromvec.h"
|
|
#include "../h/cpu.map.h"
|
|
#include "../h/eeprom.h"
|
|
#include "../dev/llcp.h"
|
|
#include "../dev/saio.h"
|
|
#include "../dev/param.h"
|
|
#include "../h/socket.h"
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
#include "../h/globram.h"
|
|
#else
|
|
#include <strings.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/types.h>
|
|
#include <mon/sunromvec.h>
|
|
#include <sunif/llcp.h>
|
|
#include <stand/saio.h>
|
|
#include <stand/param.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <mon/cpu.map.h>
|
|
#include <mon/eeprom.h>
|
|
#endif PROMCODE
|
|
|
|
|
|
#ifndef PROMCODE
|
|
struct ether_addr net_mac_addr; /* our net address */
|
|
struct ether_addr *gn_mac_addr = &net_mac_addr;
|
|
#endif
|
|
|
|
struct gn_softc {
|
|
char es_scrat[PROTOSCRATCH]; /* work space for nd */
|
|
llcp_info_t es_llcp; /* LLCP information structure */
|
|
llcp_cntrlr_info_t cinfo; /* controller info on host */
|
|
llcp_host_info_t hinfo; /* host info on controller */
|
|
char *dataaddr; /* addr of I/O or dma space */
|
|
};
|
|
|
|
/* device information */
|
|
/*
|
|
* Note that these addresses are hard-coded here
|
|
* to facilitate bringup in the case the the
|
|
* eeprom was incorrectly set-up or was not
|
|
* initialized at all. These addresses are
|
|
* unique to the first generation fddi controller
|
|
* and esthetically do not belong here.
|
|
* (ie. It hurt, but it makes everyone's life
|
|
* easier.)
|
|
* Note: these variables are externs in the file gn_inf.c
|
|
*/
|
|
u_long gnaddrs[] = { 0x16c0020, 0x18c0020, 0x1ac0020, 0x1cc0020 };
|
|
|
|
#ifndef PROMCODE
|
|
u_char gnmemtype[] = { ID_VME, ID_VME, ID_VME, ID_VME };
|
|
#define NGNADDR (sizeof(gnaddrs) / sizeof(gnaddrs[0]))
|
|
int gnctlrnum;
|
|
|
|
struct devinfo gninfo = {
|
|
sizeof (llcp_reg_t), /* size of io space */
|
|
0, /* size of dma space */
|
|
sizeof(struct gn_softc), /* size of local bytes */
|
|
NGNADDR, /* # of standard addresses */
|
|
gnaddrs, /* vector of standard addrs */
|
|
MAP_VME32A32D, /* map space 32 bit VME bus I/O */
|
|
0, /* transfer size handled by ND */
|
|
};
|
|
#endif PROMCODE
|
|
/* external interfaces */
|
|
int gnopen(), gnclose();
|
|
extern int xxprobe();
|
|
extern char *resalloc();
|
|
#ifdef PROMCODE
|
|
int nullsys();
|
|
extern int tftpboot();
|
|
#else PROMCODE
|
|
extern int xxboot(), etherstrategy(), gnstats();
|
|
#endif
|
|
|
|
|
|
struct boottab gndriver = {
|
|
#ifdef PROMCODE
|
|
"gn", xxprobe, tftpboot, gnopen, gnclose, nullsys,
|
|
#else PROMCODE
|
|
"gn", xxprobe, xxboot, gnopen, gnclose, etherstrategy,
|
|
#endif
|
|
"gn: Sun Generic Network", &gninfo,
|
|
};
|
|
/* ethernet routines */
|
|
int gnxmit(), gnpoll(), gnreset(), my_mac_addr();
|
|
struct saif gnif = {
|
|
gnxmit,
|
|
gnpoll,
|
|
gnreset,
|
|
my_mac_addr
|
|
#ifndef PROMCODE
|
|
,
|
|
gnstats
|
|
#endif PROMCODE
|
|
};
|
|
|
|
|
|
/*
|
|
* Description: Open Generic Network nd connection
|
|
* Synopsis: status = gnopen(sip)
|
|
* status :(int) = pass value from etheropen
|
|
* -1 = error
|
|
* sip :(char *) point to saio structure
|
|
*
|
|
* Routines: gninit, etheropen, gnclose
|
|
*/
|
|
int
|
|
gnopen(sip)
|
|
struct saioreq *sip;
|
|
{
|
|
int result;
|
|
|
|
sip->si_sif = &gnif; /* set interface pointers */
|
|
|
|
if ( gninit(sip) || (result = etheropen(sip)) < 0 ) {
|
|
printf("gn: open failed\n");
|
|
gnclose(sip); /* must close interface */
|
|
return (-1);
|
|
}
|
|
return (result);
|
|
} /* gnopen */
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Description: This routine sets up dma or I/O space and goes through
|
|
* llcp init sequence.
|
|
*
|
|
* Synopsis: status = gninit(sip)
|
|
* status :(int) 0 = complete
|
|
* 1 = error
|
|
* sip :(char *) point to saio structure
|
|
* Routines: gnreset
|
|
*/
|
|
int
|
|
gninit(sip)
|
|
struct saioreq *sip;
|
|
{
|
|
register struct gn_softc *es;
|
|
llcp_cntrlr_info_t *cinfoptr;
|
|
register int retry = 5; /* up to 5 retries */
|
|
#ifdef PROMCODE
|
|
int gnctlrnum = gp->g_ctlr_number;
|
|
#else PROMCODE
|
|
/* for broken code that sets si_ctlr from controller
|
|
* number to an address behind the scenes
|
|
*/
|
|
for (gnctlrnum=0; gnctlrnum < NGNADDR; gnctlrnum++)
|
|
if (gnaddrs[gnctlrnum] == sip->si_ctlr)
|
|
break;
|
|
#endif PROMCODE
|
|
|
|
es = (struct gn_softc *)sip->si_devdata; /* pt to local space */
|
|
#ifdef DEBUG
|
|
printf("Entering gninit sip = 0x%x es = 0x%x\n",sip,es);
|
|
#endif
|
|
|
|
/*
|
|
* stuff es_softc structure
|
|
*/
|
|
es->es_llcp.cinfop = cinfoptr = &es->cinfo; /* this setup needed for */
|
|
es->es_llcp.hinfop = &es->hinfo; /* generic llcp routines */
|
|
/* base address of llcp registers */
|
|
es->es_llcp.regp = (llcp_reg_t *)sip->si_devaddr;
|
|
|
|
if ( gnmemtype[gnctlrnum] == ID_VME )
|
|
cinfoptr->memtyp = SHARED_MEM;
|
|
else
|
|
cinfoptr->memtyp = DMA_MEM;
|
|
|
|
if ( cinfoptr->memtyp == DMA_MEM ) /* if dma, alloc space */
|
|
cinfoptr->buf = (u_char *)resalloc(RES_DMAMEM, LLCP_BUF_SIZ);
|
|
else /* clr addr of llcp buf */
|
|
cinfoptr->buf = (u_char *)0;
|
|
|
|
cinfoptr->timeout = LLCP_T_RESET; /* initial cmd timeout val*/
|
|
|
|
if( init_host(sip) == LLCP_FAIL ) /* initialize hinfo struct*/
|
|
return(0);
|
|
|
|
/*
|
|
* retries for simply initiating communication
|
|
* with the network controller
|
|
*/
|
|
while( --retry ) {
|
|
|
|
if ( gnreset(es) == 0 ) /* llcp reset and init */
|
|
return(0); /* return success */
|
|
|
|
if ( (cinfoptr->memtyp == SHARED_MEM) ) {
|
|
/*
|
|
* Need to release memory here berfore we try
|
|
* again but since we are the only ones running,
|
|
* there is no routine available to release alloc'd
|
|
* space. If there are a ton of resets, we might
|
|
* run out of space.
|
|
*/
|
|
cinfoptr->buf = (u_char *)0; /* rezero buf */
|
|
es->es_llcp.hinfop->buf = (u_char *)0;
|
|
}
|
|
}
|
|
|
|
return(1); /* return failure */
|
|
} /* gninit */
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Description: Basic Generic Network llcp initialization
|
|
* Goes through the llcp initialization sequence.
|
|
*
|
|
* Synopsis: status = gnreset(es)
|
|
* status :(int) 0 = complete
|
|
* 1 = error
|
|
* es :(char *) pointer to ethernet structure
|
|
*/
|
|
int
|
|
gnreset(es)
|
|
register struct gn_softc *es;
|
|
{
|
|
char *physaddr;
|
|
llcp_cntrlr_info_t *cinfoptr = es->es_llcp.cinfop;
|
|
llcp_host_info_t *hinfoptr = es->es_llcp.hinfop;
|
|
#ifdef PROMCODE
|
|
int gnctlrnum = gp->g_ctlr_number;
|
|
#endif PROMCODE
|
|
char *devalloc();
|
|
|
|
#ifdef DEBUG
|
|
printf("Entering gnreset es = 0x%x\n",es);
|
|
#endif
|
|
|
|
if ( llcp_reset(&es->es_llcp) == LLCP_FAIL )
|
|
return(1); /* return error */
|
|
|
|
if ( strt_init(&es->es_llcp) == LLCP_FAIL )
|
|
return(1); /* return error */
|
|
|
|
|
|
/*
|
|
* If we have a shared memory interface, then use the physical address
|
|
* returned by the controller to map a virtual address to.
|
|
*/
|
|
if ( (cinfoptr->memtyp == SHARED_MEM) ) {
|
|
/*
|
|
* to get the actual physical address of the buffer area,
|
|
* need to or in the base address of the llcp registers
|
|
* without any offset into the page.
|
|
*/
|
|
physaddr = (char *)(gnaddrs[gnctlrnum] + cinfoptr->phys_addr);
|
|
cinfoptr->buf = (u_char *)devalloc(MAP_VME32A32D,
|
|
physaddr, LLCP_BUF_SIZ);
|
|
hinfoptr->buf = cinfoptr->buf;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
printf("after strt_init: phys_addr=0x%x mapped addr = 0x%x\n",
|
|
cinfoptr->phys_addr,cinfoptr->buf);
|
|
#endif
|
|
if ( get_info(&es->es_llcp) == LLCP_FAIL )
|
|
return(1); /* return error */
|
|
|
|
/* check if user specified address to be provided by controller */
|
|
if (hinfoptr->partition & 0x20) {
|
|
/* save the MAC address from the controller */
|
|
bcopy((char *)cinfoptr->net_laddr,(char *)gn_mac_addr,
|
|
sizeof(struct ether_addr));
|
|
bcopy((char *)cinfoptr->net_laddr, (char *)hinfoptr->net_laddr,
|
|
sizeof(struct ether_addr));
|
|
}
|
|
else { /* else use host MAC address */
|
|
myetheraddr((struct ether_addr *)gn_mac_addr);
|
|
bcopy((char *)gn_mac_addr, (char *)cinfoptr->net_laddr,
|
|
sizeof(struct ether_addr));
|
|
bcopy((char *)gn_mac_addr,
|
|
(char *)es->es_llcp.hinfop->net_laddr,
|
|
sizeof(struct ether_addr));
|
|
}
|
|
|
|
if ( send_info(&es->es_llcp) == LLCP_FAIL )
|
|
return(1); /* return error */
|
|
|
|
if ( ena_net(&es->es_llcp) == LLCP_FAIL )
|
|
return(1); /* return error */
|
|
|
|
#ifdef DEBUG
|
|
printf("GNRESET SUCCEEDED !! \n");
|
|
#endif
|
|
return(0); /* reset succeeded */
|
|
} /* gnreset */
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Description:
|
|
* Transmits a packet of data. Waits for a maximum time as
|
|
* specified in the controller info structure. Returns 1 for
|
|
* error and 0 otherwise. If an error occurs, prints a msg
|
|
* to the console.
|
|
*
|
|
* Synopsis: status = gnxmit(es, buf, count)
|
|
* es :(char *) pointer to ethernet structure
|
|
* buf :(char *) pointer to buffer
|
|
* count :(int) character count
|
|
* Routines: iesimple, bzero, bcopy
|
|
*/
|
|
gnxmit(es, buf, count)
|
|
register struct gn_softc *es;
|
|
char *buf;
|
|
int count;
|
|
{
|
|
|
|
if ( llcp_send_pkt(&es->es_llcp, buf, count) == LLCP_FAIL ) {
|
|
printf("gn: xmit failed\n");
|
|
return(1); /* return error */
|
|
}
|
|
|
|
return(0); /* command completed successfully */
|
|
} /* gnxmit */
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Description:
|
|
* polls for a packet of data for a maximum time as specified
|
|
* in the controller info structure. Returns 0 for no packet
|
|
* or an error, otherwise returns the length of the packet
|
|
* Upon command completion if an error occurs, prints a message
|
|
* to the console.
|
|
*
|
|
* Synopsis: status = lepoll(es, buf)
|
|
* status :(int) 0 = no packet yet
|
|
* >0 = length of received packet
|
|
* es :(char *) pointer to ethernet structure
|
|
* buf :(char *) pointer of receiving buffer
|
|
* Routines:
|
|
*/
|
|
int
|
|
gnpoll(es, buf)
|
|
register struct gn_softc *es;
|
|
char *buf;
|
|
{
|
|
|
|
return( llcp_get_pkt(&es->es_llcp, buf) );
|
|
} /* gnpoll */
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Description: Close down Lance ethernet device
|
|
|
|
* Synopsis: status = gnclose(sip)
|
|
* status :(void)
|
|
* sip :(char *) pointer to saioreq structure
|
|
*/
|
|
gnclose(sip)
|
|
struct saioreq *sip;
|
|
{
|
|
if ( llcp_reset(&((struct gn_softc *)(sip->si_devdata))->es_llcp) ==
|
|
LLCP_FAIL ){
|
|
printf("gn: close failed\n");
|
|
return(1); /* return error */
|
|
}
|
|
|
|
return(0); /* return successful */
|
|
} /* gnclose */
|
|
|
|
|
|
|
|
/*
|
|
* init_host - XXX - sets up hinfo structures. This routine will
|
|
* probably be a focal point for future revisions to llcp.
|
|
*/
|
|
|
|
init_host(sip)
|
|
struct saioreq *sip;
|
|
{
|
|
#ifdef PROMCODE
|
|
int gnctlrnum = gp->g_ctlr_number;
|
|
#endif PROMCODE
|
|
register struct gn_softc *es = (struct gn_softc *)sip->si_devdata;
|
|
llcp_host_info_t *hinfoptr = es->es_llcp.hinfop;
|
|
|
|
hinfoptr->len = sizeof(llcp_host_info_t); /* len of structure */
|
|
hinfoptr->llcp_ver = LLCP_VERSION; /* version of llcp */
|
|
|
|
hinfoptr->addrtyp = LONG_ADDR; /* type of net addr */
|
|
/* MAC address initialized during getinfo handshake */
|
|
|
|
hinfoptr->memtyp = es->es_llcp.cinfop->memtyp; /* type of memory */
|
|
hinfoptr->buf = es->es_llcp.cinfop->buf;
|
|
/* hinfoptr->prom_rev = *romp->op_mon_id; /* host prom revision */
|
|
hinfoptr->prom_rev = 1; /* host prom revision */
|
|
hinfoptr->ctlr = gnctlrnum; /* cntrlr num */
|
|
hinfoptr->unit = sip->si_unit; /* Unit num in cntrlr */
|
|
hinfoptr->partition = (long) sip->si_boff; /* Partition num */
|
|
|
|
return(LLCP_SUCC);
|
|
} /* init_host */
|
|
|
|
|
|
|
|
/*
|
|
* my_mac_addr - stuffs the current MAC address into the network address
|
|
* pointer passed.
|
|
*/
|
|
|
|
my_mac_addr(ea)
|
|
struct ether_addr *ea;
|
|
{
|
|
bcopy((char *)gn_mac_addr, (char *)ea, sizeof(struct ether_addr));
|
|
} /* my_mac_addr */
|
|
|
|
|
|
#ifndef PROMCODE
|
|
gnstats()
|
|
{
|
|
/* XXX implement sometime */
|
|
}
|
|
#endif PROMCODE
|