/*-
 * 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: main.c,v 1.25 2000/04/07 00:13:41 maekawa Exp $
 */

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

#include <dev/usb/usb.h>

#include "main.h"

int indexes = 0;
int language_id = 0;
int fd;

extern struct devinfo usbdev;
extern int level;

int
main(argc, argv)
	int argc;
	char *argv[];
{
	usb_device_descriptor_t ddesc;
	struct usb_config_desc cdesc;
	struct usb_full_desc fdesc;
	struct usb_string_desc sdesc;
	char *devname;
	int n, i;
	u_int8_t *buf, tbuf;

	if (argc == 1)
#if defined(__NetBSD__)
		devname = "/dev/ugen0.00";
#else
		devname = "/dev/ugen0";
#endif
	else
		devname = argv[1];

	fd = open(devname, O_RDWR);
	if (fd < 0) {
		err(1, "%s", devname);
		exit(1);
	}

	if (ioctl(fd, USB_GET_DEVICE_DESC, &ddesc) != 0)
		err(1, "ioctl USB_GET_DEVICE_DESC");

	dump_device(&ddesc);

	for (i = 0 ; i < ddesc.bNumConfigurations ; i++) {
		cdesc.ucd_config_index = i;
		if (ioctl(fd, USB_GET_CONFIG_DESC, &cdesc) != 0)
			err(1, "ioctl USB_GET_CONFIG_DESC");

		fdesc.ufd_config_index = i;
		fdesc.ufd_size = UGETW(cdesc.ucd_desc.wTotalLength);
		fdesc.ufd_data = (u_char *) malloc(UGETW(cdesc.ucd_desc.wTotalLength));
		if (ioctl(fd, USB_GET_FULL_DESC, &fdesc) != 0) {
			free((void *)(fdesc.ufd_data));
			err(1, "ioctl USB_GET_FULL_DESC");
		}

		printf("Configuration %d:\n", i);
		dump_full(&fdesc);

		free((void *)(fdesc.ufd_data));
	}

	if (indexes != 0) {
		memset(&sdesc, 0, sizeof(sdesc));
		if (ioctl(fd, USB_GET_STRING_DESC, &sdesc) != 0)
			err(1, "ioctl USB_GET_STRING_DESC");
		dump_string(&sdesc.usd_desc, 0);

		buf = (u_int8_t *)&sdesc.usd_desc;
		n = (buf[0] - 2) / 2;

		for (i = 0 ; i < n ; i++) {
			if (UGETW(&buf[2+i*2]) == 0x0409) {
				language_id = UGETW(&buf[2+i*2]);
				break;
			}
		}

		if (language_id == 0x0409) {
			sdesc.usd_language_id = language_id;

			for (i = 1 ; i <= indexes ; i++) {
				sdesc.usd_string_index = i;
				if (ioctl(fd, USB_GET_STRING_DESC, &sdesc) != 0)
					err(1, "ioctl USB_GET_STRING_DESC");
				dump_string(&sdesc.usd_desc, i);
			}
		}
	}

	for (i = 0x21 ; i <= 0x25 ; i++) {
		printf("Descriptor (0x%02x): ", i);
		if (usb_get_descriptor(0, i, 0, 0, &tbuf, 1))
			printf("supported\n");
		else
			printf("not supported\n");
	}

	close(fd);
	exit(0);
}

int
usb_get_descriptor(addr, vh, vl, idx, dbuf, dsize)
	int addr;
	u_int8_t vh, vl, idx;
	void *dbuf;
	int dsize;
{
	struct usb_ctl_request ctlreq;

	ctlreq.ucr_addr = addr;

	ctlreq.ucr_request.bmRequestType = UT_READ_INTERFACE;
	ctlreq.ucr_request.bRequest = UR_GET_DESCRIPTOR;
	USETW2(ctlreq.ucr_request.wValue, vh, vl);
	USETW(ctlreq.ucr_request.wIndex, idx);
	USETW(ctlreq.ucr_request.wLength, dsize);

	ctlreq.ucr_data = dbuf;
	ctlreq.ucr_flags = 0;
	ctlreq.ucr_actlen = 0;

	if (ioctl(fd, USB_DO_REQUEST, &ctlreq) != 0)
		return (0);

	if (ctlreq.ucr_actlen != dsize)
		return (0);

	return (1);
}

void
bprintf(buf, bufsize)
	u_int8_t *buf;
	size_t bufsize;
{
	int i;

	for (i = 0 ; i < bufsize * 8 ; i++)
		printf("%d", BIT(buf[i / 8], 7 - (i % 8), 1));

	return;
}

void
iprintf(char *fmt, ...)
{
	va_list ap;
	int i;

	for (i = 0 ; i < level ; i++)
		printf("\t");

	va_start(ap, fmt);
	(void)vfprintf(stdout, fmt, ap);
	va_end(ap);

	return;
}
