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 #ifdef HAVE_CONFIG_H
00027 # include <config.h>
00028 #endif
00029
00030 #include <ctype.h>
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #include <getopt.h>
00035 #include <assert.h>
00036 #include <unistd.h>
00037 #include <stdarg.h>
00038
00039 #include "../logger.h"
00040 #include "../device_store.h"
00041 #include "linux_pci.h"
00042
00052 static char *pci_ids = NULL;
00053
00055 static unsigned int pci_ids_len;
00056
00058 static unsigned int pci_ids_iter_pos;
00059
00061 static void
00062 pci_ids_line_iter_init ()
00063 {
00064 pci_ids_iter_pos = 0;
00065 }
00066
00068 #define PCI_IDS_MAX_LINE_LEN 512
00069
00077 static char *
00078 pci_ids_line_iter_get_line (unsigned int *line_len)
00079 {
00080 unsigned int i;
00081 static char line[PCI_IDS_MAX_LINE_LEN];
00082
00083 for (i = 0;
00084 pci_ids_iter_pos < pci_ids_len &&
00085 i < PCI_IDS_MAX_LINE_LEN - 1 &&
00086 pci_ids[pci_ids_iter_pos] != '\n'; i++, pci_ids_iter_pos++) {
00087 line[i] = pci_ids[pci_ids_iter_pos];
00088 }
00089
00090 line[i] = '\0';
00091 if (line_len != NULL)
00092 *line_len = i;
00093
00094 pci_ids_iter_pos++;
00095
00096 return line;
00097 }
00098
00103 static dbus_bool_t
00104 pci_ids_line_iter_has_more ()
00105 {
00106 return pci_ids_iter_pos < pci_ids_len;
00107 }
00108
00109
00124 static void
00125 pci_ids_find (int vendor_id, int product_id,
00126 int subsys_vendor_id, int subsys_product_id,
00127 char **vendor_name, char **product_name,
00128 char **subsys_vendor_name, char **subsys_product_name)
00129 {
00130 char *line;
00131 unsigned int i;
00132 unsigned int line_len;
00133 unsigned int num_tabs;
00134 char rep_vi[8];
00135 char rep_pi[8];
00136 char rep_svi[8];
00137 char rep_spi[8];
00138 static char store_vn[PCI_IDS_MAX_LINE_LEN];
00139 static char store_pn[PCI_IDS_MAX_LINE_LEN];
00140 static char store_svn[PCI_IDS_MAX_LINE_LEN];
00141 static char store_spn[PCI_IDS_MAX_LINE_LEN];
00142 dbus_bool_t vendor_matched = FALSE;
00143 dbus_bool_t product_matched = FALSE;
00144
00145 snprintf (rep_vi, 8, "%04x", vendor_id);
00146 snprintf (rep_pi, 8, "%04x", product_id);
00147 snprintf (rep_svi, 8, "%04x", subsys_vendor_id);
00148 snprintf (rep_spi, 8, "%04x", subsys_product_id);
00149
00150 *vendor_name = NULL;
00151 *product_name = NULL;
00152 *subsys_vendor_name = NULL;
00153 *subsys_product_name = NULL;
00154
00155 for (pci_ids_line_iter_init (); pci_ids_line_iter_has_more ();) {
00156 line = pci_ids_line_iter_get_line (&line_len);
00157
00158
00159 if (line_len < 4)
00160 continue;
00161
00162
00163 if (line[0] == '#')
00164 continue;
00165
00166
00167 num_tabs = 0;
00168 for (i = 0; i < line_len; i++) {
00169 if (line[i] != '\t')
00170 break;
00171 num_tabs++;
00172 }
00173
00174 switch (num_tabs) {
00175 case 0:
00176
00177 vendor_matched = FALSE;
00178
00179
00180
00181 if (*subsys_vendor_name == NULL
00182 && subsys_vendor_id != 0) {
00183 if ((*((dbus_uint32_t *) line)) ==
00184 (*((dbus_uint32_t *) rep_svi))) {
00185
00186 for (i = 4; i < line_len; i++) {
00187 if (!isspace (line[i]))
00188 break;
00189 }
00190 strncpy (store_svn, line + i,
00191 PCI_IDS_MAX_LINE_LEN);
00192 *subsys_vendor_name = store_svn;
00193 }
00194 }
00195
00196
00197 if (vendor_id != 0) {
00198 if (memcmp (line, rep_vi, 4) == 0) {
00199
00200 vendor_matched = TRUE;
00201
00202 for (i = 4; i < line_len; i++) {
00203 if (!isspace (line[i]))
00204 break;
00205 }
00206 strncpy (store_vn, line + i,
00207 PCI_IDS_MAX_LINE_LEN);
00208 *vendor_name = store_vn;
00209 }
00210 }
00211
00212 break;
00213
00214 case 1:
00215 product_matched = FALSE;
00216
00217
00218 if (!vendor_matched)
00219 continue;
00220
00221
00222 if (product_id != 0) {
00223 if (memcmp (line + 1, rep_pi, 4) == 0) {
00224
00225
00226 product_matched = TRUE;
00227
00228 for (i = 5; i < line_len; i++) {
00229 if (!isspace (line[i]))
00230 break;
00231 }
00232 strncpy (store_pn, line + i,
00233 PCI_IDS_MAX_LINE_LEN);
00234 *product_name = store_pn;
00235 }
00236 }
00237 break;
00238
00239 case 2:
00240
00241 if (!vendor_matched || !product_matched)
00242 continue;
00243
00244
00245 if (subsys_vendor_id != 0
00246 && subsys_product_id != 0) {
00247 if (memcmp (line + 2, rep_svi, 4) == 0
00248 && memcmp (line + 7, rep_spi,
00249 4) == 0) {
00250
00251 for (i = 11; i < line_len; i++) {
00252 if (!isspace (line[i]))
00253 break;
00254 }
00255 strncpy (store_spn, line + i,
00256 PCI_IDS_MAX_LINE_LEN);
00257 *subsys_product_name = store_spn;
00258 }
00259 }
00260
00261 break;
00262
00263 default:
00264 break;
00265 }
00266
00267 }
00268 }
00269
00277 static dbus_bool_t
00278 pci_ids_load (const char *path)
00279 {
00280 FILE *fp;
00281 unsigned int num_read;
00282
00283 fp = fopen (path, "r");
00284 if (fp == NULL) {
00285 HAL_ERROR (("couldn't open PCI database at %s,", path));
00286 return FALSE;
00287 }
00288
00289 fseek (fp, 0, SEEK_END);
00290 pci_ids_len = ftell (fp);
00291 fseek (fp, 0, SEEK_SET);
00292
00293 pci_ids = malloc (pci_ids_len);
00294 if (pci_ids == NULL) {
00295 DIE (("Couldn't allocate %d bytes for PCI database file\n",
00296 pci_ids_len));
00297 }
00298
00299 num_read = fread (pci_ids, sizeof (char), pci_ids_len, fp);
00300 if (pci_ids_len != num_read) {
00301 HAL_ERROR (("Error loading PCI database file\n"));
00302 free (pci_ids);
00303 pci_ids = NULL;
00304 return FALSE;
00305 }
00306
00307 return TRUE;
00308 }
00309
00314 static dbus_bool_t
00315 pci_ids_free ()
00316 {
00317 if (pci_ids != NULL) {
00318 free (pci_ids);
00319 pci_ids = NULL;
00320 return TRUE;
00321 }
00322 return FALSE;
00323 }
00324
00325
00344 static char *
00345 pci_compute_udi (HalDevice * d, int append_num)
00346 {
00347 static char buf[256];
00348
00349 if (append_num == -1)
00350 sprintf (buf, "/org/freedesktop/Hal/devices/pci_%x_%x",
00351 ds_property_get_int (d, "pci.vendor_id"),
00352 ds_property_get_int (d, "pci.product_id"));
00353 else
00354 sprintf (buf, "/org/freedesktop/Hal/devices/pci_%x_%x/%d",
00355 ds_property_get_int (d, "pci.vendor_id"),
00356 ds_property_get_int (d, "pci.product_id"),
00357 append_num);
00358
00359 return buf;
00360 }
00361
00362
00371 static void
00372 pci_add_caps_from_class (HalDevice * d,
00373 int dev_class, int dev_sub_class, int dev_proto)
00374 {
00375 char *cat = NULL;
00376
00377 switch (dev_class) {
00378 case 0x01:
00379 cat = "storage_controller";
00380 ds_add_capability (d, "storage_controller");
00381 switch (dev_sub_class) {
00382 case 0x00:
00383 ds_add_capability (d, "storage_controller.scsi");
00384 break;
00385 case 0x01:
00386 ds_add_capability (d, "storage_controller.ide");
00387 break;
00388 case 0x02:
00389 ds_add_capability (d, "storage_controller.floppy");
00390 break;
00391 case 0x03:
00392 ds_add_capability (d, "storage_controller.ipi");
00393 break;
00394 case 0x04:
00395 ds_add_capability (d, "storage_controller.raid");
00396 break;
00397 }
00398 break;
00399 case 0x02:
00400 cat = "net";
00401 ds_add_capability (d, "net");
00402 switch (dev_sub_class) {
00403 case 0x00:
00404 ds_add_capability (d, "net.ethernet");
00405 break;
00406 case 0x01:
00407 ds_add_capability (d, "net.tokenring");
00408 break;
00409 case 0x02:
00410 ds_add_capability (d, "net.fddi");
00411 break;
00412 case 0x03:
00413 ds_add_capability (d, "net.atm");
00414 break;
00415 case 0x04:
00416 ds_add_capability (d, "net.isdn");
00417 break;
00418 }
00419 break;
00420 case 0x03:
00421 cat = "video";
00422 ds_add_capability (d, "video");
00423 if (dev_sub_class == 0x00 && dev_proto == 0x00)
00424 ds_add_capability (d, "video.vga");
00425 if (dev_sub_class == 0x00 && dev_proto == 0x01)
00426 ds_add_capability (d, "video.8514");
00427 else if (dev_sub_class == 0x01)
00428 ds_add_capability (d, "video.xga");
00429 else if (dev_sub_class == 0x02)
00430 ds_add_capability (d, "video.3d");
00431 break;
00432 case 0x04:
00433 cat = "multimedia";
00434 ds_add_capability (d, "multimedia");
00435 switch (dev_sub_class) {
00436 case 0x00:
00437 ds_add_capability (d, "multimedia.video");
00438 cat = "multimedia.video";
00439 break;
00440 case 0x01:
00441 ds_add_capability (d, "multimedia.audio");
00442 cat = "multimedia.audio";
00443 break;
00444 case 0x02:
00445 ds_add_capability (d, "multimedia.telephony");
00446 cat = "multimedia.telephony";
00447 break;
00448 }
00449 break;
00450 case 0x06:
00451 cat = "bridge";
00452 ds_add_capability (d, "bridge");
00453 switch (dev_sub_class) {
00454 case 0x00:
00455 ds_add_capability (d, "bridge.host");
00456 break;
00457 case 0x01:
00458 ds_add_capability (d, "bridge.isa");
00459 break;
00460 case 0x02:
00461 ds_add_capability (d, "bridge.eisa");
00462 break;
00463 case 0x03:
00464 ds_add_capability (d, "bridge.micro_channel");
00465 break;
00466 case 0x04:
00467 ds_add_capability (d, "bridge.pci");
00468 break;
00469 case 0x05:
00470 ds_add_capability (d, "bridge.pcmcia");
00471 break;
00472 case 0x06:
00473 ds_add_capability (d, "bridge.nubus");
00474 break;
00475 case 0x07:
00476 cat = "bridge.cardbus";
00477 ds_add_capability (d, "bridge.cardbus");
00478 break;
00479 case 0x08:
00480 ds_add_capability (d, "bridge.raceway");
00481 break;
00482 case 0x09:
00483 ds_add_capability (d, "bridge.semi_transparent");
00484 break;
00485 case 0x0a:
00486 ds_add_capability (d, "bridge.infiniband");
00487 break;
00488 }
00489 break;
00490 case 0x07:
00491 cat = "comm";
00492 ds_add_capability (d, "comm");
00493 if (dev_sub_class == 0x00) {
00494 cat = "comm.serial";
00495 ds_add_capability (d, "comm.serial");
00496 switch (dev_proto) {
00497 case 0x00:
00498 ds_add_capability (d, "comm.serial.8250");
00499 break;
00500 case 0x01:
00501 ds_add_capability (d, "comm.serial.16450");
00502 break;
00503 case 0x02:
00504 ds_add_capability (d, "comm.serial.16550");
00505 break;
00506 case 0x03:
00507 ds_add_capability (d, "comm.serial.16650");
00508 break;
00509 case 0x04:
00510 ds_add_capability (d, "comm.serial.16750");
00511 break;
00512 case 0x05:
00513 ds_add_capability (d, "comm.serial.16850");
00514 break;
00515 case 0x06:
00516 ds_add_capability (d, "comm.serial.16950");
00517 break;
00518 }
00519 } else if (dev_sub_class == 0x01) {
00520 cat = "comm.parallel";
00521 ds_add_capability (d, "comm.parallel");
00522 switch (dev_proto) {
00523 case 0x00:
00524 ds_add_capability (d, "comm.parallel.spp");
00525 break;
00526 case 0x01:
00527 ds_add_capability (d,
00528 "comm.parallel.bidir");
00529 break;
00530 case 0x02:
00531 ds_add_capability (d, "comm.parallel.ecp");
00532 break;
00533 case 0x03:
00534 ds_add_capability (d,
00535 "comm.parallel.ieee1284");
00536 break;
00537 case 0xfe:
00538 ds_add_capability (d,
00539 "comm.parallel.ieee1284_target");
00540 break;
00541 }
00542 } else if (dev_sub_class == 0x02) {
00543 cat = "comm.serial";
00544 ds_add_capability (d, "comm.serial");
00545 ds_add_capability (d, "comm.serial.multiport");
00546 } else if (dev_sub_class == 0x03) {
00547 cat = "modem";
00548 ds_add_capability (d, "modem");
00549 if (dev_proto >= 0x01 && dev_proto <= 0x04)
00550 ds_add_capability (d, "modem.hayes");
00551 }
00552 break;
00553 case 0x0c:
00554 cat = "serial_controller";
00555 ds_add_capability (d, "serial_controller");
00556 switch (dev_sub_class) {
00557 case 0x00:
00558 cat = "serial_controller.ieee1394";
00559 ds_add_capability (d,
00560 "serial_controller.ieee1394");
00561 if (dev_proto == 0x10)
00562 ds_add_capability (d,
00563 "serial_controller.ieee1394.ohci");
00564 break;
00565 case 0x01:
00566 ds_add_capability (d, "serial_controller.access");
00567 break;
00568 case 0x02:
00569 ds_add_capability (d, "serial_controller.ssa");
00570 break;
00571 case 0x03:
00572 cat = "serial_controller.usb";
00573 ds_add_capability (d, "serial_controller.usb");
00574 switch (dev_proto) {
00575 case 0x00:
00576 ds_add_capability (d,
00577 "serial_controller.usb.uhci");
00578 break;
00579 case 0x01:
00580 ds_add_capability (d,
00581 "serial_controller.usb.ohci");
00582 break;
00583 case 0x02:
00584 ds_add_capability (d,
00585 "serial_controller.usb.ehci");
00586 break;
00587 case 0xfe:
00588 ds_add_capability (d,
00589 "serial_controller.usb.device");
00590 break;
00591 }
00592 break;
00593 }
00594 break;
00595 case 0x0d:
00596 cat = "wireless";
00597 ds_add_capability (d, "wireless");
00598 switch (dev_sub_class) {
00599 case 0x00:
00600 ds_add_capability (d, "wireless.irda");
00601 break;
00602 case 0x01:
00603 ds_add_capability (d,
00604 "wireless.consumer_controller");
00605 break;
00606 case 0x02:
00607 ds_add_capability (d, "wireless.rf_controller");
00608 break;
00609 }
00610 break;
00611 case 0x0f:
00612 cat = "satellite_controller";
00613 ds_add_capability (d, "satellite_controller");
00614 switch (dev_sub_class) {
00615 case 0x00:
00616 ds_add_capability (d, "satellite_controller.tv");
00617 break;
00618 case 0x01:
00619 ds_add_capability (d,
00620 "satellite_controller.audio");
00621 break;
00622 case 0x02:
00623 ds_add_capability (d,
00624 "satellite_controller.video");
00625 break;
00626 case 0x03:
00627 ds_add_capability (d, "satellite_controller.data");
00628 break;
00629 }
00630 break;
00631 }
00632
00633 if (cat != NULL)
00634 ds_property_set_string (d, "info.category", cat);
00635 }
00636
00637
00638
00639 static void visit_device_pci_got_parent (HalDevice * parent,
00640 void *data1, void *data2);
00641
00650 void
00651 visit_device_pci (const char *path, struct sysfs_device *device)
00652 {
00653 int i;
00654 int len;
00655 HalDevice *d;
00656 char attr_name[SYSFS_NAME_LEN];
00657 struct sysfs_attribute *cur;
00658 int vendor_id = 0;
00659 int product_id = 0;
00660 int subsys_vendor_id = 0;
00661 int subsys_product_id = 0;
00662 char *vendor_name;
00663 char *product_name;
00664 char *subsys_vendor_name;
00665 char *subsys_product_name;
00666 const char *driver;
00667 dbus_int32_t cls = 0x00ffffff;
00668 char namebuf[512];
00669 char *parent_sysfs_path;
00670
00671
00672
00673
00674 d = ds_device_new ();
00675 ds_property_set_string (d, "info.bus", "pci");
00676 ds_property_set_string (d, "linux.sysfs_path", path);
00677 ds_property_set_string (d, "linux.sysfs_path_device", path);
00684 ds_property_set_string (d, "pci.linux.sysfs_path", path);
00685
00686
00687
00688 driver = drivers_lookup (path);
00689 if (driver != NULL)
00690 ds_property_set_string (d, "linux.driver", driver);
00691
00692 dlist_for_each_data (sysfs_get_device_attributes (device), cur,
00693 struct sysfs_attribute) {
00694
00695 if (sysfs_get_name_from_path (cur->path,
00696 attr_name,
00697 SYSFS_NAME_LEN) != 0)
00698 continue;
00699
00700
00701 len = strlen (cur->value);
00702 for (i = len - 1; isspace (cur->value[i]); --i)
00703 cur->value[i] = '\0';
00704
00705
00706
00707 if (strcmp (attr_name, "device") == 0)
00708 product_id = parse_hex (cur->value);
00709 else if (strcmp (attr_name, "vendor") == 0)
00710 vendor_id = parse_hex (cur->value);
00711 else if (strcmp (attr_name, "subsystem_device") == 0)
00712 subsys_product_id = parse_hex (cur->value);
00713 else if (strcmp (attr_name, "subsystem_vendor") == 0)
00714 subsys_vendor_id = parse_hex (cur->value);
00715 else if (strcmp (attr_name, "class") == 0) {
00716 cls = parse_hex (cur->value);
00717 }
00718 }
00719
00720 ds_property_set_int (d, "pci.vendor_id", vendor_id);
00721 ds_property_set_int (d, "pci.product_id", product_id);
00722 ds_property_set_int (d, "pci.subsys_vendor_id", subsys_vendor_id);
00723 ds_property_set_int (d, "pci.subsys_product_id",
00724 subsys_product_id);
00725
00726
00727 pci_ids_find (vendor_id, product_id, subsys_vendor_id,
00728 subsys_product_id, &vendor_name, &product_name,
00729 &subsys_vendor_name, &subsys_product_name);
00730 if (vendor_name != NULL)
00731 ds_property_set_string (d, "pci.vendor", vendor_name);
00732 if (product_name != NULL)
00733 ds_property_set_string (d, "pci.product", product_name);
00734 if (subsys_vendor_name != NULL)
00735 ds_property_set_string (d, "pci.subsys_vendor",
00736 subsys_vendor_name);
00737 if (subsys_product_name != NULL)
00738 ds_property_set_string (d, "pci.subsys_product",
00739 subsys_product_name);
00740
00741
00742
00743 if (product_name != NULL) {
00744 ds_property_set_string (d, "info.product", product_name);
00745 } else {
00746 snprintf (namebuf, 512, "Unknown (0x%04x)", product_id);
00747 ds_property_set_string (d, "info.product", namebuf);
00748 }
00749
00750
00751
00752 if (vendor_name != NULL) {
00753 ds_property_set_string (d, "info.vendor", vendor_name);
00754 } else {
00755 snprintf (namebuf, 512, "Unknown (0x%04x)", vendor_id);
00756 ds_property_set_string (d, "info.vendor", namebuf);
00757 }
00758
00759
00760 ds_property_set_int (d, "pci.device_class", (cls >> 16) & 0xff);
00761 ds_property_set_int (d, "pci.device_subclass", (cls >> 8) & 0xff);
00762 ds_property_set_int (d, "pci.device_protocol", cls & 0xff);
00763 pci_add_caps_from_class (d, (cls >> 16) & 0xff, (cls >> 8) & 0xff,
00764 cls & 0xff);
00765
00766 parent_sysfs_path = get_parent_sysfs_path (path);
00767
00768
00769
00770
00771
00772 ds_device_async_find_by_key_value_string
00773 ("linux.sysfs_path_device", parent_sysfs_path, TRUE,
00774 visit_device_pci_got_parent, (void *) d, NULL,
00775 is_probing ? 0 : HAL_LINUX_HOTPLUG_TIMEOUT);
00776
00777 free (parent_sysfs_path);
00778 }
00779
00787 static void
00788 visit_device_pci_got_parent (HalDevice * parent, void *data1, void *data2)
00789 {
00790 char *new_udi = NULL;
00791 HalDevice *new_d = NULL;
00792 HalDevice *d = (HalDevice *) data1;
00793
00794 if (parent != NULL) {
00795 ds_property_set_string (d, "info.parent", parent->udi);
00796 }
00797
00798
00799
00800
00801 new_udi = rename_and_merge (d, pci_compute_udi, "pci");
00802 if (new_udi != NULL) {
00803 new_d = ds_device_find (new_udi);
00804 if (new_d != NULL) {
00805 ds_gdl_add (new_d);
00806 }
00807 }
00808 }
00809
00810
00814 void
00815 linux_pci_init ()
00816 {
00817
00818 drivers_collect ("pci");
00819
00820
00821 pci_ids_load (HWDATA_DIR "/pci.ids");
00822 }
00823
00828 void
00829 linux_pci_detection_done ()
00830 {
00831 }
00832
00836 void
00837 linux_pci_shutdown ()
00838 {
00839 pci_ids_free ();
00840 }
00841