Subject: dayna driver for 1.5.3 (was: scsi-ethernet again)
To: None <port-mac68k@netbsd.org>
From: Mattias Sandstrom <mattias@beauty.se>
List: port-mac68k
Date: 09/21/2002 21:14:07
This is a multi-part message in MIME format.
--------------020902070609060409080804
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Mattias Sandstrom wrote:
> i'll post the modified source here for all you 
> other old source users out there as soon as i get it to compile and 
> run... ;-)

finally, i don't know if the server will accept an attachment, but i 
guess we'll see. here's a version of the dayna driver that compiles and 
runs on 1.5.3. arigato gozaimashita sempai hiroshi and others...

	/matt

--------------020902070609060409080804
Content-Type: text/plain; x-mac-type="54455854"; x-mac-creator="43574945";
 name="if_dse.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="if_dse.c"

/* if_dse.c  ver 0.01   2001/07/10 */

/*
 * Driver for DaynaPORT SCSI/Link SCSI-Ethernet
 *
 * Written by Hiroshi Noguchi <ngc@ff.iij4u.or.jp>
 *
 * Modified by Matt Sandstrom <mattias@beauty.se> for NetBSD 1.5.3
 *
 * This driver is written based on "if_se.c".
 */

/*
 * Copyright (c) 1997 Ian W. Dall <ian.dall@dsto.defence.gov.au>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Ian W. Dall.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */



#include "opt_inet.h"
#include "opt_atalk.h"
#include "opt_ccitt.h"
#include "opt_llc.h"
#include "opt_ns.h"
#include "bpfilter.h"

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/callout.h>
#include <sys/syslog.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/proc.h>
#include <sys/conf.h>

#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>

#include <sys/mbuf.h>

#include <sys/socket.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
#include <net/if_media.h>

#ifdef INET
#include <netinet/in.h>
#include <netinet/if_inarp.h>
#endif

#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif

#ifdef NETATALK
#include <netatalk/at.h>
#endif

#if defined(CCITT) && defined(LLC)
#include <sys/socketvar.h>
#include <netccitt/x25.h>
#include <netccitt/pk.h>
#include <netccitt/pk_var.h>
#include <netccitt/pk_extern.h>
#endif

#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif


/*
 * debug flag
 */
#if 0
#define	DSE_DEBUG
#endif

#if 0
#define	DEB_PRINT(s)	printf s
#else
#define	DEB_PRINT(s)
#endif

#if 0
#ifdef mac68k
#define	MAC68K_DEBUG
#endif
#endif

#if 0
#ifdef mac68k
#define MAC68K_NOT_INIT
#endif
#endif




#define DSE_TIMEOUT			(100000)
#define	DSE_OUTSTANDING		(4)
#define	DSE_RETRIES			(4)
#define DSE_MINSIZE			(60)

#define	DSE_HEADER_TX		(4)
#define	DSE_TAIL_TX			(4)
#define DSE_EXTRAS_TX		(DSE_HEADER_TX + DSE_TAIL_TX)

#define	DSE_HEADER_RX		(6)
#define	DSE_TAIL_RX			(0)
#define	DSE_EXTRAS_RX		(DSE_HEADER_RX + DSE_TAIL_RX)

#define	MAX_BYTES_RX	(ETHERMTU + sizeof(struct ether_header) + ETHER_CRC_LEN)

/* 10 full length packets appears to be the max ever returned. 16k is OK */
#define RBUF_LEN	(16 * 1024)

/*
 * Tuning parameters:
 *   We will attempt to adapt to polling fast enough to get RDATA_GOAL packets
 *   per read
 */
#define RDATA_MAX	(10)	/* maximum of returned packets (guessed) */
#define RDATA_GOAL 	(8)

/*
 * maximum of available multicast address entries (guessed)
 */
#define	DSE_MCAST_MAX	(10)


/* dse_poll and dse_poll0 are the normal polling rate and the minimum
 * polling rate respectively. dse_poll0 should be chosen so that at
 * maximum ethernet speed, we will read nearly RDATA_MAX packets. dse_poll
 * should be chosen for reasonable maximum latency.
 * In practice, if we are being saturated with min length packets, we
 * can't poll fast enough. Polling with zero delay actually
 * worsens performance. dse_poll0 is enforced to be always at least 1
 */
#ifdef MAC68K_DEBUG
#define DSE_POLL		(50)	/* default in milliseconds */
#define DSE_POLL0 		(30)	/* default in milliseconds */
#else
#define DSE_POLL		(80)	/* default in milliseconds */
#define DSE_POLL0 		(40)	/* default in milliseconds */
#endif
int dse_poll			= 0;	/* Delay in ticks set at attach time */
int dse_poll0			= 0;
int dse_max_received	= 0;	/* Instrumentation */




/*==========================================
  data type defs
==========================================*/
typedef struct {
	/* standard */
	u_int8_t	device;		/* 3 (T_CPU) */
	u_int8_t	dev_qual2;	/* 0 (fixed) */
	u_int8_t	version;	/* 0 */
	u_int8_t	response_format;	/* 0 */
	u_int8_t	additional_len;		/* 31 */
	u_int8_t	unused[2];	/* 0,0 */
	u_int8_t	flags;		/* 0x00 */
	char	vendor[8];		/* ie; "SonicSys" */
	char	product[16];	/* ie; "MicroSCSI" */
	char	revision[4];	/* ie; "2.00" */
	char	extra[8];		/* none */
} scsi_dayna_ether_inquiry_data;




typedef struct {
	u_int8_t	opcode[2];
	u_int8_t	byte3;
	u_int8_t	length[2];
	u_int8_t	byte6;
} scsi_dayna_ether_generic;

#define	DAYNA_CMD_SEND		(0x0A)		/* same as generic "Write" */
#define	DAYNA_CMD_RECV		(0x08)		/* same as generic "Read" */

#define	DAYNA_CMD_GET_ADDR	(0x09)		/* ???: read MAC address ? */
#define	REQ_LEN_GET_ADDR	(0x12)

#define	DAYNA_CMD_SET_MULTI	(0x0D)		/* set multicast address */

#define	DAYNA_CMD_VENDOR1	(0x0E)		/* ???: initialize signal ? */

#define IS_SEND(generic)	((generic)->opcode == DAYNA_CMD_SEND)
#define IS_RECV(generic)	((generic)->opcode == DAYNA_CMD_RECV)




struct dse_softc {
	struct	device sc_dev;
	struct	ethercom sc_ethercom;	/* Ethernet common part */
	struct	scsipi_link *sc_link;	/* contains our targ, lun, etc. */

	struct callout sc_ifstart_ch;
	struct callout sc_recv_ch;

	char *sc_tbuf;
	char *sc_rbuf;
	int	sc_debug;
	int	sc_flags;
	int	sc_last_timeout;
	int	sc_enabled;
};

/* bit defs of "sc_flags" */
#define DSE_NEED_RECV	(0x1)

cdev_decl(dse);

static int	dsematch __P((struct device *, struct cfdata *, void *));
static void	dseattach __P((struct device *, struct device *, void *));

static void	dsestart __P((void *));
static void	dse_ifstart __P((struct ifnet *));
static void	dse_delayed_ifstart __P((void *));

static void	dsedone __P((struct scsipi_xfer *));
static int	dse_ioctl __P((struct ifnet *, u_long, caddr_t));
static void	dsewatchdog __P((struct ifnet *));

static void	dse_recv __P((void *));
static struct mbuf*	dse_get __P((struct dse_softc *, u_int8_t *, int));
static int	dse_read __P((struct dse_softc *, u_int8_t *, int));

static int	dse_init_adaptor __P((struct dse_softc *));
static int	dse_get_addr __P((struct dse_softc *, u_int8_t *));
static int	dse_set_multi __P((struct dse_softc *));

static int	dse_reset __P((struct dse_softc *));

#if 0	/* 07/16/2000 comment-out */
static int	dse_set_mode(struct dse_softc *, int, int);
#endif
static int	dse_init __P((struct dse_softc *));
static void	dse_stop __P((struct dse_softc *));

static __inline u_int16_t	ether_cmp __P((void *, void *));
static __inline int	dse_scsipi_cmd __P((struct scsipi_link *sc_link,
					    struct scsipi_generic *scsipi_cmd,
					    int cmdlen, u_char *data_addr, int datalen,
					    int retries, int timeout, struct buf *bp,
					    int flags));

int	dse_enable __P((struct dse_softc *));
void	dse_disable __P((struct dse_softc *));

struct cfattach dse_ca = {
	sizeof(struct dse_softc), dsematch, dseattach
};

extern struct cfdriver dse_cd;

struct scsipi_device dse_switch = {

	NULL,			/* Use default error handler */
	dsestart,		/* have a queue, served by this */
	NULL,			/* have no async handler */
	dsedone,		/* deal with stats at interrupt time */
};

struct scsipi_inquiry_pattern dse_patterns[] = {
	{	T_PROCESSOR,	T_FIXED,
		"Dayna",		"SCSI/Link",		"" },
};



/*====================================================
  definitions for SCSI commands
====================================================*/

/*
 * command templates
 */
/* unknown commands */
/* Vendor #1 */
static const scsi_dayna_ether_generic	sonic_ether_vendor1 = {
	{ DAYNA_CMD_VENDOR1, 0x00 },
	0x00,
	{ 0x00, 0x00 },
	0x80
};



/*
 * Compare two Ether/802 addredses for equality, inlined and
 * unrolled for speed.
 * Note: use this like memcmp()
 */
static __inline u_int16_t
ether_cmp(one, two)
	void *one, *two;
{
	u_int16_t*	a;
	u_int16_t*	b;
	u_int16_t diff;

	a = (u_int16_t *) one;
	b = (u_int16_t *) two;

	diff = (a[0] - b[0]) | (a[1] - b[1]) | (a[2] - b[2]);

	return (diff);
}

#define ETHER_CMP	ether_cmp


/*
 * check to match with SCSI inquiry information
 */
static int
dsematch(parent, match, aux)
	struct device *parent;
	struct cfdata *match;
	void *aux;
{
	struct scsipibus_attach_args* sa;
	struct scsipi_link* sc_link;
	int priority;

	sa = aux;
	sc_link = sa->sa_sc_link;
	priority = 0;

	/*
	 * As this adaptor seems to response all LUN(0-7) when "Test Unit Ready",
	 * accept LUN = 0 and reject the others.
	 */
	if ( sc_link->scsipi_scsi.lun != 0 )
		goto l_end;
		
	(void)scsipi_inqmatch( &sa->sa_inqbuf,
	    (caddr_t)dse_patterns, sizeof(dse_patterns) / sizeof(dse_patterns[0]),
		sizeof(dse_patterns[0]), &priority );

  l_end:;
	return (priority);
}


/*
 * The routine called by the low level scsi routine when it discovers
 * a device suitable for this driver.
 */
static void
dseattach( parent, self, aux )
	struct device *parent;
	struct device *self;
	void *aux;
{
	struct dse_softc* sc = (void *)self;
	struct scsipibus_attach_args* sa = aux;
	struct scsipi_link* sc_link = sa->sa_sc_link;
	struct ifnet* ifp = &sc->sc_ethercom.ec_if;
	u_int8_t myaddr[ETHER_ADDR_LEN];

#if 0	/* 05/13/2001 add */
	sc_link->sc_link_dbflags = SCSIPI_DB1;
#endif

	printf("\n");
	SC_DEBUG(sc_link, SCSIPI_DB2, ("dseattach: "));

	callout_init( &sc->sc_ifstart_ch );
	callout_init( &sc->sc_recv_ch );

	/*
	 * Store information needed to contact our base driver
	 */
	sc->sc_link = sc_link;
     sc_link->device = &dse_switch;
     sc_link->device_softc = sc;
     if (sc_link->openings > 4 /*SEOUTSTANDING*/)
          sc_link->openings = 4 /*SEOUTSTANDING*/;

	/* XXX increase openings? */

	dse_poll = (DSE_POLL * hz) / 1000;
	dse_poll = dse_poll? dse_poll: 1;
	dse_poll0 = (DSE_POLL0 * hz) / 1000;
	dse_poll0 = dse_poll0? dse_poll0: 1;

	/*
	 * Initialize and attach a buffer
	 */
	sc->sc_tbuf = malloc( ETHERMTU + sizeof(struct ether_header) + DSE_EXTRAS_TX + 16,
						  M_DEVBUF, M_NOWAIT );
	if ( sc->sc_tbuf == NULL )
		panic("dseattach: can't allocate transmit buffer");

	sc->sc_rbuf = malloc( RBUF_LEN + 16, M_DEVBUF, M_NOWAIT );
	if (sc->sc_rbuf == NULL )
		panic("dseattach: can't allocate receive buffer");

	/* initialize adaptor and obtain MAC address */
	dse_init_adaptor( sc );
	dse_get_addr( sc, myaddr );
#if 0
	dse_get_addr( sc, myaddr );
#endif

	/* Initialize ifnet structure. */
	strcpy( ifp->if_xname, sc->sc_dev.dv_xname );
	ifp->if_softc = sc;
	ifp->if_start = dse_ifstart;
	ifp->if_ioctl = dse_ioctl;
	ifp->if_watchdog = dsewatchdog;
	ifp->if_flags =
		IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;

	/* Attach the interface. */
	if_attach(ifp);
	ether_ifattach(ifp, myaddr);

#if NBPFILTER > 0
     bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
#endif
}


/*
 * submit SCSI command
 */
static __inline int
dse_scsipi_cmd(sc_link, scsipi_cmd, cmdlen, data_addr, datalen,
		       retries, timeout, bp, flags)
	struct scsipi_link *sc_link;
	struct scsipi_generic *scsipi_cmd;
	int cmdlen;
	u_char *data_addr;
	int datalen;
	int retries;
	int timeout;
	struct buf *bp;
	int flags;
{
	int error;
	int s;

	s = splbio();
	error = scsipi_command(sc_link, scsipi_cmd, cmdlen, data_addr,
						   datalen, retries, timeout, bp, flags);
	splx(s);
	return (error);
}


/* Start routine for calling from scsi sub system */
static void
dsestart(v)
	 void *v;
{
	struct dse_softc* sc = (struct dse_softc *)v;
	struct ifnet* ifp = &sc->sc_ethercom.ec_if;
	int s = splnet();

	dse_ifstart(ifp);
	(void) splx(s);
}


static void
dse_delayed_ifstart(v)
	void *v;
{
	struct ifnet* ifp = v;
	struct dse_softc* sc = ifp->if_softc;
	int s;

	s = splnet();
	if (sc->sc_enabled) {
		ifp->if_flags &= ~IFF_OACTIVE;
		dse_ifstart(ifp);
	}
	splx(s);
}


/*
 * Start transmission on the interface.
 * Always called at splnet().
 */
static void
dse_ifstart(ifp)
	struct ifnet *ifp;
{
	struct dse_softc* sc = ifp->if_softc;
	scsi_dayna_ether_generic cmd_send;
	struct mbuf* m;
	struct mbuf* m0;
	int	len;
	int error;
	u_char*	cp;

	/* Don't transmit if interface is busy or not running */
	if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
		return;

	IF_DEQUEUE(&ifp->if_snd, m0);
	if ( m0 == NULL )
		return;
#if NBPFILTER > 0
	/* If BPF is listening on this interface, let it see the
	 * packet before we commit it to the wire.
	 */
	if ( ifp->if_bpf )
		bpf_mtap(ifp->if_bpf, m0);
#endif

	/* We need to use m->m_pkthdr.len, so require the header */
	if ( (m0->m_flags & M_PKTHDR) == 0 )
		panic("dse_ifstart: no header mbuf");
	len = m0->m_pkthdr.len;

	/* Mark the interface busy. */
	ifp->if_flags |= IFF_OACTIVE;

	/* Chain; copy into linear buffer we allocated at attach time. */
	cp = &(sc->sc_tbuf[0]);
	for ( m = m0 ; m != NULL ; ) {
		memcpy( cp, mtod(m, u_char *), m->m_len );
		cp += m->m_len;
		MFREE(m, m0);
		m = m0;
	}
#if 0
	if( len < DSE_MINSIZE ){
		memset( cp, 0, DSE_MINSIZE - len );
		len = DSE_MINSIZE;
	}
#endif

	/* Fill out SCSI command. */
	memset( &cmd_send, 0, sizeof(cmd_send) );
	cmd_send.opcode[0] = DAYNA_CMD_SEND;
	_lto2b( len, &(cmd_send.length[0]) );
	cmd_send.byte6 = 0x00;

	/* Send command to device. */
	error = dse_scsipi_cmd( sc->sc_link,
		(struct scsipi_generic *)&cmd_send, sizeof(cmd_send),
	    sc->sc_tbuf, len, DSE_RETRIES,
	    DSE_TIMEOUT, NULL, XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_OUT);
	if ( error ) {
		printf( "%s: not queued, error %d\n",
			    sc->sc_dev.dv_xname, error);
		ifp->if_oerrors++;
		ifp->if_flags &= ~IFF_OACTIVE;
	} else
		ifp->if_opackets++;

#ifdef DSE_DEBUG
	printf("dse_ifstart: scsilen = %d, packetlen = %d, proto = 0x%04x\n", scsilen, len,
	    ntohs(((struct ether_header *)&(sc->sc_tbuf[2]))->ether_type));
#endif

	if ( sc->sc_flags & DSE_NEED_RECV ) {
		sc->sc_flags &= ~DSE_NEED_RECV;
		dse_recv((void *) sc );
	}
}


/*
 * Called from the scsibus layer via our scsi device switch.
 */
static void
dsedone(xs)
	struct scsipi_xfer *xs;
{
	int error;
	struct dse_softc* sc = xs->sc_link->device_softc;
	struct scsipi_generic* cmd = xs->cmd;
	struct ifnet* ifp = &sc->sc_ethercom.ec_if;
	int s;

	error = !(xs->error == XS_NOERROR);

	s = splnet();
	if ( IS_SEND(cmd) ){
		if (xs->error == XS_BUSY) {
			printf("dse: busy, retry txmit\n");
			callout_reset( &sc->sc_ifstart_ch, hz,
				dse_delayed_ifstart, ifp );
		} else {
			ifp->if_flags &= ~IFF_OACTIVE;
			/* the generic scsipi_done will call
			 * dsestart (through scsipi_free_xs).
			 */
		}
	} else if ( IS_RECV(cmd) ) {
		/* RECV complete */
		/* pass data up. reschedule a recv */
		/* scsipi_free_xs will call start. Harmless. */
		if ( error ) {
			/* Reschedule after a delay */
			callout_reset( &sc->sc_recv_ch, dse_poll,
				dse_recv, (void *)sc );
		} else {
			int		n, ntimeo;
			n = dse_read( sc, xs->data, xs->datalen - xs->resid );
			if ( n > dse_max_received )
				dse_max_received = n;
			if ( n == 0 )
				ntimeo = dse_poll;
			else if ( n >= RDATA_MAX )
				ntimeo = dse_poll0;
			else {
				ntimeo = sc->sc_last_timeout;
				ntimeo = (ntimeo * RDATA_GOAL)/ n;
				ntimeo = (ntimeo < dse_poll0 ? dse_poll0: ntimeo);
				ntimeo = (ntimeo > dse_poll ? dse_poll: ntimeo);
			}
			sc->sc_last_timeout = ntimeo;
			if ( ntimeo == dse_poll0 &&
				ifp->if_snd.ifq_head ){
				/* Output is pending. Do next recv
				 * after the next send.  */
				sc->sc_flags |= DSE_NEED_RECV;
			} else
				callout_reset( &sc->sc_recv_ch, ntimeo,
					dse_recv, (void *)sc );
		}
	}
	splx(s);
}


/*
 * do a recv command
 */
static void
dse_recv(v)
	void *v;
{
	struct dse_softc* sc = (struct dse_softc *)v;
	scsi_dayna_ether_generic cmd_recv;
	int error;
	int	len;

	if ( sc->sc_enabled == 0 )
		return;

	/* fill out command buffer */
	memset( &cmd_recv, 0, sizeof(cmd_recv) );
	cmd_recv.opcode[0] = DAYNA_CMD_RECV;
	len = MAX_BYTES_RX + DSE_EXTRAS_RX;
	_lto2b( len, &(cmd_recv.length[0]) );
	cmd_recv.byte6 = 0xC0;

	error = dse_scsipi_cmd( sc->sc_link,
	    (struct scsipi_generic *)&cmd_recv, sizeof(cmd_recv),
		sc->sc_rbuf, RBUF_LEN, DSE_RETRIES, DSE_TIMEOUT, NULL,
		XS_CTL_NOSLEEP | XS_CTL_ASYNC | XS_CTL_DATA_IN );
	if (error)
		callout_reset( &sc->sc_recv_ch, dse_poll, dse_recv, (void *)sc );
}


/*
 * We copy the data into mbufs.  When full cluster sized units are present
 * we copy into clusters.
 */
static struct mbuf *
dse_get(sc, data, totlen)
	struct dse_softc *sc;
	u_int8_t *data;
	int totlen;
{
	struct ifnet* ifp = &sc->sc_ethercom.ec_if;
	struct mbuf	*m, *m0, *newm;
	int len;

	MGETHDR(m0, M_DONTWAIT, MT_DATA);
	if ( m0 == NULL )
		return ( NULL );
	m0->m_pkthdr.rcvif = ifp;
	m0->m_pkthdr.len = totlen;
	len	= MHLEN;
	m = m0;

	while ( totlen > 0 ){
		if ( totlen >= MINCLSIZE ){
			MCLGET(m, M_DONTWAIT );
			if( (m->m_flags & M_EXT) == 0 )
				goto bad;
			len = MCLBYTES;
		}

		if ( m == m0 ){
			caddr_t newdata = (caddr_t)
			    ALIGN(m->m_data + sizeof(struct ether_header)) -
			    sizeof(struct ether_header);
			len -= newdata - m->m_data;
			m->m_data = newdata;
		}

		m->m_len = len = min(totlen, len);
		memcpy( mtod(m, caddr_t), data, len );
		data += len;

		totlen -= len;
		if ( totlen > 0 ){
			MGET(newm, M_DONTWAIT, MT_DATA);
			if ( newm == NULL )
				goto bad;

			len = MLEN;
			m = m->m_next = newm;
		}
	}

	return (m0);

bad:
	m_freem(m0);
	return ( NULL );
}


#ifdef MAC68K_DEBUG
static int  peek_packet( u_int8_t*  buf )
{
	int					len;
	struct ether_header	*eh;
	u_int16_t			type;

	eh = (struct ether_header*)buf;
	type = _2btol( (u_int8_t*)&(eh->ether_type) );

	len = sizeof(struct ether_header);

	if( type <= ETHERMTU ){
		/* for 802.3 */
		len += type;
	} else{
		/* for Ethernet II (DIX) */
		switch( type ){
		  case ETHERTYPE_ARP:
			len += 28;
			break;
		  case ETHERTYPE_IP:
			len += _2btol( buf + sizeof(struct ether_header) + 2 );
			break;
		  default:
			len = 0;
			goto l_end;
			break;
		}
	}
	if( len < DSE_MINSIZE ){
		len = DSE_MINSIZE;
	}
	len += ETHER_CRC_LEN;

  l_end:;
	return( len );
}
#endif


/*
 * Pass packets to higher levels.
 */
static int
dse_read(sc, data, datalen)
	struct dse_softc *sc;
	u_int8_t* data;
	int datalen;
{
	struct mbuf* m;
	struct ifnet* ifp = &sc->sc_ethercom.ec_if;
	int n;
	int	len;
#ifdef MAC68K_DEBUG
	int	peek_flag = 1;
#endif

	n = 0;
	while ( datalen > DSE_HEADER_RX ){
#if 0	/* 03/10/2001  only for debug */
		{
			int	j;
			printf("\ndump[%d]: ",n);
			for( j = 0 ; j < 23 ; j++ ){
				printf("%02X ",data[j - 6]);
			}
		}
#endif
		/*
		 * fetch bytes of stream.
		 * here length = (ether frame length) + (FCS's 4 bytes)
		 */
		/* fetch frame length */
		len = _2btol( data );

		/* skip header part */
		data	+= DSE_HEADER_RX;
		datalen -= DSE_HEADER_RX;

#ifdef MAC68K_DEBUG
		if( peek_flag ){
			peek_flag = 0;
			len = peek_packet( data );
		}
#endif
		if ( len <= 0 )
			break;

#ifdef DSE_DEBUG
		printf("dse_read: datalen = %d, packetlen = %d, proto = 0x%04x\n", datalen, len,
		    ntohs(((struct ether_header *)data)->ether_type));
#endif
		if ( (len < (DSE_MINSIZE + ETHER_CRC_LEN)) || (MAX_BYTES_RX < len) ){
#ifdef DSE_DEBUG
			printf("%s: invalid packet size %d; dropping\n",
			       sc->sc_dev.dv_xname, len);
#endif
			ifp->if_ierrors++;
			break;
		}

		/* Don't need crc. Must keep ether header for BPF */
		m = dse_get( sc, data, len - ETHER_CRC_LEN );
		if ( m == NULL ){
#ifdef DSE_DEBUG
			if (sc->sc_debug)
				printf("dse_read: dse_get returned null\n");
#endif
			ifp->if_ierrors++;
			goto next_packet;
		}
		ifp->if_ipackets++;

#if NBPFILTER > 0
		/*
		 * Check if there's a BPF listener on this interface.
		 * If so, hand off the raw packet to BPF.
		 */
		if ( ifp->if_bpf )
			bpf_mtap(ifp->if_bpf, m);
#endif

 		/* Pass the packet up. */
 		(*ifp->if_input)(ifp, m);

  next_packet:
		data	+= len;
		datalen	-= len;
		n++;
	}

	return (n);
}


static void
dsewatchdog(ifp)
	struct ifnet *ifp;
{
	struct dse_softc* sc = ifp->if_softc;

	log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
	++ifp->if_oerrors;

	dse_reset( sc );
}


static int
dse_reset(sc)
	struct dse_softc *sc;
{
	int error;
	int s = splnet();
#if 0
	/* Maybe we don't *really* want to reset the entire bus
	 * because the ctron isn't working. We would like to send a
	 * "BUS DEVICE RESET" message, but don't think the ctron
	 * understands it.
	 */
	error = dse_scsipi_cmd(sc->sc_link, 0, 0, 0, 0, DSE_RETRIES, 2000, NULL,
	    XS_CTL_RESET);
#endif
	error = dse_init(sc);
	splx(s);
	return (error);
}


static int
dse_init_adaptor(sc)
	 struct dse_softc *sc;
{
	int error;
	scsi_dayna_ether_generic cmd_vend1;
	u_char tmpbuf[sizeof(cmd_vend1)];

#if 0	/* 07/21/2001 for test */
	/* Maybe we don't *really* want to reset the entire bus
	 * because the ctron isn't working. We would like to send a
	 * "BUS DEVICE RESET" message, but don't think the ctron
	 * understands it.
	 */
	error = dse_scsipi_cmd(sc->sc_link, 0, 0, 0, 0, DSE_RETRIES, 2000, NULL, XS_CTL_RESET);
#endif

	cmd_vend1 = sonic_ether_vendor1;

	error = dse_scsipi_cmd( sc->sc_link,
	    (struct scsipi_generic *)&cmd_vend1, sizeof(cmd_vend1),
		&(tmpbuf[0]), sizeof(tmpbuf),
	    DSE_RETRIES, DSE_TIMEOUT, NULL, XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK );
	if ( error )
		goto l_end;

	/* wait 500msec */
	delay( 500 * 1000 );

  l_end:;
	return( error );
}


static int
dse_get_addr(sc, myaddr)
	struct dse_softc *sc;
	u_int8_t *myaddr;
{
	int error;
	scsi_dayna_ether_generic cmd_get_addr;
	u_char tmpbuf[REQ_LEN_GET_ADDR];

	memset( &cmd_get_addr, 0, sizeof(cmd_get_addr) );
	cmd_get_addr.opcode[0] = DAYNA_CMD_GET_ADDR;
	_lto2b( REQ_LEN_GET_ADDR, cmd_get_addr.length );

	error = dse_scsipi_cmd( sc->sc_link,
	    (struct scsipi_generic *)&cmd_get_addr, sizeof(cmd_get_addr),
	    tmpbuf, sizeof(tmpbuf),
	    DSE_RETRIES, DSE_TIMEOUT, NULL, XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK );
	if( error == 0 ){
		memcpy( myaddr, &(tmpbuf[0]), ETHER_ADDR_LEN );

		printf("%s: ethernet address %s\n", sc->sc_dev.dv_xname,
			   ether_sprintf(myaddr));
	}

	return (error);
}


#if 0	/* 07/16/2000 comment-out */
static int
dse_set_mode(sc, len, mode)
	struct dse_softc *sc;
	int len;
	int mode;
{
	return( 0 );
}
#endif


static int
dse_init(sc)
	struct dse_softc *sc;
{
	struct ifnet* ifp = &sc->sc_ethercom.ec_if;
	int error = 0;

	if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) == IFF_UP) {
		ifp->if_flags |= IFF_RUNNING;
		dse_recv(sc);
		ifp->if_flags &= ~IFF_OACTIVE;
		dse_ifstart(ifp);
	}

	return (error);
}


static u_int8_t	BROADCAST_ADDR[ETHER_ADDR_LEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };


static int
dse_set_multi(sc)
	 struct dse_softc*  sc;
{
	scsi_dayna_ether_generic cmd_set_multi;
	struct ether_multistep step;
	struct ether_multi* enm;
	u_char* cp;
	u_char* mybuf;
	int error;
	int len;

	error = 0;

#ifdef DSE_DEBUG
	printf("%s: dse_set_multi: %s\n", sc->sc_dev.dv_xname, ether_sprintf(addr));
#endif

	mybuf = malloc( ETHER_ADDR_LEN * DSE_MCAST_MAX, M_DEVBUF, M_NOWAIT );
	if( mybuf == NULL ){
		error = EIO;
		goto l_end;
	}

	/*
	 * copy all entries to transfer buffer
	 */
	cp = mybuf;
	len = 0;
	ETHER_FIRST_MULTI( step, &(sc->sc_ethercom), enm );
	while ( (len < (DSE_MCAST_MAX - 1)) && (enm != NULL) ){
		memcpy( cp, enm->enm_addrlo, ETHER_ADDR_LEN );	/* ### refer low side entry */

		cp += ETHER_ADDR_LEN;
		len++;
		ETHER_NEXT_MULTI( step, enm );
	}

	/* add broadcast address as default */
	memcpy( cp, BROADCAST_ADDR, ETHER_ADDR_LEN );
	len++;

	len *= ETHER_ADDR_LEN;

	memset( &cmd_set_multi, 0, sizeof(cmd_set_multi) );
	cmd_set_multi.opcode[0] = DAYNA_CMD_SET_MULTI;
	_lto2b( len, cmd_set_multi.length );

	error = dse_scsipi_cmd( sc->sc_link,
	    (struct scsipi_generic*)&cmd_set_multi, sizeof(cmd_set_multi),
	    mybuf, len, DSE_RETRIES, DSE_TIMEOUT, NULL, XS_CTL_DATA_OUT );

	free( mybuf, M_DEVBUF );

  l_end:;
	return ( error );
}


static void
dse_stop(sc)
	struct dse_softc *sc;
{
	/* Don't schedule any reads */
	callout_stop( &sc->sc_recv_ch );

	/* How can we abort any scsi cmds in progress? */
}


/*
 * Process an ioctl request.
 */
static int
dse_ioctl(ifp, cmd, data)
	struct ifnet *ifp;
	u_long cmd;
	caddr_t data;
{
	struct dse_softc* sc;
	struct ifaddr* ifa;
	struct ifreq* ifr;
	int s, error;

	error = 0;
	sc = ifp->if_softc;
	ifa = (struct ifaddr *)data;
	ifr = (struct ifreq *)data;

	s = splnet();

	switch ( cmd ){
	case SIOCSIFADDR:
		if ( (error = dse_enable(sc)) != 0 )
			break;
		ifp->if_flags |= IFF_UP;

		switch ( ifa->ifa_addr->sa_family ){
#ifdef INET
		case AF_INET:
			if ( (error = dse_init(sc)) != 0 )
				break;
			arp_ifinit(ifp, ifa);
			break;
#endif
#ifdef NS
		case AF_NS:
		    {
				struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;

				if ( ns_nullhost(*ina) )
					ina->x_host = *(union ns_host *)LLADDR(ifp->if_sadl);
				else
					memcpy( LLADDR(ifp->if_sadl), ina->x_host.c_host, ETHER_ADDR_LEN);

				/* Set new address. */
				error = dse_init(sc);
		    }
			break;
#endif
#ifdef NETATALK
		case AF_APPLETALK:
			if ( (error = dse_init(sc)) != 0 )
				break;
			break;
#endif
		default:
			error = dse_init(sc);
			break;
		}
		break;

#if defined(CCITT) && defined(LLC)
	case SIOCSIFCONF_X25:
		if ( (error = dse_enable(sc)) != 0 )
			break;
		ifp->if_flags |= IFF_UP;
		ifa->ifa_rtrequest = cons_rtrequest; /* XXX */
		error = x25_llcglue(PRC_IFUP, ifa->ifa_addr);
		if (error == 0)
			error = dse_init(sc);
		break;
#endif /* CCITT && LLC */

	case SIOCSIFFLAGS:
		if ( ((ifp->if_flags & IFF_UP) == 0) &&
		   		((ifp->if_flags & IFF_RUNNING) != 0) ){
			/*
			 * If interface is marked down and it is running, then
			 * stop it.
			 */
			dse_stop(sc);
			ifp->if_flags &= ~IFF_RUNNING;
			dse_disable(sc);
		} else{
			if ( ((ifp->if_flags & IFF_UP) != 0) &&
					((ifp->if_flags & IFF_RUNNING) == 0) ){
				/*
				 * If interface is marked up and it is stopped, then
				 * start it.
				 */
				if ( (error = dse_enable(sc)) != 0 )
					break;
				error = dse_init(sc);
			} else if ( sc->sc_enabled ){
				/*
				 * Reset the interface to pick up changes in any other
				 * flags that affect hardware registers.
				 */
				error = dse_init(sc);
			}
		}
#ifdef DSE_DEBUG
		if ( ifp->if_flags & IFF_DEBUG )
			sc->sc_debug = 1;
		else
			sc->sc_debug = 0;
#endif
		break;

	case SIOCADDMULTI:
		if ( sc->sc_enabled == 0 ){
			error = EIO;
			break;
		}
		if ( ether_addmulti(ifr, &sc->sc_ethercom) == ENETRESET ){
			error = dse_set_multi(sc);
#ifdef DSE_DEBUG
			printf("%s: add multi: %s\n", sc->sc_dev.dv_xname,
				   ether_sprintf(ifr->ifr_addr.sa_data));
#endif
		} else
			error = 0;
		break;

	case SIOCDELMULTI:
		if ( sc->sc_enabled == 0 ){
			error = EIO;
			break;
		}
		if ( ether_delmulti(ifr, &sc->sc_ethercom) == ENETRESET ){
			error = dse_set_multi(sc);
#ifdef DSE_DEBUG
			printf("%s: delete multi: %s\n", sc->sc_dev.dv_xname,
			    ether_sprintf(ifr->ifr_addr.sa_data));
#endif
		} else
			error = 0;
		break;

	default:
		error = EINVAL;
		break;
	}

	splx(s);

	return (error);
}


/*
 * Enable the network interface.
 */
int
dse_enable(sc)
	struct dse_softc *sc;
{
#if 1
	sc->sc_enabled = 1;
	return ( 0 );
#else
	int error = 0;

	if ( (sc->sc_enabled == 0) &&
		    ((error = scsipi_adapter_addref(sc->sc_link)) == 0) )
		sc->sc_enabled = 1;
	else
		printf("%s: device enable failed\n", sc->sc_dev.dv_xname);

	return (error);
#endif
}


/*
 * Disable the network interface.
 */
void
dse_disable(sc)
	struct dse_softc *sc;
{
#if 1
	sc->sc_enabled = 0;
#else
	if ( sc->sc_enabled != 0 ){
		scsipi_adapter_delref(sc->sc_link);
		sc->sc_enabled = 0;
	}
#endif
}


#define	DSEUNIT(z)	(minor(z))

/*
 * open the device.
 */
int
dseopen(dev, flag, fmt, p)
	dev_t dev;
	int flag, fmt;
	struct proc *p;
{
	int unit, error;
	struct dse_softc *sc;
	struct scsipi_link* sc_link;

	unit = DSEUNIT(dev);
	if (unit >= dse_cd.cd_ndevs)
		return (ENXIO);

	sc = dse_cd.cd_devs[unit];
	if (sc == NULL)
		return (ENXIO);

	sc_link = sc->sc_link;

	if ((error = scsipi_adapter_addref(sc_link)) != 0)
		return (error);

	SC_DEBUG(sc_link, SCSIPI_DB1,
	    ("dseopen: dev=0x%x (unit %d (of %d))\n",
	    dev, unit,dse_cd.cd_ndevs));

	sc_link->flags |= SDEV_OPEN;

	SC_DEBUG(sc_link, SCSIPI_DB3, ("open complete\n"));

	return (0);
}


/*
 * close the device.. only called if we are the LAST
 * occurence of an open device
 */
int
dseclose(dev, flag, fmt, p)
	dev_t dev;
	int flag, fmt;
	struct proc *p;
{
	struct dse_softc* sc;
	struct scsipi_link* sc_link;

	sc = dse_cd.cd_devs[DSEUNIT(dev)];
	sc_link = sc->sc_link;

	SC_DEBUG(sc_link, SCSIPI_DB1, ("closing\n"));

	scsipi_wait_drain(sc_link);

	scsipi_adapter_delref(sc_link);
	sc_link->flags &= ~SDEV_OPEN;

	return (0);
}


/*
 * Perform special action on behalf of the user
 * Only does generic scsi ioctls.
 */
int
dseioctl(dev, cmd, addr, flag, p)
	dev_t dev;
	u_long cmd;
	caddr_t addr;
	int flag;
	struct proc *p;
{
	struct dse_softc* sc;

	sc = dse_cd.cd_devs[DSEUNIT(dev)];

	return (scsipi_do_ioctl(sc->sc_link, dev, cmd, addr, flag, p));
}


/*
 * end of file
 */

--------------020902070609060409080804--