aboutsummaryrefslogtreecommitdiff
path: root/pfinet/ethernet.c
diff options
context:
space:
mode:
Diffstat (limited to 'pfinet/ethernet.c')
-rw-r--r--pfinet/ethernet.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/pfinet/ethernet.c b/pfinet/ethernet.c
new file mode 100644
index 00000000..85162fd8
--- /dev/null
+++ b/pfinet/ethernet.c
@@ -0,0 +1,229 @@
+/*
+ Copyright (C) 1995, 1996, 1998, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <device/device.h>
+#include <device/net_status.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <error.h>
+
+#include "pfinet.h"
+
+device_t ether_port;
+
+struct port_class *etherreadclass;
+struct port_info *readpt;
+mach_port_t readptname;
+
+struct device ether_dev;
+
+struct enet_statistics retbuf;
+
+
+/* Mach doesn't provide this. DAMN. */
+struct enet_statistics *
+ethernet_get_stats (struct device *dev)
+{
+ return &retbuf;
+}
+
+int
+ethernet_stop (struct device *dev)
+{
+ return 0;
+}
+
+void
+ethernet_set_multi (struct device *dev, int numaddrs, void *addrs)
+{
+ assert (numaddrs == 0);
+}
+
+static short ether_filter[] =
+{
+ NETF_PUSHLIT | NETF_NOP,
+ 1
+};
+static int ether_filter_len = sizeof (ether_filter) / sizeof (short);
+
+static struct port_bucket *etherport_bucket;
+
+
+any_t
+ethernet_thread (any_t arg)
+{
+ ports_manage_port_operations_one_thread (etherport_bucket,
+ ethernet_demuxer,
+ 0);
+ return 0;
+}
+
+int
+ethernet_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ struct net_rcv_msg *msg = (struct net_rcv_msg *) inp;
+ struct sk_buff *skb;
+ int datalen;
+
+ if (inp->msgh_id != NET_RCV_MSG_ID)
+ return 0;
+
+ if (inp->msgh_local_port != readptname)
+ {
+ if (inp->msgh_remote_port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), inp->msgh_remote_port);
+ return 1;
+ }
+
+ datalen = ETH_HLEN
+ + msg->packet_type.msgt_number - sizeof (struct packet_header);
+
+ mutex_lock (&global_lock);
+ skb = alloc_skb (datalen, GFP_ATOMIC);
+ skb->len = datalen;
+ skb->dev = &ether_dev;
+
+ /* Copy the two parts of the frame into the buffer. */
+ bcopy (msg->header, skb->data, ETH_HLEN);
+ bcopy (msg->packet + sizeof (struct packet_header),
+ skb->data + ETH_HLEN,
+ datalen - ETH_HLEN);
+
+ /* Drop it on the queue. */
+ netif_rx (skb);
+ mutex_unlock (&global_lock);
+
+ return 1;
+}
+
+
+int
+ethernet_open (struct device *dev)
+{
+ error_t err;
+
+ if (ether_port != MACH_PORT_NULL)
+ return 0;
+
+ etherreadclass = ports_create_class (0, 0);
+ errno = ports_create_port (etherreadclass, etherport_bucket,
+ sizeof (struct port_info), &readpt);
+ assert_perror (errno);
+ readptname = ports_get_right (readpt);
+ mach_port_insert_right (mach_task_self (), readptname, readptname,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ mach_port_set_qlimit (mach_task_self (), readptname, MACH_PORT_QLIMIT_MAX);
+
+ err = device_open (master_device, D_WRITE | D_READ, dev->name, &ether_port);
+ if (err)
+ error (2, err, "%s", dev->name);
+
+ err = device_set_filter (ether_port, ports_get_right (readpt),
+ MACH_MSG_TYPE_MAKE_SEND, 0,
+ ether_filter, ether_filter_len);
+ if (err)
+ error (2, err, "%s", dev->name);
+ cthread_detach (cthread_fork (ethernet_thread, 0));
+ return 0;
+}
+
+
+/* Transmit an ethernet frame */
+int
+ethernet_xmit (struct sk_buff *skb, struct device *dev)
+{
+ u_int count;
+ int err;
+
+ err = device_write (ether_port, D_NOWAIT, 0, skb->data, skb->len, &count);
+ assert (err == 0);
+ assert (count == skb->len);
+ dev_kfree_skb (skb, FREE_WRITE);
+ return 0;
+}
+
+void
+setup_ethernet_device (char *name)
+{
+ struct net_status netstat;
+ u_int count;
+ int net_address[2];
+ int i;
+ error_t err;
+
+ etherport_bucket = ports_create_bucket ();
+
+ /* Interface buffers. */
+ ether_dev.name = name;
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init (&ether_dev.buffs[i]);
+
+ /* Functions */
+ ether_dev.open = ethernet_open;
+ ether_dev.stop = ethernet_stop;
+ ether_dev.hard_start_xmit = ethernet_xmit;
+ ether_dev.hard_header = eth_header;
+ ether_dev.rebuild_header = eth_rebuild_header;
+ ether_dev.type_trans = eth_type_trans;
+ ether_dev.get_stats = ethernet_get_stats;
+ ether_dev.set_multicast_list = ethernet_set_multi;
+
+ /* Some more fields */
+ ether_dev.type = ARPHRD_ETHER;
+ ether_dev.hard_header_len = sizeof (struct ethhdr);
+ ether_dev.addr_len = ETH_ALEN;
+ for (i = 0; i < 6; i++)
+ ether_dev.broadcast[i] = 0xff;
+ ether_dev.flags = IFF_BROADCAST | IFF_MULTICAST;
+ ether_dev.family = AF_INET; /* hmm. */
+ ether_dev.pa_addr = ether_dev.pa_brdaddr = ether_dev.pa_mask = 0;
+ ether_dev.pa_alen = sizeof (unsigned long);
+
+ ethernet_open (&ether_dev);
+
+ /* Fetch hardware information */
+ count = NET_STATUS_COUNT;
+ err = device_get_status (ether_port, NET_STATUS,
+ (dev_status_t) &netstat, &count);
+ if (err)
+ error (2, err, "%s: Cannot get device status", name);
+ ether_dev.mtu = netstat.max_packet_size - ether_dev.hard_header_len;
+ assert (netstat.header_format == HDR_ETHERNET);
+ assert (netstat.header_size == ETH_HLEN);
+ assert (netstat.address_size == ETH_ALEN);
+
+ count = 2;
+ assert (count * sizeof (int) >= ETH_ALEN);
+ err = device_get_status (ether_port, NET_ADDRESS, net_address, &count);
+ if (err)
+ error (2, err, "%s: Cannot get hardware Ethernet address", name);
+ net_address[0] = ntohl (net_address[0]);
+ net_address[1] = ntohl (net_address[1]);
+ bcopy (net_address, ether_dev.dev_addr, ETH_ALEN);
+
+ /* That should be enough. */
+
+ ether_dev.next = dev_base;
+ dev_base = &ether_dev;
+}