/*-
 * Copyright (c) 1999, 2000 MAEKAWA Masahide <maekawa@math.sci.kobe-u.ac.jp>
 * 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.
 *
 *	$Maekawa: standard.c,v 1.15 2000/04/01 05:27:48 maekawa Exp $
 */

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

#include <dev/usb/usb.h>

#include "main.h"

struct devinfo usbdev;
extern int indexes;

void
dump_full(fdesc)
	struct usb_full_desc *fdesc;
{
	usb_config_descriptor_t *cdesc;
	usb_descriptor_t *desc;
	u_int8_t *buf;
	int wTotalLength = 0, i = 0;
	int current_interface = 0;

	buf = fdesc->ufd_data;

	desc = (usb_descriptor_t *)&buf[0];
	if (desc->bDescriptorType == UDESC_CONFIG) {
		dump_config(desc);

		cdesc = (usb_config_descriptor_t *)desc;
		wTotalLength = UGETW(cdesc->wTotalLength);
		i += cdesc->bLength;
	}

	while (i < wTotalLength) {
		desc = (usb_descriptor_t *)&buf[i];

		switch (desc->bDescriptorType) {
		case UDESC_INTERFACE:
			current_interface = dump_interface(desc);
			break;
		case UDESC_ENDPOINT:
			dump_endpoint(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(&buf[i], "\t\t");
			dump_raw(&buf[i], "\t");
			break;
		}

		i += (int)desc->bLength;
	}

	return;
}

void
dump_descriptor(p, indent)
	void *p;
	const char *indent;
{
	usb_descriptor_t *desc = (usb_descriptor_t *)p;

	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);
	return;
}

void
dump_raw(buf, indent)
	u_int8_t *buf;
	const char *indent;
{
	int i;

	printf("%s", indent);

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

	printf("\n\n");

	return;
}

void
dump_device(ddesc)
	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;

	return;
}

void
dump_config(desc)
	usb_descriptor_t *desc;
{
	usb_config_descriptor_t *cdesc = (usb_config_descriptor_t *)desc;

	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\n"
	       "\t  bMaxPower           %d\n\n",
	       cdesc->bLength, cdesc->bDescriptorType,
	       UGETW(cdesc->wTotalLength), cdesc->bNumInterface,
	       cdesc->bConfigurationValue, cdesc->iConfiguration,
	       cdesc->bmAttributes, cdesc->bMaxPower);

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

	return;
}

int
dump_interface(desc)
	usb_descriptor_t *desc;
{
	usb_interface_descriptor_t *idesc = (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);
}

void
dump_endpoint(desc)
	usb_descriptor_t *desc;
{
	u_int8_t *buf = (u_int8_t *)desc;
	char *xfer_type;

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

	switch (buf[3] & UE_XFERTYPE) {
	case UE_CONTROL:
		xfer_type = "Control";
		break;
	case UE_ISOCHRONOUS:
		switch (buf[3] & 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 = "Interruput";
		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",
	       buf[0], buf[1], buf[2],
	       UE_GET_DIR(buf[2]) == UE_DIR_IN ? "in" : "out",
	       buf[3], xfer_type, UGETW(&buf[4]), buf[6]);

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

	printf("\n");

	return;
}

void
dump_string(sdesc, index)
	usb_string_descriptor_t *sdesc;
	int index;
{
	u_int8_t *buf = (u_int8_t *)sdesc;
	int n, i;

	n = (buf[0] - 2) / 2;

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

		printf("  bLength          %d\n"
		       "  bDescriptorType  %02x\n", buf[0], buf[1]);

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

	printf("\n");

	return;
}
