00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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>
00048
00049 #include "../hald.h"
00050 #include "../logger.h"
00051 #include "../device_store.h"
00052 #include "linux_class_net.h"
00053
00062
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
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
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
00149
00150
00151
00152
00153 ds_property_set_string (d, "info.category", "net");
00154 ds_add_capability (d, "net");
00155
00156
00157
00158
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
00212
00213
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
00238
00239
00240 ds_device_merge (sysdevice, d);
00241 }
00242
00243
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
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
00308 status_word = mdio_read (iface, 1);
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
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
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
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
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
00506
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
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