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.9 2004/01/13 02:01:04 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, void* data2);
00065 
00074 void visit_class_device_net(const char* path, 
00075                             struct sysfs_class_device* class_device)
00076 {
00077     int i;
00078     int len;
00079     HalDevice* d;
00080     struct sysfs_attribute* cur;
00081     char attr_name[SYSFS_NAME_LEN];
00082     char* addr_store = NULL;
00083     int media_type = 0;
00084     char* media;
00085 
00086     if( class_device->sysdevice==NULL )
00087     {
00088         HAL_WARNING(("Net class device at sysfs path %s doesn't have "
00089                      "sysdevice", path));
00090         return;
00091     }
00092 
00093     d = ds_device_new();
00094     ds_property_set_string(d, "net.interface", class_device->name);
00095     ds_property_set_string(d, "net.linux.sysfs_path", path);
00096 
00097     dlist_for_each_data(sysfs_get_classdev_attributes(class_device), cur,
00098                         struct sysfs_attribute)
00099     {
00100         if( sysfs_get_name_from_path(cur->path, 
00101                                      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         {
00111             addr_store = cur->value;
00112         }
00113         else if( strcmp(attr_name, "type")==0 )
00114         {
00115             media_type = parse_dec(cur->value);
00116         }
00117     }
00118 
00119     if( addr_store!=NULL && media_type==ARPHRD_ETHER )
00120     {
00121         unsigned int a5, a4, a3 ,a2, a1, a0;
00122 
00123         ds_property_set_string(d, "net.ethernet.mac_addr", addr_store);
00124 
00125         if( sscanf(addr_store, "%x:%x:%x:%x:%x:%x",
00126                    &a5, &a4, &a3, &a2, &a1, &a0)==6 )
00127         {
00128             dbus_uint32_t mac_upper, mac_lower;
00129 
00130             mac_upper = (a5<<16)|(a4<<8)|a3;
00131             mac_lower = (a2<<16)|(a1<<8)|a0;
00132 
00133             ds_property_set_int(d, "net.ethernet.mac_addr_upper24",
00134                                         (dbus_int32_t) mac_upper);
00135             ds_property_set_int(d, "net.ethernet.mac_addr_lower24",
00136                                         (dbus_int32_t) mac_lower);
00137         }
00138     }
00139 
00140 
00141     /* check for driver */
00142     if( class_device->driver!=NULL )
00143     {
00144         ds_property_set_string(d, "linux.driver", 
00145                                        class_device->driver->name);        
00146     }
00147 
00148 
00149     ds_property_set_int(d, "net.arp_proto_hw_id", 
00150                                 media_type);
00151 
00152     /* Always set capabilities as the last thing the addition of a 
00153      * capability triggers a signal to other apps using HAL, monitoring
00154      * daemons for instance
00155      */
00156 
00157     ds_property_set_string(d, "info.category", "net");
00158     ds_add_capability(d, "net");
00159 
00160     /* type is decimal according to net/core/net-sysfs.c and it
00161      * assumes values from /usr/include/net/if_arp.h. Either
00162      * way we store both the 
00163      */
00164     switch(media_type)
00165     {
00166     case ARPHRD_NETROM: 
00167         media="NET/ROM pseudo"; 
00168         break;
00169     case ARPHRD_ETHER: 
00170         media="Ethernet"; 
00171         ds_add_capability(d, "net.ethernet");
00172         break;
00173     case ARPHRD_EETHER: 
00174         media="Experimenal Ethernet"; 
00175         break;
00176     case ARPHRD_AX25: 
00177         media="AX.25 Level 2"; 
00178         break;
00179     case ARPHRD_PRONET: 
00180         media="PROnet tokenring"; 
00181         ds_add_capability(d, "net.tokenring");
00182         break;
00183     case ARPHRD_CHAOS: 
00184         media="Chaosnet"; 
00185         break;
00186     case ARPHRD_IEEE802: 
00187         media="IEEE802"; 
00188         break;
00189     case ARPHRD_ARCNET: 
00190         media="ARCnet"; 
00191         break;
00192     case ARPHRD_APPLETLK: 
00193         media="APPLEtalk"; 
00194         break;
00195     case ARPHRD_DLCI: 
00196         media="Frame Relay DLCI"; 
00197         break;
00198     case ARPHRD_ATM: 
00199         media="ATM"; 
00200         ds_add_capability(d, "net.atm");
00201         break;
00202     case ARPHRD_METRICOM: 
00203         media="Metricom STRIP (new IANA id)"; 
00204         break;
00205 #ifdef ARPHRD_IEEE1394
00206     case ARPHRD_IEEE1394: 
00207         media="IEEE1394 IPv4 - RFC 2734"; 
00208         break;
00209 #endif
00210     default: 
00211         media="Unknown"; 
00212         break;
00213     }
00214     ds_property_set_string(d, "net.media", media);
00215 
00216     /* Find the physical; this happens asynchronously as it might
00217      * be added later. If we are probing this can't happen so the
00218      * timeout is set to zero in that event..
00219      */
00220     ds_device_async_find_by_key_value_string(
00221         "linux.sysfs_path_device",
00222         class_device->sysdevice->path, 
00223         FALSE,
00224         visit_class_device_net_got_sysdevice,
00225         (void*) d, NULL, 
00226         is_probing ? 0 :
00227         HAL_LINUX_HOTPLUG_TIMEOUT);
00228 }
00229 
00237 static void visit_class_device_net_got_sysdevice(HalDevice* sysdevice, 
00238                                                  void* data1, void* data2)
00239 {
00240     HalDevice* d = (HalDevice*) data1;
00241 
00242     if( sysdevice==NULL )
00243     {
00244         HAL_WARNING(("Sysdevice for a class net device never appeared!"));
00245     }
00246     else
00247     {
00248         /* merge information from temporary device into the physical
00249          * device 
00250          */
00251         ds_device_merge(sysdevice, d);
00252     }
00253 
00254     /* get rid of tempoary device; it was only a placeholder after all */
00255     ds_device_destroy(d);
00256 }
00257 
00258 
00259 
00261 typedef struct link_detection_if_s
00262 {
00263     HalDevice* device;                  
00264     int skfd;                           
00265     struct ifreq ifr;                   
00266     int new_ioctl_nums;                 
00267     dbus_uint16_t status_word_baseline; 
00268     struct link_detection_if_s* next;    
00269 } link_detection_if;
00270 
00272 static link_detection_if* link_detection_list_head = NULL;
00273 
00280 static dbus_uint16_t mdio_read(link_detection_if* iface, int location)
00281 {
00282     dbus_uint16_t *data = (dbus_uint16_t *)(&(iface->ifr.ifr_data));
00283 
00284     data[1] = location;
00285 
00286     if( ioctl(iface->skfd, 
00287               iface->new_ioctl_nums ? 0x8948 : SIOCDEVPRIVATE+1, 
00288               &(iface->ifr)) < 0)
00289     {
00290         HAL_ERROR(("SIOCGMIIREG on %s failed: %s\n", 
00291                    iface->ifr.ifr_name, strerror(errno)));
00292         return -1;
00293     }
00294     return data[3];
00295 }
00296 
00301 static void link_detection_process(link_detection_if* iface)
00302 {
00303     dbus_bool_t got_link = FALSE;
00304     dbus_uint16_t status_word;
00305     dbus_uint16_t link_word;
00306     dbus_uint16_t status_word_new;
00307 
00308     /*printf("iface = 0x%0x\n", iface);*/
00309 
00310     status_word_new = mdio_read(iface, 1);
00311     if( status_word_new!=iface->status_word_baseline )
00312     {
00313         iface->status_word_baseline = status_word_new;
00314 
00315         HAL_INFO(("Ethernet link status change on hal udi %s)",
00316                   iface->device->udi));
00317 
00318         /* Read status_word again since some bits may be sticky */
00319         status_word = mdio_read(iface, 1);
00320 
00321         /* Refer to http://www.scyld.com/diag/mii-status.html for
00322          * the full explanation of the numbers
00323          *
00324          * 0x8000  Capable of 100baseT4.
00325          * 0x7800  Capable of 10/100 HD/FD (most common).
00326          * 0x0040  Preamble suppression permitted.
00327          * 0x0020  Autonegotiation complete.
00328          * 0x0010  Remote fault.
00329          * 0x0008  Capable of Autonegotiation.
00330          * 0x0004  Link established ("sticky"* on link failure)
00331          * 0x0002  Jabber detected ("sticky"* on transmit jabber)
00332          * 0x0001  Extended MII register exist.
00333          *
00334          */
00335 
00336         property_atomic_update_begin();
00337 
00338         if( (status_word&0x0016)==0x0004 )
00339         {
00340             ds_property_set_bool(iface->device, 
00341                                  "net.ethernet.link", TRUE);
00342             got_link = TRUE;
00343         }
00344         else
00345         {
00346             ds_property_set_bool(iface->device, 
00347                                  "net.ethernet.link", FALSE);
00348         }
00349 
00350         /* Read link word
00351          *
00352          * 0x8000  Link partner can send more info.
00353          * 0x4000  Link partner got our advertised abilities.
00354          * 0x2000  Fault detected by link partner (uncommon).
00355          * 0x0400  Flow control supported (currently uncommon)
00356          * 0x0200  100baseT4 supported (uncommon)
00357          * 0x0100  100baseTx-FD (full duplex) supported
00358          * 0x0080  100baseTx supported
00359          * 0x0040  10baseT-FD supported
00360          * 0x0020  10baseT supported
00361          * 0x001F  Protocol selection bits, always 0x0001 for Ethernet.
00362          */
00363         link_word = mdio_read(iface, 1);
00364 
00365         
00366         if( link_word&0x0300 )
00367         {
00368             ds_property_set_int(iface->device, "net.ethernet.rate", 
00369                                 100*1000*1000);
00370         }
00371         if( link_word&0x60 )
00372         {
00373             ds_property_set_int(iface->device, "net.ethernet.rate", 
00374                                 10*1000*1000);
00375         }
00376 
00377         property_atomic_update_end();
00378 
00379         emit_condition(iface->device, "NetLinkEvent", 
00380                        DBUS_TYPE_BOOLEAN, got_link,
00381                        DBUS_TYPE_INVALID);
00382     }
00383 }
00384 
00390 static gboolean link_detection_timer_handler(gpointer data)
00391 {
00392     link_detection_if* iface;
00393 
00394     for(iface=link_detection_list_head; iface!=NULL; iface=iface->next)
00395         link_detection_process(iface);
00396 
00397     return TRUE;
00398 }
00399 
00404 static void link_detection_add(HalDevice* device)
00405 {
00406     const char* interface_name;
00407     link_detection_if* iface;
00408 
00409     iface = malloc(sizeof(link_detection_if));
00410     if( iface==NULL )
00411         DIE(("No memory"));
00412 
00413     interface_name = ds_property_get_string(device, "net.interface");
00414     if( interface_name==NULL )
00415     {
00416         HAL_WARNING(("device '%s' does not have net.interface\n", 
00417                      device->udi));
00418         free(iface);
00419         return;
00420     }
00421 
00422     iface->device = device;
00423 
00424     snprintf(iface->ifr.ifr_name, IFNAMSIZ, interface_name);
00425 
00426     /* Open a basic socket. */
00427     if( (iface->skfd = socket(AF_INET, SOCK_DGRAM,0))<0 )
00428     {
00429         HAL_ERROR(("cannot open socket on interface %s; errno=%d\n", 
00430                    interface_name, errno));
00431         free(iface);
00432         return;
00433     }
00434 
00435     if( ioctl(iface->skfd, 0x8947, &(iface->ifr))>=0 )
00436     {
00437         iface->new_ioctl_nums = 1;
00438     } 
00439     else if( ioctl(iface->skfd, SIOCDEVPRIVATE, &(iface->ifr))>=0 )
00440     {
00441         iface->new_ioctl_nums = 0;
00442     } 
00443     else
00444     {
00445         HAL_ERROR(("SIOCGMIIPHY on %s failed: %s\n", iface->ifr.ifr_name,
00446                    strerror(errno)));
00447         (void) close(iface->skfd);
00448         free(iface);
00449         return;
00450     }
00451 
00452     iface->status_word_baseline = 0x5555;
00453 
00454 
00455     link_detection_process(iface);
00456 
00457     iface->next = link_detection_list_head;
00458     link_detection_list_head = iface;
00459 }
00460 
00464 static void link_detection_remove(HalDevice* device)
00465 {
00466     link_detection_if* iface;
00467     link_detection_if* iface_prev = NULL;
00468 
00469     for(iface=link_detection_list_head; iface!=NULL; iface=iface->next)
00470     {
00471         if( iface->device==device )
00472         {
00473 
00474             HAL_INFO(("Stopping ethernet link monitoring on device %s", 
00475                       device->udi));
00476 
00477             if( iface_prev!=NULL )
00478             {
00479                 iface_prev->next = iface->next;
00480             }
00481             else
00482             {
00483                 link_detection_list_head = iface->next;
00484             }
00485 
00486             close(iface->skfd);
00487             free(iface);
00488         }
00489 
00490         iface_prev = iface;
00491     }
00492 }
00493 
00494 
00502 static void new_capability(HalDevice* device, const char* capability,
00503                            dbus_bool_t in_gdl)
00504 {
00505     if( in_gdl )
00506     {
00507         if( strcmp(capability, "net.ethernet")==0 )
00508         {
00509             link_detection_add(device);
00510         }
00511     }
00512 }
00513 
00519 static void gdl_changed(HalDevice* device, dbus_bool_t is_added)
00520 {
00521     if( is_added )
00522     {
00523         if( ds_query_capability(device, "net.ethernet") )
00524         {
00525             link_detection_add(device);
00526         }
00527     }
00528     else
00529     {
00530         /* We may not have added this device yet, but the callee checks for
00531          * for this
00532          */
00533         link_detection_remove(device);
00534     }
00535 }
00536 
00537 
00538 
00539 
00543 void linux_class_net_init()
00544 {
00545     g_timeout_add(1000, link_detection_timer_handler, NULL);
00546 
00547     /* We want to know when net.ethernet devices appear and disappear */
00548     ds_add_cb_newcap(new_capability);
00549     ds_add_cb_gdl_changed(gdl_changed);    
00550 }
00551 
00556 void linux_class_net_detection_done()
00557 {
00558 }
00559 
00563 void linux_class_net_shutdown()
00564 {
00565 }
00566 

Generated on Sat Feb 7 22:11:47 2004 for HAL by doxygen 1.3.5