URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [net/] [core/] [dev.c] - Rev 1772
Go to most recent revision | Compare with Previous | Blame | View Log
/* * 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. * Alan Cox : Took out transmit every packet pass * Saved a few bytes in the ioctl handler * Alan Cox : Network driver sets packet type before calling netif_rx. Saves * a function call a packet. * Alan Cox : Hashed net_bh() * Richard Kooijman: Timestamp fixes. * Alan Cox : Wrong field in SIOCGIFDSTADDR * Alan Cox : Device lock protection. * Alan Cox : Fixed nasty side effect of device close changes. * Rudi Cilibrasi : Pass the right thing to set_mac_address() * Dave Miller : 32bit quantity for the device lock to make it work out * on a Sparc. * Bjorn Ekwall : Added KERNELD hack. * Alan Cox : Cleaned up the backlog initialise. * Craig Metz : SIOCGIFCONF fix if space for under * 1 device. * Thomas Bogendoerfer : Return ENODEV for dev_open, if there * is no device open function. * Lawrence V. Stefani : Changed set MTU ioctl to not assume * min MTU of 68 bytes for devices * that have change MTU functions. * */ #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 <net/ip.h> #include <net/route.h> #include <linux/skbuff.h> #include <net/sock.h> #include <net/arp.h> #include <net/slhc.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <net/br.h> #ifdef CONFIG_NET_ALIAS #include <linux/net_alias.h> #endif #ifdef CONFIG_KERNELD #include <linux/kerneld.h> #endif #ifdef CONFIG_NET_RADIO #include <linux/wireless.h> #endif /* CONFIG_NET_RADIO */ /* * The list of packet types we will receive (as opposed to discard) * and the routines to invoke. */ struct packet_type *ptype_base[16]; struct packet_type *ptype_all = NULL; /* Taps */ /* * Device list lock */ int dev_lockct=0; /* * 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; /* * 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) { int hash; if(pt->type==htons(ETH_P_ALL)) { dev_nit++; pt->next=ptype_all; ptype_all=pt; } else { hash=ntohs(pt->type)&15; pt->next = ptype_base[hash]; ptype_base[hash] = 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--; pt1=&ptype_all; } else pt1=&ptype_base[ntohs(pt->type)&15]; for(; (*pt1)!=NULL; pt1=&((*pt1)->next)) { if(pt==(*pt1)) { *pt1=pt->next; return; } } printk(KERN_WARNING "dev_remove_pack: %p not found.\n", pt); } /***************************************************************************************** Device Interface Subroutines ******************************************************************************************/ /* * Find an interface by name. */ struct device *dev_get(const char *name) { struct device *dev; for (dev = dev_base; dev != NULL; dev = dev->next) { if (strcmp(dev->name, name) == 0) return(dev); } return NULL; } /* * Find and possibly load an interface. */ #ifdef CONFIG_KERNELD extern __inline__ void dev_load(const char *name) { if(!dev_get(name) && suser()) { #ifdef CONFIG_NET_ALIAS const char *sptr; for (sptr=name ; *sptr ; sptr++) if(*sptr==':') break; if (!(*sptr && *(sptr+1))) #endif request_module(name); } } #endif /* * Prepare an interface for use. */ int dev_open(struct device *dev) { int ret = -ENODEV; /* * 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 */ dev_mc_upload(dev); notifier_call_chain(&netdev_chain, NETDEV_UP, dev); } return(ret); } /* * Completely shutdown an interface. */ int dev_close(struct device *dev) { int ct=0; /* * Call the device specific close. This cannot fail. * Only if device is UP */ if ((dev->flags & IFF_UP) && dev->stop) dev->stop(dev); /* * Device is now down. */ dev->flags&=~(IFF_UP|IFF_RUNNING); /* * Tell people we are going down */ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); /* * Flush the multicast chain */ dev_mc_discard(dev); /* * 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. */ static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) { unsigned long flags; struct sk_buff_head *list; int retransmission = 0; /* used to say if the packet should go */ /* at the front or the back of the */ /* queue - front is a retransmit try */ if(pri>=0 && !skb_device_locked(skb)) skb_device_lock(skb); /* Shove a lock on the frame */ #if CONFIG_SKB_CHECK IS_SKB(skb); #endif skb->dev = dev; /* * 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; retransmission = 1; } #ifdef CONFIG_NET_DEBUG if (pri >= DEV_NUMBUFFS) { printk(KERN_WARNING "bad priority in dev_queue_xmit.\n"); pri = 1; } #endif /* * 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; } /* * * If dev is an alias, switch to its main device. * "arp" resolution has been made with alias device, so * arp entries refer to alias, not main. * */ #ifdef CONFIG_NET_ALIAS if (net_alias_is(dev)) skb->dev = dev = net_alias_dev_tx(dev); #endif /* * If we are bridging and this is directly generated output * pass the frame via the bridge. */ #ifdef CONFIG_BRIDGE if(skb->pkt_bridged!=IS_BRIDGED && br_stats.flags & BR_UP) { if(br_tx_frame(skb)) return; } #endif list = dev->buffs + pri; save_flags(flags); /* if this isn't a retransmission, use the first packet instead... */ if (!retransmission) { if (skb_queue_len(list)) { /* avoid overrunning the device queue.. */ if (skb_queue_len(list) > dev->tx_queue_len) { dev_kfree_skb(skb, FREE_WRITE); return; } } /* copy outgoing packets to any sniffer packet handlers */ if (dev_nit) { struct packet_type *ptype; skb->stamp=xtime; for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) { /* Never send packets back to the socket * they originated from - MvS (miquels@drinkel.ow.org) */ if ((ptype->dev == dev || !ptype->dev) && ((struct sock *)ptype->data != skb->sk)) { struct sk_buff *skb2; if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) break; /* FIXME?: Wrong when the hard_header_len * is an upper bound. Is this even * used anywhere? */ skb2->h.raw = skb2->data + dev->hard_header_len; /* On soft header devices we * yank the header before mac.raw * back off. This is set by * dev->hard_header(). */ if (dev->flags&IFF_SOFTHEADERS) skb_pull(skb2,skb2->mac.raw-skb2->data); skb2->mac.raw = skb2->data; ptype->func(skb2, skb->dev, ptype); } } } if (skb_queue_len(list)) { cli(); skb_device_unlock(skb); /* Buffer is on the device queue and can be freed safely */ __skb_queue_tail(list, skb); skb = __skb_dequeue(list); skb_device_lock(skb); /* New buffer needs locking down */ restore_flags(flags); } } 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(); skb_device_unlock(skb); __skb_queue_head(list,skb); restore_flags(flags); } void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) { start_bh_atomic(); do_dev_queue_xmit(skb, dev, pri); end_bh_atomic(); } /* * 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. */ #if 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; } /* * This routine causes all interfaces to try to send some data. */ static 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 ***********************************************************************************/ /* * When we are called the queue is ready to grab, the interrupts are * on and hardware can interrupt and queue to the receive queue as 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) { struct packet_type *ptype; struct packet_type *pt_prev; unsigned short type; /* * Can we send anything now? We want to clear the * decks for any more sends that get done as we * process the input. This also minimises the * latency on a transmit interrupt bh. */ 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 */ /* * While the queue is not empty.. * * Note that the queue never shrinks due to * an interrupt, so we can do this test without * disabling interrupts. */ while (!skb_queue_empty(&backlog)) { struct sk_buff * skb = backlog.next; /* * We have a packet. Therefore the queue has shrunk */ cli(); __skb_unlink(skb, &backlog); backlog_size--; sti(); #ifdef CONFIG_BRIDGE /* * If we are bridging then pass the frame up to the * bridging code. If it is bridged then move on */ if (br_stats.flags & BR_UP) { /* * We pass the bridge a complete frame. This means * recovering the MAC header first. */ int offset=skb->data-skb->mac.raw; cli(); skb_push(skb,offset); /* Put header back on for bridge */ if(br_receive_frame(skb)) { sti(); continue; } /* * Pull the MAC header off for the copy going to * the upper layers. */ skb_pull(skb,offset); sti(); } #endif /* * Bump the pointer to the next structure. * * On entry to the protocol layer. skb->data and * skb->h.raw point to the MAC and encapsulated data */ skb->h.raw = skb->data; /* * Fetch the packet protocol ID. */ type = skb->protocol; /* * We got a packet ID. Now loop over the "known protocols" * list. There are two lists. The ptype_all list of taps (normally empty) * and the main protocol list which is hashed perfectly for normal protocols. */ pt_prev = NULL; for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next) { if(!ptype->dev || ptype->dev == skb->dev) { if(pt_prev) { struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC); if(skb2) pt_prev->func(skb2,skb->dev, pt_prev); } pt_prev=ptype; } } for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next) { if (ptype->type == type && (!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] */ #ifdef XMIT_EVERY dev_transmit(); #endif } /* End of queue loop */ /* * We have emptied the queue */ /* * One last output flush. */ #ifdef XMIT_AFTER dev_transmit(); #endif } /* * 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; unsigned long flags; struct sk_buff_head * head; /* * aliases do not transmit (for now :) ) */ #ifdef CONFIG_NET_ALIAS if (net_alias_is(dev)) return; #endif head = dev->buffs; save_flags(flags); cli(); /* * Work the queues in priority order */ for(i = 0;i < DEV_NUMBUFFS; i++,head++) { while (!skb_queue_empty(head)) { struct sk_buff *skb; skb = head->next; __skb_unlink(skb, head); /* * 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. */ do_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; /* * Have we run out of space here ? */ if (len < sizeof(struct ifreq)) break; 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); } /* * 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. */ #ifdef CONFIG_PROC_FS 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 dummy) { 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; } #endif /* CONFIG_PROC_FS */ #ifdef CONFIG_NET_RADIO #ifdef CONFIG_PROC_FS /* * Print one entry of /proc/net/wireless * This is a clone of /proc/net/dev (just above) */ static int sprintf_wireless_stats(char * buffer, struct device * dev) { /* Get stats from the driver */ struct iw_statistics *stats = (dev->get_wireless_stats ? dev->get_wireless_stats(dev) : (struct iw_statistics *) NULL); int size; if(stats != (struct iw_statistics *) NULL) size = sprintf(buffer, "%6s: %02x %3d%c %3d%c %3d%c %5d %5d %5d\n", dev->name, stats->status, stats->qual.qual, stats->qual.updated & 1 ? '.' : ' ', stats->qual.level, stats->qual.updated & 2 ? '.' : ' ', stats->qual.noise, stats->qual.updated & 3 ? '.' : ' ', stats->discard.nwid, stats->discard.code, stats->discard.misc); else size = 0; return size; } /* * Print info for /proc/net/wireless (print all entries) * This is a clone of /proc/net/dev (just above) */ int dev_get_wireless_info(char * buffer, char ** start, off_t offset, int length, int dummy) { int len = 0; off_t begin = 0; off_t pos = 0; int size; struct device * dev; size = sprintf(buffer, "Inter-|sta| Quality | Discarded packets\n" " face |tus|link level noise| nwid crypt misc\n"); pos+=size; len+=size; for(dev = dev_base; dev != NULL; dev = dev->next) { size = sprintf_wireless_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; } #endif /* CONFIG_PROC_FS */ #endif /* CONFIG_NET_RADIO */ /* * 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; } /* * 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. */ /* * * net_alias_dev_get(): dev_get() with added alias naming magic. * only allow alias creation/deletion if (getset==SIOCSIFADDR) * */ #ifdef CONFIG_KERNELD dev_load(ifr.ifr_name); #endif #ifdef CONFIG_NET_ALIAS if ((dev = net_alias_dev_get(ifr.ifr_name, getset == SIOCSIFADDR, &err, NULL, NULL)) == NULL) return(err); #else if ((dev = dev_get(ifr.ifr_name)) == NULL) return(-ENODEV); #endif switch(getset) { case SIOCGIFFLAGS: /* Get interface flags */ ifr.ifr_flags = (dev->flags & ~IFF_SOFTHEADERS); goto rarok; case SIOCSIFFLAGS: /* Set interface flags */ { int old_flags = dev->flags; if(securelevel>0) ifr.ifr_flags&=~IFF_PROMISC; /* * We are not allowed to potentially close/unload * a device until we get this lock. */ dev_lock_wait(); /* * Set the flags on our device. */ dev->flags = (ifr.ifr_flags & ( IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING | IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER | IFF_MULTICAST)) | (dev->flags & (IFF_SOFTHEADERS|IFF_UP)); /* * Load in the correct multicast list now the flags have changed. */ dev_mc_upload(dev); /* * Have we downed the interface. We handle IFF_UP ourselves * according to user attempts to set it, rather than blindly * setting it. */ if ((old_flags^ifr.ifr_flags)&IFF_UP) /* Bit is different ? */ { if(old_flags&IFF_UP) /* Gone down */ ret=dev_close(dev); else /* Come up */ { ret=dev_open(dev); if(ret<0) dev->flags&=~IFF_UP; /* Open failed */ } } else ret=0; /* * Load in the correct multicast list now the flags have changed. */ dev_mc_upload(dev); } break; case SIOCGIFADDR: /* Get interface address (and family) */ if(ifr.ifr_addr.sa_family==AF_UNSPEC) { memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN); ifr.ifr_hwaddr.sa_family=dev->type; goto rarok; } else { (*(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; } goto rarok; case SIOCSIFADDR: /* Set interface address (and family) */ /* * BSDism. SIOCSIFADDR family=AF_UNSPEC sets the * physical address. We can cope with this now. */ if(ifr.ifr_addr.sa_family==AF_UNSPEC) { if(dev->set_mac_address==NULL) return -EOPNOTSUPP; if(securelevel>0) return -EPERM; ret=dev->set_mac_address(dev,&ifr.ifr_addr); } else { u32 new_pa_addr = (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr; u16 new_family = ifr.ifr_addr.sa_family; if (new_family == dev->family && new_pa_addr == dev->pa_addr) { ret =0; break; } if (dev->flags & IFF_UP) notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); /* * if dev is an alias, must rehash to update * address change */ #ifdef CONFIG_NET_ALIAS if (net_alias_is(dev)) net_alias_dev_rehash(dev ,&ifr.ifr_addr); #endif dev->pa_addr = new_pa_addr; dev->family = new_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) */ if (!dev->pa_mask) dev->pa_mask = ip_get_mask(dev->pa_addr); #endif if (!dev->pa_brdaddr) dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask; if (dev->flags & IFF_UP) notifier_call_chain(&netdev_chain, NETDEV_UP, dev); 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; goto rarok; 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_dstaddr).sin_family = dev->family; (*(struct sockaddr_in *) &ifr.ifr_dstaddr).sin_port = 0; goto rarok; 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; goto rarok; 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; goto rarok; 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; goto rarok; case SIOCSIFMTU: /* Set the MTU of a device */ if (dev->change_mtu) ret = dev->change_mtu(dev, ifr.ifr_mtu); else { /* * MTU must be positive. */ if(ifr.ifr_mtu<68) 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 */ ret = -EINVAL; break; case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */ ret = -EINVAL; break; case SIOCGIFHWADDR: memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN); ifr.ifr_hwaddr.sa_family=dev->type; goto rarok; case SIOCSIFHWADDR: if(dev->set_mac_address==NULL) return -EOPNOTSUPP; if(securelevel > 0) return -EPERM; if(ifr.ifr_hwaddr.sa_family!=dev->type) return -EINVAL; ret=dev->set_mac_address(dev,&ifr.ifr_hwaddr); 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; goto rarok; case SIOCSIFMAP: if(dev->set_config==NULL) return -EOPNOTSUPP; return dev->set_config(dev,&ifr.ifr_map); 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; } #ifdef CONFIG_NET_RADIO if((getset >= SIOCIWFIRST) && (getset <= SIOCIWLAST)) { if(dev->do_ioctl==NULL) return -EOPNOTSUPP; /* Perform the ioctl */ ret=dev->do_ioctl(dev, &ifr, getset); /* If return args... */ if(IW_IS_GET(getset)) memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); break; } #endif /* CONFIG_NET_RADIO */ ret = -EINVAL; } return(ret); /* * The load of calls that return an ifreq and ok (saves memory). */ rarok: memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); return 0; } /* * 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 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 SIOCSIFHWADDR: 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); } #ifdef CONFIG_NET_RADIO if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { if((IW_IS_SET(cmd)) && (!suser())) return -EPERM; return dev_ifsioc(arg, cmd); } #endif /* CONFIG_NET_RADIO */ return -EINVAL; } } /* * 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. * */ extern int lance_init(void); extern int pi_init(void); extern int pt_init(void); extern int bpq_init(void); extern void sdla_setup(void); extern int dlci_setup(void); extern int sm_init(void); extern int baycom_init(void); #ifdef CONFIG_PROC_FS static struct proc_dir_entry pde1 = { PROC_NET_DEV, 3, "dev", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, dev_get_info }; #endif #ifdef CONFIG_NET_RADIO #ifdef CONFIG_PROC_FS static struct proc_dir_entry pde2 = { PROC_NET_WIRELESS, 8, "wireless", S_IFREG | S_IRUGO, 1, 0, 0, 0, &proc_net_inode_operations, dev_get_wireless_info }; #endif /* CONFIG_PROC_FS */ #endif /* CONFIG_NET_RADIO */ int net_dev_init(void) { struct device *dev, **dp; /* * Initialise the packet receive queue. */ skb_queue_head_init(&backlog); /* * The bridge has to be up before the devices */ #ifdef CONFIG_BRIDGE br_init(); #endif /* * This is Very Ugly(tm). * * Some devices want to be initialized early.. */ #if defined(CONFIG_PI) pi_init(); #endif #if defined(CONFIG_PT) pt_init(); #endif #if defined(CONFIG_BPQETHER) bpq_init(); #endif #if defined(CONFIG_DLCI) dlci_setup(); #endif #if defined(CONFIG_SDLA) sdla_setup(); #endif #if defined(CONFIG_BAYCOM) baycom_init(); #endif #if defined(CONFIG_SOUNDMODEM) sm_init(); #endif /* * SLHC if present needs attaching so other people see it * even if not opened. */ #if (defined(CONFIG_SLIP) && defined(CONFIG_SLIP_COMPRESSED)) \ || defined(CONFIG_PPP) \ || (defined(CONFIG_ISDN) && defined(CONFIG_ISDN_PPP)) slhc_install(); #endif /* * Add the devices. * If the call to dev->init fails, the dev is removed * from the chain disconnecting the device until the * next reboot. */ dp = &dev_base; while ((dev = *dp) != NULL) { int i; for (i = 0; i < DEV_NUMBUFFS; i++) { skb_queue_head_init(dev->buffs + i); } if (dev->init && dev->init(dev)) { /* * It failed to come up. Unhook it. */ *dp = dev->next; } else { dp = &dev->next; } } #ifdef CONFIG_PROC_FS proc_net_register(&pde1); #endif #ifdef CONFIG_NET_RADIO #ifdef CONFIG_PROC_FS proc_net_register(&pde2); #endif /* CONFIG_PROC_FS */ #endif /* CONFIG_NET_RADIO */ /* * Initialise net_alias engine * * - register net_alias device notifier * - register proc entries: /proc/net/alias_types * /proc/net/aliases */ #ifdef CONFIG_NET_ALIAS net_alias_init(); #endif init_bh(NET_BH, net_bh); return 0; }
Go to most recent revision | Compare with Previous | Blame | View Log