root/tools/usb/usbip/libsrc/usbip_common.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2005-2007 Takahiro Hirofuchi
 */

#include <libudev.h>
#include "usbip_common.h"
#include "names.h"

#undef  PROGNAME
#define PROGNAME "libusbip"

int usbip_use_syslog;
int usbip_use_stderr;
int usbip_use_debug;

extern struct udev *udev_context;

struct speed_string {
        int num;
        char *speed;
        char *desc;
};

static const struct speed_string speed_strings[] = {
        { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"},
        { USB_SPEED_LOW,  "1.5", "Low Speed(1.5Mbps)"  },
        { USB_SPEED_FULL, "12",  "Full Speed(12Mbps)" },
        { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" },
        { USB_SPEED_WIRELESS, "53.3-480", "Wireless"},
        { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" },
        { 0, NULL, NULL }
};

struct portst_string {
        int num;
        char *desc;
};

static struct portst_string portst_strings[] = {
        { SDEV_ST_AVAILABLE,    "Device Available" },
        { SDEV_ST_USED,         "Device in Use" },
        { SDEV_ST_ERROR,        "Device Error"},
        { VDEV_ST_NULL,         "Port Available"},
        { VDEV_ST_NOTASSIGNED,  "Port Initializing"},
        { VDEV_ST_USED,         "Port in Use"},
        { VDEV_ST_ERROR,        "Port Error"},
        { 0, NULL}
};

const char *usbip_status_string(int32_t status)
{
        for (int i = 0; portst_strings[i].desc != NULL; i++)
                if (portst_strings[i].num == status)
                        return portst_strings[i].desc;

        return "Unknown Status";
}

const char *usbip_speed_string(int num)
{
        for (int i = 0; speed_strings[i].speed != NULL; i++)
                if (speed_strings[i].num == num)
                        return speed_strings[i].desc;

        return "Unknown Speed";
}

struct op_common_status_string {
        int num;
        char *desc;
};

static struct op_common_status_string op_common_status_strings[] = {
        { ST_OK,        "Request Completed Successfully" },
        { ST_NA,        "Request Failed" },
        { ST_DEV_BUSY,  "Device busy (exported)" },
        { ST_DEV_ERR,   "Device in error state" },
        { ST_NODEV,     "Device not found" },
        { ST_ERROR,     "Unexpected response" },
        { 0, NULL}
};

const char *usbip_op_common_status_string(int status)
{
        for (int i = 0; op_common_status_strings[i].desc != NULL; i++)
                if (op_common_status_strings[i].num == status)
                        return op_common_status_strings[i].desc;

        return "Unknown Op Common Status";
}

#define DBG_UDEV_INTEGER(name)\
        dbg("%-20s = %x", to_string(name), (int) udev->name)

#define DBG_UINF_INTEGER(name)\
        dbg("%-20s = %x", to_string(name), (int) uinf->name)

void dump_usb_interface(struct usbip_usb_interface *uinf)
{
        char buff[100];

        usbip_names_get_class(buff, sizeof(buff),
                        uinf->bInterfaceClass,
                        uinf->bInterfaceSubClass,
                        uinf->bInterfaceProtocol);
        dbg("%-20s = %s", "Interface(C/SC/P)", buff);
}

void dump_usb_device(struct usbip_usb_device *udev)
{
        char buff[100];

        dbg("%-20s = %s", "path",  udev->path);
        dbg("%-20s = %s", "busid", udev->busid);

        usbip_names_get_class(buff, sizeof(buff),
                        udev->bDeviceClass,
                        udev->bDeviceSubClass,
                        udev->bDeviceProtocol);
        dbg("%-20s = %s", "Device(C/SC/P)", buff);

        DBG_UDEV_INTEGER(bcdDevice);

        usbip_names_get_product(buff, sizeof(buff),
                        udev->idVendor,
                        udev->idProduct);
        dbg("%-20s = %s", "Vendor/Product", buff);

        DBG_UDEV_INTEGER(bNumConfigurations);
        DBG_UDEV_INTEGER(bNumInterfaces);

        dbg("%-20s = %s", "speed",
                        usbip_speed_string(udev->speed));

        DBG_UDEV_INTEGER(busnum);
        DBG_UDEV_INTEGER(devnum);
}


int read_attr_value(struct udev_device *dev, const char *name,
                    const char *format)
{
        const char *attr;
        int num = 0;
        int ret;

        attr = udev_device_get_sysattr_value(dev, name);
        if (!attr) {
                err("udev_device_get_sysattr_value failed");
                goto err;
        }

        /* The client chooses the device configuration
         * when attaching it so right after being bound
         * to usbip-host on the server the device will
         * have no configuration.
         * Therefore, attributes such as bConfigurationValue
         * and bNumInterfaces will not exist and sscanf will
         * fail. Check for these cases and don't treat them
         * as errors.
         */

        ret = sscanf(attr, format, &num);
        if (ret < 1) {
                if (strcmp(name, "bConfigurationValue") &&
                                strcmp(name, "bNumInterfaces")) {
                        err("sscanf failed for attribute %s", name);
                        goto err;
                }
        }

err:

        return num;
}


int read_attr_speed(struct udev_device *dev)
{
        const char *speed;

        speed = udev_device_get_sysattr_value(dev, "speed");
        if (!speed) {
                err("udev_device_get_sysattr_value failed");
                goto err;
        }

        for (int i = 0; speed_strings[i].speed != NULL; i++) {
                if (!strcmp(speed, speed_strings[i].speed))
                        return speed_strings[i].num;
        }

err:

        return USB_SPEED_UNKNOWN;
}

#define READ_ATTR(object, type, dev, name, format)                            \
        do {                                                                  \
                (object)->name = (type) read_attr_value(dev, to_string(name), \
                                                        format);              \
        } while (0)


int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev)
{
        uint32_t busnum, devnum;
        const char *path, *name;

        READ_ATTR(udev, uint8_t,  sdev, bDeviceClass,           "%02x\n");
        READ_ATTR(udev, uint8_t,  sdev, bDeviceSubClass,        "%02x\n");
        READ_ATTR(udev, uint8_t,  sdev, bDeviceProtocol,        "%02x\n");

        READ_ATTR(udev, uint16_t, sdev, idVendor,               "%04x\n");
        READ_ATTR(udev, uint16_t, sdev, idProduct,              "%04x\n");
        READ_ATTR(udev, uint16_t, sdev, bcdDevice,              "%04x\n");

        READ_ATTR(udev, uint8_t,  sdev, bConfigurationValue,    "%02x\n");
        READ_ATTR(udev, uint8_t,  sdev, bNumConfigurations,     "%02x\n");
        READ_ATTR(udev, uint8_t,  sdev, bNumInterfaces,         "%02x\n");

        READ_ATTR(udev, uint8_t,  sdev, devnum,                 "%d\n");
        udev->speed = read_attr_speed(sdev);

        path = udev_device_get_syspath(sdev);
        name = udev_device_get_sysname(sdev);

        strncpy(udev->path,  path,  SYSFS_PATH_MAX - 1);
        udev->path[SYSFS_PATH_MAX - 1] = '\0';
        strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE - 1);
        udev->busid[SYSFS_BUS_ID_SIZE - 1] = '\0';

        sscanf(name, "%u-%u", &busnum, &devnum);
        udev->busnum = busnum;

        return 0;
}

int read_usb_interface(struct usbip_usb_device *udev, int i,
                       struct usbip_usb_interface *uinf)
{
        char busid[SYSFS_BUS_ID_SIZE];
        int size;
        struct udev_device *sif;

        size = snprintf(busid, sizeof(busid), "%s:%d.%d",
                        udev->busid, udev->bConfigurationValue, i);
        if (size < 0 || (unsigned int)size >= sizeof(busid)) {
                err("busid length %i >= %lu or < 0", size,
                    (long unsigned)sizeof(busid));
                return -1;
        }

        sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid);
        if (!sif) {
                err("udev_device_new_from_subsystem_sysname %s failed", busid);
                return -1;
        }

        READ_ATTR(uinf, uint8_t,  sif, bInterfaceClass,         "%02x\n");
        READ_ATTR(uinf, uint8_t,  sif, bInterfaceSubClass,      "%02x\n");
        READ_ATTR(uinf, uint8_t,  sif, bInterfaceProtocol,      "%02x\n");

        return 0;
}

int usbip_names_init(char *f)
{
        return names_init(f);
}

void usbip_names_free(void)
{
        names_free();
}

void usbip_names_get_product(char *buff, size_t size, uint16_t vendor,
                             uint16_t product)
{
        const char *prod, *vend;

        prod = names_product(vendor, product);
        if (!prod)
                prod = "unknown product";


        vend = names_vendor(vendor);
        if (!vend)
                vend = "unknown vendor";

        snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product);
}

void usbip_names_get_class(char *buff, size_t size, uint8_t class,
                           uint8_t subclass, uint8_t protocol)
{
        const char *c, *s, *p;

        if (class == 0 && subclass == 0 && protocol == 0) {
                snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol);
                return;
        }

        p = names_protocol(class, subclass, protocol);
        if (!p)
                p = "unknown protocol";

        s = names_subclass(class, subclass);
        if (!s)
                s = "unknown subclass";

        c = names_class(class);
        if (!c)
                c = "unknown class";

        snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol);
}