aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/drivers/pci/pci-sh5.c
blob: 873ed2b4405575a53bcb2f0c3b453ebea0cfc473 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
 * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
 * Copyright (C) 2003, 2004 Paul Mundt
 * Copyright (C) 2004 Richard Curnow
 *
 * May be copied or modified under the terms of the GNU General Public
 * License.  See linux/COPYING for more information.
 *
 * Support functions for the SH5 PCI hardware.
 */

#include <linux/kernel.h>
#include <linux/rwsem.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/irq.h>
#include <cpu/irq.h>
#include <asm/pci.h>
#include <asm/io.h>
#include "pci-sh5.h"

unsigned long pcicr_virt;
unsigned long PCI_IO_AREA;

/* Rounds a number UP to the nearest power of two. Used for
 * sizing the PCI window.
 */
static u32 __init r2p2(u32 num)
{
	int i = 31;
	u32 tmp = num;

	if (num == 0)
		return 0;

	do {
		if (tmp & (1 << 31))
			break;
		i--;
		tmp <<= 1;
	} while (i >= 0);

	tmp = 1 << i;
	/* If the original number isn't a power of 2, round it up */
	if (tmp != num)
		tmp <<= 1;

	return tmp;
}

static irqreturn_t pcish5_err_irq(int irq, void *dev_id)
{
	struct pt_regs *regs = get_irq_regs();
	unsigned pci_int, pci_air, pci_cir, pci_aint;

	pci_int = SH5PCI_READ(INT);
	pci_cir = SH5PCI_READ(CIR);
	pci_air = SH5PCI_READ(AIR);

	if (pci_int) {
		printk("PCI INTERRUPT (at %08llx)!\n", regs->pc);
		printk("PCI INT -> 0x%x\n", pci_int & 0xffff);
		printk("PCI AIR -> 0x%x\n", pci_air);
		printk("PCI CIR -> 0x%x\n", pci_cir);
		SH5PCI_WRITE(INT, ~0);
	}

	pci_aint = SH5PCI_READ(AINT);
	if (pci_aint) {
		printk("PCI ARB INTERRUPT!\n");
		printk("PCI AINT -> 0x%x\n", pci_aint);
		printk("PCI AIR -> 0x%x\n", pci_air);
		printk("PCI CIR -> 0x%x\n", pci_cir);
		SH5PCI_WRITE(AINT, ~0);
	}

	return IRQ_HANDLED;
}

static irqreturn_t pcish5_serr_irq(int irq, void *dev_id)
{
	printk("SERR IRQ\n");

	return IRQ_NONE;
}

static struct resource sh5_io_resource = { /* place holder */ };
static struct resource sh5_mem_resource = { /* place holder */ };

static struct pci_channel sh5pci_controller = {
	.pci_ops		= &sh5_pci_ops,
	.mem_resource		= &sh5_mem_resource,
	.mem_offset		= 0x00000000,
	.io_resource		= &sh5_io_resource,
	.io_offset		= 0x00000000,
};

static int __init sh5pci_init(void)
{
	unsigned long memStart = __pa(memory_start);
	unsigned long memSize = __pa(memory_end) - memStart;
	u32 lsr0;
	u32 uval;

        if (request_irq(IRQ_ERR, pcish5_err_irq,
                        IRQF_DISABLED, "PCI Error",NULL) < 0) {
                printk(KERN_ERR "PCISH5: Cannot hook PCI_PERR interrupt\n");
                return -EINVAL;
        }

        if (request_irq(IRQ_SERR, pcish5_serr_irq,
                        IRQF_DISABLED, "PCI SERR interrupt", NULL) < 0) {
                printk(KERN_ERR "PCISH5: Cannot hook PCI_SERR interrupt\n");
                return -EINVAL;
        }

	pcicr_virt = (unsigned long)ioremap_nocache(SH5PCI_ICR_BASE, 1024);
	if (!pcicr_virt) {
		panic("Unable to remap PCICR\n");
	}

	PCI_IO_AREA = (unsigned long)ioremap_nocache(SH5PCI_IO_BASE, 0x10000);
	if (!PCI_IO_AREA) {
		panic("Unable to remap PCIIO\n");
	}

	/* Clear snoop registers */
        SH5PCI_WRITE(CSCR0, 0);
        SH5PCI_WRITE(CSCR1, 0);

        /* Switch off interrupts */
        SH5PCI_WRITE(INTM,  0);
        SH5PCI_WRITE(AINTM, 0);
        SH5PCI_WRITE(PINTM, 0);

        /* Set bus active, take it out of reset */
        uval = SH5PCI_READ(CR);

	/* Set command Register */
        SH5PCI_WRITE(CR, uval | CR_LOCK_MASK | CR_CFINT| CR_FTO | CR_PFE |
		     CR_PFCS | CR_BMAM);

	uval=SH5PCI_READ(CR);

        /* Allow it to be a master */
	/* NB - WE DISABLE I/O ACCESS to stop overlap */
        /* set WAIT bit to enable stepping, an attempt to improve stability */
	SH5PCI_WRITE_SHORT(CSR_CMD,
			    PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
			    PCI_COMMAND_WAIT);

        /*
        ** Set translation mapping memory in order to convert the address
        ** used for the main bus, to the PCI internal address.
        */
        SH5PCI_WRITE(MBR,0x40000000);

        /* Always set the max size 512M */
        SH5PCI_WRITE(MBMR, PCISH5_MEM_SIZCONV(512*1024*1024));

        /*
        ** I/O addresses are mapped at internal PCI specific address
        ** as is described into the configuration bridge table.
        ** These are changed to 0, to allow cards that have legacy
        ** io such as vga to function correctly. We set the SH5 IOBAR to
        ** 256K, which is a bit big as we can only have 64K of address space
        */

        SH5PCI_WRITE(IOBR,0x0);

        /* Set up a 256K window. Totally pointless waste  of address space */
        SH5PCI_WRITE(IOBMR,0);

	/* The SH5 has a HUGE 256K I/O region, which breaks the PCI spec.
	 * Ideally, we would want to map the I/O region somewhere, but it
	 * is so big this is not that easy!
         */
	SH5PCI_WRITE(CSR_IBAR0,~0);
	/* Set memory size value */
        memSize = memory_end - memory_start;

	/* Now we set up the mbars so the PCI bus can see the memory of
	 * the machine */
	if (memSize < (1024 * 1024)) {
                printk(KERN_ERR "PCISH5: Ridiculous memory size of 0x%lx?\n",
		       memSize);
                return -EINVAL;
        }

        /* Set LSR 0 */
        lsr0 = (memSize > (512 * 1024 * 1024)) ? 0x1ff00001 :
		((r2p2(memSize) - 0x100000) | 0x1);
        SH5PCI_WRITE(LSR0, lsr0);

        /* Set MBAR 0 */
        SH5PCI_WRITE(CSR_MBAR0, memory_start);
        SH5PCI_WRITE(LAR0, memory_start);

        SH5PCI_WRITE(CSR_MBAR1,0);
        SH5PCI_WRITE(LAR1,0);
        SH5PCI_WRITE(LSR1,0);

        /* Enable the PCI interrupts on the device */
        SH5PCI_WRITE(INTM,  ~0);
        SH5PCI_WRITE(AINTM, ~0);
        SH5PCI_WRITE(PINTM, ~0);

	sh5_io_resource.start = PCI_IO_AREA;
	sh5_io_resource.end = PCI_IO_AREA + 0x10000;

	sh5_mem_resource.start = memStart;
	sh5_mem_resource.end = memStart + memSize;

	register_pci_controller(&sh5pci_controller);

	return 0;
}
arch_initcall(sh5pci_init);
'n1051' href='#n1051'>1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
/*
 *	AARP:		An implementation of the AppleTalk AARP protocol for
 *			Ethernet 'ELAP'.
 *
 *		Alan Cox  <Alan.Cox@linux.org>
 *
 *	This doesn't fit cleanly with the IP arp. Potentially we can use
 *	the generic neighbour discovery code to clean this up.
 *
 *	FIXME:
 *		We ought to handle the retransmits with a single list and a
 *	separate fast timer for when it is needed.
 *		Use neighbour discovery code.
 *		Token Ring Support.
 *
 *		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.
 *
 *
 *	References:
 *		Inside AppleTalk (2nd Ed).
 *	Fixes:
 *		Jaume Grau	-	flush caches on AARP_PROBE
 *		Rob Newberry	-	Added proxy AARP and AARP proc fs,
 *					moved probing from DDP module.
 *		Arnaldo C. Melo -	don't mangle rx packets
 *
 */

#include <linux/if_arp.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <net/datalink.h>
#include <net/psnap.h>
#include <linux/atalk.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/export.h>
#include <linux/etherdevice.h>

int sysctl_aarp_expiry_time = AARP_EXPIRY_TIME;
int sysctl_aarp_tick_time = AARP_TICK_TIME;
int sysctl_aarp_retransmit_limit = AARP_RETRANSMIT_LIMIT;
int sysctl_aarp_resolve_time = AARP_RESOLVE_TIME;

/* Lists of aarp entries */
/**
 *	struct aarp_entry - AARP entry
 *	@last_sent - Last time we xmitted the aarp request
 *	@packet_queue - Queue of frames wait for resolution
 *	@status - Used for proxy AARP
 *	expires_at - Entry expiry time
 *	target_addr - DDP Address
 *	dev - Device to use
 *	hwaddr - Physical i/f address of target/router
 *	xmit_count - When this hits 10 we give up
 *	next - Next entry in chain
 */
struct aarp_entry {
	/* These first two are only used for unresolved entries */
	unsigned long		last_sent;
	struct sk_buff_head	packet_queue;
	int			status;
	unsigned long		expires_at;
	struct atalk_addr	target_addr;
	struct net_device	*dev;
	char			hwaddr[ETH_ALEN];
	unsigned short		xmit_count;
	struct aarp_entry	*next;
};

/* Hashed list of resolved, unresolved and proxy entries */
static struct aarp_entry *resolved[AARP_HASH_SIZE];
static struct aarp_entry *unresolved[AARP_HASH_SIZE];
static struct aarp_entry *proxies[AARP_HASH_SIZE];
static int unresolved_count;

/* One lock protects it all. */
static DEFINE_RWLOCK(aarp_lock);

/* Used to walk the list and purge/kick entries.  */
static struct timer_list aarp_timer;

/*
 *	Delete an aarp queue
 *
 *	Must run under aarp_lock.
 */
static void __aarp_expire(struct aarp_entry *a)
{
	skb_queue_purge(&a->packet_queue);
	kfree(a);
}

/*
 *	Send an aarp queue entry request
 *
 *	Must run under aarp_lock.
 */
static void __aarp_send_query(struct aarp_entry *a)
{
	static unsigned char aarp_eth_multicast[ETH_ALEN] =
					{ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF };
	struct net_device *dev = a->dev;
	struct elapaarp *eah;
	int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length;
	struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
	struct atalk_addr *sat = atalk_find_dev_addr(dev);

	if (!skb)
		return;

	if (!sat) {
		kfree_skb(skb);
		return;
	}

	/* Set up the buffer */
	skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);
	skb_put(skb, sizeof(*eah));
	skb->protocol    = htons(ETH_P_ATALK);
	skb->dev	 = dev;
	eah		 = aarp_hdr(skb);

	/* Set up the ARP */
	eah->hw_type	 = htons(AARP_HW_TYPE_ETHERNET);
	eah->pa_type	 = htons(ETH_P_ATALK);
	eah->hw_len	 = ETH_ALEN;
	eah->pa_len	 = AARP_PA_ALEN;
	eah->function	 = htons(AARP_REQUEST);

	ether_addr_copy(eah->hw_src, dev->dev_addr);

	eah->pa_src_zero = 0;
	eah->pa_src_net	 = sat->s_net;
	eah->pa_src_node = sat->s_node;

	memset(eah->hw_dst, '\0', ETH_ALEN);

	eah->pa_dst_zero = 0;
	eah->pa_dst_net	 = a->target_addr.s_net;
	eah->pa_dst_node = a->target_addr.s_node;

	/* Send it */
	aarp_dl->request(aarp_dl, skb, aarp_eth_multicast);
	/* Update the sending count */
	a->xmit_count++;
	a->last_sent = jiffies;
}

/* This runs under aarp_lock and in softint context, so only atomic memory
 * allocations can be used. */
static void aarp_send_reply(struct net_device *dev, struct atalk_addr *us,
			    struct atalk_addr *them, unsigned char *sha)
{
	struct elapaarp *eah;
	int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length;
	struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);

	if (!skb)
		return;

	/* Set up the buffer */
	skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);
	skb_put(skb, sizeof(*eah));
	skb->protocol    = htons(ETH_P_ATALK);
	skb->dev	 = dev;
	eah		 = aarp_hdr(skb);

	/* Set up the ARP */
	eah->hw_type	 = htons(AARP_HW_TYPE_ETHERNET);
	eah->pa_type	 = htons(ETH_P_ATALK);
	eah->hw_len	 = ETH_ALEN;
	eah->pa_len	 = AARP_PA_ALEN;
	eah->function	 = htons(AARP_REPLY);

	ether_addr_copy(eah->hw_src, dev->dev_addr);

	eah->pa_src_zero = 0;
	eah->pa_src_net	 = us->s_net;
	eah->pa_src_node = us->s_node;

	if (!sha)
		memset(eah->hw_dst, '\0', ETH_ALEN);
	else
		ether_addr_copy(eah->hw_dst, sha);

	eah->pa_dst_zero = 0;
	eah->pa_dst_net	 = them->s_net;
	eah->pa_dst_node = them->s_node;

	/* Send it */
	aarp_dl->request(aarp_dl, skb, sha);
}

/*
 *	Send probe frames. Called from aarp_probe_network and
 *	aarp_proxy_probe_network.
 */

static void aarp_send_probe(struct net_device *dev, struct atalk_addr *us)
{
	struct elapaarp *eah;
	int len = dev->hard_header_len + sizeof(*eah) + aarp_dl->header_length;
	struct sk_buff *skb = alloc_skb(len, GFP_ATOMIC);
	static unsigned char aarp_eth_multicast[ETH_ALEN] =
					{ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF };

	if (!skb)
		return;

	/* Set up the buffer */
	skb_reserve(skb, dev->hard_header_len + aarp_dl->header_length);
	skb_reset_network_header(skb);
	skb_reset_transport_header(skb);
	skb_put(skb, sizeof(*eah));
	skb->protocol    = htons(ETH_P_ATALK);
	skb->dev	 = dev;
	eah		 = aarp_hdr(skb);

	/* Set up the ARP */
	eah->hw_type	 = htons(AARP_HW_TYPE_ETHERNET);
	eah->pa_type	 = htons(ETH_P_ATALK);
	eah->hw_len	 = ETH_ALEN;
	eah->pa_len	 = AARP_PA_ALEN;
	eah->function	 = htons(AARP_PROBE);

	ether_addr_copy(eah->hw_src, dev->dev_addr);

	eah->pa_src_zero = 0;
	eah->pa_src_net	 = us->s_net;
	eah->pa_src_node = us->s_node;

	memset(eah->hw_dst, '\0', ETH_ALEN);

	eah->pa_dst_zero = 0;
	eah->pa_dst_net	 = us->s_net;
	eah->pa_dst_node = us->s_node;

	/* Send it */
	aarp_dl->request(aarp_dl, skb, aarp_eth_multicast);
}

/*
 *	Handle an aarp timer expire
 *
 *	Must run under the aarp_lock.
 */

static void __aarp_expire_timer(struct aarp_entry **n)
{
	struct aarp_entry *t;

	while (*n)
		/* Expired ? */
		if (time_after(jiffies, (*n)->expires_at)) {
			t = *n;
			*n = (*n)->next;
			__aarp_expire(t);
		} else
			n = &((*n)->next);
}

/*
 *	Kick all pending requests 5 times a second.
 *
 *	Must run under the aarp_lock.
 */
static void __aarp_kick(struct aarp_entry **n)
{
	struct aarp_entry *t;

	while (*n)
		/* Expired: if this will be the 11th tx, we delete instead. */
		if ((*n)->xmit_count >= sysctl_aarp_retransmit_limit) {
			t = *n;
			*n = (*n)->next;
			__aarp_expire(t);
		} else {
			__aarp_send_query(*n);
			n = &((*n)->next);
		}
}

/*
 *	A device has gone down. Take all entries referring to the device
 *	and remove them.
 *
 *	Must run under the aarp_lock.
 */
static void __aarp_expire_device(struct aarp_entry **n, struct net_device *dev)
{
	struct aarp_entry *t;

	while (*n)
		if ((*n)->dev == dev) {
			t = *n;
			*n = (*n)->next;
			__aarp_expire(t);
		} else
			n = &((*n)->next);
}

/* Handle the timer event */
static void aarp_expire_timeout(unsigned long unused)
{
	int ct;

	write_lock_bh(&aarp_lock);

	for (ct = 0; ct < AARP_HASH_SIZE; ct++) {
		__aarp_expire_timer(&resolved[ct]);
		__aarp_kick(&unresolved[ct]);
		__aarp_expire_timer(&unresolved[ct]);
		__aarp_expire_timer(&proxies[ct]);
	}

	write_unlock_bh(&aarp_lock);
	mod_timer(&aarp_timer, jiffies +
			       (unresolved_count ? sysctl_aarp_tick_time :
				sysctl_aarp_expiry_time));
}

/* Network device notifier chain handler. */
static int aarp_device_event(struct notifier_block *this, unsigned long event,
			     void *ptr)
{
	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
	int ct;

	if (!net_eq(dev_net(dev), &init_net))
		return NOTIFY_DONE;

	if (event == NETDEV_DOWN) {
		write_lock_bh(&aarp_lock);

		for (ct = 0; ct < AARP_HASH_SIZE; ct++) {
			__aarp_expire_device(&resolved[ct], dev);
			__aarp_expire_device(&unresolved[ct], dev);
			__aarp_expire_device(&proxies[ct], dev);
		}

		write_unlock_bh(&aarp_lock);
	}
	return NOTIFY_DONE;
}

/* Expire all entries in a hash chain */
static void __aarp_expire_all(struct aarp_entry **n)
{
	struct aarp_entry *t;

	while (*n) {
		t = *n;
		*n = (*n)->next;
		__aarp_expire(t);
	}
}

/* Cleanup all hash chains -- module unloading */
static void aarp_purge(void)
{
	int ct;

	write_lock_bh(&aarp_lock);
	for (ct = 0; ct < AARP_HASH_SIZE; ct++) {
		__aarp_expire_all(&resolved[ct]);
		__aarp_expire_all(&unresolved[ct]);
		__aarp_expire_all(&proxies[ct]);
	}
	write_unlock_bh(&aarp_lock);
}

/*
 *	Create a new aarp entry.  This must use GFP_ATOMIC because it
 *	runs while holding spinlocks.
 */
static struct aarp_entry *aarp_alloc(void)
{
	struct aarp_entry *a = kmalloc(sizeof(*a), GFP_ATOMIC);

	if (a)
		skb_queue_head_init(&a->packet_queue);
	return a;
}

/*
 * Find an entry. We might return an expired but not yet purged entry. We
 * don't care as it will do no harm.
 *
 * This must run under the aarp_lock.
 */
static struct aarp_entry *__aarp_find_entry(struct aarp_entry *list,
					    struct net_device *dev,
					    struct atalk_addr *sat)
{
	while (list) {
		if (list->target_addr.s_net == sat->s_net &&
		    list->target_addr.s_node == sat->s_node &&
		    list->dev == dev)
			break;
		list = list->next;
	}

	return list;
}

/* Called from the DDP code, and thus must be exported. */
void aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa)
{
	int hash = sa->s_node % (AARP_HASH_SIZE - 1);
	struct aarp_entry *a;

	write_lock_bh(&aarp_lock);

	a = __aarp_find_entry(proxies[hash], dev, sa);
	if (a)
		a->expires_at = jiffies - 1;

	write_unlock_bh(&aarp_lock);
}

/* This must run under aarp_lock. */
static struct atalk_addr *__aarp_proxy_find(struct net_device *dev,
					    struct atalk_addr *sa)
{
	int hash = sa->s_node % (AARP_HASH_SIZE - 1);
	struct aarp_entry *a = __aarp_find_entry(proxies[hash], dev, sa);

	return a ? sa : NULL;
}

/*
 * Probe a Phase 1 device or a device that requires its Net:Node to
 * be set via an ioctl.
 */
static void aarp_send_probe_phase1(struct atalk_iface *iface)
{
	struct ifreq atreq;
	struct sockaddr_at *sa = (struct sockaddr_at *)&atreq.ifr_addr;
	const struct net_device_ops *ops = iface->dev->netdev_ops;

	sa->sat_addr.s_node = iface->address.s_node;
	sa->sat_addr.s_net = ntohs(iface->address.s_net);

	/* We pass the Net:Node to the drivers/cards by a Device ioctl. */
	if (!(ops->ndo_do_ioctl(iface->dev, &atreq, SIOCSIFADDR))) {
		ops->ndo_do_ioctl(iface->dev, &atreq, SIOCGIFADDR);
		if (iface->address.s_net != htons(sa->sat_addr.s_net) ||
		    iface->address.s_node != sa->sat_addr.s_node)
			iface->status |= ATIF_PROBE_FAIL;

		iface->address.s_net  = htons(sa->sat_addr.s_net);
		iface->address.s_node = sa->sat_addr.s_node;
	}
}


void aarp_probe_network(struct atalk_iface *atif)
{
	if (atif->dev->type == ARPHRD_LOCALTLK ||
	    atif->dev->type == ARPHRD_PPP)
		aarp_send_probe_phase1(atif);
	else {
		unsigned int count;

		for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) {
			aarp_send_probe(atif->dev, &atif->address);

			/* Defer 1/10th */
			msleep(100);

			if (atif->status & ATIF_PROBE_FAIL)
				break;
		}
	}
}

int aarp_proxy_probe_network(struct atalk_iface *atif, struct atalk_addr *sa)
{
	int hash, retval = -EPROTONOSUPPORT;
	struct aarp_entry *entry;
	unsigned int count;

	/*
	 * we don't currently support LocalTalk or PPP for proxy AARP;
	 * if someone wants to try and add it, have fun
	 */
	if (atif->dev->type == ARPHRD_LOCALTLK ||
	    atif->dev->type == ARPHRD_PPP)
		goto out;

	/*
	 * create a new AARP entry with the flags set to be published --
	 * we need this one to hang around even if it's in use
	 */
	entry = aarp_alloc();
	retval = -ENOMEM;
	if (!entry)
		goto out;

	entry->expires_at = -1;
	entry->status = ATIF_PROBE;
	entry->target_addr.s_node = sa->s_node;
	entry->target_addr.s_net = sa->s_net;
	entry->dev = atif->dev;

	write_lock_bh(&aarp_lock);

	hash = sa->s_node % (AARP_HASH_SIZE - 1);
	entry->next = proxies[hash];
	proxies[hash] = entry;

	for (count = 0; count < AARP_RETRANSMIT_LIMIT; count++) {
		aarp_send_probe(atif->dev, sa);

		/* Defer 1/10th */
		write_unlock_bh(&aarp_lock);
		msleep(100);
		write_lock_bh(&aarp_lock);

		if (entry->status & ATIF_PROBE_FAIL)
			break;
	}

	if (entry->status & ATIF_PROBE_FAIL) {
		entry->expires_at = jiffies - 1; /* free the entry */
		retval = -EADDRINUSE; /* return network full */
	} else { /* clear the probing flag */
		entry->status &= ~ATIF_PROBE;
		retval = 1;
	}

	write_unlock_bh(&aarp_lock);
out:
	return retval;
}

/* Send a DDP frame */
int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb,
		  struct atalk_addr *sa, void *hwaddr)
{
	static char ddp_eth_multicast[ETH_ALEN] =
		{ 0x09, 0x00, 0x07, 0xFF, 0xFF, 0xFF };
	int hash;
	struct aarp_entry *a;

	skb_reset_network_header(skb);

	/* Check for LocalTalk first */
	if (dev->type == ARPHRD_LOCALTLK) {
		struct atalk_addr *at = atalk_find_dev_addr(dev);
		struct ddpehdr *ddp = (struct ddpehdr *)skb->data;
		int ft = 2;

		/*
		 * Compressible ?
		 *
		 * IFF: src_net == dest_net == device_net
		 * (zero matches anything)
		 */

		if ((!ddp->deh_snet || at->s_net == ddp->deh_snet) &&
		    (!ddp->deh_dnet || at->s_net == ddp->deh_dnet)) {
			skb_pull(skb, sizeof(*ddp) - 4);

			/*
			 *	The upper two remaining bytes are the port
			 *	numbers	we just happen to need. Now put the
			 *	length in the lower two.
			 */
			*((__be16 *)skb->data) = htons(skb->len);
			ft = 1;
		}
		/*
		 * Nice and easy. No AARP type protocols occur here so we can
		 * just shovel it out with a 3 byte LLAP header
		 */

		skb_push(skb, 3);
		skb->data[0] = sa->s_node;
		skb->data[1] = at->s_node;
		skb->data[2] = ft;
		skb->dev     = dev;
		goto sendit;
	}

	/* On a PPP link we neither compress nor aarp.  */
	if (dev->type == ARPHRD_PPP) {
		skb->protocol = htons(ETH_P_PPPTALK);
		skb->dev = dev;
		goto sendit;
	}

	/* Non ELAP we cannot do. */
	if (dev->type != ARPHRD_ETHER)
		goto free_it;

	skb->dev = dev;
	skb->protocol = htons(ETH_P_ATALK);
	hash = sa->s_node % (AARP_HASH_SIZE - 1);

	/* Do we have a resolved entry? */
	if (sa->s_node == ATADDR_BCAST) {
		/* Send it */
		ddp_dl->request(ddp_dl, skb, ddp_eth_multicast);
		goto sent;
	}

	write_lock_bh(&aarp_lock);
	a = __aarp_find_entry(resolved[hash], dev, sa);

	if (a) { /* Return 1 and fill in the address */
		a->expires_at = jiffies + (sysctl_aarp_expiry_time * 10);
		ddp_dl->request(ddp_dl, skb, a->hwaddr);
		write_unlock_bh(&aarp_lock);
		goto sent;
	}

	/* Do we have an unresolved entry: This is the less common path */
	a = __aarp_find_entry(unresolved[hash], dev, sa);
	if (a) { /* Queue onto the unresolved queue */
		skb_queue_tail(&a->packet_queue, skb);
		goto out_unlock;
	}

	/* Allocate a new entry */
	a = aarp_alloc();
	if (!a) {
		/* Whoops slipped... good job it's an unreliable protocol 8) */
		write_unlock_bh(&aarp_lock);
		goto free_it;
	}

	/* Set up the queue */
	skb_queue_tail(&a->packet_queue, skb);
	a->expires_at	 = jiffies + sysctl_aarp_resolve_time;
	a->dev		 = dev;
	a->next		 = unresolved[hash];
	a->target_addr	 = *sa;
	a->xmit_count	 = 0;
	unresolved[hash] = a;
	unresolved_count++;

	/* Send an initial request for the address */
	__aarp_send_query(a);

	/*
	 * Switch to fast timer if needed (That is if this is the first
	 * unresolved entry to get added)
	 */

	if (unresolved_count == 1)
		mod_timer(&aarp_timer, jiffies + sysctl_aarp_tick_time);

	/* Now finally, it is safe to drop the lock. */
out_unlock:
	write_unlock_bh(&aarp_lock);

	/* Tell the ddp layer we have taken over for this frame. */
	goto sent;

sendit:
	if (skb->sk)
		skb->priority = skb->sk->sk_priority;
	if (dev_queue_xmit(skb))
		goto drop;
sent:
	return NET_XMIT_SUCCESS;
free_it:
	kfree_skb(skb);
drop:
	return NET_XMIT_DROP;
}
EXPORT_SYMBOL(aarp_send_ddp);

/*
 *	An entry in the aarp unresolved queue has become resolved. Send
 *	all the frames queued under it.
 *
 *	Must run under aarp_lock.
 */
static void __aarp_resolved(struct aarp_entry **list, struct aarp_entry *a,
			    int hash)
{
	struct sk_buff *skb;

	while (*list)
		if (*list == a) {
			unresolved_count--;
			*list = a->next;

			/* Move into the resolved list */
			a->next = resolved[hash];
			resolved[hash] = a;

			/* Kick frames off */
			while ((skb = skb_dequeue(&a->packet_queue)) != NULL) {
				a->expires_at = jiffies +
						sysctl_aarp_expiry_time * 10;
				ddp_dl->request(ddp_dl, skb, a->hwaddr);
			}
		} else
			list = &((*list)->next);
}

/*
 *	This is called by the SNAP driver whenever we see an AARP SNAP
 *	frame. We currently only support Ethernet.
 */
static int aarp_rcv(struct sk_buff *skb, struct net_device *dev,
		    struct packet_type *pt, struct net_device *orig_dev)
{
	struct elapaarp *ea = aarp_hdr(skb);
	int hash, ret = 0;
	__u16 function;
	struct aarp_entry *a;
	struct atalk_addr sa, *ma, da;
	struct atalk_iface *ifa;

	if (!net_eq(dev_net(dev), &init_net))
		goto out0;

	/* We only do Ethernet SNAP AARP. */
	if (dev->type != ARPHRD_ETHER)
		goto out0;

	/* Frame size ok? */
	if (!skb_pull(skb, sizeof(*ea)))
		goto out0;

	function = ntohs(ea->function);

	/* Sanity check fields. */
	if (function < AARP_REQUEST || function > AARP_PROBE ||
	    ea->hw_len != ETH_ALEN || ea->pa_len != AARP_PA_ALEN ||
	    ea->pa_src_zero || ea->pa_dst_zero)
		goto out0;

	/* Looks good. */
	hash = ea->pa_src_node % (AARP_HASH_SIZE - 1);

	/* Build an address. */
	sa.s_node = ea->pa_src_node;
	sa.s_net = ea->pa_src_net;

	/* Process the packet. Check for replies of me. */
	ifa = atalk_find_dev(dev);
	if (!ifa)
		goto out1;

	if (ifa->status & ATIF_PROBE &&
	    ifa->address.s_node == ea->pa_dst_node &&
	    ifa->address.s_net == ea->pa_dst_net) {
		ifa->status |= ATIF_PROBE_FAIL; /* Fail the probe (in use) */
		goto out1;
	}

	/* Check for replies of proxy AARP entries */
	da.s_node = ea->pa_dst_node;
	da.s_net  = ea->pa_dst_net;

	write_lock_bh(&aarp_lock);
	a = __aarp_find_entry(proxies[hash], dev, &da);

	if (a && a->status & ATIF_PROBE) {
		a->status |= ATIF_PROBE_FAIL;
		/*
		 * we do not respond to probe or request packets for
		 * this address while we are probing this address
		 */
		goto unlock;
	}

	switch (function) {
	case AARP_REPLY:
		if (!unresolved_count)	/* Speed up */
			break;

		/* Find the entry.  */
		a = __aarp_find_entry(unresolved[hash], dev, &sa);
		if (!a || dev != a->dev)
			break;

		/* We can fill one in - this is good. */
		ether_addr_copy(a->hwaddr, ea->hw_src);
		__aarp_resolved(&unresolved[hash], a, hash);
		if (!unresolved_count)
			mod_timer(&aarp_timer,
				  jiffies + sysctl_aarp_expiry_time);
		break;

	case AARP_REQUEST:
	case AARP_PROBE:

		/*
		 * If it is my address set ma to my address and reply.
		 * We can treat probe and request the same.  Probe
		 * simply means we shouldn't cache the querying host,
		 * as in a probe they are proposing an address not
		 * using one.
		 *