/*
 * 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";
	case USB_CLASS_STILL_IMAGE:	return "still_image";
	case USB_CLASS_PRINTER:		return "printer";
	case USB_CLASS_MASS_STORAGE:	return "mass_storage";
	case USB_CLASS_HUB:		return "hub";
	case USB_CLASS_CDC_DATA:	return "cdc_data";
	case USB_CLASS_CSCID:		return "cscid";
	case USB_CLASS_CONTENT_SEC:	return "content_sec";
	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;
{
	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_DEVICE_QUALIFIER:	return "device_qualifier";
	case USB_DT_OTHER_SPEED_CONFIG:	return "other_speed_config";
	case USB_DT_INTERFACE_POWER:	return "interface_power";
	}
	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 "?";
}

PRIVATE const char *
str_usb_device_speed(n)
	int n;
{
	switch (n) {
	case USB_SPEED_UNKNOWN:	return "Unknown";
	case USB_SPEED_LOW:	return "Low";
	case USB_SPEED_FULL:	return "Full";
	case USB_SPEED_HIGH:	return "High";
	default:		return "?";
	}
}

PRIVATE const char *
str_usb_device_state(n)
	int n;
{
	switch (n) {
	case USB_STATE_NOTATTACHED:	return "noattached";
	case USB_STATE_ATTACHED:	return "attached";
	case USB_STATE_POWERED:		return "powered";
	case USB_STATE_DEFAULT:		return "default";
	case USB_STATE_ADDRESS:		return "address";
	case USB_STATE_CONFIGURED:	return "configured";
	case USB_STATE_SUSPENDED:	return "suspended";
	default:			return "?";
	}
}

void
prhead_usb_driver()
{
	mprintf(SPTR" "SPTR" "SPTR" "SPTR" "SPTR" "SPTR" NAME\n",
		"ADDR", "OWNER", "PROBE", "DISCON", "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, nflag)
	addr_t addr;
	int nflag;
{
	struct usb_driver ud;
	char buf[256];

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

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

	return 0;
}

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

addr_t
print_usb_bus(addr, list_offset, full)
	addr_t addr;
	int list_offset;
	int full;
{
	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(FPTR " ", ub.controller);
	mprintf("%3x ", ub.busnum);
	mprintf("%3x ", ub.devnum_next);
	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);
	for (i = 0; i < LENGTHOF(ub.devmap.devicemap); i++) {
		if (i) mprintf(":");
		mprintf("%lx", ub.devmap.devicemap[i]);
	}
	mprintf("\n");
	if (full) {
		const char *p;
		mprintf("  usbfs_dentry:    " FPTR "\n", ub.usbfs_dentry);
		mprintf("  usbdevfs_dentry: " FPTR "\n", ub.usbdevfs_dentry);
		mprintf("  release:         " FPTR "  ", ub.release);
		p = getsymstr_func((addr_t)ub.release);
		mprintf("%s\n", p? p: "-");
	}

	return (addr_t)ub.bus_list.next;
}

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

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

	memread(addr, sizeof(ud), &ud, "usb_device");
	if (!full) {
		mprintf(FPTR " %3x ", addr, ud.devnum);
		mprintf("%c ",	*str_usb_device_speed(ud.speed));
		mprintf(FPTR " %x ", ud.tt, ud.ttport);
		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);
		mprintf("%x ", ud.maxchild);
		mprintf("%s ", str_usb_desc_type(ud.descriptor.bDescriptorType));
		mprintf("%s\n", str_usb_class(ud.descriptor.bDeviceClass));
		return 0;
	}

	mprintf("addr            " FPTR "\n", addr);
	mprintf("devnum          %x\n", ud.devnum);
	mprintf("state           %x  %s\n", ud.state,
		str_usb_device_state(ud.state));
	mprintf("speed           %x  %s\n", ud.speed,
		str_usb_device_speed(ud.speed));
	mprintf("tt              " FPTR "\n", ud.tt);
	mprintf("ttport          %x\n", ud.ttport);
	mprintf("toggle          %x %x\n", ud.toggle[0], ud.toggle[1]);
	mprintf("halted          %x %x\n", ud.halted[0], ud.halted[1]);
	mprintf("parent          " FPTR "  (usb_device)\n", ud.parent);
	mprintf("bus             " FPTR "  (usb_bus)\n", ud.bus);
	mprintf("desc.bsdUSB     %04x\n", ud.descriptor.bcdUSB);
	mprintf("desc.type       %s\n",
		str_usb_desc_type(ud.descriptor.bDescriptorType));
	mprintf("desc.devclass   %s\n", str_usb_class(ud.descriptor.bDeviceClass));
	mprintf("config          " FPTR "\n", ud.config);
	mprintf("actconfig       " FPTR "\n", ud.actconfig);
	mprintf("langid          %x %x\n", ud.have_langid, ud.string_langid);
	mprintf("rawdescriptors  " FPTR "\n", ud.actconfig);
	mprintf("hcpriv          " FPTR "\n", ud.hcpriv);
	mprintf("usbfs_dentry    " FPTR "  (dentry)\n", ud.usbfs_dentry);
	mprintf("usbdevfs_dentry " FPTR "  (dentry)\n", ud.usbdevfs_dentry);
	for (i = 0; i < USB_MAXCHILDREN && i < ud.maxchild; i++) {
		mprintf("children%3x     " FPTR "  (usb_device)\n", i,
			ud.children[i]);
	}
	return 0;
}

void
prhead_usb_interface()
{
	mprintf(SPTR" "SPTR" ACT NUM "SPTR" MIN\n",
		"ADDR", "ALTSET", "DRIVER");
}

addr_t
print_usb_interface(addr, full)
	addr_t addr;
	int full;
{
	struct usb_interface ui;

	memread(addr, sizeof(ui), &ui, "usb_interface");

	if (!full) {
		mprintf(FPTR " ", addr);
		mprintf(FPTR " ", ui.altsetting);
		mprintf("%3x %3x ",
			ui.act_altsetting, ui.num_altsetting);
		mprintf(FPTR " %3x\n", ui.driver, ui.minor);
		return 0;
	}

	mprintf("addr           " FPTR "\n", addr);
	mprintf("altsetting     " FPTR "\n", ui.altsetting);
	mprintf("act_altsetting %x\n", ui.act_altsetting);
	mprintf("num_altsetting %x\n", ui.num_altsetting);
	mprintf("driver         " FPTR "  (usb_driver)\n", ui.driver);
	mprintf("minor          %x\n", ui.minor);
	return 0;
}
