/*
 * 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.
 */

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

#include "usb.h"

/*
 * Parse a device descriptor, filling in the USB device information..
 */
void usb_device_descriptor(struct usb_device *dev, void * descriptor)
{
	unsigned int dev_id;

	memcpy(&dev->descriptor, descriptor, sizeof(dev->descriptor));
	printk("New device:\n");
	printk("  USB version %x.%02x\n", dev->descriptor.bcdUSB >> 8, dev->descriptor.bcdUSB & 0xff);
	printk("  Vendor:  %04x\n", dev->descriptor.idVendor);
	printk("  Product: %04x\n", dev->descriptor.idProduct);

	dev_id = ((unsigned int) dev->descriptor.idVendor << 16) | dev->descriptor.idProduct;
	switch (dev_id) {
	case 0x057b0000:
		printk("  Floppy drive?");
		break;
	case 0x04710302:
		printk("  Camera?\n");
		break;
	case 0x04511446:
		printk("  Four-port Hub?\n");
		break;
	}
}

/*
 * 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, struct usb_device *parent, unsigned int port)
{
	int devnum;

	memset(dev, 0, sizeof(*dev));

	dev->descriptor.bMaxPacketSize0 = 8;
	dev->devmap = parent->devmap;
	dev->parent = parent;
	parent->children[port] = dev;

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

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

	/* Free up all the children.. */
	for (i = 0; i < 7; i++) {
		struct usb_device *child = dev->children[i];
		if (!child)
			continue;
		dev->children[i] = NULL;
		usb_disconnect(child);
	}

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