/*
 *	NET3:	Implementation of the 802.2 LLC. Only 802.2 UI frames are
 *		dealt with. This is all that IPX and IP over 802.2 need.
 *		This would need redoing to support NetBEUI however.
 *	
 *		Alan Cox, <A.Cox@swansea.ac.uk / gw4pts@gw4pts.ampr.org>
 *
 *	This program 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.
 *
 *	Fixes:
 *		Alan Cox	:	One (or more) instance/device.
 *		Alan Cox	:	Changed calling for output().
 */

#include <linux/config.h> 
#include <linux/mm.h>
#include <linux/in.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/netprotocol.h>

struct eth8022hdr
{
	unsigned char dsap;
	unsigned char ssap;
	unsigned char type;
};

#define UI_8022	3

extern struct protocol proto_dix;
struct protocol proto_8022;

int eth8022_input(struct protocol *p, struct protocol *below, sk_buff *skb, void *saddr, void *daddr)
{
	int sz;
	struct eth8022hdr *eh=(struct eth8022hdr *)skb_pull(skb,3,&sz);
	if(sz!=3)
	{
		kfree_skb(skb, FREE_READ);
		return -EINVAL;
	}
	skb->lower_head=eh;
	if(protocol_pass_demultiplex(&proto_8022, &eh->ssap, skb, &eh->ssap, &eh->dsap)==0)
	{
		kfree_skb(skb, FREE_READ);
		return -EPROTONOSUPPORT;
	}
	return 0;
}


int eth8022_output(struct protocol *self, sk_buff *skb, int type, int subid, void *saddr, void *daddr, void *opt)
{
	struct eth8022hdr *eh=(struct eth8022hdr *)skb_push(skb,sizeof(struct eth8022hdr));
	if(eh==NULL)
	{
		printk("eth8022_output: no room!\n");
		kfree_skb(skb, FREE_READ);
		return -ENOBUFS;
	}
	if(type!=ETH_P_IPX && type!=ETH_P_SNAP)	/* We only do IPX and SNAP at the moment */
	{
		kfree_skb(skb, FREE_READ);
		return -EAFNOSUPPORT;
	}
	
	if(type==ETH_P_SNAP)
	{
		eh->ssap=0xAA;
		eh->dsap=0xAA;
	}
	else if(type==ETH_P_IPX)
	{
		eh->ssap=0xE0;
		eh->dsap=0xE0;
	}
	else
	{
		eh->ssap=(unsigned char)subid;
		eh->dsap=(unsigned char)subid;
	}
	eh->type=UI_8022;	/* 802.2 UI */
	
	if(self->stream_down==NULL)
	{
		kfree_skb(skb, FREE_WRITE);
		return -ENOTCONN;
	}
	
	return self->stream_down->output(self, skb, skb->len, 0, saddr, daddr, opt);
}

static int eth8022_get_key(int protocol, int subid, unsigned char *key)
{
	switch(protocol)
	{
		case ETH_P_SNAP:
			*key=0xAA;
			return 1;
		case ETH_P_IPX:
			*key=0xE0;
			return 1;
		case ETH_P_802_3:	/* Raw 802.3/IPX hackery */
			*key=0xFF;	/* Looks like this thanks to novell */
			return 1;
		case ETH_P_802_2:       /* User specified 8022 */
			if(subid<0||subid>255)
				return -EAFNOSUPPORT;
			*key=(unsigned char)subid;
			return 1;
		default:
			return -EAFNOSUPPORT;
	}
}
	
struct protocol proto_8022=
{
	NULL,
	"802.2",
	sizeof(struct eth8022hdr),
	0,
	sizeof(struct eth8022hdr),
	0,
	eth8022_output,
#ifdef CONFIG_FAST_PATH	
	protocol_default_fast_output,
	protocol_default_build_fast,	/* We could do fast building here but we don't yet do it */
#endif	
	eth8022_input,
	eth8022_input,
	default_protocol_control,
	eth8022_get_key,
	NULL,
	NULL,
};

void p8022_proto_init(void)
{
	protocol_register(&proto_8022);
	protocol_bind(&proto_dix, &proto_8022, ETH_P_802_2, 0);
}
