/*
  Copyright 2004, 2005 Jean-Baptiste Note
  Copyright (c) 2006 Red Hat, Inc.

  Parts of this program code are derived from the bcm43xx driver,
  Copyright 2005 Martin Langer <martin-langer@gmx.de>, et. al.
  Please refer there for the complete copyright statement.

  This file is part of prism54usb.

  prism54usb is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  prism54usb is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with prism54usb; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

*/

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
#include <linux/if_arp.h>
#include <asm/uaccess.h>
#include <linux/list.h>

#ifdef MADWIFI
#include <linux/version.h>
#include "net80211/if_ethersubr.h"
#include "net80211/if_media.h"
#include "net80211/ieee80211_var.h"
#endif				/* MADWIFI */

#include "isl_sm.h"
#include "islsm_protocol.h"
#include "islsm_ioctl.h"
#include "sent_data_ok.h"
#include "islsm_log.h"

#ifdef MADWIFI
#define DRV_NAME "islsm"
#else
#include "islusb_dev.h"
#endif

MODULE_DESCRIPTION("Prism54 softmac layer driver");
MODULE_AUTHOR("Jean-Baptiste Note <jean-baptiste.note@m4x.org>");
MODULE_LICENSE("GPL");

const char dummy_mac[ETH_ALEN] = { 0x00, 0x3d, 0xb4, 0x00, 0x00, 0x00 };

#ifdef MADWIFI
static int islsm_tx_start(struct ieee80211com *ic,
			  struct ieee80211_node *ni, struct sk_buff *skb);
#else
static int islsm_reset(struct net_device *netdev);
#endif				/* MADWIFI */

static unsigned minor = 0;

static int
islsm_close(struct net_device *netdev)
{
#ifdef MADWIFI
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
#endif
	// Is this called in case of a hot unplug ?
	FN_ENTER;

#ifndef MADWIFI
	ieee80211softmac_stop(netdev);
#endif

	// Shutdown should be called unconditionally
	if (netdev->flags & IFF_RUNNING) {
		islog(L_DEBUG, "%s: Running on close, shutting down 802.11\n",
		      DRV_NAME);
/* this should pertain to stop only */
		// Empty the lmac queues !
		/* FIXME ; find something to replace this */
		//islsm_empty_queues(netdev);
#ifdef MADWIFI
		/* stop the 802.11 ?
		 * Done last because the bh won't run after this */
		/* I'm not even sure i want this ; pump stops the
		 * interface and reboots it... We don't want to look
		 * ieee80211 state here ! */
		ieee80211_new_state(&islsm->sc_ic, IEEE80211_S_INIT, -1);
		islog(L_DEBUG, "%s: Shutdown ieee80211 layer\n", DRV_NAME);
#endif
		netif_stop_queue(netdev);
		netdev->flags &= ~IFF_RUNNING;
	}
	FN_EXIT1(0);
	return 0;
}

static void
islsm_tx_timeout(struct net_device *netdev)
{
	islog(L_DEBUG, "islsm_tx_timeout called\n");
	return;
}

static int
islsm_transmit(struct sk_buff *skb, struct net_device *netdev)
{
	int err = 0;
#ifdef MADWIFI
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct ieee80211com *ic = &islsm->sc_ic;
	struct ieee80211_node *ni = NULL;
	struct ether_header *eh;

	FN_ENTER;

	/* first poll management queue - it has higher prio */
	while (1) {
		struct sk_buff *skb0;
		struct ieee80211_node *ni;
		struct ieee80211_cb *cb;

		IF_DEQUEUE(&ic->ic_mgtq, skb0);
		if (!skb0)
			break;

		cb = (struct ieee80211_cb *) skb0->cb;
		ni = cb->ni;

		err = islsm_tx_start(ic, ni, skb0);
		/* silently drop management frame, argh */
		if (err)
			dev_kfree_skb(skb0);
	}

	if (!skb) {
		FN_EXIT1(0);
		return 0;
	}

	if ((netdev->flags & IFF_RUNNING) == 0) {
		islog(L_DEBUG, "%s: %s, discard, flags %x\n",
		      DRV_NAME, __func__, netdev->flags);
		FN_EXIT1(-ENETDOWN);
		return -ENETDOWN;
	}

	/*
	 * No data frames go out unless we're associated; this
	 * should not happen as the 802.11 layer does not enable
	 * the xmit queue until we enter the RUN state.
	 */
	if (ic->ic_state != IEEE80211_S_RUN) {
		islog(L_DEBUG, "%s: discard, state %u\n", __func__,
		      ic->ic_state);
		goto bad;
	}

	eh = (struct ether_header *) skb->data;
	ni = ieee80211_find_txnode(ic, eh->ether_dhost);
	if (ni == NULL) {
		FN_EXIT1(0);
		return 0;
	}

	/* packet is gonna be transmitted, update stats */
	islsm->statistics.tx_packets++;
	islsm->statistics.tx_bytes += skb->len;

	/*
	 * We can do the work directly now
	 */

	/*
	 * Encapsulate the packet for transmission.
	 */
	skb = ieee80211_encap(ic, skb, ni);

	if (skb == NULL) {
		islog(L_DEBUG, "%s: %s discard, encapsulation failure\n",
		      DRV_NAME, __func__);
		goto bad;
	}

	err = islsm_tx_start(ic, ni, skb);
	if (!err) {
		netdev->trans_start = jiffies;
		FN_EXIT1(0);
		return 0;
	}

      bad:
	if (ni)
		ieee80211_free_node(ni);
#endif				/* MADWIFI */
	if (!err && skb)
		dev_kfree_skb(skb);
	FN_EXIT1(err);
	return err;		/* NB: return !0 only in a ``hard error condition'' */
}

static struct net_device_stats *
islsm_statistics(struct net_device *netdev)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	/* trigger a statistics update
	   The data collected is not used for now */
	if (netdev->flags & IFF_RUNNING)
		(void) islsm_stats_readback(islsm);
	return &islsm->statistics;
}

#ifdef MADWIFI
/*
 * Better ioctl stub
 */
static int
islsm_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(ndev);
	struct ieee80211com *ic = &islsm->sc_ic;
	int error;

	/* before implementing locking, understand why it's
	 * needed. Access to which structure ? -- stats for instance ?*/

	switch (cmd) {
	case SIOCETHTOOL:
		islog(L_IOCTL, "%s: SIOCETHTOOL called, not supported\n",
		      DRV_NAME);
		error = -ENOTSUPP;
		break;
	default:
		error = ieee80211_ioctl(ic, rq, cmd);
		break;
	}
	if (error)
		islog(L_IOCTL, "%s: problem with ioctl %02x, return value %i\n",
		      DRV_NAME, cmd, error);
	return error;
}

/*
 * MADWIFI STACK SPECIFIC CALLBACKS ; dummy for now, i'll put in
 * commentary as to whether they're needed or not
 */

/* send frame to beacon slot, freeing the previously allocated beacon
 * slot. The frame will be emitted when an adapted rx/tx filter is
 * set */

static int
islsm_beacon_alloc(struct net_device *netdev, struct ieee80211_node *ni)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct ieee80211com *ic = &islsm->sc_ic;
	struct sk_buff *skb;
	int err;

	/* get the frame from the upper layer */
	skb = ieee80211_beacon_alloc(ic, ni, &islsm->islsm_boff);
	if (skb == NULL)
		return -ENOMEM;

	err = islsm_data_tx(islsm, &(islsm->smpar.beacon), skb);
	if (err)
		dev_kfree_skb(skb);

	return err;
}

static int
islsm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
{
	struct net_device *netdev = ic->ic_dev;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);

	uint8_t *bssid;
	uint16_t mode = ISLSM_TX_CONTROL_FILTER_NOTYPE;
	int error = 0;
	struct ieee80211_node *ni = 0;
	unsigned int channel = 0;
	struct ieee80211_channel *chan = 0;

	uint16_t chan_m1, chan_m2;
	u8 magic2 = ISLSM_TX_CONTROL_FILTER_MAGIC2_FORM2;
	uint32_t magic3 = ISLSM_TX_CONTROL_FILTER_MAGIC3_FORM1;
	uint16_t magic8 = 0;
	uint16_t magic9 = 0;

	/* what if it has never been initialized, is it safe ? */
	del_timer(&islsm->scan_ch);

	islog(L_DEBUG, "%s: %s, %s -> %s\n",
	      DRV_NAME, __func__,
	      ieee80211_state_name[ic->ic_state], ieee80211_state_name[nstate]);

	netif_stop_queue(netdev);	/* before we do anything else */

	if (nstate == IEEE80211_S_INIT) {
		goto done;
	}

	ni = ic->ic_bss;

	/* compute the elements needed for the rx/tx filter */
	/* bssid */
	if (nstate == IEEE80211_S_SCAN) {
		bssid = netdev->broadcast;
		mode = ISLSM_TX_CONTROL_FILTER_NOTYPE;
		if (ic->ic_flags & IEEE80211_F_ASCAN) {
			/* active scan with allocated probe request.
			   What happens if no allocated probe ? */
			chan_m1 = ISLSM_TX_CONTROL_CHANNEL_MAGIC1_EMIT_TXSLOT;
			chan_m2 = ISLSM_TX_CONTROL_CHANNEL_MAGIC2_EMIT_TXSLOT;
		} else {
			chan_m1 = ISLSM_TX_CONTROL_CHANNEL_MAGIC1_SCAN;
			chan_m2 = ISLSM_TX_CONTROL_CHANNEL_MAGIC2_SCAN;
		}
	} else {
		bssid = ni->ni_bssid;
		chan_m1 = ISLSM_TX_CONTROL_CHANNEL_MAGIC1_TX;
		chan_m2 = ISLSM_TX_CONTROL_CHANNEL_MAGIC2_TX;
	}

	/* mode  */
	if (nstate == IEEE80211_S_AUTH || nstate == IEEE80211_S_RUN) {
		switch (ic->ic_opmode) {
		case IEEE80211_M_STA:	/* infra station */
			magic2 = ISLSM_TX_CONTROL_FILTER_MAGIC2_STA;
			magic3 = ISLSM_TX_CONTROL_FILTER_MAGIC3_STA;
			magic8 = ISLSM_TX_CONTROL_FILTER_MAGIC8_STA;
			mode = ISLSM_TX_CONTROL_FILTER_STA;
			break;
		case IEEE80211_M_AHDEMO:
		case IEEE80211_M_IBSS:	/* ad-hoc */
			mode = ISLSM_TX_CONTROL_FILTER_ADHOC;
			break;
		case IEEE80211_M_HOSTAP:
			mode = ISLSM_TX_CONTROL_FILTER_HOSTAP;
			break;
		case IEEE80211_M_MONITOR:
			/* correct this -- doesn't work now */
			bssid = netdev->broadcast;
			chan_m1 = ISLSM_TX_CONTROL_CHANNEL_MAGIC1_TX;
			chan_m2 = ISLSM_TX_CONTROL_CHANNEL_MAGIC2_TX;

			magic2 = ISLSM_TX_CONTROL_FILTER_MAGIC2_MONITOR;
			magic3 = ISLSM_TX_CONTROL_FILTER_MAGIC3_MONITOR;
			mode = ISLSM_TX_CONTROL_FILTER_MONITOR;
			break;
		}
	}

	/* should also set rate, etc but i don't know yet how to set them */

	chan = ni->ni_chan;
	channel = ieee80211_mhz2ieee(chan->ic_freq, 0);

	// FIXME : we will get errors on hot unplug. is this a problem ?
	// This is a problem. Don't know why, but it is. It seems to
	// make madwifi stuck. So silently ignore.
	(void) islsm_freq_change(islsm, channel, chan->ic_freq,
				 chan_m1, chan_m2);

	(void) islsm_set_filter(islsm, mode, bssid,
				magic2, magic3, magic8, magic9);

/* what is ni_associd ?
	more or less...
	if (nstate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA)
		ath_hal_setassocid(ah, bssid, ni->ni_associd);
	else
		ath_hal_setassocid(ah, bssid, 0);
	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
		for (i = 0; i < IEEE80211_WEP_NKID; i++)
			if (ath_hal_keyisvalid(ah, i))
				ath_hal_keysetmac(ah, i, bssid);
	}
*/
	if (nstate == IEEE80211_S_RUN) {
		islog(L_DEBUG, "%s: %s(RUN), ic_flags=0x%08x iv=%d bssid=%s "
		      "capinfo=0x%04x chan=%d\n",
		      DRV_NAME, __func__,
		      ic->ic_flags, ni->ni_intval,
		      ether_sprintf(ni->ni_bssid),
		      ni->ni_capinfo, ieee80211_chan2ieee(ic, ni->ni_chan));

		/*
		 * Allocate and setup the beacon frame for AP or adhoc mode.
		 */

		if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
		    ic->ic_opmode == IEEE80211_M_IBSS) {
			error = islsm_beacon_alloc(netdev, ni);
			if (error != 0)
				goto bad;
		}

		/*
		 * No need for this and / or is done in the rx/tx filter packet
		 * Configure the beacon and sleep timers.
		 */
/*		ath_beacon_config(sc); */
	}
      done:
	/*
	 * Invoke the parent method to complete the work.
	 */
	error = islsm->ieee80211_newstate(ic, nstate, arg);

	if (nstate == IEEE80211_S_SCAN) {
		/* start ap/neighbor scan timer */
#define ISLSM_DWELLTIME 1000	/* ms */
		mod_timer(&islsm->scan_ch,
			  jiffies + ((HZ * ISLSM_DWELLTIME) / 1000));

#undef ISLSM_DWELLTIME
	}
      bad:
	netif_start_queue(netdev);
	return error;
}

static void
islsm_next_scan(unsigned long arg)
{
	struct net_device *netdev = (struct net_device *) arg;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct ieee80211com *ic = &islsm->sc_ic;

	if (ic->ic_state != IEEE80211_S_SCAN)
		return;

	/*
	   Allocate the probe request frame.
	   How does the driver do multiple probes on one channel ?
	   Try to put the windows driver into this configuration
	   see ieee80211_next_scan(struct ieee80211com *ic);
	 */

/* 	if ((ic->ic_flags & IEEE80211_F_ASCAN) && */
/* 	    (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { */
/* 		IEEE80211_SEND_MGMT(ic, ni, */
/* 				    IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); */
/* 	} */

	ieee80211_next_scan(ic);
}

static int
islsm_init(struct net_device *netdev)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct ieee80211com *ic = &islsm->sc_ic;
	struct ieee80211_node *ni;
	int err = 0;

	uint8_t *bssid = netdev->broadcast;

	// Should be the open of the device -- it's not, yet
	FN_ENTER;

	/* resize skbs in monitor mode -- good idea, leave enough
	 * headroom to fit the prism header */

	/* tell the powers that be what we really are */
	netdev->type = (ic->ic_opmode == IEEE80211_M_MONITOR) ?
	    ARPHRD_IEEE80211_PRISM : ARPHRD_ETHER;

	/* Hardware state machine reset */
	/* should be reset, not boot */
	err = islsm->isl_boot(islsm);
	if (err) {
		printk(KERN_ERR "%s: unable to boot device\n", DRV_NAME);
		goto done;
	}

	err = islsm_get_pda(islsm);
	if (err) {
		printk(KERN_ERR "%s: unable to readback the eeprom\n", DRV_NAME);
		goto done;
	}

	/* initialize protocol parameters */
	islsm_params_init(islsm);

	(void) islsm_led_perm(islsm, LED_MODE_SET, LED_POWER);
	(void) islsm_led_temp(islsm, LED_ACTIVITY, 25);

	/* submit initial data on data pipe */
	/* this seems to allow frequency change successfully */
	islsm_outofband_msg(islsm,
			    magicpkt_table[islsm->device_version].data,
			    magicpkt_table[islsm->device_version].len);

	islsm_set_filter(islsm,
			 ISLSM_TX_CONTROL_FILTER_NOTYPE, bssid,
			 ISLSM_TX_CONTROL_FILTER_MAGIC2_FORM3, 1,
			 0, ISLSM_TX_CONTROL_FILTER_MAGIC9_FORM1);

	// This one may not be necessary any more.
	islsm_set_filter(islsm,
			 ISLSM_TX_CONTROL_FILTER_NOTYPE, bssid,
			 ISLSM_TX_CONTROL_FILTER_MAGIC2_FORM2,
			 ISLSM_TX_CONTROL_FILTER_MAGIC3_FORM1, 0, 0);

	// Start the statistics update cycle
//      mod_timer(&islsm->stats_update, jiffies + ((HZ * ISLSM_STATSTIME) / 1000));

	/* Tell others we're okay */
	netdev->flags |= IFF_RUNNING;
	ic->ic_state = IEEE80211_S_INIT;

	/*
	 * The hardware should be ready to go now so it's safe
	 * to kick the 802.11 state machine as it's likely to
	 * immediately call back to us to send mgmt frames.
	 */
	ni = ic->ic_bss;
	ni->ni_chan = ic->ic_ibss_chan;

	// This will set the tx/rx filter ! this is what we need.
	//mode = ieee80211_chan2mode(ic, ni->ni_chan);
	//if (mode != sc->sc_curmode)
	//      ath_setcurmode(sc, mode);
	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
		if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
	} else
		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);

      done:
	FN_EXIT1(err);
	return err;
}

static void
islsm_setup_freqs(struct ieee80211com *ic)
{
	/*
	 * FIXME: Should be read from the device, really, but needs complete
	 * initialization, so we don't do this for now
	 * in the _real_ driver : init on probe seems necessary.
	 */
	unsigned int ieee_num, maxchans;
	maxchans = ISLSM_NR_CHANNELS;
	if (maxchans > IEEE80211_CHAN_MAX) {
		islog(L_DEBUG, "%s: Channel table table too small\n", DRV_NAME);
		maxchans = IEEE80211_CHAN_MAX;
	}

	for (ieee_num = 1; ieee_num <= ISLSM_NR_CHANNELS; ieee_num++) {
		ic->ic_channels[ieee_num].ic_freq =
		    ieee80211_ieee2mhz(ieee_num, IEEE80211_CHAN_2GHZ);
		ic->ic_channels[ieee_num].ic_flags =
		    IEEE80211_MODE_11B | IEEE80211_MODE_11G;
		islog(L_DEBUG, "%s: setup channel ieee %i, freq %iMHz\n",
		      DRV_NAME, ieee_num, ic->ic_channels[ieee_num].ic_freq);
	}
}

/* needed otherwise problems in */
static int
islsm_setup_rates(struct net_device *dev, u_int mode)
{
	struct islsm *islsm = dev->priv;
	struct ieee80211com *ic = &islsm->sc_ic;
	struct ieee80211_rateset *rs;
	int i, maxrates;
	/* standard rates observed from my prism54 in hostap:

	   2, basic yes rate 4, basic yes rate 11, basic yes rate 12, basic
	   no rate 18, basic no rate 22, basic yes rate 24, basic no
	   rate 36, basic no rate 48, basic no rate 72, basic no rate
	   96, basic no rate 108,
	 */
	int rates[ISLSM_NR_RATES] =
	    { 2, 4, 11, 12, 18, 22, 24, 36, 48, 72, 96, 108, };

	/* FIXME: should check mode */
	rs = &ic->ic_sup_rates[mode];
	/* FIXME: Should also get them from device ! */
	maxrates = ISLSM_NR_RATES;
	if (maxrates > IEEE80211_RATE_MAXSIZE) {
		islog(L_DEBUG, "%s: Rate table too small\n", DRV_NAME);
		maxrates = IEEE80211_RATE_MAXSIZE;
	}
	for (i = 0; i < maxrates; i++)
#define MADWIFI_RATE(x) ( (x) & IEEE80211_RATE_VAL )
		rs->rs_rates[i] = MADWIFI_RATE(rates[i]);
#undef MADWIFI_RATE
	rs->rs_nrates = maxrates;
	return 0;
}

/*
 * Transmit a frame. Does the tracking of everything that needs the
 * 802.11 layer.
 * TODO : offloading of some encryption protocols
 */

static int
islsm_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
	       struct sk_buff *skb)
{
	struct net_device *netdev = ic->ic_dev;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct ieee80211_frame *wh = (struct ieee80211_frame *) skb->data;
	int iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
	int err = 0;
	uint8_t rate = 0;
	uint8_t rateset[8] =
	    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
	struct sm_tx_p *txp;
	int i;

	FN_ENTER;

	/* encryption */
	if (iswep) {
		struct ieee80211_key *k;

		/*
		 * Construct the 802.11 header+trailer for an encrypted
		 * frame. The only reason this can fail is because of an
		 * unknown or unsupported cipher/key type. Be simple for
		 * now: only use software encryption.
		 */
		k = ieee80211_crypto_encap(ic, ni, skb);
		if (k == NULL) {
			/*
			 * This can happen when the key is yanked after the
			 * frame was queued.  Just discard the frame; the
			 * 802.11 layer counts failures and provides
			 * debugging/diagnostics.
			 */
			printk(KERN_ERR "%s: crypto failure, dropping skb\n",
			       DRV_NAME);
			err = -EIO;
			goto exit;
		}

		/* packet header may have moved, reset our local pointer */
		wh = (struct ieee80211_frame *) skb->data;
	}

	/* type switch. This duplicates work as we should already know
	 * if we have a mgmt frame. Anyways. */
	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
	case IEEE80211_FC0_TYPE_MGT:
	case IEEE80211_FC0_TYPE_CTL:
		/* default values for mgmt frames */
		switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
		case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
			/* send frame to probe slot, freeing the previously allocated probe
			 * slot. The frame is emitted when a specific frequency change packet is
			 * sent */
			txp = &(islsm->smpar.probe);
			break;
		case IEEE80211_FC0_SUBTYPE_BEACON:
			/* update the beacon reference ? */
			txp = &(islsm->smpar.beacon);
			break;
		default:
			txp = &(islsm->smpar.mgmt);
			break;
		}
		/* common values for the mgmt frames */
		break;

	case IEEE80211_FC0_TYPE_DATA:
		/* should do rate calculation */
		txp = &(islsm->smpar.data);

/* 		if (ic->ic_fixed_rate != -1) { */
/* 			/\* contains if != -1 an index into the rates */
/* 			   table provided by setup_rates. This is exactly */
/* 			   what we want *\/ */
/* 			rate = ic->ic_fixed_rate; */
/* 		} */

/* 		if (skb->len > ic->ic_rtsthreshold) { */
/* //			rate |= ISLSM_TX_CTSONLY; */
/* 			rate |= ISLSM_TX_RTSCTS; */
/* 		} */

		break;
	default:
		printk(KERN_ERR "%s: bad packet type\n", DRV_NAME);
		/* We silently drop the skb without returning and error;
		   therefore we free it */
		dev_kfree_skb(skb);
		err = 0;
		goto exit;
	}
	/* try and see what magic4 and magic5 really mean */
	/* do they mean that the tx must be done ASAP ? */
	for (i = 0; i < 8; i++)
		rateset[i] = rate;

	err = islsm_data_tx(islsm, txp, skb);

      exit:
	/* we propagate the error,
	   therefore do not free the skb */
	FN_EXIT1(err);
	return err;
}

/*
 * Reset the hardware w/o losing operational state.  This is
 * basically a more efficient way of doing ath_stop, ath_init,
 * followed by state transitions to the current 802.11
 * operational state.  Used to recover from errors rx overrun
 * and to reset the hardware when rf gain settings must be reset.
 */
static int
islsm_reset(struct net_device *netdev)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct ieee80211com *ic = &islsm->sc_ic;
	// What's the difference with ic->ni->... I don't know but i'll
	// have to figure out for Ad-Hoc mode i guess
	struct ieee80211_channel *chan = ic->ic_ibss_chan;
	unsigned int channel = 0;

	// Dirty code, should factor with newstate and mgtstart into a frequency
	// change function that does all things needed...
	uint16_t chan_m1 = ISLSM_TX_CONTROL_CHANNEL_MAGIC1_TX;
	uint16_t chan_m2 = ISLSM_TX_CONTROL_CHANNEL_MAGIC2_TX;

	FN_ENTER;

	channel = ieee80211_mhz2ieee(chan->ic_freq, 0);
	islsm_freq_change(islsm, channel, chan->ic_freq, chan_m1, chan_m2);

	if (ic->ic_state == IEEE80211_S_RUN)
		netif_wake_queue(netdev);	/* restart xmit */

	FN_EXIT0;
	return 0;
}

/*
 * Setup driver-specific state for a newly associated node.
 * Note that we're called also on a re-associate, the isnew
 * param tells us if this is the first time or not.
 */
static void
islsm_newassoc(struct ieee80211_node *ni, int isnew)
{
	(void) ni;
	(void) isnew;
	FN_ENTER;
	FN_EXIT0;
	return;
}

/*
 * Callback from the 802.11 layer to update the
 * slot time based on the current setting.
 */
static void
islsm_updateslot(struct net_device *dev)
{
	(void) dev;
	FN_ENTER;
	FN_EXIT0;
	return;
}

static int
islsm_media_change(struct net_device *dev)
{
	int error;
	FN_ENTER;

	error = ieee80211_media_change(dev);
	if (error == ENETRESET) {
		if ((dev->flags & (IFF_RUNNING | IFF_UP)) ==
		    (IFF_RUNNING | IFF_UP))
			error = islsm_init(dev);
		else
			error = 0;
		/* FIXME : why ? */
		error = 0;
	}

	FN_EXIT1(error);
	return error;
}

#else				/* MADWIFI */

static int
islsm_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
{
	int error = -ENOTSUPP;
	FN_ENTER;
	FN_EXIT1(error);
	return error;
}

static int
islsm_init(struct net_device *netdev)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	int err = 0;

	FN_ENTER;

	/* resize skbs in monitor mode -- good idea, leave enough
	 * headroom to fit the prism header */

	/* tell the powers that be what we really are */
	netdev->type = (islsm->ieee->iw_mode == IW_MODE_MONITOR) ?
	    ARPHRD_IEEE80211 : ARPHRD_ETHER;

	/* Hardware state machine reset */
	/* this is not a reset */
	err = islsm->isl_boot(islsm);
	if (err) {
		printk(KERN_ERR "%s: unable to boot device\n", DRV_NAME);
		goto done;
	}

	err = islsm_get_pda(islsm);
	if (err) {
		printk(KERN_ERR "%s: unable to readback the eeprom\n", DRV_NAME);
		goto done;
	}

	/* initialize parameters
	   once the readback is finished */
	islsm_params_init(islsm);

	(void) islsm_led_perm(islsm, LED_MODE_SET, LED_POWER);
	(void) islsm_led_temp(islsm, LED_ACTIVITY, 25);

	/* submit initial data on data pipe */
	/* this seems to allow frequency change successfully */
	islsm_outofband_msg(islsm,
			    magicpkt_table[islsm->device_version].data,
			    magicpkt_table[islsm->device_version].len);

	islsm_mode_set_filter(netdev, islsm->ieee->iw_mode);

	// Start the statistics update cycle
//      mod_timer(&islsm->stats_update, jiffies + ((HZ * ISLSM_STATSTIME) / 1000));

	if (islsm->last_chan == 0)	/* Nobody ran iwconfig ethX freq N */
		islsm->last_chan = 1;
	islsm_reset(netdev);

	/* Tell others we're okay */
	netdev->flags |= IFF_RUNNING;
	netif_start_queue(netdev);

	ieee80211softmac_start(netdev);

	FN_EXIT1(0);
	return 0;

      done:
	FN_EXIT1(err);
	return err;
}

int islsm_mode_set_filter(struct net_device *netdev, int mode)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	int rc;

	switch (mode) {
	case IW_MODE_MONITOR:
		rc = islsm_set_filter(islsm,
			ISLSM_TX_CONTROL_FILTER_MONITOR, netdev->broadcast,
			ISLSM_TX_CONTROL_FILTER_MAGIC2_MONITOR,
			ISLSM_TX_CONTROL_FILTER_MAGIC3_MONITOR,
			0, 0);
		if (rc != 0)
			return rc;
		break;

	case IW_MODE_ADHOC:
		/* Stolen from INFRA. Without this we only receive beacons. */
		rc = islsm_set_filter(islsm,
			ISLSM_TX_CONTROL_FILTER_NOTYPE, islsm->ieee->bssid,
			ISLSM_TX_CONTROL_FILTER_MAGIC2_FORM3, 1,
			0, ISLSM_TX_CONTROL_FILTER_MAGIC9_FORM1);
		if (rc != 0)
			return rc;

		/* INFRA uses NOTYPE, so skip ADHOC. */
		rc = islsm_set_filter(islsm,
			ISLSM_TX_CONTROL_FILTER_NOTYPE, islsm->ieee->bssid,
			ISLSM_TX_CONTROL_FILTER_MAGIC2_FORM2,
			ISLSM_TX_CONTROL_FILTER_MAGIC3_FORM1,
			0, 0);
		if (rc != 0)
			return rc;
		break;

	case IW_MODE_INFRA:
		rc = islsm_set_filter(islsm,
			ISLSM_TX_CONTROL_FILTER_NOTYPE, netdev->broadcast,
			ISLSM_TX_CONTROL_FILTER_MAGIC2_FORM3, 1,
			0, ISLSM_TX_CONTROL_FILTER_MAGIC9_FORM1);
		if (rc != 0)
			return rc;
		// This one may not be necessary any more.
		rc = islsm_set_filter(islsm,
			ISLSM_TX_CONTROL_FILTER_NOTYPE, islsm->ieee->bssid,
			ISLSM_TX_CONTROL_FILTER_MAGIC2_FORM2,
			ISLSM_TX_CONTROL_FILTER_MAGIC3_FORM1,
			0, 0);
		if (rc != 0)
			return rc;
		break;

	default:
		;
	}
	return 0;
}
EXPORT_SYMBOL(islsm_mode_set_filter);

static int
islsm_tx_start(struct net_device *netdev, struct sk_buff *skb)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct ieee80211_hdr_3addr *hdr3;
	unsigned short frame_ctl;
	struct sm_tx_p *txp;
	int err;

	hdr3 = (struct ieee80211_hdr_3addr *) skb->data;
	frame_ctl = le16_to_cpu(hdr3->frame_ctl);

	/* XXX Encryption goes here */

	switch (WLAN_FC_GET_TYPE(frame_ctl)) {
	case IEEE80211_FTYPE_MGMT:
	case IEEE80211_FTYPE_CTL:
		/* default values for mgmt frames */
		switch (WLAN_FC_GET_STYPE(frame_ctl)) {
		case IEEE80211_STYPE_PROBE_REQ:
			/*
			 * Send frame to probe slot, freeing the previously
			 * allocated probe slot. The frame is emitted when
			 * a specific frequency change packet is sent
			 */
			txp = &(islsm->smpar.probe);
			break;
		case IEEE80211_STYPE_BEACON:
			/* update the beacon reference ? */
			txp = &(islsm->smpar.beacon);
			break;
		default:
			txp = &(islsm->smpar.mgmt);
			break;
		}
		break;

	case IEEE80211_FTYPE_DATA:
		txp = &(islsm->smpar.data);
		break;

	default:
		dev_kfree_skb(skb);
		return 0;
	}

	err = islsm_data_tx(islsm, txp, skb);
	/* we propagate the error, therefore do not free the skb */
	return err;
}

static int islsm_iee80211_xmit(struct ieee80211_txb *txb,
    struct net_device *netdev, int pri)
{
	int i;
	struct sk_buff *skb;

	for (i = 0; i < txb->nr_frags; i++) {
		skb = txb->fragments[i];
		txb->fragments[i] = NULL; /* Take skb from ieee80211_txb_free */
		if (islsm_tx_start(netdev, skb) != 0) {
			dev_kfree_skb(skb);
			break;
		}
	}
	ieee80211_txb_free(txb);
	return 0;	/* Always 0 for now. Flow control for ping -f here. */
}

static int islsm_reset(struct net_device *netdev)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	unsigned int chan = islsm->last_chan;
	int freq;

	// Dirty code, should factor with newstate and mgtstart into a frequency
	// change function that does all things needed...
	uint16_t chan_m1 = ISLSM_TX_CONTROL_CHANNEL_MAGIC1_TX;
	uint16_t chan_m2 = ISLSM_TX_CONTROL_CHANNEL_MAGIC2_TX;

	freq = islsm_chan_to_freq(chan);
	islsm_freq_change(islsm, chan, freq, chan_m1, chan_m2);

	/* mode = ISLSM_TX_CONTROL_FILTER_NOTYPE; */
	/* (void) islsm_set_filter(netdev, mode, bssid,
				magic2, magic3, magic8, magic9); */

	return 0;
}

/*
 * This is called from an eventd, but under spinlock. So, no GFP_KERNEL.
 */
static void islsm_set_bssid_filter(struct net_device *netdev, const u8 *bssid)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);

	islsm_set_filter(islsm,
			ISLSM_TX_CONTROL_FILTER_NOTYPE, bssid,
			ISLSM_TX_CONTROL_FILTER_MAGIC2_FORM3, 1,
			0, ISLSM_TX_CONTROL_FILTER_MAGIC9_FORM1);

	islsm_set_filter(islsm,
			ISLSM_TX_CONTROL_FILTER_NOTYPE, bssid,
			ISLSM_TX_CONTROL_FILTER_MAGIC2_FORM2,
			ISLSM_TX_CONTROL_FILTER_MAGIC3_FORM1,
			0, 0);
}

/* This is called from an eventd on the current softmac... */
static void islsm_set_chan(struct net_device *netdev, u8 c)
{
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);

	/* XXX Semaphore here */
	islsm_freq_change(islsm,
			  c, islsm_chan_to_freq(c),
			  ISLSM_TX_CONTROL_CHANNEL_MAGIC1_TX,
			  ISLSM_TX_CONTROL_CHANNEL_MAGIC2_TX);
	islsm->last_chan = c;
}

static void islsm_geo_init(struct ieee80211_device *ieee)
{
	struct ieee80211_geo geo;
	struct ieee80211_channel *chan;
	int i;
	u8 channel;

	memset(&geo, 0, sizeof(geo));

	for (i = 0, channel = 1; channel < 15; channel++) {
		chan = &geo.bg[i++];
		chan->freq = islsm_chan_to_freq(channel);
		chan->channel = channel;
	}
	geo.bg_channels = i;

	strcpy(geo.name, "US ");	/* XXX J-B is in FR or EU. */

	ieee80211_set_geo(ieee, &geo);
}
#endif				/* MADWIFI */

struct net_device *
alloc_islsm(int sizeof_priv)
{
	struct net_device *netdev;
	struct islsm *islsm;
#ifdef MADWIFI
	struct ieee80211com *ic;
	netdev = alloc_etherdev(sizeof(struct islsm)+sizeof_priv);
#else
	struct ieee80211_device *ieee;
	netdev = alloc_ieee80211softmac(sizeof(struct islsm)+sizeof_priv);
#endif				/* MADWIFI */
	if (!netdev) {
		printk(KERN_ERR "%s: Unable allocate netdevice.\n", DRV_NAME);
		return NULL;
	}

	islsm = ISLSM_OF_NETDEV(netdev);
	islsm->netdev = netdev;
#ifndef MADWIFI
	islsm->ieee = netdev_priv(netdev);
	islsm->softmac = ieee80211_priv(netdev);
#endif

	/* fill netdevice callbacks */
	netdev->open = &islsm_init;
	netdev->stop = &islsm_close;
	netdev->get_stats = &islsm_statistics;
	netdev->do_ioctl = &islsm_ioctl;
	netdev->hard_start_xmit = &islsm_transmit;

	netdev->addr_len = ETH_ALEN;
	netdev->hard_header_len =
	    FULL_TX_CONTROL_ALLOCDATA + TX_MAX_PADDING +
	    islsm->device_tx_header_space;
	memcpy(netdev->dev_addr, dummy_mac, ETH_ALEN);
/*      netdev->set_mac_address = &prism54_set_mac_address; */
	netdev->watchdog_timeo = ISLSM_TX_TIMEOUT;
	netdev->tx_timeout = &islsm_tx_timeout;

#ifdef MADWIFI
	netdev->get_wireless_stats = &islsm_wireless_stats;
	ieee80211_ioctl_iwsetup(&islsm_iw_handler_def);
#endif				/* MADWIFI */
	/* check if this can be done right away. Race ? */
	netdev->wireless_handlers = &islsm_iw_handler_def;

#ifdef MADWIFI
	/* fill madwifi callbacks */
	islog(L_DEBUG, "%s: Start madwifi dev configuration\n", DRV_NAME);

	ic = &islsm->sc_ic;

	ic->ic_dev = netdev;
	/* verbosity of the stack -- reduce a little */
	ic->ic_debug = IEEE80211_MSG_ANY & (~IEEE80211_MSG_DUMPPKTS);
	ic->ic_devstats = &islsm->statistics;


	ic->ic_init = islsm_init;
	ic->ic_reset = islsm_reset;
	ic->ic_newassoc = islsm_newassoc;
	ic->ic_updateslot = islsm_updateslot;

	/* capabilities */
	islsm_setup_freqs(ic);
	islsm_setup_rates(netdev, IEEE80211_MODE_11B);
	islsm_setup_rates(netdev, IEEE80211_MODE_11G);

	/* XXX not right but it's not used anywhere important */
	ic->ic_phytype = IEEE80211_T_OFDM;
	ic->ic_opmode = IEEE80211_M_STA;
	ic->ic_caps = IEEE80211_C_IBSS	/* ibss, nee adhoc, mode */
	    | IEEE80211_C_HOSTAP	/* hostap mode */
	    | IEEE80211_C_MONITOR	/* monitor mode */
	    | IEEE80211_C_SHPREAMBLE	/* short preamble supported */
	    | IEEE80211_C_SHSLOT	/* short slot time supported */
	    | IEEE80211_C_TXPMGT	/* transmit power control */
	    ;

	/*
	 * initialize management queue
	 */
	skb_queue_head_init(&ic->ic_mgtq);

	/*
	 * Indicate we need the 802.11 header padded to a
	 * 32-bit boundary for 4-address and QoS frames.
	 */
	/* FIXME: not true in our devices where we can cope with
	   misaligned frames */
	ic->ic_flags |= IEEE80211_F_DATAPAD;

	/* call MI attach routine. */
	init_timer(&islsm->scan_ch);
	islsm->scan_ch.function = islsm_next_scan;
	islsm->scan_ch.data = (unsigned long) netdev;

/* 	init_timer(&islsm->stats_update); */
/* 	islsm->stats_update.function = islsm_stats_update; */
/* 	islsm->stats_update.data = (unsigned long) netdev; */

	ieee80211_ifattach(ic);

	/* hooks into default methods */
	islsm->ieee80211_newstate = ic->ic_newstate;
	ic->ic_newstate = islsm_newstate;

	/* complete initialization */
	ieee80211_media_init(ic, islsm_media_change, ieee80211_media_status);
#else

	ieee = islsm->ieee;

	ieee->iw_mode = IW_MODE_AUTO;	/* Needed? XXX */

	ieee->modulation = IEEE80211_CCK_MODULATION;
	ieee->mode = IEEE_B;
	ieee->freq_band = IEEE80211_24GHZ_BAND;

	islsm_geo_init(ieee);

	/* default to sw encryption for now */
	// bcm->ieee->host_build_iv = 0;
	// bcm->ieee->host_encrypt = 1;
	// bcm->ieee->host_decrypt = 1;

	// ieee->tx_headroom = sizeof(struct bcm43xx_txhdr);
	// ieee->set_security = bcm43xx_ieee80211_set_security;
	ieee->hard_start_xmit = islsm_iee80211_xmit;

	islsm->softmac->set_bssid_filter = islsm_set_bssid_filter;
	islsm->softmac->set_channel = islsm_set_chan;
#endif				/* MADWIFI */

	/* LMAC initialization */
	init_completion(&islsm->dev_init_comp);
	islsm->minor = minor++;
	spin_lock_init(&islsm->memory.slock);
	INIT_LIST_HEAD(&islsm->memory.indevice_skbs);

	return netdev;
}
EXPORT_SYMBOL(alloc_islsm);

/* Lightweight function to convert a frequency (in Mhz) to a channel number. */
/*
 * XXX This only serves B band currently. For A, the formula is:
 *	channel = (freq - 5000) / 5;
 */
unsigned int islsm_freq_to_chan(int freq)
{
	unsigned char chan;

	if (freq == 2484)
		chan = 14;
	else
		chan = (freq - 2407) / 5;
	return chan;
}

/* Lightweight function to convert a channel number to a frequency (in Mhz). */
/*
 * XXX This only serves B band currently. For A band, the formula is:
 *	freq = 5000 + (5 * channel);
 */
int islsm_chan_to_freq(unsigned int chan)
{
	int freq;

	if (chan == 14)
		freq = 2484;
	else
		freq = 2407 + (5 * chan);

	return freq;
}

void free_islsm(struct net_device *dev) {
	struct islsm *islsm = ISLSM_OF_NETDEV(dev);
#ifdef MADWIFI
	ieee80211_ifdetach(&islsm->sc_ic);
#endif
	vfree(islsm->eeprom);
	free_netdev(dev);
	return;
}
EXPORT_SYMBOL(free_islsm);

int register_islsm(struct net_device *netdev) {
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
#ifdef MADWIFI
	struct ieee80211com *ic = &islsm->sc_ic;
#endif				/* MADWIFI */
	int error;
	int device_minor = islsm->minor;

	islog(L_DEBUG, "%s: registering "
	      "device %i, structure %p\n", DRV_NAME, device_minor, islsm);

	error = register_netdev(netdev);
	if (error) {
		printk(KERN_WARNING "%s: unable to register netdevice\n",
		       DRV_NAME);
		goto out;
	}

	islog(L_DEBUG, "%s: Prism54 SoftMac device %i now attached to %s\n",
	      DRV_NAME, device_minor, netdev->name);

#ifdef MADWIFI
#ifdef CONFIG_SYSCTL
	ieee80211_sysctl_register(ic);
#endif				/* CONFIG_SYSCTL */
	ieee80211_announce(ic);
#endif				/* MADWIFI */
	return 0;

 out:
#ifdef MADWIFI
	ieee80211_ifdetach(ic);
#endif
	return error;
}
EXPORT_SYMBOL(register_islsm);

void unregister_islsm(struct net_device *netdev) {
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	islog(L_DEBUG, "%s: unregistering "
	      "device %i, structure %p\n", DRV_NAME, islsm->minor, islsm);
	unregister_netdev(netdev);
}
EXPORT_SYMBOL(unregister_islsm);

/* now needed for BUG() checks */
static int __init
islsm_modinit(void)
{
	int err;
	/* compiler removes this */
	BUG_ON(sizeof(struct islsm_cb) >
	       sizeof(((struct sk_buff *)0)->cb));
	err = islsm_log_init();
	return err;
}

static void __exit
islsm_modexit(void)
{
	islsm_log_cleanup();
	return;
}

module_init(islsm_modinit);
module_exit(islsm_modexit);
