text: /*-
   * Copyright (c) 1999-2001, Ivan Sharov, Vitaly Belekhov.
   * Copyright (c) 2004 Stanislav Svirid.
   * 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.
   * 
   * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
   * 
   * $RISS: if_arl/dev/arl/if_arl.c,v 1.7 2004/03/16 04:43:27 count Exp $
   */
  
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD: src/sys/dev/arl/if_arl.c,v 1.12 2005/11/11 16:04:49 ru Exp $");
  
  #include "opt_inet.h"
  
  #ifdef INET
  #define ARLCACHE
  #endif
  
  #include <sys/param.h>
  #include <sys/kernel.h>
  #include <sys/systm.h>
  #include <sys/malloc.h>
  #include <sys/mbuf.h>
  #include <sys/socket.h>
  #include <sys/sockio.h>
  #include <sys/proc.h>
  #include <sys/conf.h>
  
  #include <sys/module.h>
  #include <sys/bus.h>
  
  #include <machine/bus.h>
  #include <machine/resource.h>
  #include <sys/rman.h>
  
  #include <net/if.h>
  #include <net/if_dl.h>
  #include <net/if_media.h>
  #include <net/if_types.h>
  #include <net/ethernet.h>
  
  #include <net80211/ieee80211_var.h>
  
  #ifdef INET
  #include <netinet/in.h>
  #include <netinet/in_systm.h>
  #include <netinet/in_var.h>
  #include <netinet/ip.h>
  #endif
  
  #include <net/bpf.h>
  
  #include <machine/clock.h>
  
  #include <dev/arl/if_arlreg.h>
  
  /*#define DEBUG */
  #ifdef DEBUG
  #define D(x)	{printf("arl%d: ", sc->arl_unit); printf x; }
  #else
  #define D(x)
  #endif
  
  /*
   * channel attention
   */
  #define ARL_CHANNEL(sc) \
  	{ \
  		D(("channel ctrl %x reg %x\n", sc->arl_control, ar->controlRegister)); \
  		ar->controlRegister = (sc->arl_control ^= ARL_CHANNEL_ATTENTION); \
  	}
  
  /*
   * Check registration
   */
  #define ARL_CHECKREG(sc) (ar->registrationMode && ar->registrationStatus == 0)
  
  #define GET_ARL_PARAM(name) (arcfg.name = ar->name)
  #define SET_ARL_PARAM(name) (ar->name = arcfg.name)
  
  #ifndef BPF_MTAP
  #define BPF_MTAP(_ifp,_m)					\
  	do {							\
  		if ((_ifp)->if_bpf)				\
  			bpf_mtap((_ifp), (_m));			\
  	} while (0)
  #endif
  
  #if __FreeBSD_version < 500100
  #define BROADCASTADDR	(etherbroadcastaddr)
  #define _ARL_CURPROC	(curproc)
  #else
  #define BROADCASTADDR	(sc->arl_ifp->if_broadcastaddr)
  #define _ARL_CURPROC	(curthread)
  #endif
  
  static void	arl_hwreset	(struct arl_softc *);
  static void	arl_reset	(struct arl_softc *);
  static int	arl_ioctl	(struct ifnet *, u_long, caddr_t);
  static void	arl_init	(void *);
  static void	arl_start	(struct ifnet *);
  
  static void	arl_watchdog	(struct ifnet *);
  static void	arl_waitreg	(struct ifnet *);
  
  static void	arl_enable	(struct arl_softc *);
  static void	arl_config	(struct arl_softc *);
  static int	arl_command	(struct arl_softc *);
  static void	arl_put		(struct arl_softc *);
  
  static void	arl_read	(struct arl_softc *, caddr_t, int);
  static void	arl_recv	(struct arl_softc *);
  static struct mbuf* arl_get	(caddr_t, int, int, struct ifnet *);
  
  #ifdef ARLCACHE
  static void	arl_cache_store	(struct arl_softc *, struct ether_header *,
  					u_int8_t, u_int8_t, int);
  #endif
  
  static int  arl_media_change	(struct ifnet *);
  static void arl_media_status	(struct ifnet *, struct ifmediareq *);
  static void arl_read_config	(struct arl_softc *);
  
  devclass_t	arl_devclass;
  
  u_int8_t rate2media[4] = {
  	IFM_IEEE80211_DS354k,
  	IFM_IEEE80211_DS512k,
  	IFM_IEEE80211_DS1,
  	IFM_IEEE80211_DS2
  };
  
  /*
   * Copy config values to local cache
   */
  static void
  arl_read_config(sc)
  	struct arl_softc *sc;
  {
  	bzero(&arcfg, sizeof(arcfg));
  
  	bcopy(ar->lanCardNodeId, arcfg.lanCardNodeId,
  		sizeof(ar->lanCardNodeId));
  	bcopy(ar->specifiedRouter, arcfg.specifiedRouter,
  		sizeof(ar->specifiedRouter));
  	GET_ARL_PARAM(hardwareType);
  	GET_ARL_PARAM(majorHardwareVersion);
  	GET_ARL_PARAM(minorHardwareVersion);
  	GET_ARL_PARAM(radioModule);
  	GET_ARL_PARAM(channelSet);
  	if (!arcfg.channelSet)
  		arcfg.channelSet = ar->defaultChannelSet;
  	GET_ARL_PARAM(channelNumber);
  	GET_ARL_PARAM(spreadingCode);
  	GET_ARL_PARAM(priority);
  	GET_ARL_PARAM(receiveMode);
  	arcfg.registrationMode = 1;	/* set default TMA mode */
  	arcfg.txRetry = 0;
  
  	bcopy(ar->name, arcfg.name, ARLAN_NAME_SIZE);
  	bcopy(ar->systemId, arcfg.sid, 4 * sizeof(arcfg.sid[0]));
  }
  
  /*
   * Attach device
   */
  int
  arl_attach(dev)
  	device_t	dev;
  {
  	struct arl_softc* sc = device_get_softc(dev);
  	struct ifnet *ifp;
  	int		attached, configured = 0;
  
  	D(("attach\n"));
  
  	ifp = sc->arl_ifp = if_alloc(IFT_ETHER);
  	if (ifp == NULL)
  		return (ENOSPC);
  
  	configured = ar->configuredStatusFlag;
  	attached = (ifp->if_softc != 0);
  
  	if (!configured && bootverbose)
  		device_printf(dev, "card is not configured\n");
  	else
  		arl_read_config(sc);
  
  	arl_reset(sc);
  
  	/* Read config for default values if card was not configured */
  	if (!configured)
  		arl_read_config(sc);
  
  	/* Initialize ifnet structure. */
  	ifp->if_softc = sc;
  #if __FreeBSD_version < 502000
  	ifp->if_unit = sc->arl_unit;
  	ifp->if_name = "arl";
  #else
  	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
  #endif
  	ifp->if_mtu = ETHERMTU;
  	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
  	    IFF_NEEDSGIANT;
  	ifp->if_start = arl_start;
  	ifp->if_ioctl = arl_ioctl;
  	ifp->if_watchdog = arl_watchdog;
  	ifp->if_init = arl_init;
  	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
  	ifp->if_baudrate = 2000000;
  	ifp->if_timer = 0;
  
  	ifmedia_init(&sc->arl_ifmedia, 0, arl_media_change, arl_media_status);
  #define ADD(s, o)	ifmedia_add(&sc->arl_ifmedia, \
  	IFM_MAKEWORD(IFM_IEEE80211, (s), (o), 0), 0, NULL)
  	ADD(IFM_IEEE80211_DS354k, 0);
  	ADD(IFM_IEEE80211_DS354k, IFM_IEEE80211_ADHOC);
  	ADD(IFM_IEEE80211_DS512k, 0);
  	ADD(IFM_IEEE80211_DS512k, IFM_IEEE80211_ADHOC);
  	ADD(IFM_IEEE80211_DS1, 0);
  	ADD(IFM_IEEE80211_DS1, IFM_IEEE80211_ADHOC);
  	ADD(IFM_IEEE80211_DS2, 0);
  	ADD(IFM_IEEE80211_DS2, IFM_IEEE80211_ADHOC);
  	ifmedia_set(&sc->arl_ifmedia, IFM_MAKEWORD(IFM_IEEE80211,
  		rate2media[arcfg.spreadingCode - 1], 0, 0));
  #undef ADD
  
  	/*
  	 * Attach the interface
  	 */
  	if (!attached) {
  #if __FreeBSD_version < 500100
  		ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
  #else
  		ether_ifattach(ifp, ar->lanCardNodeId);
  #endif
  	}
  
  	return (0);
  }
  
  /*
   * Hardware reset
   * reset all setting to default (setted ARLANDGS)
   */
  static void
  arl_hwreset(sc)
  	struct arl_softc *sc;
  {
  	D(("hw reset\n"));
  
  	ar->controlRegister = 1;
  	DELAY(ARDELAY1);
  
  	if (arl_wait_reset(sc, 0x24, ARDELAY1))
  		arl_stop(sc);
  
  	ar->controlRegister = (sc->arl_control = 1);
  	DELAY(ARDELAY1);
  }
  
  
  /*
   * wait arlan command
   */
  static int
  arl_command(sc)
  	struct arl_softc *sc;
  {
  	int i;       /* long stuppid delay ??? */
  
  	D(("commandByte %x\n", ar->commandByte));
  
  	for (i = 100000; ar->commandByte && i; i--)
  		;
  
  	if (i == 0)
  		ar->commandByte = 0;
  
  	return (i == 0);
  }
  
  /*
   * Enable for recieveng
   */
  static void
  arl_enable(sc)
  	struct arl_softc *sc;
  {
  	D(("enable\n"));
  	sc->arl_control = (ARL_INTERRUPT_ENABLE | ARL_CLEAR_INTERRUPT);
  	ar->controlRegister = sc->arl_control;
  	arl_command(sc);
  
  	ar->rxStatusVector = 0;
  	ar->commandByte = 0x83;
  	ar->commandParameter[0] = 1;
  	ARL_CHANNEL(sc);
  	arl_command(sc);
  }
  
  /*
   * reset and set user parameters
   */
  static void
  arl_reset(sc)
  	struct arl_softc *sc;
  {
  	D(("reset\n"));
  	arl_hwreset(sc);
  
  	ar->resetFlag1 = 1;
  	bzero((ar), 0x1FF0);              /* fill memory board with 0 */
  	ar->resetFlag1 = 0;
  
  	sc->arl_control = 0;
  
  /*	if (arl_wait_reset(sc, 0x168, ARDELAY1))
  		return;
   */
  #if 1
  	{
  		int cnt = 0x168;
  		int delay = ARDELAY1;
  
  		ar->resetFlag = 0xFF;		/* wish reset */
  		ar->controlRegister = 0;	/* unreeze - do it */
  
  		while (ar->resetFlag && cnt--)
  			DELAY(delay);
  
  		if (cnt == 0) {
  			printf("arl%d: reset timeout\n", sc->arl_unit);
  			return;
  		}
  
  		D(("reset wait %d\n", 0x168 - cnt));
  	}
  #endif
  
  	if (ar->diagnosticInfo != 0xff) {
  		printf("arl%d: reset error\n", sc->arl_unit);
  		return;
  	}
  	arl_config(sc);
  }
  
  /*
   * configure radio parameters
   */
  static void
  arl_config(sc)
  	struct arl_softc *sc;
  {
  	int i;
  
  	D(("config\n"));
  
  	SET_ARL_PARAM(spreadingCode);
  	SET_ARL_PARAM(channelNumber);
  	SET_ARL_PARAM(channelSet);
  	SET_ARL_PARAM(registrationMode);
  	SET_ARL_PARAM(priority);
  	SET_ARL_PARAM(receiveMode);
  
  	bcopy(arcfg.sid, ar->systemId, 4 * sizeof(ar->systemId[0]));
  	bcopy(arcfg.specifiedRouter, ar->specifiedRouter, ETHER_ADDR_LEN);
  	bcopy(arcfg.lanCardNodeId, ar->lanCardNodeId, ETHER_ADDR_LEN);
  
  	bzero(ar->name, ARLAN_NAME_SIZE); /* clear name */
  	strncpy(ar->name, arcfg.name, ARLAN_NAME_SIZE);
  
  	ar->diagnosticInfo = 0;
  	ar->commandByte = 1;
  	ARL_CHANNEL(sc);
  	DELAY(ARDELAY1);
  
  	if (arl_command(sc)) {
  		D(("config failed\n"));
  		return;
  	}
  
  	for (i = 0x168; ar->diagnosticInfo == 0 && i; i--)
  		DELAY(ARDELAY1);
  
  	if (i == 0) {
  		D(("config timeout\n"));
  		return;
  	}
  
  	if (ar->diagnosticInfo != 0xff) {
  		D(("config error\n"));
  		return;
  	}
  
  	D(("config lanCardNodeId %6D\n", ar->lanCardNodeId, ":"));
  	D(("config channel set %d, frequency %d, spread %d, mode %d\n",
  	    ar->channelSet,
  	    ar->channelNumber,
  	    ar->spreadingCode,
  	    ar->registrationMode));
  	/* clear quality stat */
  	bzero(sc->arl_sigcache, MAXARLCACHE * sizeof(struct arl_sigcache));
  }
  
  /*
   * Socket Ioctl's.
   */
  static int
  arl_ioctl(ifp, cmd, data)
  	register struct ifnet *ifp;
  	u_long cmd;
  	caddr_t data;
  {
  	struct arl_softc *sc = ifp->if_softc;
  	struct ifreq *ifr = (struct ifreq *)data;
  	struct ieee80211req *ireq = (struct ieee80211req *)data;
  	d_thread_t *td = _ARL_CURPROC;
  	struct arl_req arlan_io;
  	int count, s, error = 0;
  	u_int8_t tmpstr[IEEE80211_NWID_LEN*2];
  	u_int8_t *tmpptr;
  	u_int32_t newsid;
  	caddr_t user;
  
  	D(("ioctl %lx\n", cmd));
  	s = splimp();
  
  	switch (cmd) {
  	case SIOCSIFADDR:
  	case SIOCGIFADDR:
  	case SIOCSIFMTU:
  		error = ether_ioctl(ifp, cmd, data);
  		break;
  
  	case SIOCSIFFLAGS:
  		if (ifp->if_flags & IFF_UP) {
  			if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
  				arl_init(sc);
  		} else {
  			if (ifp->if_drv_flags & IFF_DRV_RUNNING)
  				arl_stop(sc);
  		}
  		break;
  	case SIOCSIFMEDIA:
  	case SIOCGIFMEDIA:
  		error = ifmedia_ioctl(ifp, ifr, &sc->arl_ifmedia, cmd);
  		break;
  
  	case SIOCG80211:
  		switch (ireq->i_type) {
  		case IEEE80211_IOC_SSID:
  			if (ireq->i_val != -1) {
  				error = EINVAL;
  				break;
  			}
  			bzero(tmpstr, IEEE80211_NWID_LEN);
  			snprintf(tmpstr, IEEE80211_NWID_LEN - 1, "0x%08x",
  					*(int *)arcfg.sid);
  			ireq->i_len = IEEE80211_NWID_LEN;
  			error = copyout(tmpstr, ireq->i_data,
  				IEEE80211_NWID_LEN);
  			break;
  		case IEEE80211_IOC_STATIONNAME:
  			ireq->i_len = sizeof(arcfg.name);
  			tmpptr = arcfg.name;
  			bzero(tmpstr, IEEE80211_NWID_LEN);
  			bcopy(tmpptr, tmpstr, ireq->i_len);
  			error = copyout(tmpstr, ireq->i_data,
  				IEEE80211_NWID_LEN);
  			break;
  		case IEEE80211_IOC_CHANNEL:
  			ireq->i_val = arcfg.channelNumber;
  			break;
  		case IEEE80211_IOC_POWERSAVE:
  			ireq->i_val = (arcfg.registrationMode == 2 ?
  				IEEE80211_POWERSAVE_PSP :
  				IEEE80211_POWERSAVE_OFF);
  			break;
  		default:
  			error = EINVAL;
  			break;
  		}
  		break;
  
  	case SIOCS80211:
  		if ((error = suser(td)))
  			break;
  		switch (ireq->i_type) {
  		case IEEE80211_IOC_SSID:
  			if (ireq->i_len > 4) {
  				error = EINVAL;
  				break;
  			}
  			bzero(&newsid, sizeof(newsid));
  			error = copyin(ireq->i_data,
  				(u_char *)(&newsid) + 4 - ireq->i_len,
  				ireq->i_len);
  
  			if (error)
  				break;
  
  			newsid = htonl(newsid);
  			if (newsid < 0  || newsid % 2) {
  				error = EINVAL;
  				break;
  			}
  
  			bcopy(&newsid, arcfg.sid, sizeof(arcfg.sid));
  			break;
  		case IEEE80211_IOC_STATIONNAME:
  			if (ireq->i_len > ARLAN_NAME_SIZE) {
  				error = EINVAL;
  				break;
  			}
  			bzero(arcfg.name, ARLAN_NAME_SIZE);
  			error = copyin(ireq->i_data, arcfg.name, ireq->i_len);
  			break;
  		case IEEE80211_IOC_CHANNEL:
  			if (ireq->i_val < 0 || ireq->i_val > 5) {
  				error = EINVAL;
  				break;
  			}
  			arcfg.channelNumber = ireq->i_val;
  			break;
  		case IEEE80211_IOC_POWERSAVE:
  			switch (ireq->i_val) {
  			case IEEE80211_POWERSAVE_OFF:
  				if (arcfg.registrationMode == 2)
  					arcfg.registrationMode = 1;
  				break;
  			case IEEE80211_POWERSAVE_ON:
  			case IEEE80211_POWERSAVE_PSP:
  				arcfg.registrationMode = 2;
  				break;
  			default:
  				error = EINVAL;
  				break;
  			}
  			break;
  		default:
  			error = EINVAL;
  			break;
  		}
  
  		if (!error)
  			arl_config(sc);
  
  		break;
  
  #define GET_PARAM(name) (arlan_io.cfg.name = arcfg.name)
  
  #define GET_COPY_PARAM(name)						\
  	{								\
  		bzero(arlan_io.cfg.name, sizeof(arlan_io.cfg.name));	\
  		bcopy(arcfg.name, arlan_io.cfg.name, sizeof(arlan_io.cfg.name)); \
  	}
  	case SIOCGARLALL:
  		bzero(&arlan_io, sizeof(arlan_io));
  		if (!suser(td)) {
  			bcopy(ar->systemId, arlan_io.cfg.sid, 4);
  		}
  
  		GET_COPY_PARAM(name);
  		GET_COPY_PARAM(lanCardNodeId);
  		GET_COPY_PARAM(specifiedRouter);
  		GET_PARAM(channelNumber);
  		GET_PARAM(channelSet);
  		GET_PARAM(spreadingCode);
  		GET_PARAM(registrationMode);
  		GET_PARAM(hardwareType);
  		GET_PARAM(majorHardwareVersion);
  		GET_PARAM(minorHardwareVersion);
  		GET_PARAM(radioModule);
  		GET_PARAM(priority);
  		GET_PARAM(receiveMode);
  		GET_PARAM(txRetry);
  
  		user = (void *)ifr->ifr_data;
  		for (count = 0; count < sizeof(arlan_io); count++)
  			if (subyte(user + count, ((char *)&arlan_io)[count]))
  				return (EFAULT);
  		break;
  
  #define SET_PARAM(name)							\
  	do {								\
  		if (arlan_io.what_set & ARLAN_SET_##name)		\
  			arcfg.name = arlan_io.cfg.name;			\
  	} while (0)
  #define SET_COPY_PARAM(name)						\
  	do {								\
  		if (arlan_io.what_set & ARLAN_SET_##name) {		\
  			bzero(arcfg.name, sizeof(arcfg.name));		\
  			bcopy(arlan_io.cfg.name, arcfg.name, sizeof(arcfg.name)); \
  		}							\
  	} while (0)
  
  	case SIOCSARLALL:
  		if (suser(td))
  			break;
  
  		user = (void *)ifr->ifr_data;
  		for (count = 0; count < sizeof(arlan_io); count++) {
  			if (fubyte(user + count) < 0)
  				return (EFAULT);
  		}
  
  		bcopy(user, (char *)&arlan_io, sizeof(arlan_io));
  
  		D(("need set 0x%04x\n", arlan_io.what_set));
  
  		if (arlan_io.what_set) {
  			SET_COPY_PARAM(name);
  			SET_COPY_PARAM(sid);
  			SET_COPY_PARAM(specifiedRouter);
  			SET_COPY_PARAM(lanCardNodeId);
  			SET_PARAM(channelSet);
  			SET_PARAM(channelNumber);
  			SET_PARAM(spreadingCode);
  			SET_PARAM(registrationMode);
  			SET_PARAM(priority);
  			SET_PARAM(receiveMode);
  			SET_PARAM(txRetry);
  
  			arl_config(sc);
  		}
  #undef SET_COPY_PARAM
  #undef SET_PARAM
  #undef GET_COPY_PARAM
  #undef GET_PARAM
  		break;
  #ifdef ARLCACHE
  	case SIOCGARLQLT:
  		user = (void *)ifr->ifr_data;
  		for (count = 0; count < sizeof(sc->arl_sigcache); count++) {
  			if (fubyte(user + count) < 0)
  				return (EFAULT);
  		}
  		while (ar->interruptInProgress) ; /* wait */
  		bcopy(&(sc->arl_sigcache), (void *)ifr->ifr_data, sizeof(sc->arl_sigcache));
  		break;
  #endif
  	case SIOCGARLSTB:
  		user = (void *)ifr->ifr_data;
  		for (count = 0; count < sizeof(struct arl_stats); count++) {
  			if (fubyte(user + count) < 0)
  				return (EFAULT);
  		}
  
  		while (ar->lancpuLock) ;
  		ar->hostcpuLock = 1;
  		bcopy(&(ar->stat), (void *)ifr->ifr_data, sizeof(struct arl_stats));
  		ar->hostcpuLock  = 0;
  		break;
  
  	default:
  		error = EINVAL;
  	}
  
  	splx(s);
  
  	return (error);
  }
  
  /*
   * Wait registration
   */
  static void
  arl_waitreg(ifp)
  	struct ifnet		*ifp;
  {
  	struct arl_softc	*sc = ifp->if_softc;
  
  	D(("wait reg\n"));
  
  	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
  		if (ARL_CHECKREG(sc)) {
  			/* wait registration */
  			D(("wait registration\n"));
  			ifp->if_watchdog = arl_waitreg;
  			ifp->if_timer = 2;
  		} else {
  			/* registration restored */
  			D(("registration restored\n"));
  			ifp->if_timer = 0;
  			arl_init(sc);
  		}
  	}
  }
  
  /*
   * Handle transmit timeouts.
   */
  static void
  arl_watchdog(ifp)
  	struct ifnet		*ifp;
  {
  	struct arl_softc        *sc = ifp->if_softc;
  
  	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
  		return;
  
  	D(("device timeout\n"));
  
  	if (ARL_CHECKREG(sc)) {
  		/* Lost registratoin */
  		D(("timeout lost registration\n"));
  		ifp->if_watchdog = arl_waitreg;
  		ifp->if_timer = 2;
  	}
  }
  
  /*
   * Initialize
   */
  static void
  arl_init(xsc)
  	void *xsc;
  {
  	struct arl_softc *sc = xsc;
  	struct ifnet *ifp = sc->arl_ifp;
  	int s;
  
  	D(("init\n"));
  
  	s = splimp();
  
  	if (ARL_CHECKREG(sc))
  		arl_reset(sc);
  
  	arl_enable(sc);
  
  	/* set flags */
  
  	ifp->if_drv_flags |= IFF_DRV_RUNNING;
  	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
  
  	arl_start(ifp);
  
  	splx(s);
  	D(("init done\n"));
  }
  
  /*
   * Put buffer into arlan buffer and start transmit
   */
  static void
  arl_put(sc)
  	struct arl_softc *sc;
  {
  	struct arl_tx_param txp;
  	int i;
  
  	if (ARL_CHECKREG(sc))
  		sc->arl_ifp->if_oerrors++;
  
  	/* copy dst adr */
  	for(i = 0; i < 6; i++)
  		txp.dest[i] = sc->arl_tx[i];
  	txp.clear	= 0;
  	txp.retries	= arcfg.txRetry;      /* use default value */
  	txp.routing	= 1;
  	txp.scrambled	= 0;
  	txp.offset	= (intptr_t)ar->txBuffer - (intptr_t)(ar);
  	txp.length	= sc->tx_len - ARLAN_HEADER_SIZE;
  
  #ifdef SEND_ARLAN_HEADER
  	if (ar->registrationMode != 1)
  		txp.length = sc->tx_len;
  #endif
  
  	/* copy from internal buffer to adapter memory */
  #ifdef SEND_ARLAN_HEADER
  	if (ar->registrationMode)
  #endif
  		bcopy(sc->arl_tx + ARLAN_HEADER_SIZE,
  		      ar->txBuffer,
  		      sc->tx_len - ARLAN_HEADER_SIZE);
  #ifdef SEND_ARLAN_MODE
  	else
  		bcopy(sc->arl_tx, ar->txBuffer, sc->tx_len);
  #endif
  
  	/* copy command parametr */
  	bcopy(&txp, ar->commandParameter, 14);
  	ar->commandByte = 0x85;       /* send command */
  	ARL_CHANNEL(sc);
  	if (arl_command(sc))
  		sc->arl_ifp->if_oerrors++;
  }
  
  /*
   * start output
   */
  static void
  arl_start(ifp)
  	struct ifnet		*ifp;
  {
  	struct arl_softc	*sc;
  	struct mbuf		*m;
  	struct mbuf		*m0 = NULL;
  
  	sc = ifp->if_softc;
  
  	D(("start\n"));
  
  	/* Don't do anything if output is active */
  	if (ifp->if_drv_flags & IFF_DRV_OACTIVE)
  		return;
  
  	/* Dequeue the next datagram */
  	IF_DEQUEUE(&ifp->if_snd, m0);
  
  	/* If there's nothing to send, return. */
  	if (m0 != NULL) {
  		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
  
  		/* Copy the datagram to the buffer. */
  		sc->tx_len = 0;
  		for(m = m0; m != NULL; m = m->m_next) {
  			if (m->m_len == 0)
  				continue;
  			bcopy(mtod(m, caddr_t),
  			      sc->arl_tx + sc->tx_len, m->m_len);
  			sc->tx_len += m->m_len;
  		}
  
  		/* if packet size is less than minimum ethernet frame size,
  		 * pad it with zeroes to that size */
  		if (sc->tx_len < ETHER_MIN_LEN) {
  			bzero(sc->arl_tx + sc->tx_len, ETHER_MIN_LEN - sc->tx_len);
  			sc->tx_len = ETHER_MIN_LEN;
  		}
  
  		/* Give the packet to the bpf, if any */
  		BPF_MTAP(ifp, m0);
  
  		m_freem(m0);
  
  		/* Now transmit the datagram */
  		ifp->if_timer = 1;        /* wait 1 sec */
  
  		ifp->if_watchdog = arl_watchdog;
  		arl_put(sc);
  	}
  }
  
  /*
   * stop interface
   */
  void
  arl_stop(sc)
  	struct arl_softc *sc;
  {
  	struct ifnet	*ifp;
  	int		s;
  
  	s = splimp();
  
  	ifp = sc->arl_ifp;
  
  	ifp->if_timer = 0;        /* disable timer */
  	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING|IFF_DRV_OACTIVE);
  	/*  arl_hwreset(unit);  */
  	sc->rx_len = 0;
  	sc->tx_len = 0;
  	/* disable interrupt */
  	ar->controlRegister = 0;
  
  	splx(s);
  }
  
  /*
   * Pull read data off a interface.
   * Len is length of data, with local net header stripped.
   */
  static struct mbuf*
  arl_get(buf, totlen, off0, ifp)
  	caddr_t		buf;
  	int		totlen;
  	int		off0;
  	struct ifnet *	ifp;
  {
  	struct mbuf *top, **mp, *m;
  	int off = off0, len;
  	caddr_t cp = buf;
  	char *epkt;
  
  	cp = buf;
  	epkt = cp + totlen;
  
  	if (off) {
  		cp += off + 2 * sizeof(u_short);
  		totlen -= 2 * sizeof(u_short);
  	}
  
  	MGETHDR(m, M_DONTWAIT, MT_DATA);
  	if (m == 0)
  		return (0);
  	m->m_pkthdr.rcvif = ifp;
  	m->m_pkthdr.len = totlen;
  	m->m_len = MHLEN;
  	top = 0;
  	mp = &top;
  	while (totlen > 0) {
  		if (top) {
  			MGET(m, M_DONTWAIT, MT_DATA);
  			if (m == 0) {
  				m_freem(top);
  				return (0);
  			}
  			m->m_len = MLEN;
  		}
  		len = min(totlen, epkt - cp);
  		if (len >= MINCLSIZE) {
  			MCLGET(m, M_DONTWAIT);
  			if (m->m_flags & M_EXT)
  				m->m_len = len = min(len, MCLBYTES);
  			else
  				len = m->m_len;
  		} else {
  			/*
  			 *			  * Place initial small packet/header at end of mbuf.
  			 *						   */
  			if (len < m->m_len) {
  				if (top == 0 && len + max_linkhdr <= m->m_len)
  					m->m_data += max_linkhdr;
  				m->m_len = len;
  			} else
  				len = m->m_len;
  		}
  		bcopy(cp, mtod(m, caddr_t), (unsigned)len);
  		cp += len;
  		*mp = m;
  		mp = &m->m_next;
  		totlen -= len;
  		if (cp == epkt)
  			cp = buf;
  	}
  
  	return (top);
  }
  
  /* ------------------------------------------------------------------
   *  * Pass a packet up to the higher levels.
   *   */
  static void
  arl_read(sc, buf, len)
  	struct arl_softc	*sc;
  	caddr_t			buf;
  	int			len;
  {
  	register struct ether_header *eh;
  	struct ifnet *ifp = sc->arl_ifp;
  	struct mbuf *m;
  
  	eh = (struct ether_header *)buf;
  	/*
  	 * Check if there's a bpf filter listening on this interface.
  	 * If so, hand off the raw packet to bpf.
  	 */
  	if (ifp->if_bpf) {
  		/*
  		 * Note that the interface cannot be in promiscuous mode if
  		 * there are no bpf listeners.  And if el are in promiscuous
  		 * mode, el have to check if this packet is really ours.
  		 *
  		 * This test does not support multicasts.
  		 */
  		if ((ifp->if_flags & IFF_PROMISC)
  		   && bcmp(eh->ether_dhost, IF_LLADDR(sc->arl_ifp),
  			   sizeof(eh->ether_dhost)) != 0
  		   && bcmp(eh->ether_dhost, BROADCASTADDR,
  			   sizeof(eh->ether_dhost)) != 0)
  			return;
  	}
  	/*
  	 * Pull packet off interface.
  	 */
  #if __FreeBSD_version < 500100
  	buf += sizeof(struct ether_header);
  	len -= sizeof(struct ether_header);
  #endif
  	m = arl_get(buf, len, 0, ifp);
  	if (m == 0)
  		return;
  
  #ifdef ARLCACHE
  	arl_cache_store(sc, eh, ar->rxQuality & 0x0f,
  			(ar->rxQuality & 0xf0) >> 4, ARLCACHE_RX);
  #endif
  
  #if __FreeBSD_version < 500100
  	ether_input(ifp, eh, m);
  #else
  	(*ifp->if_input)(ifp, m);
  #endif
  }
  
  /*
   * get packet from adapter
   */
  static void
  arl_recv(sc)
  	struct arl_softc *sc;
  {
  	sc->rx_len = ar->rxLength;
  
  	if (sc->rx_len) {
  #ifdef SEND_ARLAN_HEADER
  		if (ar->registrationMode == 1) {
  #endif
  			bcopy(ar->ultimateDestAddress, sc->arl_rx, 6);
  			bcopy(ar->rxSrc, (char*)sc->arl_rx + 6, 6);
  			bcopy((char *)(ar) + ar->rxOffset,
  			      (char *)sc->arl_rx + 12,
  			      sc->rx_len);
  			sc->rx_len += ARLAN_HEADER_SIZE;
  #ifdef SEND_ARLAN_HEADER
  		} else {
  			bcopy((char *)(ar) + ar->rxOffset,
  			      (char *)sc->arl_rx, sc->rx_len);
  		}
  #endif
  	}
  }
  
  /*
   * Ethernet interface interrupt processor
   */
  void
  arl_intr(arg)
  	void *arg;
  {
  	register struct arl_softc *sc = (struct arl_softc *) arg;
  	struct ifnet *ifp = sc->arl_ifp;
  
  	/* enable interrupt */
  	ar->controlRegister = (sc->arl_control & ~ARL_CLEAR_INTERRUPT);
  	ar->controlRegister = (sc->arl_control | ARL_CLEAR_INTERRUPT);
  
  	if (ar->txStatusVector) {
  		if (ar->txStatusVector != 1)
  			sc->arl_ifp->if_collisions++;
  		ifp->if_timer = 0;     /* disable timer */
  		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
  		arl_start(ifp);
  		ar->txStatusVector = 0;
  #ifdef ARLCACHE
  		arl_cache_store(sc,
  			(struct ether_header *)(sc->arl_tx),
  			ar->txAckQuality & 0x0f,
  			(ar->txAckQuality & 0xf0) >> 4, ARLCACHE_TX);
  #endif
  	}
  
  	if (ar->rxStatusVector) {
  		if (ar->rxStatusVector == 1) {   /* it is data frame */
  			arl_recv(sc);
  			arl_read(sc, sc->arl_rx, sc->rx_len);
  			ifp->if_opackets++;
  		}
  		ar->rxStatusVector = 0;
  
  		ar->commandByte = 0x83;
  		ar->commandParameter[0] = 1;
  		ARL_CHANNEL(sc);
  		if (arl_command(sc))
  			ifp->if_ierrors++;
  	}
  
  	return;
  }
  
  /*
   * waiting for resetFlag dropped
   */
  int
  arl_wait_reset(sc, cnt, delay)
  	struct arl_softc *sc;
  	int cnt;
  	int delay;
  {
  	D(("wait_reset cnt=%d delay=%d\n", cnt, delay));
  
  	ar->resetFlag = 1;		/* wish reset */
  	ar->controlRegister = 0;	/* unreeze - do it */
  
  	while (ar->resetFlag && cnt--)
  		DELAY(delay);
  
  	D(("reset done. %d cycles left\n", cnt));
  
  	if (cnt == 0)
  		printf("arl%d: reset failed\n", sc->arl_unit);
  
  	return (cnt == 0);
  }
  
  /*
   * Allocate an irq resource with the given resource id
   */
  int
  arl_alloc_irq(dev, rid, flags)
  	device_t dev;
  	int rid;
  	int flags;
  {
  	struct arl_softc *sc = device_get_softc(dev);
  	struct resource *res;
  
  	res = bus_alloc_resource_any(
  	    dev, SYS_RES_IRQ, &rid, (RF_ACTIVE | flags));
  	if (res) {
  		sc->irq_rid = rid;
  		sc->irq_res = res;
  		return (0);
  	} else
  		return (ENOENT);
  }
  
  /*
   * Allocate an memory resource with the given resource id
   */
  int
  arl_alloc_memory(dev, rid, size)
  	device_t dev;
  	int rid;
  	int size;
  {
  	struct arl_softc *sc = device_get_softc(dev);
  	struct resource *res;
  
  	res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
  				 0ul, ~0ul, size, RF_ACTIVE);
  	if (res) {
  		sc->mem_rid = rid;
  		sc->mem_res = res;
  		return (0);
  	} else
  		return (ENOENT);
  }
  
  /*
   * Release all resources
   */
  void
  arl_release_resources(dev)
  	device_t dev;
  {
  	struct arl_softc *sc = device_get_softc(dev);
  
  	if (sc->mem_res) {
  		bus_release_resource(dev, SYS_RES_MEMORY,
  				     sc->mem_rid, sc->mem_res);
  		sc->mem_res = 0;
  	}
  	if (sc->irq_res) {
  		bus_release_resource(dev, SYS_RES_IRQ,
  				     sc->irq_rid, sc->irq_res);
  		sc->irq_res = 0;
  	}
  }
  
  #ifdef ARLCACHE
  static void
  arl_cache_store(sc, eh, level, quality, dir)
  	struct arl_softc *sc;
  	struct ether_header *eh;
  	u_int8_t level;
  	u_int8_t quality;
  	int	dir;
  {
  	int i;
  	static int cache_slot = 0;
  	static int wrapindex = 0;
  	u_int8_t zero[6] = {0, 0, 0, 0, 0, 0};
  	u_char *mac;
  
  	if ((ntohs(eh->ether_type) != ETHERTYPE_IP)) {
  		return;
  	}
  
  	mac = (dir == ARLCACHE_RX ? eh->ether_shost : eh->ether_dhost);
  
  	for (i = 0; i < MAXARLCACHE; i++) {
  		if (!bcmp(zero, sc->arl_sigcache[i].macsrc, 6) ||
  		    !bcmp(mac, sc->arl_sigcache[i].macsrc, 6))
  			break;
  	}
  
  	if (i < MAXARLCACHE)
  		cache_slot = i;
  	else {
  		if (wrapindex == MAXARLCACHE)
  			wrapindex = 0;
  		cache_slot = wrapindex++;
  	}
  
  	bcopy(dir == ARLCACHE_RX ? eh->ether_shost : eh->ether_dhost,
  	    sc->arl_sigcache[cache_slot].macsrc, 6);
  
  	sc->arl_sigcache[cache_slot].level[dir] = level;
  	sc->arl_sigcache[cache_slot].quality[dir] = quality;
  }
  #endif
  
  static int
  arl_media_change(ifp)
  	struct ifnet	*ifp;
  {
  	struct arl_softc *sc = ifp->if_softc;
  	int otype = arcfg.registrationMode;
  	int orate = arcfg.spreadingCode;
  	int nrate, i;
  
  	nrate = IFM_SUBTYPE(sc->arl_ifmedia.ifm_cur->ifm_media);
  
  	for(i = 1; i <= 4; i++) {
  		if (rate2media[i - 1] == nrate)
  			break;
  	}
  
  	if (i == 5)
  		return (EINVAL);
  
  	arcfg.spreadingCode = i;
  
  	/* XXX Need fix for PSP mode */
  	if ((sc->arl_ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0)
  		arcfg.registrationMode = 0;
  	else
  		arcfg.registrationMode = 1;
  
  	if (otype != arcfg.registrationMode ||
  	    orate != arcfg.spreadingCode)
  		arl_config(sc);
  
  	return (0);
  }
  
  static void
  arl_media_status(ifp, imr)
  	struct ifnet		*ifp;
  	struct ifmediareq	*imr;
  {
  	struct arl_softc	*sc = ifp->if_softc;
  
  	imr->ifm_active = IFM_IEEE80211;
  
  	if (arcfg.registrationMode == 0)
  		imr->ifm_active |= IFM_IEEE80211_ADHOC;
  
  	imr->ifm_active |= IFM_MAKEWORD(IFM_IEEE80211,
  				rate2media[arcfg.spreadingCode - 1], 0, 0);
  	imr->ifm_status = IFM_AVALID;
  	if (!ARL_CHECKREG(sc))
  		imr->ifm_status |= IFM_ACTIVE;
  }
  