diff options
author | Thomas Bushnell <thomas@gnu.org> | 1999-04-26 05:45:37 +0000 |
---|---|---|
committer | Thomas Bushnell <thomas@gnu.org> | 1999-04-26 05:45:37 +0000 |
commit | 195a918c4a64e79bfccb585801aeec0077d68a24 (patch) | |
tree | b7fb8cd4955b8302a6d028c4f3bd6bad4526cb13 /linux/dev/glue/net.c | |
parent | 063bab9f9919bd7460386ed11a15e7f67673077e (diff) | |
download | gnumach-195a918c4a64e79bfccb585801aeec0077d68a24.tar.gz gnumach-195a918c4a64e79bfccb585801aeec0077d68a24.tar.bz2 gnumach-195a918c4a64e79bfccb585801aeec0077d68a24.zip |
1999-02-04 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
* device/kmsg.c (kmsginit): Add a missing semicolon.
(kmsggetstat): Fix typos,
DEV_GET_DEVICE_SIZE -> DEV_GET_SIZE_DEVICE_SIZE and
DEV_GET_RECORD_SIZE -> DEV_GET_SIZE_RECORD_SIZE.
(kmsg_putchar): Fix a typo kmsg_done_init -> kmsg_init_done.
* linux/dev/glue/block.c (device_get_status): Allocate a hd_geometry
on the stack.
* linux/dev/drivers/block/ide.c: New file.
* linux/dev/drivers/scsi/sd_ioctl.c: New file.
1999-02-01 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
* linux/dev/glue/block.c (dev_getstat): Fix a missing `struct'.
* device/cons.c (cninit): Don't call kmsginit.
* kmsg.c (kmsg_buffer): Defined as static.
(kmsg_write_offset): Likewise.
(kmsg_read_offset): Likewise.
(kmsg_read_queue): Likewise.
(kmsg_in_use): Likewise.
(kmsg_lock): Likewise.
(kmsginit): Likewise, and call queue_init instead of setting
PREV and NEXT manually.
(kmsg_done_init): New variable.
(kmsg_putchar): Call kmsginit if not initialized yet.
(kmsggetstat): New function.
* kmsg.h (kmsggetstat): Add the prototype.
* i386/i386at/kd_event.c (kbdgetstat): Handle DEV_GET_SIZE.
(kbdread): Check if the amount a user specify is a multiple
of sizeof(kd_event).
* i386/i386at/kd_mouse.c (mousegetstat): New function.
(mouseread): Check if the amount a user specify is a multiple
of sizeof(kd_event).
* i386/i386at/conf.c (dev_name_list): Set the mouse getstat entry
to mousegetstat and the kmsg getstat entry to kmsggetstat.
Use MACH_COM instead of NCOM to determine if com is used.
Use MACH_LPR instead of NLPR to determine if lpr is used.
* configure.in (--enable-com): New option.
(--enable-lpr): Likewise.
* Makefile.in (enable_com): New variable.
(enable_lpr): Likewise.
* i386/Makefrag (i386at-files): Remove com.c.
(objfiles): Add com.o if enable_com is yes, and lpr.o if enable_lpr
is yes.
1999-01-24 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
* device/kmsg.h (KMSGBUFSIZE): Deleted.
* device/kmsg.c: Rewritten completely to provide stream interface.
* linux/dev/glue/block.c (device_getstat): Added V_GETPARMS support.
* config.guess: New version from automake-1.4.
* config.sub: Likewise.
* install-sh: Likewise.
1998-12-30 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
* linux/dev/glue/net.c (dev_alloc_skb): Change the skb arragement.
(dev_kfree_skb): Free only skb.
(device_write): Keep skb elements up-to-date.
1998-12-02 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
* configure.in: Fix linuxdev option handling.
* linux/Drivers.in: Remove linuxdev option and fix linking files.
* linux/Makefile.in: Replace @DEFS@ with -DLINUX_DEV.
* linux/dev/arch/i386/kernel/irq.c: Include missing header files.
* linux/dev/arch/i386/kernel/setup.c: Include <device-drivers.h>.
* linux/dev/glue/kmem.c: Add printf declaration.
* linux/dev/glue/misc.c: Include <linux/types.h>.
* linux/dev/init/main.c: Call linux_sched_init instead of sched_init.
* linux/dev/kernel/sched.c: Add timer_bh declaration.
(tqueue_bh): Fix the argument.
(linux_sched_init): Renamed from sched_init.
1998-11-30 OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
Clean up linux emulation code to make it architecture-independent
as much as possible.
* linux: Renamed from linuxdev.
* Makefile.in (objfiles): Add linux.o instead of linuxdev.o.
(MAKE): New variable. Used for the linux.o target.
* configure.in: Add AC_CHECK_TOOL(MAKE, make).
* i386/i386/spl.h: Include <i386/ipl.h>, for compatibility with
OSF Mach 3.0. Suggested by Elgin Lee <ehl@funghi.com>.
* linux/src: Renamed from linux/linux.
* linux/dev: Renamed from linux/mach.
* linux/Drivers.in (AC_INIT): Use dev/include/linux/autoconf.h,
instead of mach/include/linux/autoconf.h.
* Makefile.in (all): Target ../linux.o instead of ../linuxdev.o.
* linux/dev/drivers/block/genhd.c: Include <machine/spl.h> instead
of <i386/ipl.h>.
* linux/dev/drivers/net/auto_irq.c: Remove unneeded header files,
<i386/ipl.h> and <i386/pic.h>.
* linux/dev/init/main.c: Many i386-dependent codes moved to ...
* linux/dev/arch/i386/irq.c: ... here.
* linux/dev/arch/i386/setup.c: New file.
* linux/dev/arch/i386/linux_emul.h: Likewise.
* linux/dev/arch/i386/glue/timer.c: Merged into sched.c.
* linux/dev/arch/i386/glue/sched.c: Include <machine/spl.h> instead
of <i386/ipl.h>, and moved to ...
* linux/dev/kernel/sched.c: ... here.
* linux/dev/arch/i386/glue/block.c: Include <machine/spl.h> and
<linux_emul.h>, instead of i386-dependent header files, and
moved to ...
* linux/dev/glue/blocl.c: ... here.
* linux/dev/arch/i386/glue/net.c: Include <machine/spl.h> and
<linux_emul.h>, instead of i386-dependent header files, and
moved to ...
* linux/dev/glue/net.c: ... here.
* linux/dev/arch/i386/glue/misc.c: Remove `x86' and moved to ...
* linux/dev/glue/misc.c: ... here.
* linux/dev/arch/i386/glue/kmem.c: Moved to ...
* linux/dev/glue/kmem.c: ... here.
Diffstat (limited to 'linux/dev/glue/net.c')
-rw-r--r-- | linux/dev/glue/net.c | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/linux/dev/glue/net.c b/linux/dev/glue/net.c new file mode 100644 index 00000000..93556db2 --- /dev/null +++ b/linux/dev/glue/net.c @@ -0,0 +1,530 @@ +/* + * Linux network driver support. + * + * Copyright (C) 1996 The University of Utah and the Computer Systems + * Laboratory at the University of Utah (CSL) + * + * 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, or (at your option) + * any later version. + * + * This program 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, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Shantanu Goel, University of Utah CSL + */ + +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Ethernet-type device handling. + * + * Version: @(#)eth.c 1.0.7 05/25/93 + * + * Authors: Ross Biro, <bir7@leland.Stanford.Edu> + * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> + * Mark Evans, <evansmp@uhura.aston.ac.uk> + * Florian La Roche, <rzsfl@rz.uni-sb.de> + * Alan Cox, <gw4pts@gw4pts.ampr.org> + * + * Fixes: + * Mr Linux : Arp problems + * Alan Cox : Generic queue tidyup (very tiny here) + * Alan Cox : eth_header ntohs should be htons + * Alan Cox : eth_rebuild_header missing an htons and + * minor other things. + * Tegge : Arp bug fixes. + * Florian : Removed many unnecessary functions, code cleanup + * and changes for new arp and skbuff. + * Alan Cox : Redid header building to reflect new format. + * Alan Cox : ARP only when compiled with CONFIG_INET + * Greg Page : 802.2 and SNAP stuff. + * Alan Cox : MAC layer pointers/new format. + * Paul Gortmaker : eth_copy_and_sum shouldn't csum padding. + * Alan Cox : Protect against forwarding explosions with + * older network drivers and IFF_ALLMULTI + * + * 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 <sys/types.h> +#include <machine/spl.h> + +#include <mach/mach_types.h> +#include <mach/kern_return.h> +#include <mach/mig_errors.h> +#include <mach/port.h> +#include <mach/vm_param.h> +#include <mach/notify.h> + +#include <ipc/ipc_port.h> +#include <ipc/ipc_space.h> + +#include <vm/vm_map.h> +#include <vm/vm_kern.h> +#include <vm/vm_page.h> + +#include <device/device_types.h> +#include <device/device_port.h> +#include <device/if_hdr.h> +#include <device/if_ether.h> +#include <device/if_hdr.h> +#include <device/net_io.h> +#include "device_reply.h" + +#include <linux_emul.h> + +#define MACH_INCLUDE +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/malloc.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> + +extern int linux_intr_pri; + +/* One of these is associated with each instance of a device. */ +struct net_data +{ + ipc_port_t port; /* device port */ + struct ifnet ifnet; /* Mach ifnet structure (needed for filters) */ + struct device device; /* generic device structure */ + struct linux_device *dev; /* Linux network device structure */ +}; + +/* List of sk_buffs waiting to be freed. */ +static struct sk_buff_head skb_done_list; + +/* Forward declarations. */ + +extern struct device_emulation_ops linux_net_emulation_ops; + +static int print_packet_size = 0; + +/* Linux kernel network support routines. */ + +/* Requeue packet SKB for transmission after the interface DEV + has timed out. The priority of the packet is PRI. + In Mach, we simply drop the packet like the native drivers. */ +void +dev_queue_xmit (struct sk_buff *skb, struct linux_device *dev, int pri) +{ + dev_kfree_skb (skb, FREE_WRITE); +} + +/* Close the device DEV. */ +int +dev_close (struct linux_device *dev) +{ + return 0; +} + +/* Network software interrupt handler. */ +void +net_bh (void) +{ + int len; + struct sk_buff *skb; + struct linux_device *dev; + + /* Start transmission on interfaces. */ + for (dev = dev_base; dev; dev = dev->next) + { + if (dev->base_addr && dev->base_addr != 0xffe0) + while (1) + { + skb = skb_dequeue (&dev->buffs[0]); + if (skb) + { + len = skb->len; + if ((*dev->hard_start_xmit) (skb, dev)) + { + skb_queue_head (&dev->buffs[0], skb); + mark_bh (NET_BH); + break; + } + else if (print_packet_size) + printf ("net_bh: length %d\n", len); + } + else + break; + } + } +} + +/* Free all sk_buffs on the done list. + This routine is called by the iodone thread in ds_routines.c. */ +void +free_skbuffs () +{ + struct sk_buff *skb; + + while (1) + { + skb = skb_dequeue (&skb_done_list); + if (skb) + { + if (skb->copy) + { + vm_map_copy_discard (skb->copy); + skb->copy = NULL; + } + if (IP_VALID (skb->reply)) + { + ds_device_write_reply (skb->reply, skb->reply_type, 0, skb->len); + skb->reply = IP_NULL; + } + dev_kfree_skb (skb, FREE_WRITE); + } + else + break; + } +} + +/* Allocate an sk_buff with SIZE bytes of data space. */ +struct sk_buff * +alloc_skb (unsigned int size, int priority) +{ + return dev_alloc_skb (size); +} + +/* Free SKB. */ +void +kfree_skb (struct sk_buff *skb, int priority) +{ + dev_kfree_skb (skb, priority); +} + +/* Allocate an sk_buff with SIZE bytes of data space. */ +struct sk_buff * +dev_alloc_skb (unsigned int size) +{ + struct sk_buff *skb; + unsigned char *bptr; + int len = size; + + size = (size + 15) & ~15; + size += sizeof (struct sk_buff); + + bptr = linux_kmalloc (size, GFP_KERNEL); + if (bptr == NULL) + return NULL; + + /* XXX: In Mach, a sk_buff is located at the head, + while it's located at the tail in Linux. */ + skb = bptr; + skb->dev = NULL; + skb->reply = IP_NULL; + skb->copy = NULL; + skb->len = 0; + skb->prev = skb->next = NULL; + skb->list = NULL; + skb->data = bptr + sizeof (struct sk_buff); + skb->tail = skb->data; + skb->head = skb->data; + skb->end = skb->data + len; + + return skb; +} + +/* Free the sk_buff SKB. */ +void +dev_kfree_skb (struct sk_buff *skb, int mode) +{ + unsigned flags; + extern void *io_done_list; + + /* Queue sk_buff on done list if there is a + page list attached or we need to send a reply. + Wakeup the iodone thread to process the list. */ + if (skb->copy || IP_VALID (skb->reply)) + { + skb_queue_tail (&skb_done_list, skb); + save_flags (flags); + thread_wakeup ((event_t) & io_done_list); + restore_flags (flags); + return; + } + linux_kfree (skb); +} + +/* Accept packet SKB received on an interface. */ +void +netif_rx (struct sk_buff *skb) +{ + ipc_kmsg_t kmsg; + struct ether_header *eh; + struct packet_header *ph; + struct linux_device *dev = skb->dev; + + assert (skb != NULL); + + if (print_packet_size) + printf ("netif_rx: length %ld\n", skb->len); + + /* Allocate a kernel message buffer. */ + kmsg = net_kmsg_get (); + if (!kmsg) + { + dev_kfree_skb (skb, FREE_READ); + return; + } + + /* Copy packet into message buffer. */ + eh = (struct ether_header *) (net_kmsg (kmsg)->header); + ph = (struct packet_header *) (net_kmsg (kmsg)->packet); + memcpy (eh, skb->data, sizeof (struct ether_header)); + memcpy (ph + 1, skb->data + sizeof (struct ether_header), + skb->len - sizeof (struct ether_header)); + ph->type = eh->ether_type; + ph->length = (skb->len - sizeof (struct ether_header) + + sizeof (struct packet_header)); + + dev_kfree_skb (skb, FREE_READ); + + /* Pass packet up to the microkernel. */ + net_packet (&dev->net_data->ifnet, kmsg, + ph->length, ethernet_priority (kmsg)); +} + +/* Mach device interface routines. */ + +/* Return a send right associated with network device ND. */ +static ipc_port_t +dev_to_port (void *nd) +{ + return (nd + ? ipc_port_make_send (((struct net_data *) nd)->port) + : IP_NULL); +} + +static io_return_t +device_open (ipc_port_t reply_port, mach_msg_type_name_t reply_port_type, + dev_mode_t mode, char *name, device_t *devp) +{ + io_return_t err = D_SUCCESS; + ipc_port_t notify; + struct ifnet *ifp; + struct linux_device *dev; + struct net_data *nd; + + /* Search for the device. */ + for (dev = dev_base; dev; dev = dev->next) + if (dev->base_addr + && dev->base_addr != 0xffe0 + && !strcmp (name, dev->name)) + break; + if (!dev) + return D_NO_SUCH_DEVICE; + + /* Allocate and initialize device data if this is the first open. */ + nd = dev->net_data; + if (!nd) + { + dev->net_data = nd = ((struct net_data *) + kalloc (sizeof (struct net_data))); + if (!nd) + { + err = D_NO_MEMORY; + goto out; + } + nd->dev = dev; + nd->device.emul_data = nd; + nd->device.emul_ops = &linux_net_emulation_ops; + nd->port = ipc_port_alloc_kernel (); + if (nd->port == IP_NULL) + { + err = KERN_RESOURCE_SHORTAGE; + goto out; + } + ipc_kobject_set (nd->port, (ipc_kobject_t) & nd->device, IKOT_DEVICE); + notify = ipc_port_make_sonce (nd->port); + ip_lock (nd->port); + ipc_port_nsrequest (nd->port, 1, notify, ¬ify); + assert (notify == IP_NULL); + + ifp = &nd->ifnet; + ifp->if_unit = dev->name[strlen (dev->name) - 1] - '0'; + ifp->if_flags = IFF_UP | IFF_RUNNING; + ifp->if_mtu = dev->mtu; + ifp->if_header_size = dev->hard_header_len; + ifp->if_header_format = dev->type; + ifp->if_address_size = dev->addr_len; + ifp->if_address = dev->dev_addr; + if_init_queues (ifp); + + if (dev->open) + { + linux_intr_pri = SPL6; + if ((*dev->open) (dev)) + err = D_NO_SUCH_DEVICE; + } + + out: + if (err) + { + if (nd) + { + if (nd->port != IP_NULL) + { + ipc_kobject_set (nd->port, IKO_NULL, IKOT_NONE); + ipc_port_dealloc_kernel (nd->port); + } + kfree ((vm_offset_t) nd, sizeof (struct net_data)); + nd = NULL; + dev->net_data = NULL; + } + } + else + { + dev->flags |= LINUX_IFF_UP | LINUX_IFF_RUNNING; + skb_queue_head_init (&dev->buffs[0]); + } + if (IP_VALID (reply_port)) + ds_device_open_reply (reply_port, reply_port_type, + err, dev_to_port (nd)); + return MIG_NO_REPLY; + } + + *devp = &nd->device; + return D_SUCCESS; +} + +static io_return_t +device_write (void *d, ipc_port_t reply_port, + mach_msg_type_name_t reply_port_type, dev_mode_t mode, + recnum_t bn, io_buf_ptr_t data, unsigned int count, + int *bytes_written) +{ + unsigned char *p; + int i, amt, skblen, s; + io_return_t err = 0; + vm_map_copy_t copy = (vm_map_copy_t) data; + struct net_data *nd = d; + struct linux_device *dev = nd->dev; + struct sk_buff *skb; + + if (count == 0 || count > dev->mtu + dev->hard_header_len) + return D_INVALID_SIZE; + + /* Allocate a sk_buff. */ + amt = PAGE_SIZE - (copy->offset & PAGE_MASK); + skblen = (amt >= count) ? 0 : count; + skb = dev_alloc_skb (skblen); + if (!skb) + return D_NO_MEMORY; + + /* Copy user data. This is only required if it spans multiple pages. */ + if (skblen == 0) + { + assert (copy->cpy_npages == 1); + + skb->copy = copy; + skb->data = ((void *) copy->cpy_page_list[0]->phys_addr + + (copy->offset & PAGE_MASK)); + skb->len = count; + skb->head = skb->data; + skb->tail = skb->data + skb->len; + skb->end = skb->tail; + } + else + { + skb->len = skblen; + skb->tail = skb->data + skblen; + skb->end = skb->tail; + + memcpy (skb->data, + ((void *) copy->cpy_page_list[0]->phys_addr + + (copy->offset & PAGE_MASK)), + amt); + count -= amt; + p = skb->data + amt; + for (i = 1; count > 0 && i < copy->cpy_npages; i++) + { + amt = PAGE_SIZE; + if (amt > count) + amt = count; + memcpy (p, (void *) copy->cpy_page_list[i]->phys_addr, amt); + count -= amt; + p += amt; + } + + assert (count == 0); + + vm_map_copy_discard (copy); + } + + skb->dev = dev; + skb->reply = reply_port; + skb->reply_type = reply_port_type; + + /* Queue packet for transmission and schedule a software interrupt. */ + s = splimp (); + if (dev->buffs[0].next != (struct sk_buff *) &dev->buffs[0] + || (*dev->hard_start_xmit) (skb, dev)) + { + __skb_queue_tail (&dev->buffs[0], skb); + mark_bh (NET_BH); + } + splx (s); + + return MIG_NO_REPLY; +} + +static io_return_t +device_get_status (void *d, dev_flavor_t flavor, dev_status_t status, + mach_msg_type_number_t *count) +{ + return net_getstat (&((struct net_data *) d)->ifnet, flavor, status, count); +} + +static io_return_t +device_set_filter (void *d, ipc_port_t port, int priority, + filter_t * filter, unsigned filter_count) +{ + return net_set_filter (&((struct net_data *) d)->ifnet, + port, priority, filter, filter_count); +} + +struct device_emulation_ops linux_net_emulation_ops = +{ + NULL, + NULL, + dev_to_port, + device_open, + NULL, + device_write, + NULL, + NULL, + NULL, + NULL, + device_get_status, + device_set_filter, + NULL, + NULL, + NULL, + NULL +}; + +/* Do any initialization required for network devices. */ +void +linux_net_emulation_init () +{ + skb_queue_head_init (&skb_done_list); +} |