/*
 * Copyright (C) 2000-2003 ASANO Masahiro
 */

#include "def.h"

#define __KERNEL__
#include <linux/usb.h>

PRIVATE const char *
str_usb_class(n)
	int n;
{
	switch (n) {
	case USB_CLASS_PER_INTERFACE:	return "per_interface";
	case USB_CLASS_AUDIO:		return "audio";
	case USB_CLASS_COMM:		return "comm";
	case USB_CLASS_HID:		return "hid";
	case USB_CLASS_PHYSICAL:	return "physical";
#ifdef USB_CLASS_STILL_IMAGE
	case USB_CLASS_STILL_IMAGE:	return "still_image";
#endif
	case USB_CLASS_PRINTER:		return "printer";
	case USB_CLASS_MASS_STORAGE:	return "mass_storage";
	case USB_CLASS_HUB:		return "hub";
#ifdef USB_CLASS_DATA
	case USB_CLASS_DATA:		return "data";
#endif
#ifdef USB_CLASS_CDC_DATA
	case USB_CLASS_CDC_DATA:	return "cdc_data";
#endif
#ifdef USB_CLASS_CSCID
	case USB_CLASS_CSCID:		return "cscid";
#endif
#ifdef USB_CLASS_CONTENT_SEC
	case USB_CLASS_CONTENT_SEC:	return "content_sec";
#endif
	case USB_CLASS_APP_SPEC:	return "app_spec";
	case USB_CLASS_VENDOR_SPEC:	return "vendor_spec";
	}
	return "?";
}

PRIVATE const char *
str_usb_type(n)
	int n;
{
#ifndef USB_TYPE_MASK
#define USB_TYPE_MASK USB_TYPE_RESERVED
#endif
	switch (n & USB_TYPE_MASK) {
	case USB_TYPE_STANDARD:		return "standard";
	case USB_TYPE_CLASS:		return "class";
	case USB_TYPE_VENDOR:		return "vendor";
	case USB_TYPE_RESERVED:		return "reserved";
	}
	return "?";
}

PRIVATE const char *
str_usb_desc_type(n)
	int n;
{
	switch (n) {
	case USB_DT_DEVICE:		return "device";
	case USB_DT_CONFIG:		return "config";
	case USB_DT_STRING:		return "string";
	case USB_DT_INTERFACE:		return "interface";
	case USB_DT_ENDPOINT:		return "endpoint";

	case USB_DT_HID:		return "hid";
	case USB_DT_REPORT:		return "report";
	case USB_DT_PHYSICAL:		return "physical";
	case USB_DT_HUB:		return "hub";
	}
	return "?";
}

PRIVATE const char *
str_usb_recip(n)
	int n;
{
	switch (n & USB_RECIP_MASK) {
	case USB_RECIP_DEVICE:		return "device";
	case USB_RECIP_INTERFACE:	return "interface";
	case USB_RECIP_ENDPOINT:	return "endpoint";
	case USB_RECIP_OTHER:		return "other";
	}
	return "?";
}

void
prhead_usb_driver()
{
	mprintf(SPTR" "SPTR" "SPTR" "SPTR" MIN "SPTR" "SPTR" NAME\n",
		"ADDR", "PROBE", "DISCON", "FOPS", "IOCTL", "ID_TABLE");
}

PRIVATE const char *
S(s)
	const char *s;
{
	int i;
#if PTRSIZE==8
	static char r[17];
#else
	static char r[9];
#endif
	if (s == NULL) {
		r[0] = '-';
		r[1] = '\0';
	} else {
		for (i = 0; i < sizeof(r) - 1; i++)
			if ((r[i] = s[i]) == 0)
				break;
	}
	return r;
}

addr_t
print_usb_driver(addr, list_offset, nflag)
	addr_t addr;
	int list_offset;
	int nflag;
{
	struct usb_driver ud;
	char buf[256];

	if (list_offset)
		addr -= OFFSET(struct usb_driver, driver_list);

	memread(addr, sizeof(ud), &ud, "usb_driver");
	mprintf(FPTR " ", addr);

	if (nflag) {
		mprintf(SPTR " ", S(getsymstr_func((addr_t)ud.probe)));
		mprintf(SPTR " ", S(getsymstr_func((addr_t)ud.disconnect)));
		mprintf(SPTR " %3x ", S(getsymstr_func((addr_t)ud.fops)), ud.minor);
		mprintf(SPTR " ", S(getsymstr_func((addr_t)ud.ioctl)));
		mprintf(SPTR " ", S(getsymstr_func((addr_t)ud.id_table)));
	} else {
		mprintf(FPTR " " FPTR " " FPTR " %3x " FPTR " " FPTR " ",
			ud.probe, ud.disconnect, ud.fops, ud.minor, ud.ioctl,
			ud.id_table);
	}
	memread((addr_t)ud.name, sizeof(buf), buf, "name");
	mprintstr(buf, sizeof(buf));
	mprintf("\n");

	return (addr_t)ud.driver_list.next;
}

void
prhead_usb_bus()
{
	mprintf(SPTR" NUM "SPTR" "SPTR" "SPTR" ALC REQ REQ CNT DEVMAP\n", "ADDR", "OP", "ROOT_HUB", "HCPRIV");
}

addr_t
print_usb_bus(addr, list_offset)
	addr_t addr;
	int list_offset;
{
	struct usb_bus ub;
	int i;

	if (list_offset)
		addr -= OFFSET(struct usb_bus, bus_list);

	memread(addr, sizeof(ub), &ub, "usb_bus");
	mprintf(FPTR " ", addr);

	mprintf("%3x ", ub.busnum);
	mprintf(FPTR " " FPTR " " FPTR " ", ub.op, ub.root_hub, ub.hcpriv);
	mprintf("%3x %3x %3x ", ub.bandwidth_allocated,
		ub.bandwidth_int_reqs, ub.bandwidth_isoc_reqs);
#if SUBLEVEL>=4
	mprintf("%3x ", ATOMIC_READ(ub.refcnt));
#endif
	for (i = 0; i < LENGTHOF(ub.devmap.devicemap); i++) {
		if (i) mprintf(":");
		mprintf("%lx", ub.devmap.devicemap[i]);
	}
	mprintf("\n");

	return (addr_t)ub.bus_list.next;
}

void
prhead_usb_device()
{
	mprintf(SPTR" DEV S "SPTR" P CNT -- -- "SPTR" "SPTR" "SPTR" "SPTR"\n",
		"ADDR", "DEVICE", "PARENT", "BUS", "CONFIG", "HCPRIV");
}

addr_t
print_usb_device(addr)
	addr_t addr;
{
	struct usb_device ud;
	int i;

	memread(addr, sizeof(ud), &ud, "usb_device");
	mprintf(FPTR " ", addr);

	mprintf("%3x ", ud.devnum);
#if SUBLEVEL>=10
	mprintf("%c ",	ud.speed==USB_SPEED_UNKNOWN? '0':
			(ud.speed==USB_SPEED_LOW? 'L':
			(ud.speed==USB_SPEED_FULL? 'F':
			(ud.speed==USB_SPEED_HIGH? 'H': '?'))));
	mprintf(FPTR " %x ", ud.tt, ud.ttport);
#endif
	mprintf("%3x ", ATOMIC_READ(ud.refcnt));
	mprintf("%x%x %x%x ", ud.toggle[0], ud.toggle[1], ud.halted[0], ud.halted[1]);
	mprintf(FPTR " " FPTR " " FPTR " " FPTR " ", ud.parent, ud.bus, ud.config, ud.hcpriv);
#if 0
	mprintf("%02x%02x ", ud.descriptor.bcdUSB >> 8, ud.descriptor.bcdUSB & 0xff);
#endif
	mprintf("%s ", str_usb_desc_type(ud.descriptor.bDescriptorType));
	mprintf("%s\n", str_usb_class(ud.descriptor.bDeviceClass));
	if (ud.maxchild) {
		for (i = 0; i < USB_MAXCHILDREN && i < ud.maxchild; i++)
			mprintf(" " FPTR, ud.children[i]);
		mprintf("\n");
	}
	return 0;
}
