root/src/bin/listdev/listdev.cpp
/*
 * Copyright 2006-2012, Haiku, Inc. All Rights Reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *      Jérôme Duval
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <drivers/device_manager.h>
#include <drivers/module.h>
#include <drivers/PCI.h>
#include <drivers/bus/PCI.h>
#include <drivers/bus/SCSI.h>
#include <drivers/bus/USB.h>

extern "C" {
        #include "dm_wrapper.h"
        #include "pcihdr.h"
        #include "pci-utils.h"
        #include "usb-utils.h"
}


extern const char *__progname;

#define DUMP_MODE       0
#define USER_MODE       1
int gMode = USER_MODE;

#define BUS_ISA         1
#define BUS_PCI         2
#define BUS_SCSI        3
#define BUS_USB         4


static const char *
get_scsi_device_type(uint8 type)
{
        switch (type) {
                case 0x0: return "Direct Access";
                case 0x1: return "Sequential Access";
                case 0x2: return "Printer";
                case 0x3: return "Processor";
                case 0x4: return "WORM";
                case 0x5: return "CDROM";
                case 0x6: return "Scanner";
                case 0x7: return "Optical memory";
                case 0x8: return "Medium changer";
                case 0x9: return "Communication";
                case 0xc: return "Storage array controller";
                case 0xd: return "Enclosure services";
                case 0xe: return "Simplified Direct Access";
                default: return "";
        }
}


static void
usage()
{
        fprintf(stderr, "usage: %s [-d]\n", __progname);
        fprintf(stderr, "Displays devices in a user friendly way\n");
        fprintf(stderr, "-d : dumps the tree\n");
        exit(0);
}


static void
put_level(int32 level)
{
        while (level-- > 0)
                printf("   ");
}


static void
dump_attribute(struct device_attr_info *attr, int32 level)
{
        if (attr == NULL)
                return;

        put_level(level);
        printf("\"%s\" : ", attr->name);
        switch (attr->type) {
                case B_STRING_TYPE:
                        printf("string : \"%s\"", attr->value.string);
                        break;
                case B_UINT8_TYPE:
                        printf("uint8 : %" B_PRIu8 " (%#" B_PRIx8 ")", attr->value.ui8,
                                attr->value.ui8);
                        break;
                case B_UINT16_TYPE:
                        printf("uint16 : %" B_PRIu16 " (%#" B_PRIx16 ")", attr->value.ui16,
                                attr->value.ui16);
                        break;
                case B_UINT32_TYPE:
                        printf("uint32 : %" B_PRIu32 " (%#" B_PRIx32 ")", attr->value.ui32,
                                attr->value.ui32);
                        break;
                case B_UINT64_TYPE:
                        printf("uint64 : %" B_PRIu64 " (%#" B_PRIx64 ")", attr->value.ui64,
                                attr->value.ui64);
                        break;
                default:
                        printf("raw data");
        }
        printf("\n");
}


static void
dump_device(device_node_cookie *node, uint8 level)
{
        char data[256];
        struct device_attr_info attr;
        attr.cookie = 0;
        attr.node_cookie = *node;
        attr.value.raw.data = data;
        attr.value.raw.length = sizeof(data);

        put_level(level);
        printf("(%d)\n", level);
        while (dm_get_next_attr(&attr) == B_OK) {
                dump_attribute(&attr, level);
        }
}


static void
dump_nodes(device_node_cookie *node, uint8 level)
{
        status_t err;
        device_node_cookie child = *node;
        dump_device(node, level);

        if (get_child(&child) != B_OK)
                return;

        do {
                dump_nodes(&child, level + 1);
        } while ((err = get_next_child(&child)) == B_OK);

}


static int32
display_device(device_node_cookie *node, uint8 level)
{
        uint8 new_level = level;

        char data[256];
        struct device_attr_info attr;

        // BUS attributes
        char device_bus[64];
        uint8 scsi_path_id = 255;
        int bus = 0;
        uint16 vendor_id = 0;
        uint16 device_id = 0;

        // PCI attributes
        uint8 pci_class_base_id = 0;
        uint8 pci_class_sub_id = 0;
        uint8 pci_class_api_id = 0;
        uint16 pci_subsystem_vendor_id = 0;
        uint16 pci_subsystem_id = 0;

        // SCSI attributes
        uint8 scsi_target_lun = 0;
        uint8 scsi_target_id = 0;
        uint8 scsi_type = 255;
        char scsi_vendor[64];
        char scsi_product[64];

        // USB attributes
        uint8 usb_class_base_id = 0;
        uint8 usb_class_sub_id = 0;
        uint8 usb_class_proto_id = 0;

        attr.cookie = 0;
        attr.node_cookie = *node;
        attr.value.raw.data = data;
        attr.value.raw.length = sizeof(data);

        while (dm_get_next_attr(&attr) == B_OK) {
                if (!strcmp(attr.name, B_DEVICE_BUS)
                        && attr.type == B_STRING_TYPE) {
                        strlcpy(device_bus, attr.value.string, 64);
                } else if (!strcmp(attr.name, "scsi/path_id")
                        && attr.type == B_UINT8_TYPE) {
                        scsi_path_id = attr.value.ui8;
                } else if (!strcmp(attr.name, B_DEVICE_TYPE)
                        && attr.type == B_UINT16_TYPE)
                        pci_class_base_id = attr.value.ui8;
                else if (!strcmp(attr.name, B_DEVICE_SUB_TYPE)
                        && attr.type == B_UINT16_TYPE)
                        pci_class_sub_id = attr.value.ui8;
                else if (!strcmp(attr.name, B_DEVICE_INTERFACE)
                        && attr.type == B_UINT16_TYPE)
                        pci_class_api_id = attr.value.ui8;
                else if (!strcmp(attr.name, B_DEVICE_VENDOR_ID)
                        && attr.type == B_UINT16_TYPE)
                        vendor_id = attr.value.ui16;
                else if (!strcmp(attr.name, B_DEVICE_ID)
                        && attr.type == B_UINT16_TYPE)
                        device_id = attr.value.ui16;
                else if (!strcmp(attr.name, SCSI_DEVICE_TARGET_LUN_ITEM)
                        && attr.type == B_UINT8_TYPE)
                        scsi_target_lun = attr.value.ui8;
                else if (!strcmp(attr.name, SCSI_DEVICE_TARGET_ID_ITEM)
                        && attr.type == B_UINT8_TYPE)
                        scsi_target_id = attr.value.ui8;
                else if (!strcmp(attr.name, SCSI_DEVICE_TYPE_ITEM)
                        && attr.type == B_UINT8_TYPE)
                        scsi_type = attr.value.ui8;
                else if (!strcmp(attr.name, SCSI_DEVICE_VENDOR_ITEM)
                        && attr.type == B_STRING_TYPE)
                        strlcpy(scsi_vendor, attr.value.string, 64);
                else if (!strcmp(attr.name, SCSI_DEVICE_PRODUCT_ITEM)
                        && attr.type == B_STRING_TYPE)
                        strlcpy(scsi_product, attr.value.string, 64);
                else if (!strcmp(attr.name, USB_DEVICE_CLASS)
                        && attr.type == B_UINT8_TYPE)
                        usb_class_base_id = attr.value.ui8;
                else if (!strcmp(attr.name, USB_DEVICE_SUBCLASS)
                        && attr.type == B_UINT8_TYPE)
                        usb_class_sub_id = attr.value.ui8;
                else if (!strcmp(attr.name, USB_DEVICE_PROTOCOL)
                        && attr.type == B_UINT8_TYPE)
                        usb_class_proto_id = attr.value.ui8;

                if (!strcmp(device_bus, "isa"))
                        bus = BUS_ISA;
                else if (!strcmp(device_bus, "pci"))
                        bus = BUS_PCI;
                else if (!strcmp(device_bus, "usb"))
                        bus = BUS_USB;
                else if (scsi_path_id < 255)
                        bus = BUS_SCSI;

                /*else if (!strcmp(attr.name, PCI_DEVICE_SUBVENDOR_ID_ITEM)
                        && attr.type == B_UINT16_TYPE)
                        pci_subsystem_vendor_id = attr.value.ui16;
                else if (!strcmp(attr.name, PCI_DEVICE_SUBSYSTEM_ID_ITEM)
                        && attr.type == B_UINT16_TYPE)
                        pci_subsystem_id = attr.value.ui16;*/

                attr.value.raw.data = data;
                attr.value.raw.length = sizeof(data);
        }

        switch (bus) {
                case BUS_ISA:
                        new_level = level + 1;
                        break;
                case BUS_PCI:
                        printf("\n");
                        {
                                char classInfo[128];
                                get_class_info(pci_class_base_id, pci_class_sub_id,
                                        pci_class_api_id, classInfo, 64);
                                put_level(level);
                                printf("device %s [%x|%x|%x]\n", classInfo, pci_class_base_id,
                                        pci_class_sub_id, pci_class_api_id);
                        }

                        put_level(level);
                        printf("  ");
                        const char *venShort;
                        const char *venFull;
                        const char *devShort;
                        const char *devFull;

                        get_vendor_info(vendor_id, &venShort, &venFull);
                        if (!venShort && !venFull) {
                                printf("vendor %04x: Unknown\n", vendor_id);
                        } else if (venShort && venFull) {
                                printf("vendor %04x: %s - %s\n", vendor_id,
                                        venShort, venFull);
                        } else {
                                printf("vendor %04x: %s\n", vendor_id,
                                        venShort ? venShort : venFull);
                        }

                        put_level(level);
                        printf("  ");
                        get_device_info(vendor_id, device_id, pci_subsystem_vendor_id, pci_subsystem_id,
                                &devShort, &devFull);
                        if (!devShort && !devFull) {
                                printf("device %04x: Unknown\n", device_id);
                        } else if (devShort && devFull) {
                                printf("device %04x: %s (%s)\n", device_id,
                                        devShort, devFull);
                        } else {
                                printf("device %04x: %s\n", device_id,
                                        devShort ? devShort : devFull);
                        }
                        new_level = level + 1;
                        break;
                case BUS_SCSI:
                        if (scsi_type == 255)
                                break;
                        put_level(level);
                        printf("  device [%x|%x]\n", scsi_target_id, scsi_target_lun);
                        put_level(level);
                        printf("  vendor %15s\tmodel %15s\ttype %s\n", scsi_vendor,
                                scsi_product, get_scsi_device_type(scsi_type));

                        new_level = level + 1;
                        break;

                case BUS_USB:
                        {
                                printf("\n");
                                char classInfo[128];
                                usb_get_class_info(usb_class_base_id, usb_class_sub_id, usb_class_proto_id,
                                        classInfo, sizeof(classInfo));
                                put_level(level);
                                printf("device %s [%x|%x|%x]\n", classInfo, usb_class_base_id,
                                        usb_class_sub_id, usb_class_proto_id);

                                put_level(level);
                                printf("  ");
                                const char* vendorName = NULL;
                                const char* deviceName = NULL;
                                usb_get_vendor_info(vendor_id, &vendorName);
                                usb_get_device_info(vendor_id, device_id, &deviceName);
                                printf("vendor %04x: %s\n", vendor_id, vendorName != NULL ? vendorName : "Unknown");
                                put_level(level);
                                printf("  ");
                                printf("device %04x: %s\n", device_id, deviceName != NULL ? deviceName : "Unknown");
                                new_level = level + 1;
                                break;
                        }
        }

        return new_level;
}


static void
display_nodes(device_node_cookie *node, uint8 level)
{
        status_t err;
        device_node_cookie child = *node;
        level = display_device(node, level);

        if (get_child(&child) != B_OK)
                return;

        do {
                display_nodes(&child, level);
        } while ((err = get_next_child(&child)) == B_OK);
}


int
main(int argc, char **argv)
{
        status_t error;
        device_node_cookie root;

        if ((error = init_dm_wrapper()) < 0) {
                printf("Error initializing device manager (%s)\n", strerror(error));
                return error;
        }

        if (argc > 2)
                usage();

        if (argc == 2) {
                if (!strcmp(argv[1], "-d")) {
                        gMode = DUMP_MODE;
                } else {
                        usage();
                }
        }

        if (gMode == DUMP_MODE) {
                get_root(&root);
                dump_nodes(&root, 0);
        } else {
                get_root(&root);
                display_nodes(&root, 0);
        }

        uninit_dm_wrapper();

        return 0;
}