/*
 * driver/usb/usb.c
 *
 * (C) Copyright Linus Torvalds 1999
 *
 * NOTE! This is not actually a driver at all, rather this is
 * just a collection of helper routines that implement the
 * generic USB things that the real drivers can use..
 *
 * Think of this as a "USB library" rather than anything else.
 * It should be considered a slave, with no callbacks. Callbacks
 * are evil.
 */

/*
 * Table 9-2
 *
 * Offset	Field		Size	Value		Desc
 * 0		bmRequestType	1	Bitmap		D7:	Direction
 *							0 = Host-to-device
 *							1 = Device-to-host
 *							D6..5:	Type
 *							0 = Standard
 *							1 = Class
 *							2 = Vendor
 *							3 = Reserved
 *							D4..0:	Recipient
 *							0 = Device
 *							1 = Interface
 *							2 = Endpoint
 *							3 = Other
 *							4..31 = Reserved
 * 1		bRequest	1	Value		Specific request (9-3)
 * 2		wValue		2	Value		Varies
 * 4		wIndex		2	Index/Offset	Varies
 * 6		wLength		2	Count		Bytes for data
 */

#include <linux/string.h>
#include <linux/bitops.h>
#include <linux/malloc.h>

#include "usb.h"

/*
 * We have a per-interface "registered driver" list.
 */
static LIST_HEAD(usb_driver_list);

int usb_register(struct usb_driver *new_driver)
{
	/* Add it to the list of known drivers */
	list_add(&new_driver->driver_list, &usb_driver_list);

	/*
	 * We should go through all existing devices, and see if any of
	 * them would be acceptable to the new driver.. Let's do that
	 * in version 2.0.
	 */
	return 0;
}

void usb_deregister(struct usb_driver *driver)
{
	list_del(&driver->driver_list);
}

/*
 * This entrypoint gets called for each new device.
 *
 * We now walk the list of registered USB drivers,
 * looking for one that will accept this device as
 * his..
 */
void usb_device_descriptor(struct usb_device *dev)
{
	struct list_head *tmp = usb_driver_list.next;

	while (tmp != &usb_driver_list) {
		struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list);
		tmp = tmp->next;
		if (driver->probe(dev))
			continue;
		dev->driver = driver;
		return;
	}

	/*
	 * Ok, no driver accepted the device, so show the info
	 * for debugging..
	 */
	printk("Unknown new USB device:\n");
	usb_show_device(dev);
}

/*
 * Parse the fairly incomprehensible output of
 * the USB configuration data, and build up the
 * USB device database.
 */
static int usb_expect_descriptor(unsigned char *ptr, int len, unsigned char desctype, unsigned char descindex)
{
	int parsed = 0;

	for (;;) {
		unsigned short n_desc;
		int n_len, i;

		if (len < descindex)
			return -1;
		n_desc = *(unsigned short *)ptr;
		if (n_desc == ((desctype << 8) + descindex))
			return parsed;
		printk(
		"Expected descriptor %02X/%02X, got %02X/%02X - skipping\n",
			desctype, descindex,
			(n_desc >> 8) & 0xFF, n_desc & 0xFF);
		n_len = n_desc & 0xff;
		if (n_len < 2 || n_len > len)
			return -1;
		for (i = 0 ; i < n_len; i++)
			printk("   %d %02x\n", i, ptr[i]);
		len -= n_len;
		ptr += n_len;
		parsed += n_len;
	}
}

static int usb_parse_endpoint(struct usb_endpoint_descriptor *endpoint, unsigned char *ptr, int len)
{
	int parsed = usb_expect_descriptor(ptr, len, USB_DT_ENDPOINT, 7);

	if (parsed < 0)
		return parsed;

	memcpy(endpoint, ptr + parsed, 7);
	return parsed + 7;
}

static int usb_parse_interface(struct usb_interface_descriptor *interface, unsigned char *ptr, int len)
{
	int i;
	int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, 9);
	int retval;

	if (parsed < 0)
		return parsed;

	memcpy(interface, ptr + parsed, 9);
	len -= 9;
	parsed += 9;

	if (interface->bNumEndpoints > USB_MAXENDPOINTS)
		return -1;

	for (i = 0; i < interface->bNumEndpoints; i++) {
		if(((USB_DT_HID << 8) | 9) == *(unsigned short*)(ptr + parsed)) {
			parsed += 9;	/* skip over the HID descriptor for now */
			len -= 9;
		}
		retval = usb_parse_endpoint(interface->endpoint + i, ptr + parsed, len);
		if (retval < 0) return retval;
		parsed += retval;
		len -= retval;
	}
	return parsed;
}

static int usb_parse_config(struct usb_config_descriptor *config, unsigned char *ptr, int len)
{
	int i;
	int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9);

	if (parsed < 0)
		return parsed;

	memcpy(config, ptr + parsed, 9);
	len -= 9;
	parsed += 9;

	if (config->bNumInterfaces > USB_MAXINTERFACES)
		return -1;

	for (i = 0; i < config->bNumInterfaces; i++) {
		int retval = usb_parse_interface(config->interface + i, ptr + parsed, len);
		if (retval < 0)
			return retval;
		parsed += retval;
		len -= retval;
	}
	return parsed;
}

int usb_parse_configuration(struct usb_device *dev, void *__buf, int bytes)
{
	int i;
	unsigned char *ptr = __buf;

	if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG)
		return -1;

	for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
		int retval = usb_parse_config(dev->config + i, ptr, bytes);
		if (retval < 0)
			return retval;
		ptr += retval;
		bytes += retval;
	}
	return 0;
}

void usb_init_root_hub(struct usb_device *dev)
{
	dev->devnum = -1;
	dev->slow = 0;
}

/*
 * Something got disconnected. Get rid of it, and all of its children.
 */
void usb_disconnect(struct usb_device **pdev)
{
	struct usb_device * dev = *pdev;

	if (dev) {
		int i;

		*pdev = NULL;

		printk("USB disconnect on device %d\n", dev->devnum);

		if(dev->driver) dev->driver->disconnect(dev);

		/* Free up all the children.. */
		for (i = 0; i < USB_MAXCHILDREN; i++) {
			struct usb_device **child = dev->children + i;
			usb_disconnect(child);
		}

		/* Free up the device itself, including its device number */
		if (dev->devnum > 0)
			clear_bit(dev->devnum, &dev->bus->devmap.devicemap);
		dev->bus->op->deallocate(dev);
	}
}


/*
 * Connect a new USB device. This basically just initializes
 * the USB device information and sets up the topology - it's
 * up to the low-level driver to reset the port and actually
 * do the setup (the upper levels don't know how to do that).
 */
void usb_connect(struct usb_device *dev)
{
	int devnum;

	dev->descriptor.bMaxPacketSize0 = 8;  /* XXX fixed 8 bytes for now */

	devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1);
	if (devnum < 128) {
		set_bit(devnum, dev->bus->devmap.devicemap);
		dev->devnum = devnum;
	}
}

/*
 * These are the actual routines to send
 * and receive control messages.
 */
int usb_set_address(struct usb_device *dev)
{
	devrequest dr;

	dr.requesttype = 0;
	dr.request = USB_REQ_SET_ADDRESS;
	dr.value = dev->devnum;
	dr.index = 0;
	dr.length = 0;

	return dev->bus->op->control_msg(dev, usb_snddefctrl(dev), &dr, NULL, 0);
}

int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
{
	devrequest dr;

	dr.requesttype = 0x80;
	dr.request = USB_REQ_GET_DESCRIPTOR;
	dr.value = (type << 8) + index;
	dr.index = 0;
	dr.length = size;

	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size);
}

int usb_get_device_descriptor(struct usb_device *dev)
{
	return usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor));
}

int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size)
{
	devrequest dr;

	dr.requesttype = USB_RT_HUB | 0x80;
	dr.request = USB_REQ_GET_DESCRIPTOR;
	dr.value = (USB_DT_HUB << 8);
	dr.index = 0;
	dr.length = size;

	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, size);
}

int usb_clear_port_feature(struct usb_device *dev, int port, int feature)
{
	devrequest dr;

	dr.requesttype = USB_RT_PORT;
	dr.request = USB_REQ_CLEAR_FEATURE;
	dr.value = feature;
	dr.index = port;
	dr.length = 0;

	return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
}

int usb_set_port_feature(struct usb_device *dev, int port, int feature)
{
	devrequest dr;

	dr.requesttype = USB_RT_PORT;
	dr.request = USB_REQ_SET_FEATURE;
	dr.value = feature;
	dr.index = port;
	dr.length = 0;

	return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
}

int usb_get_hub_status(struct usb_device *dev, void *data)
{
	devrequest dr;

	dr.requesttype = USB_RT_HUB | 0x80;
	dr.request = USB_REQ_GET_STATUS;
	dr.value = 0;
	dr.index = 0;
	dr.length = 4;

	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
}

int usb_get_port_status(struct usb_device *dev, int port, void *data)
{
	devrequest dr;

	dr.requesttype = USB_RT_PORT | 0x80;
	dr.request = USB_REQ_GET_STATUS;
	dr.value = 0;
	dr.index = port;
	dr.length = 4;

	return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4);
}

int usb_get_protocol(struct usb_device *dev)
{
	unsigned char buf[8];
	devrequest dr;

	dr.requesttype = USB_RT_HIDD | 0x80;
	dr.request = USB_REQ_GET_PROTOCOL;
	dr.value = 0;
	dr.index = 1;
	dr.length = 1;

	if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 1))
		return -1;

	return buf[0];
}

int usb_set_protocol(struct usb_device *dev, int protocol)
{
	devrequest dr;

	dr.requesttype = USB_RT_HIDD;
	dr.request = USB_REQ_SET_PROTOCOL;
	dr.value = protocol;
	dr.index = 1;
	dr.length = 0;

	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
		return -1;

	return 0;
}

/* keyboards want a nonzero duration according to HID spec, but
   mice should use infinity (0) -keryan */
int usb_set_idle(struct usb_device *dev,  int duration, int report_id)
{
	devrequest dr;

	dr.requesttype = USB_RT_HIDD;
	dr.request = USB_REQ_SET_IDLE;
	dr.value = (duration << 8) | report_id;
	dr.index = 1;
	dr.length = 0;

	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
		return -1;

	return 0;
}

int usb_set_configuration(struct usb_device *dev, int configuration)
{
	devrequest dr;

	dr.requesttype = 0;
	dr.request = USB_REQ_SET_CONFIGURATION;
	dr.value = configuration;
	dr.index = 0;
	dr.length = 0;

	if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
		return -1;

	return 0;
}

int usb_get_report(struct usb_device *dev)
{
	unsigned char buf[8];
	devrequest dr;

	dr.requesttype = USB_RT_HIDD | 0x80;
	dr.request = USB_REQ_GET_REPORT;
	dr.value = 0x100;
	dr.index = 1;
	dr.length = 3;

	if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 3))
		return -1;

	return buf[0];
}

int usb_get_configuration(struct usb_device *dev)
{
	unsigned int size;
	unsigned char buffer[128];

	/* Get the first 8 bytes - guaranteed */
	if (usb_get_descriptor(dev, USB_DT_CONFIG, 0, buffer, 8))
		return -1;

	/* Get the full buffer */
	size = *(unsigned short *)(buffer+2);
	if (size > sizeof(buffer))
		size = sizeof(buffer);

	if (usb_get_descriptor(dev, USB_DT_CONFIG, 0, buffer, size))
		return -1;

	return usb_parse_configuration(dev, buffer, size);
}

/*
 * By the time we get here, the device has gotten a new device ID
 * and is in the default state. We need to identify the thing and
 * get the ball rolling..
 */
void usb_new_device(struct usb_device *dev)
{
	printk("USB new device connect, assigned device number %d\n",
		dev->devnum);
	if (usb_set_address(dev))
		return;
	if (usb_get_device_descriptor(dev))
		return;
	if (usb_get_configuration(dev))
		return;

	usb_device_descriptor(dev);
}

int usb_request_irq(struct usb_device *dev, unsigned int pipe,
	usb_device_irq handler, void *data, int period, void *dev_id)
{
	return dev->bus->op->request_irq(dev, pipe, handler, data, period, dev_id);
}

