summaryrefslogtreecommitdiffstats
path: root/net/econet
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-03-13 20:55:15 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-03-13 20:55:15 +0000
commit1471f525455788c20b130690e0f104df451aeb43 (patch)
tree3778beba56558beb9a9548ea5b467e9c44ea966f /net/econet
parente80d2c5456d30ebba5b0eb8a9d33e17d815d4d83 (diff)
Merge with Linux 2.3.51.
Diffstat (limited to 'net/econet')
-rw-r--r--net/econet/Makefile13
-rw-r--r--net/econet/af_econet.c (renamed from net/econet/econet.c)278
-rw-r--r--net/econet/sysctl_net_ec.c43
3 files changed, 199 insertions, 135 deletions
diff --git a/net/econet/Makefile b/net/econet/Makefile
index 367584873..8da31b692 100644
--- a/net/econet/Makefile
+++ b/net/econet/Makefile
@@ -7,17 +7,10 @@
#
# Note 2! The CFLAGS definition is now in the main makefile...
+O_TARGET := econet.o
MOD_LIST_NAME := NET_MISC_MODULES
-O_OBJS :=
-M_OBJS :=
-
-ifeq ($(CONFIG_ECONET),y)
- O_OBJS += econet.o
-else
- ifeq ($(CONFIG_ECONET), m)
- M_OBJS += econet.o
- endif
-endif
+O_OBJS := af_econet.o sysctl_net_ec.o
+M_OBJS := $(O_TARGET)
include $(TOPDIR)/Rules.make
diff --git a/net/econet/econet.c b/net/econet/af_econet.c
index 6e7523eed..091155076 100644
--- a/net/econet/econet.c
+++ b/net/econet/af_econet.c
@@ -2,8 +2,6 @@
* An implementation of the Acorn Econet and AUN protocols.
* Philip Blundell <philb@gnu.org>
*
- * Fixes:
- *
* 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
@@ -14,9 +12,6 @@
#include <linux/config.h>
#include <linux/module.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -45,15 +40,24 @@
#include <net/ip.h>
#include <linux/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
static struct proto_ops econet_ops;
static struct sock *econet_sklist;
-static spinlock_t aun_queue_lock;
+/* Since there are only 256 possible network numbers (or fewer, depends
+ how you count) it makes sense to use a simple lookup table. */
+static struct net_device *net2dev_map[256];
#ifdef CONFIG_ECONET_AUNUDP
+static spinlock_t aun_queue_lock;
static struct socket *udpsock;
#define AUN_PORT 0x8000
+#define EC_PORT_IP 0xd2
+
struct aunhdr
{
unsigned char code; /* AUN magic protocol byte */
@@ -87,48 +91,6 @@ struct ec_cb
#endif
};
-struct ec_device
-{
- struct net_device *dev; /* Real device structure */
- unsigned char station, net; /* Econet protocol address */
- struct ec_device *prev, *next; /* Linked list */
-};
-
-static struct ec_device *edevlist = NULL;
-
-static spinlock_t edevlist_lock;
-
-/*
- * Faster version of edev_get - call with IRQs off
- */
-
-static __inline__ struct ec_device *__edev_get(struct net_device *dev)
-{
- struct ec_device *edev;
- for (edev = edevlist; edev; edev = edev->next)
- {
- if (edev->dev == dev)
- break;
- }
- return edev;
-}
-
-/*
- * Find an Econet device given its `dev' pointer. This is IRQ safe.
- *
- * Against what is it safe? --ANK
- */
-
-static struct ec_device *edev_get(struct net_device *dev)
-{
- struct ec_device *edev;
- unsigned long flags;
- spin_lock_irqsave(&edevlist_lock, flags);
- edev = __edev_get(dev);
- spin_unlock_irqrestore(&edevlist_lock, flags);
- return edev;
-}
-
/*
* Pull a packet from our receive queue and hand it to the user.
* If necessary we block.
@@ -274,7 +236,6 @@ static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
struct sockaddr_ec *saddr=(struct sockaddr_ec *)msg->msg_name;
struct net_device *dev;
struct ec_addr addr;
- struct ec_device *edev;
int err;
unsigned char port, cb;
struct sk_buff *skb;
@@ -318,14 +279,16 @@ static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
}
/* Look for a device with the right network number. */
- for (edev = edevlist; edev && (edev->net != addr.net);
- edev = edev->next);
+ dev = net2dev_map[addr.net];
- /* Bridge? What's that? */
- if (edev == NULL)
- return -ENETUNREACH;
-
- dev = edev->dev;
+ /* If not directly reachable, use some default */
+ if (dev == NULL)
+ {
+ dev = net2dev_map[0];
+ /* No interfaces at all? */
+ if (dev == NULL)
+ return -ENETDOWN;
+ }
if (dev->type == ARPHRD_ECONET)
{
@@ -349,8 +312,15 @@ static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len,
if (dev->hard_header) {
int res;
+ struct ec_framehdr *fh;
err = -EINVAL;
- res = dev->hard_header(skb, dev, ntohs(proto), &addr, NULL, len);
+ res = dev->hard_header(skb, dev, ntohs(proto),
+ &addr, NULL, len);
+ /* Poke in our control byte and
+ port number. Hack, hack. */
+ fh = (struct ec_framehdr *)(skb->data);
+ fh->cb = cb;
+ fh->port = port;
if (sock->type != SOCK_DGRAM) {
skb->tail = skb->data;
skb->len = 0;
@@ -602,7 +572,6 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void *arg)
struct ifreq ifr;
struct ec_device *edev;
struct net_device *dev;
- unsigned long flags;
struct sockaddr_ec *sec;
/*
@@ -620,43 +589,33 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void *arg)
switch (cmd)
{
case SIOCSIFADDR:
- spin_lock_irqsave(&edevlist_lock, flags);
- edev = __edev_get(dev);
+ edev = dev->ec_ptr;
if (edev == NULL)
{
/* Magic up a new one. */
- printk("Get fascist grenade!!!\n");
- *(int*)0 = 0;
- /* Note to author: please, remove this spinlock.
- You do not change edevlist from interrupts,
- so that such aggressive protection is redundant.
-
- BTW not all scans of edev_list are protected.
- */
edev = kmalloc(GFP_KERNEL, sizeof(struct ec_device));
if (edev == NULL) {
printk("af_ec: memory squeeze.\n");
- spin_unlock_irqrestore(&edevlist_lock, flags);
dev_put(dev);
return -ENOMEM;
}
memset(edev, 0, sizeof(struct ec_device));
- edev->dev = dev;
- edev->next = edevlist;
- edevlist = edev;
+ dev->ec_ptr = edev;
}
+ else
+ net2dev_map[edev->net] = NULL;
edev->station = sec->addr.station;
edev->net = sec->addr.net;
- spin_unlock_irqrestore(&edevlist_lock, flags);
+ net2dev_map[sec->addr.net] = dev;
+ if (!net2dev_map[0])
+ net2dev_map[0] = dev;
dev_put(dev);
return 0;
case SIOCGIFADDR:
- spin_lock_irqsave(&edevlist_lock, flags);
- edev = __edev_get(dev);
+ edev = dev->ec_ptr;
if (edev == NULL)
{
- spin_unlock_irqrestore(&edevlist_lock, flags);
dev_put(dev);
return -ENODEV;
}
@@ -664,7 +623,6 @@ static int ec_dev_ioctl(struct socket *sock, unsigned int cmd, void *arg)
sec->addr.station = edev->station;
sec->addr.net = edev->net;
sec->sec_family = AF_ECONET;
- spin_unlock_irqrestore(&edevlist_lock, flags);
dev_put(dev);
if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
return -EFAULT;
@@ -773,8 +731,8 @@ SOCKOPS_WRAP(econet, PF_ECONET);
* Find the listening socket, if any, for the given data.
*/
-static struct sock *ec_listening_socket(unsigned char port, unsigned char
- station, unsigned char net)
+struct sock *ec_listening_socket(unsigned char port, unsigned char
+ station, unsigned char net)
{
struct sock *sk = econet_sklist;
@@ -835,23 +793,43 @@ static void aun_send_response(__u32 addr, unsigned long seq, int code, int cb)
}
/*
+ * Queue a received packet for a socket.
+ */
+
+static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
+ unsigned char stn, unsigned char net,
+ unsigned char cb, unsigned char port)
+{
+ struct ec_cb *eb = (struct ec_cb *)&skb->cb;
+ struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
+
+ memset(sec, 0, sizeof(struct sockaddr_ec));
+ sec->sec_family = AF_ECONET;
+ sec->type = ECTYPE_PACKET_RECEIVED;
+ sec->port = port;
+ sec->cb = cb;
+ sec->addr.net = net;
+ sec->addr.station = stn;
+
+ return sock_queue_rcv_skb(sk, skb);
+}
+
+/*
* Handle incoming AUN packets. Work out if anybody wants them,
* and send positive or negative acknowledgements as appropriate.
*/
static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
{
- struct ec_device *edev = edev_get(skb->dev);
struct iphdr *ip = skb->nh.iph;
unsigned char stn = ntohl(ip->saddr) & 0xff;
struct sock *sk;
struct sk_buff *newskb;
- struct ec_cb *eb;
- struct sockaddr_ec *sec;
+ struct ec_device *edev = skb->dev->ec_ptr;
+
+ if (! edev)
+ goto bad;
- if (edev == NULL)
- return; /* Device not configured for AUN */
-
if ((sk = ec_listening_socket(ah->port, stn, edev->net)) == NULL)
goto bad; /* Nobody wants it */
@@ -864,20 +842,10 @@ static void aun_incoming(struct sk_buff *skb, struct aunhdr *ah, size_t len)
goto bad;
}
- eb = (struct ec_cb *)&newskb->cb;
- sec = (struct sockaddr_ec *)&eb->sec;
- memset(sec, 0, sizeof(struct sockaddr_ec));
- sec->sec_family = AF_ECONET;
- sec->type = ECTYPE_PACKET_RECEIVED;
- sec->port = ah->port;
- sec->cb = ah->cb;
- sec->addr.net = edev->net;
- sec->addr.station = stn;
-
memcpy(skb_put(newskb, len - sizeof(struct aunhdr)), (void *)(ah+1),
len - sizeof(struct aunhdr));
- if (sock_queue_rcv_skb(sk, newskb) < 0)
+ if (ec_queue_packet(sk, newskb, stn, edev->net, ah->cb, ah->port))
{
/* Socket is bankrupt. */
kfree_skb(newskb);
@@ -923,6 +891,7 @@ foundit:
tx_result(skb->sk, eb->cookie, result);
skb_unlink(skb);
spin_unlock_irqrestore(&aun_queue_lock, flags);
+ kfree_skb(skb);
}
/*
@@ -983,7 +952,6 @@ static void aun_data_available(struct sock *sk, int slen)
* drop the packet.
*/
-
static void ab_cleanup(unsigned long h)
{
struct sk_buff *skb;
@@ -1000,6 +968,7 @@ static void ab_cleanup(unsigned long h)
tx_result(skb->sk, eb->cookie,
ECTYPE_TRANSMIT_NOT_PRESENT);
skb_unlink(skb);
+ kfree_skb(skb);
}
skb = newskb;
}
@@ -1054,31 +1023,85 @@ release:
}
#endif
+#ifdef CONFIG_ECONET_NATIVE
+
+/*
+ * Receive an Econet frame from a device.
+ */
+
+static int econet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct ec_framehdr *hdr = (struct ec_framehdr *)skb->data;
+ struct sock *sk;
+ struct ec_device *edev = dev->ec_ptr;
+
+ if (! edev)
+ {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (skb->len < sizeof(struct ec_framehdr))
+ {
+ /* Frame is too small to be any use */
+ kfree_skb(skb);
+ return 0;
+ }
+
+ /* First check for encapsulated IP */
+ if (hdr->port == EC_PORT_IP)
+ {
+ skb->protocol = htons(ETH_P_IP);
+ skb_pull(skb, sizeof(struct ec_framehdr));
+ netif_rx(skb);
+ return 0;
+ }
+
+ sk = ec_listening_socket(hdr->port, hdr->src_stn, hdr->src_net);
+ if (!sk)
+ {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ return ec_queue_packet(sk, skb, edev->net, hdr->src_stn, hdr->cb,
+ hdr->port);
+}
+
+struct packet_type econet_packet_type=
+{
+ 0,
+ NULL,
+ econet_rcv,
+ NULL,
+ NULL
+};
+
+static void econet_hw_initialise(void)
+{
+ econet_packet_type.type = htons(ETH_P_ECONET);
+ dev_add_pack(&econet_packet_type);
+}
+
+#endif
+
static int econet_notifier(struct notifier_block *this, unsigned long msg, void *data)
{
struct net_device *dev = (struct net_device *)data;
struct ec_device *edev;
- unsigned long flags;
switch (msg) {
case NETDEV_UNREGISTER:
/* A device has gone down - kill any data we hold for it. */
- spin_lock_irqsave(&edevlist_lock, flags);
- for (edev = edevlist; edev; edev = edev->next)
+ edev = dev->ec_ptr;
+ if (edev)
{
- if (edev->dev == dev)
- {
- if (edev->prev)
- edev->prev->next = edev->next;
- else
- edevlist = edev->next;
- if (edev->next)
- edev->next->prev = edev->prev;
- kfree(edev);
- break;
- }
+ if (net2dev_map[0] == dev)
+ net2dev_map[0] = 0;
+ net2dev_map[edev->net] = NULL;
+ kfree(edev);
+ dev->ec_ptr = NULL;
}
- spin_unlock_irqrestore(&edevlist_lock, flags);
break;
}
@@ -1091,9 +1114,9 @@ struct notifier_block econet_netdev_notifier={
0
};
-#ifdef MODULE
-void cleanup_module(void)
+void __exit econet_proto_exit(void)
{
+ extern void econet_sysctl_unregister(void);
#ifdef CONFIG_ECONET_AUNUDP
del_timer(&ab_cleanup_timer);
if (udpsock)
@@ -1101,25 +1124,30 @@ void cleanup_module(void)
#endif
unregister_netdevice_notifier(&econet_netdev_notifier);
sock_unregister(econet_family_ops.family);
- return;
+#ifdef CONFIG_SYSCTL
+ econet_sysctl_unregister();
+#endif
}
-int init_module(void)
-#else
-void __init econet_proto_init(struct net_proto *pro)
-#endif
+int __init econet_proto_init(struct net_proto *pro)
{
- spin_lock_init(&edevlist_lock);
+ extern void econet_sysctl_register(void);
spin_lock_init(&aun_queue_lock);
- /* Stop warnings from happening on UP systems. */
- (void)edevlist_lock;
- (void)aun_queue_lock;
sock_register(&econet_family_ops);
#ifdef CONFIG_ECONET_AUNUDP
aun_udp_initialise();
#endif
+#ifdef CONFIG_ECONET_NATIVE
+ econet_hw_initialise();
+#endif
register_netdevice_notifier(&econet_netdev_notifier);
-#ifdef MODULE
- return 0;
+#ifdef CONFIG_SYSCTL
+ econet_sysctl_register();
#endif
+ return 0;
}
+
+#ifdef MODULE
+module_init(econet_proto_init);
+module_exit(econet_proto_exit);
+#endif
diff --git a/net/econet/sysctl_net_ec.c b/net/econet/sysctl_net_ec.c
new file mode 100644
index 000000000..9790a3a9b
--- /dev/null
+++ b/net/econet/sysctl_net_ec.c
@@ -0,0 +1,43 @@
+/*
+ * An implementation of the Acorn Econet and AUN protocols.
+ * Philip Blundell <philb@gnu.org>
+ *
+ * Fixes:
+ *
+ * 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 <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table econet_table[] = {
+ {0}
+};
+
+static struct ctl_table_header *econet_sysctl_header;
+
+static ctl_table econet_net_table[] = {
+ {NET_ECONET, "econet", NULL, 0, 0555, econet_table},
+ {0}
+};
+
+static ctl_table econet_root_table[] = {
+ {CTL_NET, "net", NULL, 0, 0555, econet_net_table},
+ {0}
+};
+
+void econet_sysctl_register(void)
+{
+ econet_sysctl_header = register_sysctl_table(econet_root_table, 0);
+}
+
+#ifdef MODULE
+void econet_sysctl_unregister(void)
+{
+ unregister_sysctl_table(econet_sysctl_header);
+}
+#endif /* MODULE */