|
|
1.1 ! root 1: /* ! 2: * Linux network driver support. ! 3: * ! 4: * Copyright (C) 1996 The University of Utah and the Computer Systems ! 5: * Laboratory at the University of Utah (CSL) ! 6: * ! 7: * This program is free software; you can redistribute it and/or modify ! 8: * it under the terms of the GNU General Public License as published by ! 9: * the Free Software Foundation; either version 2, or (at your option) ! 10: * any later version. ! 11: * ! 12: * This program is distributed in the hope that it will be useful, ! 13: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 15: * GNU General Public License for more details. ! 16: * ! 17: * You should have received a copy of the GNU General Public License ! 18: * along with this program; if not, write to the Free Software ! 19: * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ! 20: * ! 21: * Author: Shantanu Goel, University of Utah CSL ! 22: */ ! 23: ! 24: /* ! 25: * INET An implementation of the TCP/IP protocol suite for the LINUX ! 26: * operating system. INET is implemented using the BSD Socket ! 27: * interface as the means of communication with the user level. ! 28: * ! 29: * Ethernet-type device handling. ! 30: * ! 31: * Version: @(#)eth.c 1.0.7 05/25/93 ! 32: * ! 33: * Authors: Ross Biro, <[email protected]> ! 34: * Fred N. van Kempen, <[email protected]> ! 35: * Mark Evans, <[email protected]> ! 36: * Florian La Roche, <[email protected]> ! 37: * Alan Cox, <[email protected]> ! 38: * ! 39: * Fixes: ! 40: * Mr Linux : Arp problems ! 41: * Alan Cox : Generic queue tidyup (very tiny here) ! 42: * Alan Cox : eth_header ntohs should be htons ! 43: * Alan Cox : eth_rebuild_header missing an htons and ! 44: * minor other things. ! 45: * Tegge : Arp bug fixes. ! 46: * Florian : Removed many unnecessary functions, code cleanup ! 47: * and changes for new arp and skbuff. ! 48: * Alan Cox : Redid header building to reflect new format. ! 49: * Alan Cox : ARP only when compiled with CONFIG_INET ! 50: * Greg Page : 802.2 and SNAP stuff. ! 51: * Alan Cox : MAC layer pointers/new format. ! 52: * Paul Gortmaker : eth_copy_and_sum shouldn't csum padding. ! 53: * Alan Cox : Protect against forwarding explosions with ! 54: * older network drivers and IFF_ALLMULTI ! 55: * ! 56: * This program is free software; you can redistribute it and/or ! 57: * modify it under the terms of the GNU General Public License ! 58: * as published by the Free Software Foundation; either version ! 59: * 2 of the License, or (at your option) any later version. ! 60: */ ! 61: ! 62: #include <sys/types.h> ! 63: ! 64: #include <mach/mach_types.h> ! 65: #include <mach/kern_return.h> ! 66: #include <mach/mig_errors.h> ! 67: #include <mach/port.h> ! 68: #include <mach/vm_param.h> ! 69: #include <mach/notify.h> ! 70: ! 71: #include <ipc/ipc_port.h> ! 72: #include <ipc/ipc_space.h> ! 73: ! 74: #include <vm/vm_map.h> ! 75: #include <vm/vm_kern.h> ! 76: #include <vm/vm_page.h> ! 77: ! 78: #include <device/device_types.h> ! 79: #include <device/device_port.h> ! 80: #include <device/if_hdr.h> ! 81: #include <device/if_ether.h> ! 82: #include <device/if_hdr.h> ! 83: #include <device/net_io.h> ! 84: #include "device_reply.h" ! 85: ! 86: #include <i386at/dev_hdr.h> ! 87: #include <i386at/device_emul.h> ! 88: ! 89: #include <i386at/gpl/linux/linux_emul.h> ! 90: ! 91: #define MACH_INCLUDE ! 92: #include <linux/kernel.h> ! 93: #include <linux/sched.h> ! 94: #include <linux/string.h> ! 95: #include <linux/errno.h> ! 96: #include <linux/delay.h> ! 97: #include <linux/interrupt.h> ! 98: #include <linux/malloc.h> ! 99: #include <linux/netdevice.h> ! 100: #include <linux/etherdevice.h> ! 101: ! 102: /* One of these is associated with each instance of a device. */ ! 103: struct net_data ! 104: { ! 105: ipc_port_t port; /* device port */ ! 106: struct ifnet ifnet; /* Mach ifnet structure (needed for filters) */ ! 107: struct device device; /* generic device structure */ ! 108: struct linux_device *dev; /* Linux network device structure */ ! 109: }; ! 110: ! 111: /* List of sk_buffs waiting to be freed. */ ! 112: static struct sk_buff_head skb_done_list; ! 113: ! 114: /* Forward declarations. */ ! 115: ! 116: extern struct device_emulation_ops linux_net_emulation_ops; ! 117: ! 118: static int print_packet_size = 0; ! 119: ! 120: /* Linux kernel network support routines. */ ! 121: ! 122: /* Requeue packet SKB for transmission after the interface DEV ! 123: has timed out. The priority of the packet is PRI. ! 124: In Mach, we simply drop the packet like the native drivers. */ ! 125: void ! 126: dev_queue_xmit (struct sk_buff *skb, struct linux_device *dev, int pri) ! 127: { ! 128: dev_kfree_skb (skb, FREE_WRITE); ! 129: } ! 130: ! 131: /* Close the device DEV. */ ! 132: int ! 133: dev_close (struct linux_device *dev) ! 134: { ! 135: return 0; ! 136: } ! 137: ! 138: /* Network software interrupt handler. */ ! 139: void ! 140: net_bh (void *xxx) ! 141: { ! 142: int len; ! 143: struct sk_buff *skb; ! 144: struct linux_device *dev; ! 145: ! 146: /* Start transmission on interfaces. */ ! 147: for (dev = dev_base; dev; dev = dev->next) ! 148: { ! 149: if (dev->base_addr && dev->base_addr != 0xffe0) ! 150: while (1) ! 151: { ! 152: skb = skb_dequeue (&dev->buffs[0]); ! 153: if (skb) ! 154: { ! 155: len = skb->len; ! 156: if ((*dev->hard_start_xmit) (skb, dev)) ! 157: { ! 158: skb_queue_head (&dev->buffs[0], skb); ! 159: mark_bh (NET_BH); ! 160: break; ! 161: } ! 162: else if (print_packet_size) ! 163: printf ("net_bh: length %d\n", len); ! 164: } ! 165: else ! 166: break; ! 167: } ! 168: } ! 169: } ! 170: ! 171: /* Free all sk_buffs on the done list. ! 172: This routine is called by the iodone thread in ds_routines.c. */ ! 173: void ! 174: free_skbuffs () ! 175: { ! 176: struct sk_buff *skb; ! 177: ! 178: while (1) ! 179: { ! 180: skb = skb_dequeue (&skb_done_list); ! 181: if (skb) ! 182: { ! 183: if (skb->copy) ! 184: { ! 185: vm_map_copy_discard (skb->copy); ! 186: skb->copy = NULL; ! 187: } ! 188: if (IP_VALID (skb->reply)) ! 189: { ! 190: ds_device_write_reply (skb->reply, skb->reply_type, 0, skb->len); ! 191: skb->reply = IP_NULL; ! 192: } ! 193: dev_kfree_skb (skb, FREE_WRITE); ! 194: } ! 195: else ! 196: break; ! 197: } ! 198: } ! 199: ! 200: /* Allocate an sk_buff with SIZE bytes of data space. */ ! 201: struct sk_buff * ! 202: alloc_skb (unsigned int size, int priority) ! 203: { ! 204: return dev_alloc_skb (size); ! 205: } ! 206: ! 207: /* Free SKB. */ ! 208: void ! 209: kfree_skb (struct sk_buff *skb, int priority) ! 210: { ! 211: dev_kfree_skb (skb, priority); ! 212: } ! 213: ! 214: /* Allocate an sk_buff with SIZE bytes of data space. */ ! 215: struct sk_buff * ! 216: dev_alloc_skb (unsigned int size) ! 217: { ! 218: struct sk_buff *skb; ! 219: ! 220: skb = linux_kmalloc (sizeof (struct sk_buff) + size, GFP_KERNEL); ! 221: if (skb) ! 222: { ! 223: skb->dev = NULL; ! 224: skb->reply = IP_NULL; ! 225: skb->copy = NULL; ! 226: skb->len = size; ! 227: skb->prev = skb->next = NULL; ! 228: skb->list = NULL; ! 229: if (size) ! 230: { ! 231: skb->data = (unsigned char *) (skb + 1); ! 232: skb->tail = skb->data + size; ! 233: } ! 234: else ! 235: skb->data = skb->tail = NULL; ! 236: skb->head = skb->data; ! 237: } ! 238: return skb; ! 239: } ! 240: ! 241: /* Free the sk_buff SKB. */ ! 242: void ! 243: dev_kfree_skb (struct sk_buff *skb, int mode) ! 244: { ! 245: unsigned flags; ! 246: extern void *io_done_list; ! 247: ! 248: /* Queue sk_buff on done list if there is a ! 249: page list attached or we need to send a reply. ! 250: Wakeup the iodone thread to process the list. */ ! 251: if (skb->copy || IP_VALID (skb->reply)) ! 252: { ! 253: skb_queue_tail (&skb_done_list, skb); ! 254: save_flags (flags); ! 255: thread_wakeup ((event_t) &io_done_list); ! 256: restore_flags (flags); ! 257: return; ! 258: } ! 259: linux_kfree (skb); ! 260: } ! 261: ! 262: /* Accept packet SKB received on an interface. */ ! 263: void ! 264: netif_rx (struct sk_buff *skb) ! 265: { ! 266: ipc_kmsg_t kmsg; ! 267: struct ether_header *eh; ! 268: struct packet_header *ph; ! 269: struct linux_device *dev = skb->dev; ! 270: ! 271: assert (skb != NULL); ! 272: ! 273: if (print_packet_size) ! 274: printf ("netif_rx: length %d\n", skb->len); ! 275: ! 276: /* Allocate a kernel message buffer. */ ! 277: kmsg = net_kmsg_get (); ! 278: if (! kmsg) ! 279: { ! 280: dev_kfree_skb (skb, FREE_READ); ! 281: return; ! 282: } ! 283: ! 284: /* Copy packet into message buffer. */ ! 285: eh = (struct ether_header *) (net_kmsg (kmsg)->header); ! 286: ph = (struct packet_header *) (net_kmsg (kmsg)->packet); ! 287: memcpy (eh, skb->data, sizeof (struct ether_header)); ! 288: memcpy (ph + 1, skb->data + sizeof (struct ether_header), ! 289: skb->len - sizeof (struct ether_header)); ! 290: ph->type = eh->ether_type; ! 291: ph->length = (skb->len - sizeof (struct ether_header) ! 292: + sizeof (struct packet_header)); ! 293: ! 294: dev_kfree_skb (skb, FREE_READ); ! 295: ! 296: /* Pass packet up to the microkernel. */ ! 297: net_packet (&dev->net_data->ifnet, kmsg, ! 298: ph->length, ethernet_priority (kmsg)); ! 299: } ! 300: ! 301: /* Mach device interface routines. */ ! 302: ! 303: /* Return a send right associated with network device ND. */ ! 304: static ipc_port_t ! 305: dev_to_port (void *nd) ! 306: { ! 307: return (nd ! 308: ? ipc_port_make_send (((struct net_data *) nd)->port) ! 309: : IP_NULL); ! 310: } ! 311: ! 312: static io_return_t ! 313: device_open (ipc_port_t reply_port, mach_msg_type_name_t reply_port_type, ! 314: dev_mode_t mode, char *name, device_t *devp) ! 315: { ! 316: io_return_t err = D_SUCCESS; ! 317: ipc_port_t notify; ! 318: struct ifnet *ifp; ! 319: struct linux_device *dev; ! 320: struct net_data *nd; ! 321: ! 322: /* Search for the device. */ ! 323: for (dev = dev_base; dev; dev = dev->next) ! 324: if (dev->base_addr ! 325: && dev->base_addr != 0xffe0 ! 326: && ! strcmp (name, dev->name)) ! 327: break; ! 328: if (! dev) ! 329: return D_NO_SUCH_DEVICE; ! 330: ! 331: /* Allocate and initialize device data if this is the first open. */ ! 332: nd = dev->net_data; ! 333: if (! nd) ! 334: { ! 335: dev->net_data = nd = ((struct net_data *) ! 336: kalloc (sizeof (struct net_data))); ! 337: if (! nd) ! 338: { ! 339: err = D_NO_MEMORY; ! 340: goto out; ! 341: } ! 342: nd->dev = dev; ! 343: nd->device.emul_data = nd; ! 344: nd->device.emul_ops = &linux_net_emulation_ops; ! 345: nd->port = ipc_port_alloc_kernel (); ! 346: if (nd->port == IP_NULL) ! 347: { ! 348: err = KERN_RESOURCE_SHORTAGE; ! 349: goto out; ! 350: } ! 351: ipc_kobject_set (nd->port, (ipc_kobject_t) &nd->device, IKOT_DEVICE); ! 352: notify = ipc_port_make_sonce (nd->port); ! 353: ip_lock (nd->port); ! 354: ipc_port_nsrequest (nd->port, 1, notify, ¬ify); ! 355: assert (notify == IP_NULL); ! 356: ! 357: ifp = &nd->ifnet; ! 358: ifp->if_unit = dev->name[strlen (dev->name) - 1] - '0'; ! 359: ifp->if_flags = IFF_UP|IFF_RUNNING; ! 360: ifp->if_mtu = dev->mtu; ! 361: ifp->if_header_size = dev->hard_header_len; ! 362: ifp->if_header_format = dev->type; ! 363: ifp->if_address_size = dev->addr_len; ! 364: ifp->if_address = dev->dev_addr; ! 365: if_init_queues (ifp); ! 366: ! 367: if (dev->open) ! 368: { ! 369: linux_intr_pri = SPL6; ! 370: if ((*dev->open) (dev)) ! 371: err = D_NO_SUCH_DEVICE; ! 372: } ! 373: ! 374: out: ! 375: if (err) ! 376: { ! 377: if (nd) ! 378: { ! 379: if (nd->port != IP_NULL) ! 380: { ! 381: ipc_kobject_set (nd->port, IKO_NULL, IKOT_NONE); ! 382: ipc_port_dealloc_kernel (nd->port); ! 383: } ! 384: kfree ((vm_offset_t) nd, sizeof (struct net_data)); ! 385: nd = NULL; ! 386: dev->net_data = NULL; ! 387: } ! 388: } ! 389: else ! 390: { ! 391: dev->flags |= LINUX_IFF_UP|LINUX_IFF_RUNNING; ! 392: skb_queue_head_init (&dev->buffs[0]); ! 393: } ! 394: if (IP_VALID (reply_port)) ! 395: ds_device_open_reply (reply_port, reply_port_type, ! 396: err, dev_to_port (nd)); ! 397: return MIG_NO_REPLY; ! 398: } ! 399: ! 400: *devp = &nd->device; ! 401: return D_SUCCESS; ! 402: } ! 403: ! 404: static io_return_t ! 405: device_write (void *d, ipc_port_t reply_port, ! 406: mach_msg_type_name_t reply_port_type, dev_mode_t mode, ! 407: recnum_t bn, io_buf_ptr_t data, unsigned int count, ! 408: int *bytes_written) ! 409: { ! 410: unsigned char *p; ! 411: int i, amt, skblen, s; ! 412: io_return_t err = 0; ! 413: vm_map_copy_t copy = (vm_map_copy_t) data; ! 414: struct net_data *nd = d; ! 415: struct linux_device *dev = nd->dev; ! 416: struct sk_buff *skb; ! 417: ! 418: if (count == 0 || count > dev->mtu + dev->hard_header_len) ! 419: return D_INVALID_SIZE; ! 420: ! 421: /* Allocate a sk_buff. */ ! 422: amt = PAGE_SIZE - (copy->offset & PAGE_MASK); ! 423: skblen = (amt >= count) ? 0 : count; ! 424: skb = dev_alloc_skb (skblen); ! 425: if (! skb) ! 426: return D_NO_MEMORY; ! 427: ! 428: /* Copy user data. This is only required if it spans multiple pages. */ ! 429: if (skblen == 0) ! 430: { ! 431: assert (copy->cpy_npages == 1); ! 432: ! 433: skb->copy = copy; ! 434: skb->data = ((void *) copy->cpy_page_list[0]->phys_addr ! 435: + (copy->offset & PAGE_MASK)); ! 436: skb->len = count; ! 437: skb->head = skb->data; ! 438: skb->tail = skb->data + skb->len; ! 439: } ! 440: else ! 441: { ! 442: memcpy (skb->data, ! 443: ((void *) copy->cpy_page_list[0]->phys_addr ! 444: + (copy->offset & PAGE_MASK)), ! 445: amt); ! 446: count -= amt; ! 447: p = skb->data + amt; ! 448: for (i = 1; count > 0 && i < copy->cpy_npages; i++) ! 449: { ! 450: amt = PAGE_SIZE; ! 451: if (amt > count) ! 452: amt = count; ! 453: memcpy (p, (void *) copy->cpy_page_list[i]->phys_addr, amt); ! 454: count -= amt; ! 455: p += amt; ! 456: } ! 457: ! 458: assert (count == 0); ! 459: ! 460: vm_map_copy_discard (copy); ! 461: } ! 462: ! 463: skb->dev = dev; ! 464: skb->reply = reply_port; ! 465: skb->reply_type = reply_port_type; ! 466: ! 467: /* Queue packet for transmission and schedule a software interrupt. */ ! 468: s = splimp (); ! 469: if (dev->buffs[0].next != (struct sk_buff *) &dev->buffs[0] ! 470: || (*dev->hard_start_xmit) (skb, dev)) ! 471: { ! 472: __skb_queue_tail (&dev->buffs[0], skb); ! 473: mark_bh (NET_BH); ! 474: } ! 475: splx (s); ! 476: ! 477: return MIG_NO_REPLY; ! 478: } ! 479: ! 480: static io_return_t ! 481: device_get_status (void *d, dev_flavor_t flavor, dev_status_t status, ! 482: mach_msg_type_number_t *count) ! 483: { ! 484: return net_getstat (&((struct net_data *) d)->ifnet, flavor, status, count); ! 485: } ! 486: ! 487: static io_return_t ! 488: device_set_filter (void *d, ipc_port_t port, int priority, ! 489: filter_t *filter, unsigned filter_count) ! 490: { ! 491: return net_set_filter (&((struct net_data *) d)->ifnet, ! 492: port, priority, filter, filter_count); ! 493: } ! 494: ! 495: struct device_emulation_ops linux_net_emulation_ops = ! 496: { ! 497: NULL, ! 498: NULL, ! 499: dev_to_port, ! 500: device_open, ! 501: NULL, ! 502: device_write, ! 503: NULL, ! 504: NULL, ! 505: NULL, ! 506: NULL, ! 507: device_get_status, ! 508: device_set_filter, ! 509: NULL, ! 510: NULL, ! 511: NULL, ! 512: NULL ! 513: }; ! 514: ! 515: /* Do any initialization required for network devices. */ ! 516: void ! 517: linux_net_emulation_init () ! 518: { ! 519: skb_queue_head_init (&skb_done_list); ! 520: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.