Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

linux_class_net.c

00001 /***************************************************************************
00002  * CVSID: $Id: linux_class_net.c,v 1.10 2004/03/03 17:56:56 david Exp $
00003  *
00004  * linux_class_net.c : Network device functions on Linux 2.6
00005  *
00006  * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
00007  *
00008  * Some parts of this file is based on mii-diag.c, Copyright 1997-2003 by
00009  * Donald Becker <becker@scyld.com>
00010  *
00011  * This program is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024  *
00025  **************************************************************************/
00026 
00027 #ifdef HAVE_CONFIG_H
00028 #  include <config.h>
00029 #endif
00030 
00031 #include <ctype.h>
00032 #include <stdio.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <getopt.h>
00036 #include <assert.h>
00037 #include <unistd.h>
00038 #include <stdarg.h>
00039 
00040 #include <glib.h>
00041 
00042 #include <errno.h>
00043 #include <net/if.h>
00044 #include <sys/ioctl.h>
00045 #include <sys/socket.h>
00046 
00047 #include <net/if_arp.h>     /* for ARPHRD_ETHER etc. */
00048 
00049 #include "../hald.h"
00050 #include "../logger.h"
00051 #include "../device_store.h"
00052 #include "linux_class_net.h"
00053 
00062 /* fwd decl */
00063 static void visit_class_device_net_got_sysdevice (HalDevice * parent,
00064                           void *data1,
00065                           void *data2);
00066 
00075 void
00076 visit_class_device_net (const char *path,
00077             struct sysfs_class_device *class_device)
00078 {
00079     int i;
00080     int len;
00081     HalDevice *d;
00082     struct sysfs_attribute *cur;
00083     char attr_name[SYSFS_NAME_LEN];
00084     char *addr_store = NULL;
00085     int media_type = 0;
00086     char *media;
00087 
00088     if (class_device->sysdevice == NULL) {
00089         HAL_WARNING (("Net class device at sysfs path %s doesn't "
00090                   "have sysdevice", path));
00091         return;
00092     }
00093 
00094     d = ds_device_new ();
00095     ds_property_set_string (d, "net.interface", class_device->name);
00096     ds_property_set_string (d, "net.linux.sysfs_path", path);
00097 
00098     dlist_for_each_data (sysfs_get_classdev_attributes (class_device),
00099                  cur, struct sysfs_attribute) {
00100         if (sysfs_get_name_from_path
00101             (cur->path, attr_name, SYSFS_NAME_LEN) != 0)
00102             continue;
00103 
00104         /* strip whitespace */
00105         len = strlen (cur->value);
00106         for (i = len - 1; i >= 0 && isspace (cur->value[i]); --i)
00107             cur->value[i] = '\0';
00108 
00109         if (strcmp (attr_name, "address") == 0) {
00110             addr_store = cur->value;
00111         } else if (strcmp (attr_name, "type") == 0) {
00112             media_type = parse_dec (cur->value);
00113         }
00114     }
00115 
00116     if (addr_store != NULL && media_type == ARPHRD_ETHER) {
00117         unsigned int a5, a4, a3, a2, a1, a0;
00118 
00119         ds_property_set_string (d, "net.ethernet.mac_addr",
00120                     addr_store);
00121 
00122         if (sscanf (addr_store, "%x:%x:%x:%x:%x:%x",
00123                 &a5, &a4, &a3, &a2, &a1, &a0) == 6) {
00124             dbus_uint32_t mac_upper, mac_lower;
00125 
00126             mac_upper = (a5 << 16) | (a4 << 8) | a3;
00127             mac_lower = (a2 << 16) | (a1 << 8) | a0;
00128 
00129             ds_property_set_int (d,
00130                          "net.ethernet.mac_addr_upper24",
00131                          (dbus_int32_t) mac_upper);
00132             ds_property_set_int (d,
00133                          "net.ethernet.mac_addr_lower24",
00134                          (dbus_int32_t) mac_lower);
00135         }
00136     }
00137 
00138 
00139     /* check for driver */
00140     if (class_device->driver != NULL) {
00141         ds_property_set_string (d, "linux.driver",
00142                     class_device->driver->name);
00143     }
00144 
00145 
00146     ds_property_set_int (d, "net.arp_proto_hw_id", media_type);
00147 
00148     /* Always set capabilities as the last thing the addition of a 
00149      * capability triggers a signal to other apps using HAL, monitoring
00150      * daemons for instance
00151      */
00152 
00153     ds_property_set_string (d, "info.category", "net");
00154     ds_add_capability (d, "net");
00155 
00156     /* type is decimal according to net/core/net-sysfs.c and it
00157      * assumes values from /usr/include/net/if_arp.h. Either
00158      * way we store both the 
00159      */
00160     switch (media_type) {
00161     case ARPHRD_NETROM:
00162         media = "NET/ROM pseudo";
00163         break;
00164     case ARPHRD_ETHER:
00165         media = "Ethernet";
00166         ds_add_capability (d, "net.ethernet");
00167         break;
00168     case ARPHRD_EETHER:
00169         media = "Experimenal Ethernet";
00170         break;
00171     case ARPHRD_AX25:
00172         media = "AX.25 Level 2";
00173         break;
00174     case ARPHRD_PRONET:
00175         media = "PROnet tokenring";
00176         ds_add_capability (d, "net.tokenring");
00177         break;
00178     case ARPHRD_CHAOS:
00179         media = "Chaosnet";
00180         break;
00181     case ARPHRD_IEEE802:
00182         media = "IEEE802";
00183         break;
00184     case ARPHRD_ARCNET:
00185         media = "ARCnet";
00186         break;
00187     case ARPHRD_APPLETLK:
00188         media = "APPLEtalk";
00189         break;
00190     case ARPHRD_DLCI:
00191         media = "Frame Relay DLCI";
00192         break;
00193     case ARPHRD_ATM:
00194         media = "ATM";
00195         ds_add_capability (d, "net.atm");
00196         break;
00197     case ARPHRD_METRICOM:
00198         media = "Metricom STRIP (new IANA id)";
00199         break;
00200 #ifdef ARPHRD_IEEE1394
00201     case ARPHRD_IEEE1394:
00202         media = "IEEE1394 IPv4 - RFC 2734";
00203         break;
00204 #endif
00205     default:
00206         media = "Unknown";
00207         break;
00208     }
00209     ds_property_set_string (d, "net.media", media);
00210 
00211     /* Find the physical; this happens asynchronously as it might
00212      * be added later. If we are probing this can't happen so the
00213      * timeout is set to zero in that event..
00214      */
00215     ds_device_async_find_by_key_value_string
00216         ("linux.sysfs_path_device", class_device->sysdevice->path,
00217          FALSE, visit_class_device_net_got_sysdevice, (void *) d, NULL,
00218          is_probing ? 0 : HAL_LINUX_HOTPLUG_TIMEOUT);
00219 }
00220 
00228 static void
00229 visit_class_device_net_got_sysdevice (HalDevice * sysdevice,
00230                       void *data1, void *data2)
00231 {
00232     HalDevice *d = (HalDevice *) data1;
00233 
00234     if (sysdevice == NULL) {
00235         HAL_WARNING (("Sysdevice for a class net device never appeared!"));
00236     } else {
00237         /* merge information from temporary device into the physical
00238          * device 
00239          */
00240         ds_device_merge (sysdevice, d);
00241     }
00242 
00243     /* get rid of tempoary device; it was only a placeholder after all */
00244     ds_device_destroy (d);
00245 }
00246 
00247 
00248 
00250 typedef struct link_detection_if_s {
00251     HalDevice *device;      
00252     int skfd;           
00253     struct ifreq ifr;       
00254     int new_ioctl_nums;     
00255     dbus_uint16_t status_word_baseline;
00257     struct link_detection_if_s *next;
00258 } link_detection_if;
00259 
00261 static link_detection_if *link_detection_list_head = NULL;
00262 
00269 static dbus_uint16_t
00270 mdio_read (link_detection_if * iface, int location)
00271 {
00272     dbus_uint16_t *data = (dbus_uint16_t *) (&(iface->ifr.ifr_data));
00273 
00274     data[1] = location;
00275 
00276     if (ioctl (iface->skfd,
00277            iface->new_ioctl_nums ? 0x8948 : SIOCDEVPRIVATE + 1,
00278            &(iface->ifr)) < 0) {
00279         HAL_ERROR (("SIOCGMIIREG on %s failed: %s\n",
00280                 iface->ifr.ifr_name, strerror (errno)));
00281         return -1;
00282     }
00283     return data[3];
00284 }
00285 
00290 static void
00291 link_detection_process (link_detection_if * iface)
00292 {
00293     dbus_bool_t got_link = FALSE;
00294     dbus_uint16_t status_word;
00295     dbus_uint16_t link_word;
00296     dbus_uint16_t status_word_new;
00297 
00298     /*printf("iface = 0x%0x\n", iface); */
00299 
00300     status_word_new = mdio_read (iface, 1);
00301     if (status_word_new != iface->status_word_baseline) {
00302         iface->status_word_baseline = status_word_new;
00303 
00304         HAL_INFO (("Ethernet link status change on hal udi %s)",
00305                iface->device->udi));
00306 
00307         /* Read status_word again since some bits may be sticky */
00308         status_word = mdio_read (iface, 1);
00309 
00310         /* Refer to http://www.scyld.com/diag/mii-status.html for
00311          * the full explanation of the numbers
00312          *
00313          * 0x8000  Capable of 100baseT4.
00314          * 0x7800  Capable of 10/100 HD/FD (most common).
00315          * 0x0040  Preamble suppression permitted.
00316          * 0x0020  Autonegotiation complete.
00317          * 0x0010  Remote fault.
00318          * 0x0008  Capable of Autonegotiation.
00319          * 0x0004  Link established ("sticky"* on link failure)
00320          * 0x0002  Jabber detected ("sticky"* on transmit jabber)
00321          * 0x0001  Extended MII register exist.
00322          *
00323          */
00324 
00325         property_atomic_update_begin ();
00326 
00327         if ((status_word & 0x0016) == 0x0004) {
00328             ds_property_set_bool (iface->device,
00329                           "net.ethernet.link", TRUE);
00330             got_link = TRUE;
00331         } else {
00332             ds_property_set_bool (iface->device,
00333                           "net.ethernet.link", FALSE);
00334         }
00335 
00336         /* Read link word
00337          *
00338          * 0x8000  Link partner can send more info.
00339          * 0x4000  Link partner got our advertised abilities.
00340          * 0x2000  Fault detected by link partner (uncommon).
00341          * 0x0400  Flow control supported (currently uncommon)
00342          * 0x0200  100baseT4 supported (uncommon)
00343          * 0x0100  100baseTx-FD (full duplex) supported
00344          * 0x0080  100baseTx supported
00345          * 0x0040  10baseT-FD supported
00346          * 0x0020  10baseT supported
00347          * 0x001F  Protocol selection bits, always 0x0001 for Ethernet.
00348          */
00349         link_word = mdio_read (iface, 1);
00350 
00351 
00352         if (link_word & 0x0300) {
00353             ds_property_set_int (iface->device,
00354                          "net.ethernet.rate",
00355                          100 * 1000 * 1000);
00356         }
00357         if (link_word & 0x60) {
00358             ds_property_set_int (iface->device,
00359                          "net.ethernet.rate",
00360                          10 * 1000 * 1000);
00361         }
00362 
00363         property_atomic_update_end ();
00364 
00365         emit_condition (iface->device, "NetLinkEvent",
00366                 DBUS_TYPE_BOOLEAN, got_link,
00367                 DBUS_TYPE_INVALID);
00368     }
00369 }
00370 
00376 static gboolean
00377 link_detection_timer_handler (gpointer data)
00378 {
00379     link_detection_if *iface;
00380 
00381     for (iface = link_detection_list_head; iface != NULL;
00382          iface = iface->next)
00383         link_detection_process (iface);
00384 
00385     return TRUE;
00386 }
00387 
00392 static void
00393 link_detection_add (HalDevice * device)
00394 {
00395     const char *interface_name;
00396     link_detection_if *iface;
00397 
00398     iface = malloc (sizeof (link_detection_if));
00399     if (iface == NULL)
00400         DIE (("No memory"));
00401 
00402     interface_name = ds_property_get_string (device, "net.interface");
00403     if (interface_name == NULL) {
00404         HAL_WARNING (("device '%s' does not have net.interface\n",
00405                   device->udi));
00406         free (iface);
00407         return;
00408     }
00409 
00410     iface->device = device;
00411 
00412     snprintf (iface->ifr.ifr_name, IFNAMSIZ, interface_name);
00413 
00414     /* Open a basic socket. */
00415     if ((iface->skfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
00416         HAL_ERROR (("cannot open socket on interface %s; errno=%d\n",
00417                 interface_name, errno));
00418         free (iface);
00419         return;
00420     }
00421 
00422     if (ioctl (iface->skfd, 0x8947, &(iface->ifr)) >= 0) {
00423         iface->new_ioctl_nums = 1;
00424     } else if (ioctl (iface->skfd, SIOCDEVPRIVATE, &(iface->ifr)) >= 0) {
00425         iface->new_ioctl_nums = 0;
00426     } else {
00427         HAL_ERROR (("SIOCGMIIPHY on %s failed: %s\n",
00428                 iface->ifr.ifr_name, strerror (errno)));
00429         (void) close (iface->skfd);
00430         free (iface);
00431         return;
00432     }
00433 
00434     iface->status_word_baseline = 0x5555;
00435 
00436 
00437     link_detection_process (iface);
00438 
00439     iface->next = link_detection_list_head;
00440     link_detection_list_head = iface;
00441 }
00442 
00446 static void
00447 link_detection_remove (HalDevice * device)
00448 {
00449     link_detection_if *iface;
00450     link_detection_if *iface_prev = NULL;
00451 
00452     for (iface = link_detection_list_head; iface != NULL;
00453          iface = iface->next) {
00454         if (iface->device == device) {
00455 
00456             HAL_INFO (("Stopping ethernet link monitoring on "
00457                    "device %s", device->udi));
00458 
00459             if (iface_prev != NULL) {
00460                 iface_prev->next = iface->next;
00461             } else {
00462                 link_detection_list_head = iface->next;
00463             }
00464 
00465             close (iface->skfd);
00466             free (iface);
00467         }
00468 
00469         iface_prev = iface;
00470     }
00471 }
00472 
00473 
00481 static void
00482 new_capability (HalDevice * device, const char *capability,
00483         dbus_bool_t in_gdl)
00484 {
00485     if (in_gdl) {
00486         if (strcmp (capability, "net.ethernet") == 0) {
00487             link_detection_add (device);
00488         }
00489     }
00490 }
00491 
00497 static void
00498 gdl_changed (HalDevice * device, dbus_bool_t is_added)
00499 {
00500     if (is_added) {
00501         if (ds_query_capability (device, "net.ethernet")) {
00502             link_detection_add (device);
00503         }
00504     } else {
00505         /* We may not have added this device yet, but the 
00506          * callee checks for for this
00507          */
00508         link_detection_remove (device);
00509     }
00510 }
00511 
00512 
00513 
00514 
00518 void
00519 linux_class_net_init ()
00520 {
00521     g_timeout_add (1000, link_detection_timer_handler, NULL);
00522 
00523     /* We want to know when net.ethernet devices appear and disappear */
00524     ds_add_cb_newcap (new_capability);
00525     ds_add_cb_gdl_changed (gdl_changed);
00526 }
00527 
00532 void
00533 linux_class_net_detection_done ()
00534 {
00535 }
00536 
00540 void
00541 linux_class_net_shutdown ()
00542 {
00543 }
00544 

Generated on Thu Mar 11 21:32:22 2004 for HAL by doxygen 1.3.6-20040222