--- linux/drivers/usb/usb.c~	Fri Aug  4 02:20:49 2000
+++ linux/drivers/usb/usb.c	Fri Aug 25 18:21:38 2000
@@ -7,6 +7,7 @@
  * (C) Copyright Gregory P. Smith 1999
  * (C) Copyright Deti Fliegl 1999 (new USB architecture)
  * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright Fabrice Bellet 2000 (string caching)
  *
  * NOTE! This is not actually a driver at all, rather this is
  * just a collection of helper routines that implement the
@@ -25,6 +26,7 @@
 #include <linux/bitops.h>
 #include <linux/malloc.h>
 #include <linux/interrupt.h>  /* for in_interrupt() */
+#include <linux/smp_lock.h>
 
 
 #if	defined(CONFIG_KMOD) && defined(CONFIG_HOTPLUG)
@@ -1514,11 +1516,102 @@
 		(type << 8) + id, ifnum, buf, size, HZ * GET_TIMEOUT);
 }
 
+struct usb_string_cache_type {
+	__u16 idVendor;
+	__u16 idProduct;
+	unsigned short langid;
+	unsigned char index;
+	void *buf;
+	int size;
+};
+
+#define STRING_CACHE_SIZE 32
+static struct usb_string_cache_type usb_string_cache[STRING_CACHE_SIZE];
+static int usb_string_cache_idx=-1;
+
+static void
+usb_string_cache_add (struct usb_device *dev, unsigned short langid, 
+		unsigned char index, void *buf, int size)
+{
+	int i;
+
+	lock_kernel();
+	if (usb_string_cache_idx==-1) {
+		for (i = 0; i < STRING_CACHE_SIZE; i++)
+			usb_string_cache[i].buf=0;
+		usb_string_cache_idx=0;
+	}
+	if (usb_string_cache_idx == STRING_CACHE_SIZE)
+		usb_string_cache_idx = 0;
+	usb_string_cache[usb_string_cache_idx].idVendor=dev->descriptor.idVendor;
+	usb_string_cache[usb_string_cache_idx].idProduct=dev->descriptor.idProduct;
+	usb_string_cache[usb_string_cache_idx].langid=langid;
+	usb_string_cache[usb_string_cache_idx].index=index;
+	usb_string_cache[usb_string_cache_idx].size=size;
+	if (usb_string_cache[usb_string_cache_idx].buf)
+		kfree(usb_string_cache[usb_string_cache_idx].buf);
+	usb_string_cache[usb_string_cache_idx].buf=kmalloc (size,GFP_KERNEL);
+	memcpy (usb_string_cache[usb_string_cache_idx].buf, buf, size);
+#ifdef DEBUG
+	info("usb_string_cache_add() : added entry #%d",usb_string_cache_idx);
+	info("   idVendor=%x idProduct=%x langid=%x index=%x",
+		dev->descriptor.idVendor,
+		dev->descriptor.idProduct,
+		langid, index);
+#endif
+	usb_string_cache_idx++;
+	unlock_kernel();
+}
+
+static int
+usb_string_cache_lookup (struct usb_device *dev, unsigned short langid, 
+		unsigned char index, void *buf)
+{
+	int i;
+	int ret;
+
+	lock_kernel();
+	if (usb_string_cache_idx<=0) {
+		unlock_kernel();
+		return -1;
+	}
+	for (i = 0; i < STRING_CACHE_SIZE; i++)
+		if (usb_string_cache[i].buf
+				&& usb_string_cache[i].idVendor == dev->descriptor.idVendor
+				&& usb_string_cache[i].idProduct == dev->descriptor.idProduct
+				&& usb_string_cache[i].langid == langid
+				&& usb_string_cache[i].index == index) {
+#ifdef DEBUG
+			info("usb_string_cache_lookup(): copying cache entry #%d into buf",i);
+#endif
+			memcpy (buf, usb_string_cache[i].buf, usb_string_cache[i].size);
+			ret = usb_string_cache[i].size;
+			unlock_kernel();
+			return ret;
+		}
+	unlock_kernel();
+	return -1;
+}
+
 int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
 {
-	return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
-		USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
-		(USB_DT_STRING << 8) + index, langid, buf, size, HZ * GET_TIMEOUT);
+	int ret;
+
+	ret = usb_string_cache_lookup (dev, langid, index, buf);
+	if (ret == -1) {
+#ifdef DEBUG
+		info("usb_get_string() : value not cached for "
+			"idVendor=%x, idProduct=%x, langid=%x, index=%x",
+			dev->descriptor.idVendor,
+			dev->descriptor.idProduct,
+			langid, index);
+#endif
+		ret=usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
+			(USB_DT_STRING << 8) + index, langid, buf, size, HZ * GET_TIMEOUT);
+		usb_string_cache_add (dev, langid, index, buf, ret);
+	}
+	return ret;
 }
 
 int usb_get_device_descriptor(struct usb_device *dev)
@@ -1864,6 +1957,9 @@
 int usb_new_device(struct usb_device *dev)
 {
 	int err;
+#if !defined(DEBUG)
+	char *buf;
+#endif
 
 	info("USB new device connect, assigned device number %d", dev->devnum);
  
@@ -1935,6 +2031,17 @@
 		usb_show_string(dev, "Product", dev->descriptor.iProduct);
 	if (dev->descriptor.iSerialNumber)
 		usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
+#else
+	buf = kmalloc(128, GFP_KERNEL);
+	if (buf) {
+		if (dev->descriptor.iManufacturer)
+			usb_string(dev, dev->descriptor.iManufacturer, buf, 128);
+		if (dev->descriptor.iProduct)
+			usb_string(dev, dev->descriptor.iProduct, buf, 128);
+		if (dev->descriptor.iSerialNumber)
+			usb_string(dev, dev->descriptor.iSerialNumber, buf, 128);
+		kfree(buf);
+	}
 #endif
 
 	/* now that the basic setup is over, add a /proc/bus/usb entry */
