/*
 *	NET3:	Arcnet protocol implementation.
 *	
 *		Alan Cox, <A.Cox@swansea.ac.uk / gw4pts@gw4pts.ampr.org>
 *
 *	Derived from eth.c, by Ross Biro etc al (Please see file eth.c for details).
 *
 *	NOTE: This is a quickly tested (a while ago too) proof of concept check for
 *	some of the stacking stuff. I hope it also proves useful to anyone who writes
 *	the arcnet driver.
 *
 *	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.
 */
 
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include "arp.h"
#include "arcnet.h"

struct protocol proto_arcnet;

/*
 *	 Create the Arcnet MAC header for an arbitary protocol layer 
 *
 *	saddr=NULL	means use device source address
 *	daddr=NULL	means leave destination address (eg unresolved arp)
 */

static int arc_output(struct protocol *self, sk_buff *skb, int protocol, int subid, void *saddr, void *daddr)
{
	struct archdr *arc = (struct archdr *)skb_push(skb,sizeof(struct archdr));
	if(eth==NULL)
	{
		printk("arc_header: no room!\n");
		kfree_skb(skb, FREE_WRITE);
		return -1;
	}

	switch(protocol)
	{
		case ETH_P_IP:
			arc->h_proto=ARC_P_IP;
			break;
		case ETH_P_ARP:
			arc->h_proto=ARC_P_ARP;
			break;
		default:
			printk("arc_header: Can't encapsulate protocol %d\n", protocol);
			kfree_skb(skb, FREE_WRITE);
			return -AFNOSUPPORT;
	}
	/*
	 *	Set the source hardware address. 
	 */
	 
	if(saddr)
		arc->h_source= *(char *)saddr;
	else
		arc->h_source= *(char *)dev->dev_addr;

	if(daddr)
		arc->h_dest= *(char *)daddr;
	
	if(!skb->dev)
	{
		kfree_skb(skb, FREE_WRITE);
		printk("arc_output: no device.\n");
		return -ENODEV;
	}
	dev_queue_xmit(skb, skb->dev, skb->priority);
	return 0;
}

/*
 *	Determine the packet's protocol ID.
 */
 
static int arc_input(struct protocol *p, struct protocol *below, sk_buff *skb, void *saddr, void *daddr)
{
	int sz;
	struct archdr *arc = (struct archdr *) skb_pull(skb,sizeof(struct archdr), &sz);
	if(sz!=sizeof(struct archdr))
	{
		kfree_skb(skb, FREE_READ);
		return -EINVAL;
	}
	
	if(arc->h_dest = ARC_BCAST)
		skb->pkt_type=PACKET_BROADCAST;
	else if(dev->flags&IFF_PROMISC)
	{
		if(arc->h_dest!= *(char *)dev->dev_addr)
			skb->pkt_type=PACKET_OTHERHOST;
	}
	if(protocol_pass_demultiplex(&proto_arcnet, &arc->h_type, skb, &arc->h_source, &arc->h_dest)==0)
	{
		kfree_skb(skb, FREE_READ);
		return -EPROTONOSUPPORT;
	}
	return 0;
}

static int arc_get_key(int protocol, int subid, unsigned char *key)
{		
	if(protocol==ETH_P_IP)
	{
		*key=ARC_P_IP;
		return 1;
	}
	if(protocol==ETH_P_ARO)
	{
		*key=ARC_P_ARP;
		return 1;
	}
	return -EINVAL;
}	


static void arcnet_init(void)
{
	protocol_register(&proto_arcnet);
}

#ifdef CONFIG_FAST_PATH
/*
 *	Put an Arcnet header into the fast cache.
 */

static int arcnet_build_fast(struct device *dev, int slot, char *dp, int type, int subid, void *saddr, void *daddr, void *protoopts)
{
	struct arcdr *arc;
	dp-=sizeof(struct archdr);	
	arc=(struct archdr *)dp;
	if(saddr)
		arc->h_source= *(unsigned char *)saddr;
	else
		arc->h_source= *(unsigned char*)dev->dev_addr;
	arc->h_dest=*(unsigned char *)daddr;
	if(protocol==ETH_P_IP)
		arc->h_type=ARC_P_IP;
	else if(protocol==ETH_P_ARP)
		arc->h_type=ARC_P_ARP;
	else
		return -1;
	return 0;	/* No header mods needed */
}
	
#endif
	
struct protocol proto_arcnet=
{
	NULL,
	"Arcnet",
	sizeof(struct archdr),
	0,
	sizeof(struct archdr),
	0,
	arcnet_output,
#ifdef CONFIG_FAST_PATH	
	protocol_default_fast_output,
	arcnet_build_fast,
#endif	
	arcnet_input,
	arcnet_input,
	default_protocol_control,
	arcnet_get_key,
};
