/*-
 * Copyright (c) 1999-2003 MAEKAWA Masahide <gehenna@daemon-systems.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	$Id: standard.c,v 1.18 2003/10/07 13:59:00 maekawa Exp $
 */

#include <stdio.h>

#include <ctype.h>
#include <unistd.h>

#include <dev/usb/usb.h>

#include "main.h"

struct devinfo usbdev;

static void dump_config(const usb_config_descriptor_t *);
static int dump_interface(const usb_descriptor_t *);
static void dump_interface_power(const usb_descriptor_t *);
static void dump_endpoint(const usb_descriptor_t *);

extern int indexes;

void
dump_full(const struct usb_full_desc *fdesc)
{
	usb_config_descriptor_t *cdesc;
	usb_descriptor_t *desc = (usb_descriptor_t *)fdesc->ufd_data;
	uint8_t *buffer = (uint8_t *)fdesc->ufd_data;
	int wTotalLength = 0, current_interface = 0, i;

	if (desc->bDescriptorType != UDESC_CONFIG) {
		puts("must be started from configuration descriptor.");
		return;
	}

	cdesc = (usb_config_descriptor_t *)desc;
	wTotalLength = UGETW(cdesc->wTotalLength);

	for (i = 0 ; i < wTotalLength ; i += desc->bLength) {
		desc = (usb_descriptor_t *)&buffer[i];

		switch (desc->bDescriptorType) {
		case UDESC_CONFIG:
			dump_config((usb_config_descriptor_t *)desc);
			break;
		case UDESC_INTERFACE:
			current_interface = dump_interface(desc);
			break;
		case UDESC_ENDPOINT:
			dump_endpoint(desc);
			break;
		case UDESC_INTERFACE_POWER:
			dump_interface_power(desc);
			break;
		case UDESC_CS_DEVICE:
			dump_cs_device(desc, current_interface);
			break;
		case UDESC_CS_INTERFACE:
			dump_cs_interface(desc);
			break;
		case UDESC_CS_ENDPOINT:
			dump_cs_endpoint(desc);
			break;
		default:
			dump_descriptor(&buffer[i], "\t\t");
			break;
		}
	}
}

void
dump_descriptor(const void *p, const char *indent)
{
	const usb_descriptor_t *desc = (const usb_descriptor_t *)p;
	const uint8_t *buffer = (const uint8_t *)p;
	int i;

	printf("%sDescriptor:\n%s  bLength            %d\n"
	       "%s  bDescriptorType    %02x\n%s  bDescriptorSubtype %02x\n",
	       indent, indent, desc->bLength, indent, desc->bDescriptorType,
	       indent, desc->bDescriptorSubtype);

	printf("%s", indent);

	for (i = 0 ; i < buffer[0] ; i++)
		printf("%02x ", buffer[i]);

	printf("\n\n");
}

void
dump_device(const usb_device_descriptor_t *ddesc)
{
	usbdev.vendor  = UGETW(ddesc->idVendor);
	usbdev.product = UGETW(ddesc->idProduct);

	printf("Standard Device Descriptor:\n  bLength            %d\n"
	       "  bDescriptorType    %02x\n  bcdUSB             %04x\n"
	       "  bDeviceClass       %02x\n  bDeviceSubClass    %02x\n"
	       "  bDeviceProtocol    %02x\n  bMaxPacketSize     %d\n"
	       "  idVendor           %04x\n  idProduct          %04x\n"
	       "  bcdDevice          %04x\n  iManufacturer      %d\n"
	       "  iProduct           %d\n  iSerialNumber      %d\n"
	       "  bNumConfigurations %d\n\n",
	       ddesc->bLength, ddesc->bDescriptorType, UGETW(ddesc->bcdUSB),
	       ddesc->bDeviceClass, ddesc->bDeviceSubClass,
	       ddesc->bDeviceProtocol, ddesc->bMaxPacketSize,
	       UGETW(ddesc->idVendor), UGETW(ddesc->idProduct),
	       UGETW(ddesc->bcdDevice), ddesc->iManufacturer, ddesc->iProduct,
	       ddesc->iSerialNumber, ddesc->bNumConfigurations);

	if (ddesc->iManufacturer > indexes)
		indexes = ddesc->iManufacturer;
	if (ddesc->iProduct > indexes)
		indexes = ddesc->iProduct;
	if (ddesc->iSerialNumber > indexes)
		indexes = ddesc->iSerialNumber;
}

static void
dump_config(const usb_config_descriptor_t *cdesc)
{
	int powered = 0, wakeup = 0;

	if (cdesc->bmAttributes & UC_SELF_POWERED)
		powered = 1;
	if (cdesc->bmAttributes & UC_REMOTE_WAKEUP)
		wakeup = 1;

	printf("\tStandard Configuration Descriptor:\n"
	       "\t  bLength             %d\n\t  bDescriptorType     %02x\n"
	       "\t  wTotalLength        %d\n\t  bNumInterface       %d\n"
	       "\t  bConfigurationValue %d\n\t  iConfiguration      %d\n"
	       "\t  bmAttributes        %02x%s%s%s%s%s\n"
	       "\t  bMaxPower           %d (%d mA)\n\n",
	       cdesc->bLength, cdesc->bDescriptorType,
	       UGETW(cdesc->wTotalLength), cdesc->bNumInterface,
	       cdesc->bConfigurationValue, cdesc->iConfiguration,
	       cdesc->bmAttributes, (powered || wakeup) ? " (" : "",
	       powered ? "self-powered" : "", (powered && wakeup) ? ", " : "",
	       wakeup ? "remote-wakeup" : "", (powered || wakeup) ? ")" : "",
	       cdesc->bMaxPower, cdesc->bMaxPower * 2);

	if (cdesc->iConfiguration > indexes)
		indexes = cdesc->iConfiguration;
}

static int
dump_interface(const usb_descriptor_t *desc)
{
	const usb_interface_descriptor_t *idesc =
		(const usb_interface_descriptor_t *)desc;

	usbdev.class    = idesc->bInterfaceClass;
	usbdev.subclass = idesc->bInterfaceSubClass;
	usbdev.protocol = idesc->bInterfaceProtocol;

	printf("\tStandard Interface Descriptor:\n\t  bLength            %d\n"
	       "\t  bDescriptorType    %02x\n\t  bInterfaceNumber   %d\n"
	       "\t  bAlternateSetting  %d\n\t  bNumEndpoints      %d\n"
	       "\t  bInterfaceClass    %02x\n\t  bInterfaceSubClass %02x\n"
	       "\t  bInterfaceProtocol %02x\n\t  iInterface         %d\n\n",
	       idesc->bLength, idesc->bDescriptorType, idesc->bInterfaceNumber,
	       idesc->bAlternateSetting, idesc->bNumEndpoints,
	       idesc->bInterfaceClass, idesc->bInterfaceSubClass,
	       idesc->bInterfaceProtocol, idesc->iInterface);

	if (idesc->iInterface > indexes)
		indexes = idesc->iInterface;

	return (idesc->bInterfaceNumber);
}

static void
dump_interface_power(const usb_descriptor_t *desc)
{
	const uint8_t *buffer = (const uint8_t *)desc;
	uint8_t bitmap;

	printf("\tStandard Interface Power Descriptor:\n"
	       "\t  bLength             %d\n\t  bDescriptorType     %02x\n"
	       "\t  bmCapabilitiesFlags %02x",
	       buffer[0], buffer[1], buffer[2]);

	bitmap = buffer[2];

	if (bitmap & 0x0f) {
		printf(" (receives ");
		if (bitmap & 0x01)
			printf("D0");
		if (bitmap & 0x02) {
			if (bitmap & 0x01)
				printf(", ");
			printf("D1");
		}
		if (bitmap & 0x04) {
			if (bitmap & 0x03)
				printf(", ");
			printf("D2");
		}
		if (bitmap & 0x08) {
			if (bitmap & 0x07)
				printf(", ");
			printf("D3");
		}
	}
	if (bitmap & 0x30) {
		if (bitmap & 0x0f)
			printf(", ");
		else
			printf("(");
		printf("wakeup from ");
		if (bitmap & 0x10)
			printf("D1");
		if (bitmap & 0x20) {
			if (bitmap & 0x10)
				printf(", ");
			printf("D2");
		}
	}
	if (bitmap != 0)
		printf(")");

	printf("\n\n");
}

static void
dump_endpoint(const usb_descriptor_t *desc)
{
	const usb_endpoint_descriptor_t *edesc =
		(const usb_endpoint_descriptor_t *)desc;
	const char *xfer_type;

	printf("\tStandard Endpoint Descriptor:\n");

	switch (edesc->bmAttributes & UE_XFERTYPE) {
	case UE_CONTROL:
		xfer_type = "Control";
		break;
	case UE_ISOCHRONOUS:
		switch (edesc->bmAttributes & UE_ISO_TYPE) {
		case UE_ISO_ASYNC:
			xfer_type = "Isochronous-Asynchronous";
			break;
		case UE_ISO_ADAPT:
			xfer_type = "Isochronous-Adaptive";
			break;
		case UE_ISO_SYNC:
			xfer_type = "Isochronous-Synchronous";
			break;
		default:
			xfer_type = "Isochronous";
		}
		break;
	case UE_BULK:
		xfer_type = "Bulk";
		break;
	case UE_INTERRUPT:
		xfer_type = "Interrupt";
		break;
	default:
		xfer_type = "unknown";
	}

	printf("\t  bLength          %d\n\t  bDescriptorType  %02x\n"
	       "\t  bEndpointAddress %02x (%s)\n"
	       "\t  bmAttributes     %02x (%s)\n\t  wMaxPacketSize   %d\n"
	       "\t  bInterval        %d\n",
	       edesc->bLength, edesc->bDescriptorType, edesc->bEndpointAddress,
	       UE_GET_DIR(edesc->bEndpointAddress) == UE_DIR_IN ? "in" : "out",
	       edesc->bmAttributes, xfer_type, UGETW(edesc->wMaxPacketSize),
	       edesc->bInterval);

	if (usbdev.class == UICLASS_AUDIO) {
		const uint8_t *buffer = (const uint8_t *)edesc;
		printf("\t  bRefresh         %d\n\t  bSynchAddress    %02x\n",
		       buffer[7], buffer[8]);
	}

	printf("\n");
}

void
dump_string(const usb_string_descriptor_t *sdesc, int idx)
{
	uint8_t *buffer = (uint8_t *)sdesc->bString;
	int n, i;

	n = (sdesc->bLength - 2) / 2;

	if (idx == 0) {
		printf("Codes Representing Languages by the Device:\n");

		printf("  bLength          %d\n  bDescriptorType  %02x\n",
		       sdesc->bLength, sdesc->bDescriptorType);

		for (i = 0 ; i < n ; i++) {
			printf("  wLANGID[%d]       %04x\n",
			       i, UGETW(&buffer[i*2]));
		}
	} else {
		printf("String (index %d): ", idx);
		for (i = 0 ; i < n ; i++)
			printf("%c", isprint(UGETW(&buffer[i*2])) ?
			       UGETW(&buffer[i*2]) : '.');
		printf("\n");
	}

	printf("\n");
}


syntax highlighted by Code2HTML, v. 0.9.1