/*-
 * Copyright (c) 1999, 2000 MAEKAWA Masahide <maekawa@math.sci.kobe-u.ac.jp>
 *			    UCHIYAMA Yasushi
 * 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: hid.c,v 1.34 2000/04/03 20:52:18 maekawa Exp $
 */

#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>

#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>

#include "main.h"
#include "hid.h"

#include <hiddefs.h>
#include <hiddefs_data.h>

static void hid_report		__P((u_int8_t *, size_t));

extern int fd, level;

/*
 * Class: Human Interface Devices (HID)
 */
void
hid_device(desc, interface)
	usb_descriptor_t *desc;
	int interface;
{
	u_int8_t *buf = (u_int8_t *)desc, *tbuf;
	int n, length, i, j;

	printf("\t\tHID Descriptor:\n"
	       "\t\t  bLength           %d\n"
	       "\t\t  bDescriptorType   %02x\n"
	       "\t\t  bcdHID            %04x\n"
	       "\t\t  bCountryCode      %02x\n"
	       "\t\t  bNumDescriptors   %d\n",
	       buf[0], buf[1], UGETW(&buf[2]), buf[4], buf[5]);

	n = buf[5];

	for (i = 0 ; i < n ; i++) {
		length = UGETW(&buf[6+i*3+1]);
		printf("\t\t  bDescriptorType   %02x\n"
		       "\t\t  wDescriptorLength %d\n\n",
		       buf[6+i*3], length);
		tbuf = (u_int8_t *)malloc(length);
		if (tbuf == NULL) {
			err(1, "hid_device");
		}
		if (usb_get_descriptor(interface, buf[6+i*3], i, interface,
				       tbuf, length)) {
			if (buf[6+i*3] == UDESC_REPORT) {
				printf("\t\tReport Desctiptor (ID: %d) {\n", i);
				hid_report(tbuf, length);
				printf("\t\t}\n");
			} else {
				printf("\t\t");
				for (j = 0 ; j < length ; j++) {
					printf(" %02x", tbuf[j]);
				}
				printf("\n");
			}
		}
		free((void *)tbuf);
	}

	printf("\n");

	return;
}

void
hid_report(buf, bufsize)
	u_int8_t *buf;
	size_t bufsize;
{
	int bSize, bType, bTag, i = 0;
	u_int8_t *end, item;
	char *name;

	for (end = buf + bufsize; buf < end; ) {
		item = *buf++;
		bSize = HID_ITEM_SIZE_GET(HID_ITEM_SIZE(item));
		bType = HID_ITEM_TYPE(item);
		bTag  = HID_ITEM_TAG(item);

		if (bTag == HID_ITEM_TAG_LONGITEM) {
			fprintf(stderr, "WARNING: long item (reserved for "
					"future use.\n");
			bSize = *buf++;
			bTag = *buf++;
		}

		name = hid_get_item_name(bType, bTag);
		if (name) {
			if (bType == HID_ITEM_TYPE_MAIN &&
			    bTag == HID_MAIN_ITEM_ENDCOLLECTION)
				level--;
			iprintf("%s", name);
		} else {
			iprintf("Unknown Item(%01x, %01x): ", bType, bTag);
			for (i = 0 ; i < bSize ; i++) {
				printf("%02x ", buf[i]);
			}
		}

		switch (bType) {
		case HID_ITEM_TYPE_MAIN:
			hid_main_item(bTag, buf, bSize);
			break;
		case HID_ITEM_TYPE_GLOBAL:
			hid_global_item(bTag, buf, bSize);
			break;
		case HID_ITEM_TYPE_LOCAL:
			hid_local_item(bTag, buf, bSize);
			break;
		}

		buf += bSize; /* next item */
	}

	return;
}

char *
hid_get_item_name(type, tag)
	u_int8_t type, tag;
{
	struct hid_item *hi;

	for (hi = hid_items ; hi->name != NULL ; hi++) {
		if (hi->type == type && hi->tag == tag)
			break;
	}
	return (hi->name);
}

char *
hid_get_collection_name(type)
	u_int8_t type;
{
	struct hid_collection *hc;

	if (type >= HID_COLLECTION_VENDOR_START &&
	    type <= HID_COLLECTION_VENDOR_END)
		return ("Vendor-defined");

	for (hc = hid_collections ; hc->name != NULL ; hc++) {
		if (hc->type == type)
			break;
	}
	return (hc->name);
}

char *
hid_get_page_name(page)
	u_int16_t page;
{
	struct hid_usage_page *hp;

	if (page >= HID_USAGE_PAGE_VENDOR_START &&
	    page <= HID_USAGE_PAGE_VENDOR_END)
		return ("Vendor-defined");

	for (hp = hid_usage_pages ; hp->name != NULL ; hp++) {
		if (hp->page == page)
			break;
	}

	return (hp->name);
}

char *
hid_get_usage_name(page, usage)
	u_int16_t page, usage;
{
	struct hid_usage *hu;

	for (hu = hid_usages ; hu->name != NULL ; hu++) {
		if (hu->page == page && hu->usage == usage)
			break;
	}
	return (hu->name);
}

u_int32_t
hid_get_usage_type(page, usage)
	u_int16_t page, usage;
{
	struct hid_usage *hu;

	for (hu = hid_usages ; hu->name != NULL ; hu++) {
		if (hu->page == page && hu->usage == usage)
			break;
	}
	return (hu->type);
}
