|
|
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: #include <machine/spl.h> ! 64: ! 65: #include <mach/mach_types.h> ! 66: #include <mach/kern_return.h> ! 67: #include <mach/mig_errors.h> ! 68: #include <mach/port.h> ! 69: #include <mach/vm_param.h> ! 70: #include <mach/notify.h> ! 71: ! 72: #include <ipc/ipc_port.h> ! 73: #include <ipc/ipc_space.h> ! 74: ! 75: #include <vm/vm_map.h> ! 76: #include <vm/vm_kern.h> ! 77: #include <vm/vm_page.h> ! 78: ! 79: #include <device/device_types.h> ! 80: #include <device/device_port.h> ! 81: #include <device/if_hdr.h> ! 82: #include <device/if_ether.h> ! 83: #include <device/if_hdr.h> ! 84: #include <device/net_io.h> ! 85: #include "device_reply.h" ! 86: ! 87: #include <linux_emul.h> ! 88: ! 89: #define MACH_INCLUDE ! 90: #include <linux/kernel.h> ! 91: #include <linux/sched.h> ! 92: #include <linux/string.h> ! 93: #include <linux/errno.h> ! 94: #include <linux/delay.h> ! 95: #include <linux/interrupt.h> ! 96: #include <linux/malloc.h> ! 97: #include <linux/netdevice.h> ! 98: #include <linux/etherdevice.h> ! 99: ! 100: extern int linux_intr_pri; ! 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) ! 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: unsigned char *bptr; ! 220: int len = size; ! 221: ! 222: size = (size + 15) & ~15; ! 223: size += sizeof (struct sk_buff); ! 224: ! 225: bptr = linux_kmalloc (size, GFP_KERNEL); ! 226: if (bptr == NULL) ! 227: return NULL; ! 228: ! 229: /* XXX: In Mach, a sk_buff is located at the head, ! 230: while it's located at the tail in Linux. */ ! 231: skb = bptr; ! 232: skb->dev = NULL; ! 233: skb->reply = IP_NULL; ! 234: skb->copy = NULL; ! 235: skb->len = 0; ! 236: skb->prev = skb->next = NULL; ! 237: skb->list = NULL; ! 238: skb->data = bptr + sizeof (struct sk_buff); ! 239: skb->tail = skb->data; ! 240: skb->head = skb->data; ! 241: skb->end = skb->data + len; ! 242: ! 243: return skb; ! 244: } ! 245: ! 246: /* Free the sk_buff SKB. */ ! 247: void ! 248: dev_kfree_skb (struct sk_buff *skb, int mode) ! 249: { ! 250: unsigned flags; ! 251: extern void *io_done_list; ! 252: ! 253: /* Queue sk_buff on done list if there is a ! 254: page list attached or we need to send a reply. ! 255: Wakeup the iodone thread to process the list. */ ! 256: if (skb->copy || IP_VALID (skb->reply)) ! 257: { ! 258: skb_queue_tail (&skb_done_list, skb); ! 259: save_flags (flags); ! 260: thread_wakeup ((event_t) & io_done_list); ! 261: restore_flags (flags); ! 262: return; ! 263: } ! 264: linux_kfree (skb); ! 265: } ! 266: ! 267: /* Accept packet SKB received on an interface. */ ! 268: void ! 269: netif_rx (struct sk_buff *skb) ! 270: { ! 271: ipc_kmsg_t kmsg; ! 272: struct ether_header *eh; ! 273: struct packet_header *ph; ! 274: struct linux_device *dev = skb->dev; ! 275: ! 276: assert (skb != NULL); ! 277: ! 278: if (print_packet_size) ! 279: printf ("netif_rx: length %ld\n", skb->len); ! 280: ! 281: /* Allocate a kernel message buffer. */ ! 282: kmsg = net_kmsg_get (); ! 283: if (!kmsg) ! 284: { ! 285: dev_kfree_skb (skb, FREE_READ); ! 286: return; ! 287: } ! 288: ! 289: /* Copy packet into message buffer. */ ! 290: eh = (struct ether_header *) (net_kmsg (kmsg)->header); ! 291: ph = (struct packet_header *) (net_kmsg (kmsg)->packet); ! 292: memcpy (eh, skb->data, sizeof (struct ether_header)); ! 293: memcpy (ph + 1, skb->data + sizeof (struct ether_header), ! 294: skb->len - sizeof (struct ether_header)); ! 295: ph->type = eh->ether_type; ! 296: ph->length = (skb->len - sizeof (struct ether_header) ! 297: + sizeof (struct packet_header)); ! 298: ! 299: dev_kfree_skb (skb, FREE_READ); ! 300: ! 301: /* Pass packet up to the microkernel. */ ! 302: net_packet (&dev->net_data->ifnet, kmsg, ! 303: ph->length, ethernet_priority (kmsg)); ! 304: } ! 305: ! 306: /* Mach device interface routines. */ ! 307: ! 308: /* Return a send right associated with network device ND. */ ! 309: static ipc_port_t ! 310: dev_to_port (void *nd) ! 311: { ! 312: return (nd ! 313: ? ipc_port_make_send (((struct net_data *) nd)->port) ! 314: : IP_NULL); ! 315: } ! 316: ! 317: static io_return_t ! 318: device_open (ipc_port_t reply_port, mach_msg_type_name_t reply_port_type, ! 319: dev_mode_t mode, char *name, device_t *devp) ! 320: { ! 321: io_return_t err = D_SUCCESS; ! 322: ipc_port_t notify; ! 323: struct ifnet *ifp; ! 324: struct linux_device *dev; ! 325: struct net_data *nd; ! 326: ! 327: /* Search for the device. */ ! 328: for (dev = dev_base; dev; dev = dev->next) ! 329: if (dev->base_addr ! 330: && dev->base_addr != 0xffe0 ! 331: && !strcmp (name, dev->name)) ! 332: break; ! 333: if (!dev) ! 334: return D_NO_SUCH_DEVICE; ! 335: ! 336: /* Allocate and initialize device data if this is the first open. */ ! 337: nd = dev->net_data; ! 338: if (!nd) ! 339: { ! 340: dev->net_data = nd = ((struct net_data *) ! 341: kalloc (sizeof (struct net_data))); ! 342: if (!nd) ! 343: { ! 344: err = D_NO_MEMORY; ! 345: goto out; ! 346: } ! 347: nd->dev = dev; ! 348: nd->device.emul_data = nd; ! 349: nd->device.emul_ops = &linux_net_emulation_ops; ! 350: nd->port = ipc_port_alloc_kernel (); ! 351: if (nd->port == IP_NULL) ! 352: { ! 353: err = KERN_RESOURCE_SHORTAGE; ! 354: goto out; ! 355: } ! 356: ipc_kobject_set (nd->port, (ipc_kobject_t) & nd->device, IKOT_DEVICE); ! 357: notify = ipc_port_make_sonce (nd->port); ! 358: ip_lock (nd->port); ! 359: ipc_port_nsrequest (nd->port, 1, notify, ¬ify); ! 360: assert (notify == IP_NULL); ! 361: ! 362: ifp = &nd->ifnet; ! 363: ifp->if_unit = dev->name[strlen (dev->name) - 1] - '0'; ! 364: ifp->if_flags = IFF_UP | IFF_RUNNING; ! 365: ifp->if_mtu = dev->mtu; ! 366: ifp->if_header_size = dev->hard_header_len; ! 367: ifp->if_header_format = dev->type; ! 368: ifp->if_address_size = dev->addr_len; ! 369: ifp->if_address = dev->dev_addr; ! 370: if_init_queues (ifp); ! 371: ! 372: if (dev->open) ! 373: { ! 374: linux_intr_pri = SPL6; ! 375: if ((*dev->open) (dev)) ! 376: err = D_NO_SUCH_DEVICE; ! 377: } ! 378: ! 379: out: ! 380: if (err) ! 381: { ! 382: if (nd) ! 383: { ! 384: if (nd->port != IP_NULL) ! 385: { ! 386: ipc_kobject_set (nd->port, IKO_NULL, IKOT_NONE); ! 387: ipc_port_dealloc_kernel (nd->port); ! 388: } ! 389: kfree ((vm_offset_t) nd, sizeof (struct net_data)); ! 390: nd = NULL; ! 391: dev->net_data = NULL; ! 392: } ! 393: } ! 394: else ! 395: { ! 396: dev->flags |= LINUX_IFF_UP | LINUX_IFF_RUNNING; ! 397: skb_queue_head_init (&dev->buffs[0]); ! 398: } ! 399: if (IP_VALID (reply_port)) ! 400: ds_device_open_reply (reply_port, reply_port_type, ! 401: err, dev_to_port (nd)); ! 402: return MIG_NO_REPLY; ! 403: } ! 404: ! 405: *devp = &nd->device; ! 406: return D_SUCCESS; ! 407: } ! 408: ! 409: static io_return_t ! 410: device_write (void *d, ipc_port_t reply_port, ! 411: mach_msg_type_name_t reply_port_type, dev_mode_t mode, ! 412: recnum_t bn, io_buf_ptr_t data, unsigned int count, ! 413: int *bytes_written) ! 414: { ! 415: unsigned char *p; ! 416: int i, amt, skblen, s; ! 417: io_return_t err = 0; ! 418: vm_map_copy_t copy = (vm_map_copy_t) data; ! 419: struct net_data *nd = d; ! 420: struct linux_device *dev = nd->dev; ! 421: struct sk_buff *skb; ! 422: ! 423: if (count == 0 || count > dev->mtu + dev->hard_header_len) ! 424: return D_INVALID_SIZE; ! 425: ! 426: /* Allocate a sk_buff. */ ! 427: amt = PAGE_SIZE - (copy->offset & PAGE_MASK); ! 428: skblen = (amt >= count) ? 0 : count; ! 429: skb = dev_alloc_skb (skblen); ! 430: if (!skb) ! 431: return D_NO_MEMORY; ! 432: ! 433: /* Copy user data. This is only required if it spans multiple pages. */ ! 434: if (skblen == 0) ! 435: { ! 436: assert (copy->cpy_npages == 1); ! 437: ! 438: skb->copy = copy; ! 439: skb->data = ((void *) copy->cpy_page_list[0]->phys_addr ! 440: + (copy->offset & PAGE_MASK)); ! 441: skb->len = count; ! 442: skb->head = skb->data; ! 443: skb->tail = skb->data + skb->len; ! 444: skb->end = skb->tail; ! 445: } ! 446: else ! 447: { ! 448: skb->len = skblen; ! 449: skb->tail = skb->data + skblen; ! 450: skb->end = skb->tail; ! 451: ! 452: memcpy (skb->data, ! 453: ((void *) copy->cpy_page_list[0]->phys_addr ! 454: + (copy->offset & PAGE_MASK)), ! 455: amt); ! 456: count -= amt; ! 457: p = skb->data + amt; ! 458: for (i = 1; count > 0 && i < copy->cpy_npages; i++) ! 459: { ! 460: amt = PAGE_SIZE; ! 461: if (amt > count) ! 462: amt = count; ! 463: memcpy (p, (void *) copy->cpy_page_list[i]->phys_addr, amt); ! 464: count -= amt; ! 465: p += amt; ! 466: } ! 467: ! 468: assert (count == 0); ! 469: ! 470: vm_map_copy_discard (copy); ! 471: } ! 472: ! 473: skb->dev = dev; ! 474: skb->reply = reply_port; ! 475: skb->reply_type = reply_port_type; ! 476: ! 477: /* Queue packet for transmission and schedule a software interrupt. */ ! 478: s = splimp (); ! 479: if (dev->buffs[0].next != (struct sk_buff *) &dev->buffs[0] ! 480: || (*dev->hard_start_xmit) (skb, dev)) ! 481: { ! 482: __skb_queue_tail (&dev->buffs[0], skb); ! 483: mark_bh (NET_BH); ! 484: } ! 485: splx (s); ! 486: ! 487: return MIG_NO_REPLY; ! 488: } ! 489: ! 490: static io_return_t ! 491: device_get_status (void *d, dev_flavor_t flavor, dev_status_t status, ! 492: mach_msg_type_number_t *count) ! 493: { ! 494: return net_getstat (&((struct net_data *) d)->ifnet, flavor, status, count); ! 495: } ! 496: ! 497: static io_return_t ! 498: device_set_filter (void *d, ipc_port_t port, int priority, ! 499: filter_t * filter, unsigned filter_count) ! 500: { ! 501: return net_set_filter (&((struct net_data *) d)->ifnet, ! 502: port, priority, filter, filter_count); ! 503: } ! 504: ! 505: struct device_emulation_ops linux_net_emulation_ops = ! 506: { ! 507: NULL, ! 508: NULL, ! 509: dev_to_port, ! 510: device_open, ! 511: NULL, ! 512: device_write, ! 513: NULL, ! 514: NULL, ! 515: NULL, ! 516: NULL, ! 517: device_get_status, ! 518: device_set_filter, ! 519: NULL, ! 520: NULL, ! 521: NULL, ! 522: NULL ! 523: }; ! 524: ! 525: /* Do any initialization required for network devices. */ ! 526: void ! 527: linux_net_emulation_init () ! 528: { ! 529: skb_queue_head_init (&skb_done_list); ! 530: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.