/* input.c: 802.11 mgmt packet input processing.
 *
 * Copyright (C) 2003 David S. Miller (davem@redhat.com)
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/init.h>

#include "if_80211.h"
#include "p80211_impl.h"

typedef int (*p80211_recv_t)(struct sk_buff *, struct wireless_info *);

static int p80211_recv_invalid(struct sk_buff *, struct wireless_info *);

/* management */
static int p80211_recv_assoc_req(struct sk_buff *, struct wireless_info *);
static int p80211_recv_assoc_resp(struct sk_buff *, struct wireless_info *);
static int p80211_recv_reassoc_req(struct sk_buff *, struct wireless_info *);
static int p80211_recv_reassoc_resp(struct sk_buff *, struct wireless_info *);
static int p80211_recv_probe_req(struct sk_buff *, struct wireless_info *);
static int p80211_recv_probe_resp(struct sk_buff *, struct wireless_info *);
static int p80211_recv_beacon(struct sk_buff *, struct wireless_info *);
static int p80211_recv_atim(struct sk_buff *, struct wireless_info *);
static int p80211_recv_disassoc(struct sk_buff *, struct wireless_info *);
static int p80211_recv_auth(struct sk_buff *, struct wireless_info *);
static int p80211_recv_deauth(struct sk_buff *, struct wireless_info *);

/* control */
static int p80211_recv_pspoll(struct sk_buff *, struct wireless_info *);
static int p80211_recv_rts(struct sk_buff *, struct wireless_info *);
static int p80211_recv_cts(struct sk_buff *, struct wireless_info *);
static int p80211_recv_ack(struct sk_buff *, struct wireless_info *);
static int p80211_recv_cfend(struct sk_buff *, struct wireless_info *);
static int p80211_recv_cfendack(struct sk_buff *, struct wireless_info *);

/* data */
static int p80211_recv_data(struct sk_buff *, struct wireless_info *);
static int p80211_recv_data_cfack(struct sk_buff *, struct wireless_info *);
static int p80211_recv_data_cfpoll(struct sk_buff *, struct wireless_info *);
static int p80211_recv_data_cfackpoll(struct sk_buff *, struct wireless_info *);
static int p80211_recv_nullfunc(struct sk_buff *, struct wireless_info *);
static int p80211_recv_cfack(struct sk_buff *, struct wireless_info *);
static int p80211_recv_cfpoll(struct sk_buff *, struct wireless_info *);
static int p80211_recv_cfackpoll(struct sk_buff *, struct wireless_info *);

static p80211_recv_t input_methods[0x4][0x10] = {
/* management */
[IEEE802_11_FTYPE_MGMT >> IEEE802_11_FTYPE_SHIFT] = {
	[IEEE802_11_STYPE_ASSOC_REQ >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_assoc_req,
	[IEEE802_11_STYPE_ASSOC_RESP >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_assoc_resp,
	[IEEE802_11_STYPE_REASSOC_REQ >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_reassoc_req,
	[IEEE802_11_STYPE_REASSOC_RESP >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_reassoc_resp,
	[IEEE802_11_STYPE_PROBE_REQ >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_probe_req,
	[IEEE802_11_STYPE_PROBE_RESP >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_probe_resp,
	[0x6] = p80211_recv_invalid,
	[0x7] = p80211_recv_invalid,
	[IEEE802_11_STYPE_BEACON >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_beacon,
	[IEEE802_11_STYPE_ATIM >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_atim,
	[IEEE802_11_STYPE_DISASSOC >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_disassoc,
	[IEEE802_11_STYPE_AUTH >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_auth,
	[IEEE802_11_STYPE_DEAUTH >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_deauth,
	[0xd] = p80211_recv_invalid,
	[0xe] = p80211_recv_invalid,
	[0xf] = p80211_recv_invalid,
},
/* control */
[IEEE802_11_FTYPE_CTL >> IEEE802_11_FTYPE_SHIFT] = {
	[0x0] = p80211_recv_invalid,
	[0x1] = p80211_recv_invalid,
	[0x2] = p80211_recv_invalid,
	[0x3] = p80211_recv_invalid,
	[0x4] = p80211_recv_invalid,
	[0x5] = p80211_recv_invalid,
	[0x6] = p80211_recv_invalid,
	[0x7] = p80211_recv_invalid,
	[0x8] = p80211_recv_invalid,
	[0x9] = p80211_recv_invalid,
	[IEEE802_11_STYPE_PSPOLL >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_pspoll,
	[IEEE802_11_STYPE_RTS >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_rts,
	[IEEE802_11_STYPE_CTS >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_cts,
	[IEEE802_11_STYPE_ACK >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_ack,
	[IEEE802_11_STYPE_CFEND >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_cfend,
	[IEEE802_11_STYPE_CFENDACK >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_cfendack,
},
/* data */
[IEEE802_11_FTYPE_DATA >> IEEE802_11_FTYPE_SHIFT] = {
	[IEEE802_11_STYPE_DATA >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_data,
	[IEEE802_11_STYPE_DATA_CFACK >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_data_cfack,
	[IEEE802_11_STYPE_DATA_CFPOLL >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_data_cfpoll,
	[IEEE802_11_STYPE_DATA_CFACKPOLL >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_data_cfackpoll,
	[IEEE802_11_STYPE_NULLFUNC >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_nullfunc,
	[IEEE802_11_STYPE_CFACK >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_cfack,
	[IEEE802_11_STYPE_CFPOLL >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_cfpoll,
	[IEEE802_11_STYPE_CFACKPOLL >> IEEE802_11_STYPE_SHIFT] =
		p80211_recv_cfackpoll,
	[0x8] = p80211_recv_invalid,
	[0x9] = p80211_recv_invalid,
	[0xa] = p80211_recv_invalid,
	[0xb] = p80211_recv_invalid,
	[0xc] = p80211_recv_invalid,
	[0xd] = p80211_recv_invalid,
	[0xe] = p80211_recv_invalid,
	[0xf] = p80211_recv_invalid,
},
/* invalid ftype */
[IEEE802_11_FTYPE_INVALID >> 2] = {
	[0x0] = p80211_recv_invalid,
	[0x1] = p80211_recv_invalid,
	[0x2] = p80211_recv_invalid,
	[0x3] = p80211_recv_invalid,
	[0x4] = p80211_recv_invalid,
	[0x5] = p80211_recv_invalid,
	[0x6] = p80211_recv_invalid,
	[0x7] = p80211_recv_invalid,
	[0x8] = p80211_recv_invalid,
	[0x9] = p80211_recv_invalid,
	[0xa] = p80211_recv_invalid,
	[0xb] = p80211_recv_invalid,
	[0xc] = p80211_recv_invalid,
	[0xd] = p80211_recv_invalid,
	[0xe] = p80211_recv_invalid,
	[0xf] = p80211_recv_invalid,
},
};

static inline int demultiplex(struct sk_buff *skb, struct wireless_info *wp, u16 ctl)
{
	int type, subtype;

	type = (ctl & IEEE802_11_FCTL_FTYPE) >> IEEE802_11_FTYPE_SHIFT;
	subtype = (ctl & IEEE802_11_FCTL_STYPE) >> IEEE802_11_STYPE_SHIFT;

	return input_methods[type][subtype](skb, wp);
}

static int p80211_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
	struct ieee802_11_hdr *p = (struct ieee802_11_hdr *) skb->data;
	struct wireless_info *wp = p80211_find_by_dev(dev);
	int ret;

	if (wp) {
		ret = demultiplex(skb, wp, le16_to_cpu(p->frame_ctl));
		wireless_put(wp);
	} else {
		ret = NET_RX_DROP;
		kfree_skb(skb);
	}
	return ret;
}

int p80211_recv_invalid(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

/* management */
int p80211_recv_assoc_req(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_assoc_resp(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_reassoc_req(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_reassoc_resp(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_probe_req(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_probe_resp(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_beacon(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_atim(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_disassoc(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_auth(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_deauth(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

/* control */
int p80211_recv_pspoll(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_rts(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_cts(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_ack(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_cfend(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_cfendack(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

/* data */
int p80211_recv_data(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_data_cfack(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_data_cfpoll(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_data_cfackpoll(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_nullfunc(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_cfack(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_cfpoll(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

int p80211_recv_cfackpoll(struct sk_buff *skb, struct wireless_info *wp)
{
	kfree_skb(skb);
	return NET_RX_DROP;
}

static struct packet_type p80211_packet_type = {
	.type	=	__constant_htons(ETH_P_802_11),
	.func	=	p80211_rcv,
	.data	=	(void *) 1, /* understands shared SKBs */
};

int p80211_input_init(void)
{
	dev_add_pack(&p80211_packet_type);

	return 0;
}

void p80211_input_cleanup(void)
{
	dev_remove_pack(&p80211_packet_type);
}
