diff options
author | Roland McGrath <roland@gnu.org> | 2000-02-04 06:32:39 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 2000-02-04 06:32:39 +0000 |
commit | db9a26cbd125ec106e040ea6084911f9bc0c0198 (patch) | |
tree | 8394aedf0addf9de2afd46d00c192d20d9eb9092 /pfinet/linux-inet/dev.c | |
parent | 8880a73970b23f10c720011cb910c0e0e1e02975 (diff) | |
download | hurd-db9a26cbd125ec106e040ea6084911f9bc0c0198.tar.gz hurd-db9a26cbd125ec106e040ea6084911f9bc0c0198.tar.bz2 hurd-db9a26cbd125ec106e040ea6084911f9bc0c0198.zip |
2000-02-03 Roland McGrath <roland@baalperazim.frob.com>
Complete overhaul of pfinet based on the IPv4 networking code from the
Linux 2.2.12 kernel sources. This page describes a single unified set
of interdependent changes, but there are so many changes that I have
broken up the log entry into paragraphs based on rough topical
divisions of the work involved.
Subset of verbatim Linux 2.2.12 sources imported on a vendor branch.
* linux-src: New directory, see README for details.
* README: New file, describes linux-src layout and procedures for
tracking Linux source updates.
Light modifications to linux-src files to avoid really bending over
backwards with the glue macros. All modifications to files in
linux-src are conditionalized by #ifdef _HURD_.
* linux-src/include/linux/net.h [_HURD_] (struct socket): New members
refcnt and identity; elide members fasync_list, file.
* linux-src/include/linux/rtnetlink.h [! CONFIG_RTNETLINK]
(rtnl_shlock, rtnl_shunlock) [! _HURD_]: Conditionalize contents on
this, making these no-ops #ifdef _HURD_.
* linux-src/net/core/dev.c [_HURD_] (dev_ioctl): Don't define the
function, instead #define it to 0.
* linux-src/net/ipv4/af_inet.c [_HURD_] (inet_ioctl): Likewise.
* linux-src/net/ipv4/arp.c [_HURD_] (arp_ioctl): Likewise.
* linux-src/net/ipv4/udp.c [_HURD_] (udp_ioctl): Likewise.
* linux-src/net/ipv4/tcp.c [_HURD_] (tcp_ioctl): Likewise.
[_HURD_] (tcp_tiocinq): New function, TIOCINQ code from tcp_ioctl.
* linux-src/net/ipv4/devinet.c [_HURD_] (devinet_ioctl): Don't define
the function, instead #define it to 0.
[_HURD_] (configure_device): New function, cobbled from SIOCSIFADDR
and SIOCSIFNETMASK code from devinet_ioctl.
* glue-include/asm, glue-include/linux: New directories.
These contain glue kludge headers that replace all of the
Linux <asm/*.h> headers except checksum.h, and several of
the Linux <linux/*.h> headers (the remainder come from
linux-src/include/linux and are mostly unmodified).
* glue-include/asm/atomic.h: New file, glue replacement header.
* glue-include/asm/bitops.h: New file, glue replacement header.
* glue-include/asm/byteorder.h: New file, glue replacement header.
* glue-include/asm/checksum.h: New file, glue replacement header.
* glue-include/asm/errno.h: New file, glue replacement header.
* glue-include/asm/hardirq.h: New file, glue replacement header.
* glue-include/asm/init.h: New file, glue replacement header.
* glue-include/asm/segment.h: New file, glue replacement header.
* glue-include/asm/spinlock.h: New file, glue replacement header.
* glue-include/asm/system.h: New file, glue replacement header.
* glue-include/asm/types.h: New file, glue replacement header.
* glue-include/asm/uaccess.h: New file, glue replacement header.
* glue-include/linux/autoconf.h: New file, glue replacement header.
* glue-include/linux/binfmts.h: New file, glue replacement header.
* glue-include/linux/config.h: New file, glue replacement header.
* glue-include/linux/errno.h: New file, glue replacement header.
* glue-include/linux/fcntl.h: New file, glue replacement header.
* glue-include/linux/fs.h: New file, glue replacement header.
* glue-include/linux/in.h: New file, glue replacement header.
* glue-include/linux/in6.h: New file, glue replacement header.
* glue-include/linux/interrupt.h: New file, glue replacement header.
* glue-include/linux/ioctl.h: New file, glue replacement header.
* glue-include/linux/ipv6.h: New file, glue replacement header.
* glue-include/linux/kernel.h: New file, glue replacement header.
* glue-include/linux/limits.h: New file, glue replacement header.
* glue-include/linux/major.h: New file, glue replacement header.
* glue-include/linux/malloc.h: New file, glue replacement header.
* glue-include/linux/mm.h: New file, glue replacement header.
* glue-include/linux/param.h: New file, glue replacement header.
* glue-include/linux/personality.h: New file, glue replacement header.
* glue-include/linux/poll.h: New file, glue replacement header.
* glue-include/linux/proc_fs.h: New file, glue replacement header.
* glue-include/linux/sched.h: New file, glue replacement header.
* glue-include/linux/slab.h: New file, glue replacement header.
* glue-include/linux/socket.h: New file, glue replacement header.
* glue-include/linux/sockios.h: New file, glue replacement header.
* glue-include/linux/stat.h: New file, glue replacement header.
* glue-include/linux/string.h: New file, glue replacement header.
* glue-include/linux/termios.h: New file, glue replacement header.
* glue-include/linux/time.h: New file, glue replacement header.
* glue-include/linux/timer.h: New file, glue replacement header.
* glue-include/linux/timex.h: New file, glue replacement header.
* glue-include/linux/types.h: New file, glue replacement header.
* glue-include/linux/un.h: New file, glue replacement header.
* glue-include/linux/version.h: New file, glue replacement header.
* glue-include/linux/wait.h: New file, glue replacement header.
* kmem_cache.c: New file. Glue code replaces Linux kmem_cache_t et al.
* stubs.c: New file. No-op functions and stub variables for a few
things the Linux networking code needs to link.
* Makefile (core-srcs, arch-lib-srcs, ethernet-srcs, ipv4-srcs): New
variables, listing sources used from linux-src subdirectories.
(LINUXSRCS): Define using those.
(SRCS): Remove devices.c; add kmem_cache.c, stubs.c.
(UNUSEDSRC): Variable removed.
(vpath %.c): Remove vpath for $(srcdir)/linux-inet directory.
Add vpaths for $(srcdir)/linux-src subdirectories.
(CPPFLAGS): Add -D_HURD_SYSTYPE defining it to $(asm_syntax) as a
double-quoted string. Add -I's for glue-include and linux-src/include.
* pfinet.h: Include <sys/socket.h>, and not <linux/netdevice.h>.
(master_device): Remove decl.
(global_lock, packet_queue_lock): Remove common defns.
(global_lock, net_bh_lock): Declare them as externs.
(struct sockaddr): Remove len member, make address member just a
struct sockaddr rather than a 0-length array.
(setup_loopback_device, become_task_protid, become_task): Remove decls.
(ethernet_initialize): Declare it.
(input_work_thread): Remove decl.
(net_bh_thread): Declare it.
(tcp_readable): Remove decl.
(tcp_tiocinq): Declare it.
* config.h: Rewritten based on Linux 2.2.12 set of CONFIG_* options.
(CONFIG_NET, CONFIG_INET, CONFIG_SKB_LARGE): These are the only
Linux config options we set.
(CONFIG_IP_NOSIOCRT): New macro (not a proper config option, but
used conveniently in the code).
* ethernet.c (ethernet_set_multi): Take only one parameter.
Remove assert, since we always get passed IGMP_ALL_HOSTS.
(ethernet_thread): Make static.
(ethernet_demuxer): Use __mutex_lock in place of mutex_lock, so as to
get cthreads instead of linux/spinlock.h glue macros. Lock
net_bh_lock instead of global_lock. Set SKB->protocol with
eth_type_trans before calling netif_rx.
(ethernet_initialize): New function, one-time initialization broken
out of ethernet_open.
(ethernet_open): Ports setup moved to ethernet_initialize.
Don't use `errno' to avoid glue conflicts.
Use get_privileged_ports here to get the master device port, and
deallocate it after calling device_open.
(ethernet_xmit): Use assert_perror. Only one arg to dev_kfree_skb now.
(setup_ethernet_device): Change initializations for structure changes.
Call dev_init_buffers and register_netdevice on the device.
* timer-emul.c (all functions): Use __mutex_lock instead of mutex_lock.
Adjust for renaming of `prevp' member to `prev' in struct timer_list.
(mod_timer): New function.
* socket.c (proto_ops): Variable removed.
(net_families): New variable replaces it.
(sock_register): Rewritten for new calling convention, set
net_families rather than proto_ops.
(make_sock_user, clean_socketport, sock_alloc, sock_release):
Functions moved here from misc.c.
* sched.c (packet_queue_lock): Variable removed.
(net_bh_lock, net_bh_wakeup): New variables.
(current): Variable removed (now a macro in the glue headers).
(interruptible_sleep_on, wake_up_interruptible): Functions removed.
They are replaced by inlines in the glue headers.
(become_task, become_task_protid): Functions removed; they are
replaced by macros in glue-include/linux/sched.h.
(net_bh_worker): New function.
* loopback.c: Completely rewritten, mostly copied from linux-2.2.12's
drivers/net/loopback.c source file.
* io-ops.c (all functions): Use __mutex_lock in place of mutex_lock.
(S_io_write): Call ops->sendmsg instead of ops->write,
which no longer exists. If O_NONBLOCK is set, set MSG_DONTWAIT in
msg_flags.
(S_io_read): Call ops->recvmsg instead of ops->read,
which no longer exists If O_NONBLOCK is set, pass MSG_DONTWAIT.
(S_io_readable): Use USER->sock->data in place of USER->sock->sk.
For SOCK_STREAM and SOCK_SEQPACKET types, call tcp_tiocinq.
(S_io_set_all_openmodes, S_io_get_openmodes, S_io_set_some_openmodes,
S_io_clear_some_openmodes): Member USER->sock->userflags is now
renamed USER->sock->flags.
(S_io_select): Completely rewritten using ops->poll.
(select_wait): Function removed.
(S_io_stat): Set st_mode to reflect S_IFSOCK.
* socket-ops.c (all functions): Use __mutex_lock instead of mutex_lock.
(S_socket_create): Don't set SOCK->ops or call SOCK->ops->create.
Instead, call net_families[PF_INET]->create.
(S_socket_listen): Remove extra checks; just call ops->listen.
(S_socket_accept): Remove extra checks before ops->accept call.
Avoid use of goto.
(S_socket_connect): Remove extra checks; just call ops->connect.
(S_socket_bind): Adjust for struct sock_addr changes.
(S_socket_create_address): Likewise.
(S_socket_whatis_address): Likewise.
(S_socket_connect2): Don't diddle data structures after
ops->socketpair call.
(S_socket_getopt): Use sock_getsockopt if LEVEL is SOL_SOCKET.
Accept any data size, not just sizeof (int).
(S_socket_setopt): Use sock_setsockopt if LEVEL is SOL_SOCKET.
(S_socket_send): Always use ops->sendmsg instead of ops->send or
ops->sendto, which no longer exist. If O_NONBLOCK is set, set
MSG_DONTWAIT in msg_flags.
(S_socket_recv): Always use ops->recvmsg instead of ops->recv, which
no longer exists. If O_NONBLOCK is set, set MSG_DONTWAIT in flags.
Check for error from S_socket_create_address.
* main.c (find_device): Don't try to set ether_dev.pa_mask (it's gone).
(main): Don't call init_devices. Call ethernet_initialize.
Start net_bh_worker instead of input_work_thread. Don't call
setup_loopback_device. Instead, take global_lock, do prepare_current,
and then call sk_init, skb_init, inet_proto_init, and net_dev_init.
Keep global_lock held while calling argp_parse.
Call arrange_shutdown_notification only after all that.
Fix error call for "contacting parent" to pass ERR instead of errno.
* options.c (ADDR): #undef before defining macro.
(parse_opt): #if 0 out EDESTADDRREQ check (I don't understand it).
To apply settings, call configure_devices.
(ADD_ADDR_OPT): #if 0 --address and --netmask options. Needs fixed.
* misc.c (make_sock_user, clean_socketport, sock_alloc, sock_release):
Functions moved to socket.c.
(sock_release_peer): Function removed.
(make_sockaddr_port): Use struct sockaddr_storage to size buffer.
Fix size calculation for new struct sock_addr layout.
Initialize sa_family and sa_len of new struct sock_addr.
Remove the old Linux (2.0.??) network stack and the glue code for it.
* linux-inet, asm, linux: Directories and all files removed.
Some of the new files in glue-include came from the old glue headers
in the asm and linux directories, but most were substantially modified.
* devices.c: File removed. The equivalent glue is now elsewhere.
Diffstat (limited to 'pfinet/linux-inet/dev.c')
-rw-r--r-- | pfinet/linux-inet/dev.c | 1449 |
1 files changed, 0 insertions, 1449 deletions
diff --git a/pfinet/linux-inet/dev.c b/pfinet/linux-inet/dev.c deleted file mode 100644 index d393af11..00000000 --- a/pfinet/linux-inet/dev.c +++ /dev/null @@ -1,1449 +0,0 @@ -/* - * NET3 Protocol independent device support routines. - * - * 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. - * - * Derived from the non IP parts of dev.c 1.0.19 - * Authors: Ross Biro, <bir7@leland.Stanford.Edu> - * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> - * Mark Evans, <evansmp@uhura.aston.ac.uk> - * - * Additional Authors: - * Florian la Roche <rzsfl@rz.uni-sb.de> - * Alan Cox <gw4pts@gw4pts.ampr.org> - * David Hinds <dhinds@allegro.stanford.edu> - * - * Changes: - * Alan Cox : device private ioctl copies fields back. - * Alan Cox : Transmit queue code does relevant stunts to - * keep the queue safe. - * Alan Cox : Fixed double lock. - * Alan Cox : Fixed promisc NULL pointer trap - * ???????? : Support the full private ioctl range - * Alan Cox : Moved ioctl permission check into drivers - * Tim Kordas : SIOCADDMULTI/SIOCDELMULTI - * Alan Cox : 100 backlog just doesn't cut it when - * you start doing multicast video 8) - * Alan Cox : Rewrote net_bh and list manager. - * Alan Cox : Fix ETH_P_ALL echoback lengths. - * - * Cleaned up and recommented by Alan Cox 2nd April 1994. I hope to have - * the rest as well commented in the end. - */ - -/* - * A lot of these includes will be going walkies very soon - */ - -#include <asm/segment.h> -#include <asm/system.h> -#include <asm/bitops.h> -#include <linux/config.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/socket.h> -#include <linux/sockios.h> -#include <linux/in.h> -#include <linux/errno.h> -#include <linux/interrupt.h> -#include <linux/if_ether.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/notifier.h> -#include "ip.h" -#include "route.h" -#include <linux/skbuff.h> -#include "sock.h" -#include "arp.h" - - -/* - * The list of packet types we will receive (as opposed to discard) - * and the routines to invoke. - */ - -struct packet_type *ptype_base = NULL; - -/* - * Our notifier list - */ - -struct notifier_block *netdev_chain=NULL; - -/* - * Device drivers call our routines to queue packets here. We empty the - * queue in the bottom half handler. - */ - -static struct sk_buff_head backlog = -{ - (struct sk_buff *)&backlog, (struct sk_buff *)&backlog -#ifdef CONFIG_SKB_CHECK - ,SK_HEAD_SKB -#endif -}; - -/* - * We don't overdo the queue or we will thrash memory badly. - */ - -static int backlog_size = 0; - -/* - * Return the lesser of the two values. - */ - -static __inline__ unsigned long min(unsigned long a, unsigned long b) -{ - return (a < b)? a : b; -} - - -/****************************************************************************************** - - Protocol management and registration routines - -*******************************************************************************************/ - -/* - * For efficiency - */ - -static int dev_nit=0; - -/* - * Add a protocol ID to the list. Now that the input handler is - * smarter we can dispense with all the messy stuff that used to be - * here. - */ - -void dev_add_pack(struct packet_type *pt) -{ - if(pt->type==htons(ETH_P_ALL)) - dev_nit++; - pt->next = ptype_base; - ptype_base = pt; -} - - -/* - * Remove a protocol ID from the list. - */ - -void dev_remove_pack(struct packet_type *pt) -{ - struct packet_type **pt1; - if(pt->type==htons(ETH_P_ALL)) - dev_nit--; - for(pt1=&ptype_base; (*pt1)!=NULL; pt1=&((*pt1)->next)) - { - if(pt==(*pt1)) - { - *pt1=pt->next; - return; - } - } -} - -/***************************************************************************************** - - Device Interface Subroutines - -******************************************************************************************/ - -/* - * Find an interface by name. - */ - -struct device *dev_get(char *name) -{ - struct device *dev; - - for (dev = dev_base; dev != NULL; dev = dev->next) - { - if (strcmp(dev->name, name) == 0) - return(dev); - } - return(NULL); -} - - -/* - * Prepare an interface for use. - */ - -int dev_open(struct device *dev) -{ - int ret = 0; - - /* - * Call device private open method - */ - if (dev->open) - ret = dev->open(dev); - - /* - * If it went open OK then set the flags - */ - - if (ret == 0) - { - dev->flags |= (IFF_UP | IFF_RUNNING); - /* - * Initialise multicasting status - */ -#ifdef CONFIG_IP_MULTICAST - /* - * Join the all host group - */ - ip_mc_allhost(dev); -#endif - dev_mc_upload(dev); - notifier_call_chain(&netdev_chain, NETDEV_UP, dev); - } - return(ret); -} - - -/* - * Completely shutdown an interface. - */ - -int dev_close(struct device *dev) -{ - /* - * Only close a device if it is up. - */ - - if (dev->flags != 0) - { - int ct=0; - dev->flags = 0; - /* - * Call the device specific close. This cannot fail. - */ - if (dev->stop) - dev->stop(dev); - - notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); -#if 0 - /* - * Delete the route to the device. - */ -#ifdef CONFIG_INET - ip_rt_flush(dev); - arp_device_down(dev); -#endif -#ifdef CONFIG_IPX - ipxrtr_device_down(dev); -#endif -#endif - /* - * Flush the multicast chain - */ - dev_mc_discard(dev); - /* - * Blank the IP addresses - */ - dev->pa_addr = 0; - dev->pa_dstaddr = 0; - dev->pa_brdaddr = 0; - dev->pa_mask = 0; - /* - * Purge any queued packets when we down the link - */ - while(ct<DEV_NUMBUFFS) - { - struct sk_buff *skb; - while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL) - if(skb->free) - kfree_skb(skb,FREE_WRITE); - ct++; - } - } - return(0); -} - - -/* - * Device change register/unregister. These are not inline or static - * as we export them to the world. - */ - -int register_netdevice_notifier(struct notifier_block *nb) -{ - return notifier_chain_register(&netdev_chain, nb); -} - -int unregister_netdevice_notifier(struct notifier_block *nb) -{ - return notifier_chain_unregister(&netdev_chain,nb); -} - - - -/* - * Send (or queue for sending) a packet. - * - * IMPORTANT: When this is called to resend frames. The caller MUST - * already have locked the sk_buff. Apart from that we do the - * rest of the magic. - */ - -void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) -{ - unsigned long flags; - int nitcount; - struct packet_type *ptype; - int where = 0; /* used to say if the packet should go */ - /* at the front or the back of the */ - /* queue - front is a retransmit try */ - - if (dev == NULL) - { - printk("dev.c: dev_queue_xmit: dev = NULL\n"); - return; - } - - if(pri>=0 && !skb_device_locked(skb)) - skb_device_lock(skb); /* Shove a lock on the frame */ -#ifdef CONFIG_SLAVE_BALANCING - save_flags(flags); - cli(); - if(dev->slave!=NULL && dev->slave->pkt_queue < dev->pkt_queue && - (dev->slave->flags & IFF_UP)) - dev=dev->slave; - restore_flags(flags); -#endif -#ifdef CONFIG_SKB_CHECK - IS_SKB(skb); -#endif - skb->dev = dev; - - /* - * This just eliminates some race conditions, but not all... - */ - - if (skb->next != NULL) - { - /* - * Make sure we haven't missed an interrupt. - */ - printk("dev_queue_xmit: worked around a missed interrupt\n"); - dev->hard_start_xmit(NULL, dev); - return; - } - - /* - * Negative priority is used to flag a frame that is being pulled from the - * queue front as a retransmit attempt. It therefore goes back on the queue - * start on a failure. - */ - - if (pri < 0) - { - pri = -pri-1; - where = 1; - } - - if (pri >= DEV_NUMBUFFS) - { - printk("bad priority in dev_queue_xmit.\n"); - pri = 1; - } - - /* - * If the address has not been resolved. Call the device header rebuilder. - * This can cover all protocols and technically not just ARP either. - */ - - if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) { - return; - } - - save_flags(flags); - cli(); - if (!where) { -#ifdef CONFIG_SLAVE_BALANCING - skb->in_dev_queue=1; -#endif - skb_queue_tail(dev->buffs + pri,skb); - skb_device_unlock(skb); /* Buffer is on the device queue and can be freed safely */ - skb = skb_dequeue(dev->buffs + pri); - skb_device_lock(skb); /* New buffer needs locking down */ -#ifdef CONFIG_SLAVE_BALANCING - skb->in_dev_queue=0; -#endif - } - restore_flags(flags); - - /* copy outgoing packets to any sniffer packet handlers */ - if(!where) - { - for (nitcount= dev_nit, ptype = ptype_base; nitcount > 0 && ptype != NULL; ptype = ptype->next) - { - /* Never send packets back to the socket - * they originated from - MvS (miquels@drinkel.ow.org) - */ - if (ptype->type == htons(ETH_P_ALL) && - (ptype->dev == dev || !ptype->dev) && - ((struct sock *)ptype->data != skb->sk)) - { - struct sk_buff *skb2; - if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) - break; - /* - * The protocol knows this has (for other paths) been taken off - * and adds it back. - */ - skb2->len-=skb->dev->hard_header_len; - ptype->func(skb2, skb->dev, ptype); - nitcount--; - } - } - } - if (dev->hard_start_xmit(skb, dev) == 0) { - /* - * Packet is now solely the responsibility of the driver - */ - return; - } - - /* - * Transmission failed, put skb back into a list. Once on the list it's safe and - * no longer device locked (it can be freed safely from the device queue) - */ - cli(); -#ifdef CONFIG_SLAVE_BALANCING - skb->in_dev_queue=1; - dev->pkt_queue++; -#endif - skb_device_unlock(skb); - skb_queue_head(dev->buffs + pri,skb); - restore_flags(flags); -} - -/* - * Receive a packet from a device driver and queue it for the upper - * (protocol) levels. It always succeeds. This is the recommended - * interface to use. - */ - -void netif_rx(struct sk_buff *skb) -{ - static int dropping = 0; - - /* - * Any received buffers are un-owned and should be discarded - * when freed. These will be updated later as the frames get - * owners. - */ - skb->sk = NULL; - skb->free = 1; - if(skb->stamp.tv_sec==0) - skb->stamp = xtime; - - /* - * Check that we aren't overdoing things. - */ - - if (!backlog_size) - dropping = 0; - else if (backlog_size > 300) - dropping = 1; - - if (dropping) - { - kfree_skb(skb, FREE_READ); - return; - } - - /* - * Add it to the "backlog" queue. - */ -#ifdef CONFIG_SKB_CHECK - IS_SKB(skb); -#endif - skb_queue_tail(&backlog,skb); - backlog_size++; - - /* - * If any packet arrived, mark it for processing after the - * hardware interrupt returns. - */ - - mark_bh(NET_BH); - return; -} - - -/* - * The old interface to fetch a packet from a device driver. - * This function is the base level entry point for all drivers that - * want to send a packet to the upper (protocol) levels. It takes - * care of de-multiplexing the packet to the various modules based - * on their protocol ID. - * - * Return values: 1 <- exit I can't do any more - * 0 <- feed me more (i.e. "done", "OK"). - * - * This function is OBSOLETE and should not be used by any new - * device. - */ - -int dev_rint(unsigned char *buff, long len, int flags, struct device *dev) -{ - static int dropping = 0; - struct sk_buff *skb = NULL; - unsigned char *to; - int amount, left; - int len2; - - if (dev == NULL || buff == NULL || len <= 0) - return(1); - - if (flags & IN_SKBUFF) - { - skb = (struct sk_buff *) buff; - } - else - { - if (dropping) - { - if (skb_peek(&backlog) != NULL) - return(1); - printk("INET: dev_rint: no longer dropping packets.\n"); - dropping = 0; - } - - skb = alloc_skb(len, GFP_ATOMIC); - if (skb == NULL) - { - printk("dev_rint: packet dropped on %s (no memory) !\n", - dev->name); - dropping = 1; - return(1); - } - - /* - * First we copy the packet into a buffer, and save it for later. We - * in effect handle the incoming data as if it were from a circular buffer - */ - - to = skb->data; - left = len; - - len2 = len; - while (len2 > 0) - { - amount = min(len2, (unsigned long) dev->rmem_end - - (unsigned long) buff); - memcpy(to, buff, amount); - len2 -= amount; - left -= amount; - buff += amount; - to += amount; - if ((unsigned long) buff == dev->rmem_end) - buff = (unsigned char *) dev->rmem_start; - } - } - - /* - * Tag the frame and kick it to the proper receive routine - */ - - skb->len = len; - skb->dev = dev; - skb->free = 1; - - netif_rx(skb); - /* - * OK, all done. - */ - return(0); -} - - -/* - * This routine causes all interfaces to try to send some data. - */ - -void dev_transmit(void) -{ - struct device *dev; - - for (dev = dev_base; dev != NULL; dev = dev->next) - { - if (dev->flags != 0 && !dev->tbusy) { - /* - * Kick the device - */ - dev_tint(dev); - } - } -} - - -/********************************************************************************** - - Receive Queue Processor - -***********************************************************************************/ - -/* - * This is a single non-reentrant routine which takes the received packet - * queue and throws it at the networking layers in the hope that something - * useful will emerge. - */ - -volatile char in_bh = 0; /* Non-reentrant remember */ - -int in_net_bh() /* Used by timer.c */ -{ - return(in_bh==0?0:1); -} - -/* - * When we are called the queue is ready to grab, the interrupts are - * on and hardware can interrupt and queue to the receive queue a we - * run with no problems. - * This is run as a bottom half after an interrupt handler that does - * mark_bh(NET_BH); - */ - -void net_bh(void *tmp) -{ - struct sk_buff *skb; - struct packet_type *ptype; - struct packet_type *pt_prev; - unsigned short type; - - /* - * Atomically check and mark our BUSY state. - */ - - if (set_bit(1, (void*)&in_bh)) - return; - - /* - * Can we send anything now? We want to clear the - * decks for any more sends that get done as we - * process the input. - */ - - dev_transmit(); - - /* - * Any data left to process. This may occur because a - * mark_bh() is done after we empty the queue including - * that from the device which does a mark_bh() just after - */ - - cli(); - - /* - * While the queue is not empty - */ - - while((skb=skb_dequeue(&backlog))!=NULL) - { - /* - * We have a packet. Therefore the queue has shrunk - */ - backlog_size--; - - sti(); - - /* - * Bump the pointer to the next structure. - * This assumes that the basic 'skb' pointer points to - * the MAC header, if any (as indicated by its "length" - * field). Take care now! - */ - - skb->h.raw = skb->data + skb->dev->hard_header_len; - skb->len -= skb->dev->hard_header_len; - - /* - * Fetch the packet protocol ID. This is also quite ugly, as - * it depends on the protocol driver (the interface itself) to - * know what the type is, or where to get it from. The Ethernet - * interfaces fetch the ID from the two bytes in the Ethernet MAC - * header (the h_proto field in struct ethhdr), but other drivers - * may either use the ethernet ID's or extra ones that do not - * clash (eg ETH_P_AX25). We could set this before we queue the - * frame. In fact I may change this when I have time. - */ - - type = skb->dev->type_trans(skb, skb->dev); - - /* - * We got a packet ID. Now loop over the "known protocols" - * table (which is actually a linked list, but this will - * change soon if I get my way- FvK), and forward the packet - * to anyone who wants it. - * - * [FvK didn't get his way but he is right this ought to be - * hashed so we typically get a single hit. The speed cost - * here is minimal but no doubt adds up at the 4,000+ pkts/second - * rate we can hit flat out] - */ - pt_prev = NULL; - for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) - { - if ((ptype->type == type || ptype->type == htons(ETH_P_ALL)) && (!ptype->dev || ptype->dev==skb->dev)) - { - /* - * We already have a match queued. Deliver - * to it and then remember the new match - */ - if(pt_prev) - { - struct sk_buff *skb2; - - skb2=skb_clone(skb, GFP_ATOMIC); - - /* - * Kick the protocol handler. This should be fast - * and efficient code. - */ - - if(skb2) - pt_prev->func(skb2, skb->dev, pt_prev); - } - /* Remember the current last to do */ - pt_prev=ptype; - } - } /* End of protocol list loop */ - - /* - * Is there a last item to send to ? - */ - - if(pt_prev) - pt_prev->func(skb, skb->dev, pt_prev); - /* - * Has an unknown packet has been received ? - */ - - else - kfree_skb(skb, FREE_WRITE); - - /* - * Again, see if we can transmit anything now. - * [Ought to take this out judging by tests it slows - * us down not speeds us up] - */ - - dev_transmit(); - cli(); - } /* End of queue loop */ - - /* - * We have emptied the queue - */ - - in_bh = 0; - sti(); - - /* - * One last output flush. - */ - - dev_transmit(); -} - - -/* - * This routine is called when an device driver (i.e. an - * interface) is ready to transmit a packet. - */ - -void dev_tint(struct device *dev) -{ - int i; - struct sk_buff *skb; - unsigned long flags; - - save_flags(flags); - /* - * Work the queues in priority order - */ - - for(i = 0;i < DEV_NUMBUFFS; i++) - { - /* - * Pull packets from the queue - */ - - - cli(); - while((skb=skb_dequeue(&dev->buffs[i]))!=NULL) - { - /* - * Stop anyone freeing the buffer while we retransmit it - */ - skb_device_lock(skb); - restore_flags(flags); - /* - * Feed them to the output stage and if it fails - * indicate they re-queue at the front. - */ - dev_queue_xmit(skb,dev,-i - 1); - /* - * If we can take no more then stop here. - */ - if (dev->tbusy) - return; - cli(); - } - } - restore_flags(flags); -} - - -/* - * Perform a SIOCGIFCONF call. This structure will change - * size shortly, and there is nothing I can do about it. - * Thus we will need a 'compatibility mode'. - */ - -static int dev_ifconf(char *arg) -{ - struct ifconf ifc; - struct ifreq ifr; - struct device *dev; - char *pos; - int len; - int err; - - /* - * Fetch the caller's info block. - */ - - err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf)); - if(err) - return err; - memcpy_fromfs(&ifc, arg, sizeof(struct ifconf)); - len = ifc.ifc_len; - pos = ifc.ifc_buf; - - /* - * We now walk the device list filling each active device - * into the array. - */ - - err=verify_area(VERIFY_WRITE,pos,len); - if(err) - return err; - - /* - * Loop over the interfaces, and write an info block for each. - */ - - for (dev = dev_base; dev != NULL; dev = dev->next) - { - if(!(dev->flags & IFF_UP)) /* Downed devices don't count */ - continue; - memset(&ifr, 0, sizeof(struct ifreq)); - strcpy(ifr.ifr_name, dev->name); - (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family; - (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr; - - /* - * Write this block to the caller's space. - */ - - memcpy_tofs(pos, &ifr, sizeof(struct ifreq)); - pos += sizeof(struct ifreq); - len -= sizeof(struct ifreq); - - /* - * Have we run out of space here ? - */ - - if (len < sizeof(struct ifreq)) - break; - } - - /* - * All done. Write the updated control block back to the caller. - */ - - ifc.ifc_len = (pos - ifc.ifc_buf); - ifc.ifc_req = (struct ifreq *) ifc.ifc_buf; - memcpy_tofs(arg, &ifc, sizeof(struct ifconf)); - - /* - * Report how much was filled in - */ - - return(pos - arg); -} - - -/* - * This is invoked by the /proc filesystem handler to display a device - * in detail. - */ - -static int sprintf_stats(char *buffer, struct device *dev) -{ - struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL); - int size; - - if (stats) - size = sprintf(buffer, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n", - dev->name, - stats->rx_packets, stats->rx_errors, - stats->rx_dropped + stats->rx_missed_errors, - stats->rx_fifo_errors, - stats->rx_length_errors + stats->rx_over_errors - + stats->rx_crc_errors + stats->rx_frame_errors, - stats->tx_packets, stats->tx_errors, stats->tx_dropped, - stats->tx_fifo_errors, stats->collisions, - stats->tx_carrier_errors + stats->tx_aborted_errors - + stats->tx_window_errors + stats->tx_heartbeat_errors); - else - size = sprintf(buffer, "%6s: No statistics available.\n", dev->name); - - return size; -} - -/* - * Called from the PROCfs module. This now uses the new arbitrary sized /proc/net interface - * to create /proc/net/dev - */ - -int dev_get_info(char *buffer, char **start, off_t offset, int length) -{ - int len=0; - off_t begin=0; - off_t pos=0; - int size; - - struct device *dev; - - - size = sprintf(buffer, "Inter-| Receive | Transmit\n" - " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n"); - - pos+=size; - len+=size; - - - for (dev = dev_base; dev != NULL; dev = dev->next) - { - size = sprintf_stats(buffer+len, dev); - len+=size; - pos=begin+len; - - if(pos<offset) - { - len=0; - begin=pos; - } - if(pos>offset+length) - break; - } - - *start=buffer+(offset-begin); /* Start of wanted data */ - len-=(offset-begin); /* Start slop */ - if(len>length) - len=length; /* Ending slop */ - return len; -} - - -/* - * This checks bitmasks for the ioctl calls for devices. - */ - -static inline int bad_mask(unsigned long mask, unsigned long addr) -{ - if (addr & (mask = ~mask)) - return 1; - mask = ntohl(mask); - if (mask & (mask+1)) - return 1; - return 0; -} - -#ifndef _HURD_ -/* - * Perform the SIOCxIFxxx calls. - * - * The socket layer has seen an ioctl the address family thinks is - * for the device. At this point we get invoked to make a decision - */ - -static int dev_ifsioc(void *arg, unsigned int getset) -{ - struct ifreq ifr; - struct device *dev; - int ret; - - /* - * Fetch the caller's info block into kernel space - */ - - int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq)); - if(err) - return err; - - memcpy_fromfs(&ifr, arg, sizeof(struct ifreq)); - - /* - * See which interface the caller is talking about. - */ - - if ((dev = dev_get(ifr.ifr_name)) == NULL) - return(-ENODEV); - - switch(getset) - { - case SIOCGIFFLAGS: /* Get interface flags */ - ifr.ifr_flags = dev->flags; - memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); - ret = 0; - break; - case SIOCSIFFLAGS: /* Set interface flags */ - { - int old_flags = dev->flags; -#ifdef CONFIG_SLAVE_BALANCING - if(dev->flags&IFF_SLAVE) - return -EBUSY; -#endif - dev->flags = ifr.ifr_flags & ( - IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK | - IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING | - IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER - | IFF_MULTICAST); -#ifdef CONFIG_SLAVE_BALANCING - if(!(dev->flags&IFF_MASTER) && dev->slave) - { - dev->slave->flags&=~IFF_SLAVE; - dev->slave=NULL; - } -#endif - /* - * Load in the correct multicast list now the flags have changed. - */ - - dev_mc_upload(dev); -#if 0 - if( dev->set_multicast_list!=NULL) - { - - /* - * Has promiscuous mode been turned off - */ - - if ( (old_flags & IFF_PROMISC) && ((dev->flags & IFF_PROMISC) == 0)) - dev->set_multicast_list(dev,0,NULL); - - /* - * Has it been turned on - */ - - if ( (dev->flags & IFF_PROMISC) && ((old_flags & IFF_PROMISC) == 0)) - dev->set_multicast_list(dev,-1,NULL); - } -#endif - /* - * Have we downed the interface - */ - - if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0)) - { - ret = dev_close(dev); - } - else - { - /* - * Have we upped the interface - */ - - ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP)) - ? dev_open(dev) : 0; - /* - * Check the flags. - */ - if(ret<0) - dev->flags&=~IFF_UP; /* Didn't open so down the if */ - } - } - break; - - case SIOCGIFADDR: /* Get interface address (and family) */ - (*(struct sockaddr_in *) - &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr; - (*(struct sockaddr_in *) - &ifr.ifr_addr).sin_family = dev->family; - (*(struct sockaddr_in *) - &ifr.ifr_addr).sin_port = 0; - memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); - ret = 0; - break; - - case SIOCSIFADDR: /* Set interface address (and family) */ - dev->pa_addr = (*(struct sockaddr_in *) - &ifr.ifr_addr).sin_addr.s_addr; - dev->family = ifr.ifr_addr.sa_family; - -#ifdef CONFIG_INET - /* This is naughty. When net-032e comes out It wants moving into the net032 - code not the kernel. Till then it can sit here (SIGH) */ - dev->pa_mask = ip_get_mask(dev->pa_addr); -#endif - dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask; - ret = 0; - break; - - case SIOCGIFBRDADDR: /* Get the broadcast address */ - (*(struct sockaddr_in *) - &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr; - (*(struct sockaddr_in *) - &ifr.ifr_broadaddr).sin_family = dev->family; - (*(struct sockaddr_in *) - &ifr.ifr_broadaddr).sin_port = 0; - memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); - ret = 0; - break; - - case SIOCSIFBRDADDR: /* Set the broadcast address */ - dev->pa_brdaddr = (*(struct sockaddr_in *) - &ifr.ifr_broadaddr).sin_addr.s_addr; - ret = 0; - break; - - case SIOCGIFDSTADDR: /* Get the destination address (for point-to-point links) */ - (*(struct sockaddr_in *) - &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr; - (*(struct sockaddr_in *) - &ifr.ifr_broadaddr).sin_family = dev->family; - (*(struct sockaddr_in *) - &ifr.ifr_broadaddr).sin_port = 0; - memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); - ret = 0; - break; - - case SIOCSIFDSTADDR: /* Set the destination address (for point-to-point links) */ - dev->pa_dstaddr = (*(struct sockaddr_in *) - &ifr.ifr_dstaddr).sin_addr.s_addr; - ret = 0; - break; - - case SIOCGIFNETMASK: /* Get the netmask for the interface */ - (*(struct sockaddr_in *) - &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask; - (*(struct sockaddr_in *) - &ifr.ifr_netmask).sin_family = dev->family; - (*(struct sockaddr_in *) - &ifr.ifr_netmask).sin_port = 0; - memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); - ret = 0; - break; - - case SIOCSIFNETMASK: /* Set the netmask for the interface */ - { - unsigned long mask = (*(struct sockaddr_in *) - &ifr.ifr_netmask).sin_addr.s_addr; - ret = -EINVAL; - /* - * The mask we set must be legal. - */ - if (bad_mask(mask,0)) - break; - dev->pa_mask = mask; - ret = 0; - } - break; - - case SIOCGIFMETRIC: /* Get the metric on the interface (currently unused) */ - - ifr.ifr_metric = dev->metric; - memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); - ret = 0; - break; - - case SIOCSIFMETRIC: /* Set the metric on the interface (currently unused) */ - dev->metric = ifr.ifr_metric; - ret = 0; - break; - - case SIOCGIFMTU: /* Get the MTU of a device */ - ifr.ifr_mtu = dev->mtu; - memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); - ret = 0; - break; - - case SIOCSIFMTU: /* Set the MTU of a device */ - - /* - * MTU must be positive and under the page size problem - */ - - if(ifr.ifr_mtu<1 || ifr.ifr_mtu>3800) - return -EINVAL; - dev->mtu = ifr.ifr_mtu; - ret = 0; - break; - - case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently - do not support it */ - printk("NET: ioctl(SIOCGIFMEM, %p)\n", arg); - ret = -EINVAL; - break; - - case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */ - printk("NET: ioctl(SIOCSIFMEM, %p)\n", arg); - ret = -EINVAL; - break; - - case OLD_SIOCGIFHWADDR: /* Get the hardware address. This will change and SIFHWADDR will be added */ - memcpy(ifr.old_ifr_hwaddr,dev->dev_addr, MAX_ADDR_LEN); - memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); - ret=0; - break; - - case SIOCGIFHWADDR: - memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN); - ifr.ifr_hwaddr.sa_family=dev->type; - memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); - ret=0; - break; - - case SIOCSIFHWADDR: - if(dev->set_mac_address==NULL) - return -EOPNOTSUPP; - if(ifr.ifr_hwaddr.sa_family!=dev->type) - return -EINVAL; - ret=dev->set_mac_address(dev,ifr.ifr_hwaddr.sa_data); - break; - - case SIOCGIFMAP: - ifr.ifr_map.mem_start=dev->mem_start; - ifr.ifr_map.mem_end=dev->mem_end; - ifr.ifr_map.base_addr=dev->base_addr; - ifr.ifr_map.irq=dev->irq; - ifr.ifr_map.dma=dev->dma; - ifr.ifr_map.port=dev->if_port; - memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); - ret=0; - break; - - case SIOCSIFMAP: - if(dev->set_config==NULL) - return -EOPNOTSUPP; - return dev->set_config(dev,&ifr.ifr_map); - - case SIOCGIFSLAVE: -#ifdef CONFIG_SLAVE_BALANCING - if(dev->slave==NULL) - return -ENOENT; - strncpy(ifr.ifr_name,dev->name,sizeof(ifr.ifr_name)); - memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); - ret=0; -#else - return -ENOENT; -#endif - break; -#ifdef CONFIG_SLAVE_BALANCING - case SIOCSIFSLAVE: - { - - /* - * Fun game. Get the device up and the flags right without - * letting some scummy user confuse us. - */ - unsigned long flags; - struct device *slave=dev_get(ifr.ifr_slave); - save_flags(flags); - if(slave==NULL) - { - return -ENODEV; - } - cli(); - if((slave->flags&(IFF_UP|IFF_RUNNING))!=(IFF_UP|IFF_RUNNING)) - { - restore_flags(flags); - return -EINVAL; - } - if(dev->flags&IFF_SLAVE) - { - restore_flags(flags); - return -EBUSY; - } - if(dev->slave!=NULL) - { - restore_flags(flags); - return -EBUSY; - } - if(slave->flags&IFF_SLAVE) - { - restore_flags(flags); - return -EBUSY; - } - dev->slave=slave; - slave->flags|=IFF_SLAVE; - dev->flags|=IFF_MASTER; - restore_flags(flags); - ret=0; - } - break; -#endif - - case SIOCADDMULTI: - if(dev->set_multicast_list==NULL) - return -EINVAL; - if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC) - return -EINVAL; - dev_mc_add(dev,ifr.ifr_hwaddr.sa_data, dev->addr_len, 1); - return 0; - - case SIOCDELMULTI: - if(dev->set_multicast_list==NULL) - return -EINVAL; - if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC) - return -EINVAL; - dev_mc_delete(dev,ifr.ifr_hwaddr.sa_data,dev->addr_len, 1); - return 0; - /* - * Unknown or private ioctl - */ - - default: - if((getset >= SIOCDEVPRIVATE) && - (getset <= (SIOCDEVPRIVATE + 15))) { - if(dev->do_ioctl==NULL) - return -EOPNOTSUPP; - ret=dev->do_ioctl(dev, &ifr, getset); - memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); - break; - } - - ret = -EINVAL; - } - return(ret); -} - - -/* - * This function handles all "interface"-type I/O control requests. The actual - * 'doing' part of this is dev_ifsioc above. - */ - -int dev_ioctl(unsigned int cmd, void *arg) -{ - switch(cmd) - { - case SIOCGIFCONF: - (void) dev_ifconf((char *) arg); - return 0; - - /* - * Ioctl calls that can be done by all. - */ - - case SIOCGIFFLAGS: - case SIOCGIFADDR: - case SIOCGIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCGIFMETRIC: - case SIOCGIFMTU: - case SIOCGIFMEM: - case SIOCGIFHWADDR: - case SIOCSIFHWADDR: - case OLD_SIOCGIFHWADDR: - case SIOCGIFSLAVE: - case SIOCGIFMAP: - return dev_ifsioc(arg, cmd); - - /* - * Ioctl calls requiring the power of a superuser - */ - - case SIOCSIFFLAGS: - case SIOCSIFADDR: - case SIOCSIFDSTADDR: - case SIOCSIFBRDADDR: - case SIOCSIFNETMASK: - case SIOCSIFMETRIC: - case SIOCSIFMTU: - case SIOCSIFMEM: - case SIOCSIFMAP: - case SIOCSIFSLAVE: - case SIOCADDMULTI: - case SIOCDELMULTI: - if (!suser()) - return -EPERM; - return dev_ifsioc(arg, cmd); - - case SIOCSIFLINK: - return -EINVAL; - - /* - * Unknown or private ioctl. - */ - - default: - if((cmd >= SIOCDEVPRIVATE) && - (cmd <= (SIOCDEVPRIVATE + 15))) { - return dev_ifsioc(arg, cmd); - } - return -EINVAL; - } -} -#endif - - -/* - * Initialize the DEV module. At boot time this walks the device list and - * unhooks any devices that fail to initialise (normally hardware not - * present) and leaves us with a valid list of present and active devices. - * - * The PCMCIA code may need to change this a little, and add a pair - * of register_inet_device() unregister_inet_device() calls. This will be - * needed for ethernet as modules support. - */ - -void dev_init(void) -{ - struct device *dev, *dev2; - - /* - * Add the devices. - * If the call to dev->init fails, the dev is removed - * from the chain disconnecting the device until the - * next reboot. - */ - - dev2 = NULL; - for (dev = dev_base; dev != NULL; dev=dev->next) - { - if (dev->init && dev->init(dev)) - { - /* - * It failed to come up. Unhook it. - */ - - if (dev2 == NULL) - dev_base = dev->next; - else - dev2->next = dev->next; - } - else - { - dev2 = dev; - } - } -} |