/*
  Copyright 2004, 2005 Jean-Baptiste Note

  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 "isl_sm.h"
#include "islsm_protocol.h"

#include <linux/netdevice.h>

#ifdef MADWIFI
#include <linux/version.h>
#include "net80211/if_media.h"
#include "net80211/ieee80211_var.h"
#include "wlan-monitor.h"
#else
#include <linux/etherdevice.h>
#endif				/* MADWIFI */

#include "islsm_log.h"

#define DRV_NAME "islsm"

static int islsm_input_lm87(struct sk_buff *skb);
#ifndef MADWIFI
static void islsm_to_stats(struct ieee80211_rx_stats *st,
    struct islsm_rx_packet_header *data_head, int len);
#endif
static void islsm_input_fault(struct sk_buff *skb);

/*
 * Data packet input functions
 */

#ifdef MADWIFI
# define ISLSM_IS_MODE_MONITOR(islsm) \
	((islsm)->sc_ic.ic_opmode == IEEE80211_M_MONITOR)
#else
# define ISLSM_IS_MODE_MONITOR(islsm) \
	((islsm)->ieee->iw_mode == IW_MODE_MONITOR)
#endif

static void
input_data_frame(struct sk_buff *skb)
{
	struct net_device *netdev = skb->input_dev;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct islsm_rx_packet_header *data_head =
	    (struct islsm_rx_packet_header *) (skb->data);
	unsigned int trim_length;
#ifdef MADWIFI
	struct ieee80211com *ic = &islsm->sc_ic;
	struct ieee80211_node *ni;
#else
	struct ieee80211_rx_stats stats;	/* XXX On stack?! */
	struct ieee80211_hdr_3addr *hdr3;
	struct ieee80211_hdr_4addr *hdr4;
	unsigned short frame_ctl;
#endif				/* MADWIFI */
	/* we keep this information and put it in the avc header,
	   where it will be meaningless, but there we'll be able
	   to see it in ethereal */
	uint16_t magic1;
	uint16_t unknown1;
	uint16_t unknown2;

	FN_ENTER;

	magic1 = le16_to_cpu(((struct islsm_id *) (skb->data))->magic1);
	unknown1 = le16_to_cpu(data_head->unknown1);
	unknown2 = le16_to_cpu(data_head->unknown2);

	/* problem : I don't know how to report the FCS in monitor
	 * mode ! */
	trim_length = le16_to_cpu(((struct islsm_id *) skb->data)->length);
	skb_pull(skb, SIZE_RX_HEADER);
	skb_trim(skb, trim_length);
	/* strip FCS. Needed for madwifi. How to deal with it in
	 * monitor mode ? */
	skb_trim(skb, skb->len - 4);

	/* catch monitor mode */
	if (ISLSM_IS_MODE_MONITOR(islsm)) {
#ifdef MADWIFI
#if 1
		struct avs_80211_1_header *avs;
		/* time scale is at 1MHZ. maybe there's a fractional part */
		u64 clock = le64_to_cpu(data_head->timestamp);
		uint16_t freq = le16_to_cpu(data_head->freq);
		uint8_t rssi = data_head->signal_strength;
		uint8_t rate = data_head->rate;
		short rxfilter = islsm->soft_rxfilter;
		int err;

		/*
		 * Drop bad FCS packets according to rxfilter
		 * parameter. Per-device parameter via sysfs ?
		 */
		if (rxfilter && !(magic1 & rxfilter)) {
			islog(L_SM_INPUT, "dropped skb unfit for rxfilter\n");
			dev_kfree_skb(skb);
			FN_EXIT0;
			return;
		}

		/* FIXME: ideally, the rx skbs allocated will have this
		 * headroom built-in and reallocation will be
		 * unnecessary */
		err = skb_cow(skb, sizeof(struct avs_80211_1_header));
		if (err) {
			islog(L_SM_INPUT, "dropped skb in monitor mode\n");
			dev_kfree_skb(skb);
			FN_EXIT0;
			return;
		}

		/* make room for the new header and fill it. */
		avs = (void *) skb_push(skb, sizeof (struct avs_80211_1_header));

		avs->version = cpu_to_be32(P80211CAPTURE_VERSION);
		avs->length = cpu_to_be32(sizeof (struct avs_80211_1_header));
		avs->mactime = cpu_to_be64((u64) clock);
		avs->hosttime = cpu_to_be64(jiffies);
		avs->phytype = cpu_to_be32(6);	/* OFDM: 6 for (g), 8 for (a) */
		avs->channel = cpu_to_be32(islsm_ref_to_chan(freq));
		avs->datarate = cpu_to_be32(islsm_rate_table[rate % 12] * 5);	/* units = hundreds of kbits/s. Convert to this. */
		/* Warning : those are given a bogus meaning for the time being ! */
		avs->antenna = cpu_to_be32((uint32_t) unknown2);	/* unknown */
		avs->priority = cpu_to_be32((uint32_t) magic1);	/* unknown */
		avs->ssi_type = cpu_to_be32(3);	/* 2: dBm, 3: raw RSSI */
		avs->ssi_signal = cpu_to_be32(rssi);	/* why put & 0x7f here ? what does the upper bit mean in original prism54 code ? */
		avs->ssi_noise = cpu_to_be32(0);
		avs->preamble = cpu_to_be32(0);	/*unknown */
		avs->encoding = cpu_to_be32(0);	/*unknown */
#endif				/* 1 */
		skb->dev = netdev;
		skb->protocol = htons(ETH_P_80211_RAW);
		skb->mac.raw = skb->data;
		skb->pkt_type = PACKET_OTHERHOST;
		skb->ip_summed = CHECKSUM_NONE;

		netif_rx(skb);
#else
		islsm_to_stats(&stats, data_head, skb->len);
		if (!ieee80211_rx(islsm->ieee, skb, &stats))
			dev_kfree_skb(skb);
#endif /* MADWIFI */
		FN_EXIT0;
		return;
	}

	/*
	 * print magic ; Remove packets with bad FCS
	 */
	islog(L_SM_INPUT, "receive packet with "
	      "magic %i, unknown1 %i, unknown2 %i\n", magic1, unknown1,
	      unknown2);
	if (!(magic1 & ISLSM_ID_FCS_OK)) {
		islog(L_SM_INPUT, "packet with bad FCS\n");
		/* should be dropped. For now, we keep it, as long as
		   we're not sure */
	}
#ifdef MADWIFI
#define	IS_CTL(wh) \
	((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
	/*
	 * From this point on we assume the frame is at least
	 * as large as ieee80211_frame_min; verify that.
	 */
	if (skb->len < IEEE80211_MIN_LEN) {
		islog(L_DEBUG, "short packet %d\n", skb->len);
		dev_kfree_skb(skb);
		return;
	}

	skb->protocol = ETH_P_CONTROL;	/* XXX */

	/*
	 * Locate the node for sender, track state, and then
	 * pass the (referenced) node up to the 802.11 layer
	 * for its use.
	 * TODO : use a cache.
	 * TODO : test ad-hoc / master mode with this.
	 */

	ni = ieee80211_find_rxnode(ic,
				   (struct ieee80211_frame_min *) skb->data);

	/*
	 * Track rx rssi and do any rx antenna management.
	 */

	/*
	 * Send frame up for processing.
	 */

	ieee80211_input(ic, skb, ni,
			data_head->signal_strength,
			(u32) le64_to_cpu(data_head->timestamp));

	/*
	 * Reclaim node reference.
	 */
	ieee80211_free_node(ni);

#undef IS_CTL
#else				/* MADWIFI */

	if (skb->len < IEEE80211_HLEN) {
		dev_kfree_skb(skb);
		goto drop;
	}

	/* XXX Drop all this garbage and use ieee80211_rx_any when it's fixed */

	switch (islsm->ieee->iw_mode) {
	case IW_MODE_ADHOC:
		hdr3 = (struct ieee80211_hdr_3addr *) skb->data;

		frame_ctl = le16_to_cpu(hdr3->frame_ctl);
		if (WLAN_FC_GET_TYPE(frame_ctl) == IEEE80211_FTYPE_MGMT) {
			islsm_to_stats(&stats, data_head, skb->len);
			hdr4 = (struct ieee80211_hdr_4addr *)skb->data;
			ieee80211_rx_mgt(islsm->ieee, hdr4, &stats);
			dev_kfree_skb(skb);
			goto drop;
		}

		if (WLAN_FC_GET_TYPE(frame_ctl) != IEEE80211_FTYPE_DATA) {
			dev_kfree_skb(skb);
			goto drop;
		}

		if (compare_ether_addr(hdr3->addr3, islsm->ieee->bssid) != 0) {
			dev_kfree_skb(skb);
			goto drop;
		}

		if (compare_ether_addr(hdr3->addr1, netdev->dev_addr) != 0 &&
		    !is_broadcast_ether_addr(hdr3->addr1) &&
		    !is_multicast_ether_addr(hdr3->addr1))
		{
			dev_kfree_skb(skb);
			goto drop;
		}

		islsm_to_stats(&stats, data_head, skb->len);
		if (!ieee80211_rx(islsm->ieee, skb, &stats)) {
			dev_kfree_skb(skb);
			goto drop;
		}
		break;

	default:
		hdr4 = (struct ieee80211_hdr_4addr *) skb->data;
		frame_ctl = le16_to_cpu(hdr4->frame_ctl);
		if (WLAN_FC_GET_TYPE(frame_ctl) == IEEE80211_FTYPE_MGMT) {
			islsm_to_stats(&stats, data_head, skb->len);
			ieee80211_rx_mgt(islsm->ieee, hdr4, &stats);
			dev_kfree_skb(skb);
			goto drop;
		}

		dev_kfree_skb(skb);
	}

 drop: /* go here for FN_EXIT0 */
#endif				/* MADWIFI */
	FN_EXIT0;
	return;
}

static void islsm_input_stats(struct sk_buff *skb)
{
	struct islsm_control_stats *stats = (void *) skb->data;
	islog
	    (L_SM_INPUT,
	     "stats report : "
	     "FCS ok %08x, FCS bad %08x, RX aborted %08x, RX error %08x,"
	     "%08x, %08x, %08x, timestamp %08x, rssi %02x\n",
	     le32_to_cpu(stats->rx_pkt_fcsok), le32_to_cpu(stats->rx_pkt_fcsbad),
	     le32_to_cpu(stats->rx_pkt_abort), le32_to_cpu(stats->rx_pkt_size_error),
	     le32_to_cpu(stats->stat4), le32_to_cpu(stats->stat5),
	     le32_to_cpu(stats->stat6), le32_to_cpu(stats->timestamp),
	     le32_to_cpu(stats->rssi));
	dev_kfree_skb(skb);
	return;
}

#ifndef MADWIFI
static void
islsm_to_stats(struct ieee80211_rx_stats *st,
    struct islsm_rx_packet_header *data_head, int len)
{
	unsigned short freq = le16_to_cpu(data_head->freq);
	unsigned int rate = data_head->rate;

	memset(st, 0, sizeof(struct ieee80211_rx_stats));
	st->mac_time = le64_to_cpu(data_head->timestamp);
	st->rssi = data_head->signal_strength;
	/* FIXME: use the rate table provided by the firmware */
	if (rate < 12)
		st->rate = islsm_rate_table[rate];
	st->received_channel = islsm_ref_to_chan(freq);
	st->mask =
	    IEEE80211_STATMASK_SIGNAL |
	    IEEE80211_STATMASK_RSSI |
	    IEEE80211_STATMASK_RATE;
	st->freq = IEEE80211_24GHZ_BAND;
	st->len = len;
}
#endif /* MADWIFI */

static
    void
islsm_input_txack(struct sk_buff *skb, uint32_t lmac_addr)
{
	struct net_device *netdev = skb->input_dev;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);

	if (isl_debug & L_SM_INPUT) {
		struct islsm_control_queueemptied *free = (void *) skb->data;
		islog(L_SM_INPUT,
		      "tx ack : status %x, ack strength %x, sequence %04x, %x\n",
		      free->status, le16_to_cpu(free->ack_strength),
		      free->sequence, free->unknown);
	}

	/* if ok, then free. This should be conditioned by something
	   (some flags) to avoid the double-free problems */
	islsm_free(&islsm->memory, lmac_addr);
	dev_kfree_skb(skb);
	return;
}

static void islsm_input_freqdone(struct sk_buff *skb)
{
	struct islsm_control_freqchange *freq =
	    (struct islsm_control_freqchange *) skb->data;
	islog(L_SM_INPUT, "frequency sucessfully set to %i MHz\n",
	      le16_to_cpu(freq->frequency));
	dev_kfree_skb(skb);
}

static void input_bbp_frame(struct sk_buff *skb)
{
	struct net_device *netdev = skb->input_dev;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct islsm_control_bbp *bbp = (void *) skb->data;
	int reg, val;
	reg = le32_to_cpu(bbp->reg);
	val = le32_to_cpu(bbp->val);
	islog(L_SM_INPUT, "readback bbp register %i value %i\n", reg, val);

	islsm->bbp_reg_val = val;
	/* signal return data is ready */
	complete(&islsm->dev_init_comp);
	dev_kfree_skb(skb);
}

/* first pass only copies the buffer */
static void input_eeprom_frame(struct sk_buff *skb)
{
	struct net_device *netdev = skb->input_dev;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	union islsm_control_eeprom *txe = (void *) skb->data;
	const char *data;
	unsigned offset;
	unsigned length;

	if (islsm->fw_type == ISLSM_FW_LM86) {
		offset = le16_to_cpu(txe->lm86.offset);
		length = le16_to_cpu(txe->lm86.len);
		data   = txe->lm86.data;
	} else {
		offset = le32_to_cpu(txe->lm87.address);
		length = le16_to_cpu(txe->lm87.len);
		data   = txe->lm87.data;
	}

	if (offset + length > islsm->eeprom_size)
		printk(KERN_WARNING DRV_NAME ": eeprom data"
		       "beyond allocated memory\n");
	else
		memcpy(&islsm->eeprom[offset], data, length);

	/* signal we saw this frame */
	complete(&islsm->dev_init_comp);

	dev_kfree_skb(skb);
	return;
}

/* second level input function (along with data_input at top) */

static void
islsm_input_islheader(struct sk_buff *skb)
{
	struct net_device *netdev = skb->input_dev;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	struct islsm_tx_control *rx = (void *) skb->data;
	unsigned int trim_length = le16_to_cpu(rx->id.length);
	unsigned int type = le16_to_cpu(rx->type);
	uint16_t flags = le16_to_cpu(rx->id.magic1);
	uint32_t lmac_addr = rx->req_id;

	skb_pull(skb, SIZE_TX_CONTROL);
	skb_trim(skb, trim_length);

	/* free the associated frame in out image of the lmac memory
	   map. Normally free only if bit IC_FREE is *not* set (meaning
	   that we did not set it).
	 */
	if (!(flags & ISLSM_IC_FLAG_FREE))
		islsm_free(&islsm->memory, lmac_addr);

	switch (type) {
	case ISLSM_TX_CONTROL_TYPE_FREQDONE:
		islsm_input_freqdone(skb);
		break;
	case ISLSM_TX_CONTROL_TYPE_TXDONE:
		islsm_input_txack(skb, lmac_addr);
		break;
	case ISLSM_TX_CONTROL_TYPE_STAT_READBACK:
		islsm_input_stats(skb);
		break;
	case ISLSM_TX_CONTROL_TYPE_EEPROM_READBACK:
		input_eeprom_frame(skb);
		break;
	case ISLSM_TX_CONTROL_TYPE_BBP:
		input_bbp_frame(skb);
	default:
		/* signal something went wrong */
		printk(KERN_CRIT "unknown control packet "
		       "length %i, req_id %08x, type %04x\n",
		       le16_to_cpu(rx->id.length), rx->req_id, type);
		dev_kfree_skb(skb);
	}

	return;
}

/*
 * first level input function, dispatches on id
 */

void
islsm_data_input(struct sk_buff *skb)
{
	struct net_device *netdev = skb->input_dev;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	unsigned length = skb->len;
	unsigned int magic1;

	/* TODO: get the queue information from the skb cb -- there are
	   usually two queues, control and bulk data */
	islsm_rxdata_debug(0, skb->data, length);

	if (length == 0) {
		islog(L_DEBUG, "data_input : null packet\n");
		goto error;
	}

	/* switch on firmware type */
	switch (islsm->fw_type) {
	case ISLSM_FW_LM87:
		if (islsm_input_lm87(skb))
			goto error;
		break;
	default:
		break;
	};

	/* FIXME : is this still necessary ? */
	skb->dev = netdev;

	magic1 = le16_to_cpu(((struct islsm_id *) (skb->data))->magic1);

	islog(L_SM_INPUT, "islsm input packet magic %x\n", magic1);
	/* actually i should just check on an and with 0x8000, but until
	 * the fields are known, i'd rather not to... */
	switch (magic1 & ISLSM_ID_MASK) {
	case ISLSM_ID_DATA_PKT:
	case ISLSM_ID_MGMT_PKT:
		input_data_frame(skb);
		return;

	case ISLSM_ID_EEPROM_READBACK:
		islsm_input_islheader(skb);
		return;

	case ISLSM_ID_FAULT:
		islsm_input_fault(skb);
		return;

	default:
		printk(KERN_CRIT "unknown magic1 %i packet\n", magic1);
		isl_dump_bytes(skb->data, skb->len);
	};

 error:
	dev_kfree_skb(skb);
	return;
}

EXPORT_SYMBOL(islsm_data_input);

/*
 * lm87 frame input
 */

static int
islsm_input_lm87(struct sk_buff *skb)
{
	islsm_lm87_hdr_t *hdr = (void *)skb->data;
	u32 crc, computed_crc;

	if (skb->len < 4) {
		printk(KERN_WARNING "Short frame received\n");
		return -EIO;
	}

	crc = le32_to_cpu(hdr->checksum);
	skb_pull(skb, sizeof(hdr->checksum));

	computed_crc = islsm_crc((u32*)skb->data, skb->len);

	if (computed_crc != crc) {
		printk(KERN_WARNING "Invalid CRC for rx frame\n");
		return -EIO;
	};

	return 0;
}

static
    void
islsm_input_fault(struct sk_buff *skb)
{
	printk(KERN_CRIT "ROM abort, need to implement reacting on this\n");
	/* initiate shutdown/restart of the device */
	return;
}

void islsm_bootup_input(struct sk_buff *skb)
{
	struct net_device *netdev = skb->input_dev;
	struct islsm *islsm = ISLSM_OF_NETDEV(netdev);
	char *buffer = (char *) skb->data;
	unsigned length = skb->len;

	/* don't do anything fancy for now */
	islsm_rxdata_debug(0, buffer, length);

	/* When receiving g, we're done !
	 * Maybe we should split the receive OK / send go / receive g process
	 * Someone tests this ?
	 */
	if (length >= 5 && !strncmp(buffer, "ERROR", 5)) {
		printk(KERN_ERR "FW Upload not successful: (%.*s)\n",
		       length, buffer);
	} else if (length >= 2 && !strncmp(buffer, "OK", 2)) {
		islog(L_DEBUG, "FW Upload successful: (%.*s)\n",
		      length, buffer);
		complete(&islsm->dev_init_comp);
	} else if (length >= 1 && !strncmp(buffer, "g", 1)) {
		islog(L_DEBUG, "FW boot successful: (%.*s)\n",
		      length, buffer);
		complete(&islsm->dev_init_comp);
	}
	dev_kfree_skb(skb);
	return;
}

EXPORT_SYMBOL(islsm_bootup_input);

/*
 * Frame timeout management
 */

/* the timeout value must take into account the buffer preparation time
 * and usb transmit time. And maybe the multi-rate retry mechanism.
 * Safe value should be around 512ms */
static void __attribute__ ((unused))
islsm_ack_timeout(unsigned long data)
{
	uint32_t frame_addr = data;
	islog(L_SM_INPUT, "ack timeout happened for frame %x\n", frame_addr);
	/* this needs more reverse-engeneering and a change in semantics
	   WRT the  empty_queue function, which really is a status
	   inquiry */
	/* Ask for a status of the frame */
/* 	(void) islsm_empty_queue(txq->netdev,txq->usb_id); */
/* 	do nothing, just reuse it */

/* 	(void) islsm_txqueue_free(txq->netdev, txq->usb_id); */

	/* let's hope the sending is successfull, otherwise the
	   frame won't be freed */
	return;
}

static inline int
islsm_frame_timer(uint32_t frame_addr)
{
/* 	struct timer_list *tx_timer = &txq->tx_timer; */
/* 	tx_timer->expires = jiffies + ISLSM_ACK_TIMEOUT; */
/* 	tx_timer->data = (unsigned long) frame_addr; */
/* 	tx_timer->function = islsm_ack_timeout; */
/* 	add_timer(tx_timer); */
	return 0;
}
