diff options
Diffstat (limited to 'pfinet/ethernet.c')
-rw-r--r-- | pfinet/ethernet.c | 229 |
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 = ðer_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, ðer_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 (ðer_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 (ðer_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 = ðer_dev; +} |