patch-2.4.11-dontuse linux/drivers/usb/uhci.c

Next file: linux/drivers/usb/uhci.h
Previous file: linux/drivers/usb/uhci-debug.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.10/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
@@ -61,7 +61,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION ""
+#define DRIVER_VERSION "v1.1"
 #define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
 #define DRIVER_DESC "USB Universal Host Controller Interface driver"
 
@@ -1258,21 +1258,38 @@
 			return -ENOMEM;
 
 		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) |
+		uhci_fill_td(td, status, destination |
+			(((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) |
 			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
 			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
 			data);
 
 		data += pktsze;
-		len -= maxsze;
-
-		if (len <= 0)
-			td->status |= TD_CTRL_IOC;
+		len -= pktsze;
 
 		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
 			usb_pipeout(urb->pipe));
 	} while (len > 0);
 
+	if (usb_pipeout(urb->pipe) && (urb->transfer_flags & USB_ZERO_PACKET) &&
+	   urb->transfer_buffer_length) {
+		td = uhci_alloc_td(uhci, urb->dev);
+		if (!td)
+			return -ENOMEM;
+
+		uhci_add_td_to_urb(urb, td);
+		uhci_fill_td(td, status, destination | UHCI_NULL_DATA_SIZE |
+			(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			 usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE),
+			data);
+
+		usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			usb_pipeout(urb->pipe));
+	}
+
+	/* Set the flag on the last packet */
+	td->status |= TD_CTRL_IOC;
+
 	qh = uhci_alloc_qh(uhci, urb->dev);
 	if (!qh)
 		return -ENOMEM;
@@ -2343,11 +2360,11 @@
 
 	if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) {
 		if (status & USBSTS_HSE)
-			printk(KERN_ERR "uhci: host system error, PCI problems?\n");
+			err("%x: host system error, PCI problems?", io_addr);
 		if (status & USBSTS_HCPE)
-			printk(KERN_ERR "uhci: host controller process error. something bad happened\n");
+			err("%x: host controller process error. something bad happened", io_addr);
 		if ((status & USBSTS_HCH) && !uhci->is_suspended) {
-			printk(KERN_ERR "uhci: host controller halted. very bad\n");
+			err("%x: host controller halted. very bad", io_addr);
 			/* FIXME: Reset the controller, fix the offending TD */
 		}
 	}
@@ -2393,7 +2410,7 @@
 {
 	unsigned int io_addr = uhci->io_addr;
 
-	dbg("suspend_hc");
+	dbg("%x: suspend_hc", io_addr);
 
 	outw(USBCMD_EGSM, io_addr + USBCMD);
 
@@ -2405,7 +2422,7 @@
 	unsigned int io_addr = uhci->io_addr;
 	unsigned int status;
 
-	dbg("wakeup_hc");
+	dbg("%x: wakeup_hc", io_addr);
 
 	outw(0, io_addr + USBCMD);
 	
@@ -2463,37 +2480,74 @@
 	outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
 }
 
-static int uhci_alloc_root_hub(struct uhci *uhci)
+#ifdef CONFIG_PROC_FS
+static int uhci_num = 0;
+#endif
+
+static void free_uhci(struct uhci *uhci)
 {
-	struct usb_device *dev;
+	kfree(uhci);
+}
 
-	dev = usb_alloc_dev(NULL, uhci->bus);
-	if (!dev)
-		return -1;
+/*
+ * De-allocate all resources..
+ */
+static void release_uhci(struct uhci *uhci)
+{
+	int i;
+#ifdef CONFIG_PROC_FS
+	char buf[8];
+#endif
 
-	uhci->bus->root_hub = dev;
-	uhci->rh.dev = dev;
+	if (uhci->irq >= 0) {
+		free_irq(uhci->irq, uhci);
+		uhci->irq = -1;
+	}
 
-	return 0;
-}
+	for (i = 0; i < UHCI_NUM_SKELQH; i++)
+		if (uhci->skelqh[i]) {
+			uhci_free_qh(uhci, uhci->skelqh[i]);
+			uhci->skelqh[i] = NULL;
+		}
 
-static int uhci_start_root_hub(struct uhci *uhci)
-{
-	usb_connect(uhci->rh.dev);
+	for (i = 0; i < UHCI_NUM_SKELTD; i++)
+		if (uhci->skeltd[i]) {
+			uhci_free_td(uhci, uhci->skeltd[i]);
+			uhci->skeltd[i] = NULL;
+		}
 
-	if (usb_new_device(uhci->rh.dev) != 0) {
-		usb_free_dev(uhci->rh.dev);
+	if (uhci->qh_pool) {
+		pci_pool_destroy(uhci->qh_pool);
+		uhci->qh_pool = NULL;
+	}
 
-		return -1;
+	if (uhci->td_pool) {
+		pci_pool_destroy(uhci->td_pool);
+		uhci->td_pool = NULL;
 	}
 
-	return 0;
-}
+	if (uhci->fl) {
+		pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+		uhci->fl = NULL;
+	}
+
+	if (uhci->bus) {
+		usb_free_bus(uhci->bus);
+		uhci->bus = NULL;
+	}
 
 #ifdef CONFIG_PROC_FS
-static int uhci_num = 0;
+	if (uhci->proc_entry) {
+		sprintf(buf, "hc%d", uhci->num);
+
+		remove_proc_entry(buf, uhci_proc_root);
+		uhci->proc_entry = NULL;
+	}
 #endif
 
+	free_uhci(uhci);
+}
+
 /*
  * Allocate a frame list, and then setup the skeleton
  *
@@ -2504,27 +2558,96 @@
  *  - any isochronous events handled before any
  *    of the queues. We don't do that here, because
  *    we'll create the actual TD entries on demand.
- *  - The first queue is the "interrupt queue".
- *  - The second queue is the "control queue", split into low and high speed
- *  - The third queue is "bulk data".
+ *  - The first queue is the interrupt queue.
+ *  - The second queue is the control queue, split into low and high speed
+ *  - The third queue is bulk queue.
+ *  - The fourth queue is the bandwidth reclamation queue, which loops back
+ *    to the high speed control queue.
  */
-static struct uhci *alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size)
+static int alloc_uhci(struct pci_dev *dev, unsigned int io_addr, unsigned int io_size)
 {
-	int i, port;
 	struct uhci *uhci;
+	int retval = -EBUSY;
+	char buf[8], *bufp = buf;
+	int i, port;
 	struct usb_bus *bus;
 	dma_addr_t dma_handle;
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *ent;
+#endif
 
-	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
-	if (!uhci)
-		return NULL;
+	if (!request_region(io_addr, io_size, "usb-uhci")) {
+		err("couldn't allocate I/O range %x - %x", io_addr,
+			io_addr + io_size - 1);
+		goto err_request_region;
+	}
+
+	if (!dev->irq) {
+		err("found UHCI device with no IRQ assigned. check BIOS settings!");
+		retval = -EINVAL;
+		goto err_invalid_irq;
+	}
+
+	if (!pci_dma_supported(dev, 0xFFFFFFFF)) {
+		err("PCI subsystem doesn't support 32 bit addressing?");
+		retval = -ENODEV;
+		goto err_pci_dma_supported;
+	}
+
+	if (pci_enable_device(dev) < 0) {
+		err("couldn't enable PCI device");
+		goto err_enable_device;
+	}
+
+	pci_set_master(dev);
+
+#ifndef __sparc__
+	sprintf(buf, "%d", dev->irq);
+#else
+	bufp = __irq_itoa(dev->irq);
+#endif
+	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
+		io_addr, bufp);
 
-	memset(uhci, 0, sizeof(*uhci));
+	if (pci_set_dma_mask(dev, 0xFFFFFFFF)) {
+		err("couldn't set PCI dma mask");
+		retval = -ENODEV;
+		goto err_pci_set_dma_mask;
+	}
+
+	uhci = kmalloc(sizeof(*uhci), GFP_KERNEL);
+	if (!uhci) {
+		err("couldn't allocate uhci structure");
+		retval = -ENOMEM;
+		goto err_alloc_uhci;
+	}
 
 	uhci->dev = dev;
-	uhci->irq = -1;
 	uhci->io_addr = io_addr;
 	uhci->io_size = io_size;
+	dev->driver_data = uhci;
+
+#ifdef CONFIG_PROC_FS
+	uhci->num = uhci_num++;
+
+	sprintf(buf, "hc%d", uhci->num);
+
+	ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
+	if (!ent) {
+		err("couldn't create uhci proc entry");
+		retval = -ENOMEM;
+		goto err_create_proc_entry;
+	}
+
+	ent->data = uhci;
+	ent->proc_fops = &uhci_proc_operations;
+	ent->size = 0;
+	uhci->proc_entry = ent;
+#endif
+
+	/* Reset here so we don't get any interrupts from an old setup */
+	/*  or broken setup */
+	reset_hc(uhci);
 
 	spin_lock_init(&uhci->qh_remove_list_lock);
 	INIT_LIST_HEAD(&uhci->qh_remove_list);
@@ -2542,10 +2665,13 @@
 
 	/* We need exactly one page (per UHCI specs), how convenient */
 	/* We assume that one page is atleast 4k (1024 frames * 4 bytes) */
+#if PAGE_SIZE < (4 * 1024)
+#error PAGE_SIZE is not atleast 4k
+#endif
 	uhci->fl = pci_alloc_consistent(uhci->dev, sizeof(*uhci->fl), &dma_handle);
 	if (!uhci->fl) {
-		printk(KERN_ERR "Unable to allocate consistent memory for frame list\n");
-		goto free_uhci;
+		err("unable to allocate consistent memory for frame list");
+		goto err_alloc_fl;
 	}
 
 	memset((void *)uhci->fl, 0, sizeof(*uhci->fl));
@@ -2555,34 +2681,38 @@
 	uhci->td_pool = pci_pool_create("uhci_td", uhci->dev,
 		sizeof(struct uhci_td), 16, 0, GFP_DMA | GFP_ATOMIC);
 	if (!uhci->td_pool) {
-		printk(KERN_ERR "Unable to create td pci_pool\n");
-		goto free_fl;
+		err("unable to create td pci_pool");
+		goto err_create_td_pool;
 	}
 
 	uhci->qh_pool = pci_pool_create("uhci_qh", uhci->dev,
 		sizeof(struct uhci_qh), 16, 0, GFP_DMA | GFP_ATOMIC);
 	if (!uhci->qh_pool) {
-		printk(KERN_ERR "Unable to create qh pci_pool\n");
-		goto free_td_pool;
+		err("unable to create qh pci_pool");
+		goto err_create_qh_pool;
 	}
 
 	bus = usb_alloc_bus(&uhci_device_operations);
-	if (!bus)
-		goto free_qh_pool;
+	if (!bus) {
+		err("unable to allocate bus");
+		goto err_alloc_bus;
+	}
 
 	uhci->bus = bus;
 	bus->hcpriv = uhci;
 
+	usb_register_bus(uhci->bus);
+
 	/* Initialize the root hub */
 
 	/* UHCI specs says devices must have 2 ports, but goes on to say */
 	/*  they may have more but give no way to determine how many they */
 	/*  have. However, according to the UHCI spec, Bit 7 is always set */
 	/*  to 1. So we try to use this to our advantage */
-	for (port = 0; port < (io_size - 0x10) / 2; port++) {
+	for (port = 0; port < (uhci->io_size - 0x10) / 2; port++) {
 		unsigned int portstatus;
 
-		portstatus = inw(io_addr + 0x10 + (port * 2));
+		portstatus = inw(uhci->io_addr + 0x10 + (port * 2));
 		if (!(portstatus & 0x0080))
 			break;
 	}
@@ -2598,15 +2728,16 @@
 
 	uhci->rh.numports = port;
 
-	if (uhci_alloc_root_hub(uhci)) {
+	uhci->bus->root_hub = uhci->rh.dev = usb_alloc_dev(NULL, uhci->bus);
+	if (!uhci->rh.dev) {
 		err("unable to allocate root hub");
-		goto free_fl;
+		goto err_alloc_root_hub;
 	}
 
 	uhci->skeltd[0] = uhci_alloc_td(uhci, uhci->rh.dev);
 	if (!uhci->skeltd[0]) {
 		err("unable to allocate TD 0");
-		goto free_fl;
+		goto err_alloc_skeltd;
 	}
 
 	/*
@@ -2619,7 +2750,7 @@
 		td = uhci->skeltd[i] = uhci_alloc_td(uhci, uhci->rh.dev);
 		if (!td) {
 			err("unable to allocate TD %d", i);
-			goto free_tds;
+			goto err_alloc_skeltd;
 		}
 
 		uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0);
@@ -2628,15 +2759,15 @@
 
 	uhci->skel_term_td = uhci_alloc_td(uhci, uhci->rh.dev);
 	if (!uhci->skel_term_td) {
-		err("unable to allocate TD 0");
-		goto free_fl;
+		err("unable to allocate skel TD term");
+		goto err_alloc_skeltd;
 	}
 
 	for (i = 0; i < UHCI_NUM_SKELQH; i++) {
 		uhci->skelqh[i] = uhci_alloc_qh(uhci, uhci->rh.dev);
 		if (!uhci->skelqh[i]) {
 			err("unable to allocate QH %d", i);
-			goto free_qhs;
+			goto err_alloc_skelqh;
 		}
 	}
 
@@ -2695,157 +2826,88 @@
 		uhci->fl->frame[i] =  uhci->skeltd[irq]->dma_handle;
 	}
 
-	return uhci;
-
-/*
- * error exits:
- */
-free_qhs:
-	for (i = 0; i < UHCI_NUM_SKELQH; i++)
-		if (uhci->skelqh[i]) {
-			uhci_free_qh(uhci, uhci->skelqh[i]);
-			uhci->skelqh[i] = NULL;
-		}
+	start_hc(uhci);
 
-free_tds:
-	for (i = 0; i < UHCI_NUM_SKELTD; i++)
-		if (uhci->skeltd[i]) {
-			uhci_free_td(uhci, uhci->skeltd[i]);
-			uhci->skeltd[i] = NULL;
-		}
+	if (request_irq(dev->irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci))
+		goto err_request_irq;
 
-free_qh_pool:
-	pci_pool_destroy(uhci->qh_pool);
+	uhci->irq = dev->irq;
 
-free_td_pool:
-	pci_pool_destroy(uhci->td_pool);
+	/* disable legacy emulation */
+	pci_write_config_word(uhci->dev, USBLEGSUP, USBLEGSUP_DEFAULT);
 
-free_fl:
-	pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+	usb_connect(uhci->rh.dev);
 
-free_uhci:
-	kfree(uhci);
+	if (usb_new_device(uhci->rh.dev) != 0) {
+		err("unable to start root hub");
+		retval = -ENOMEM;
+		goto err_start_root_hub;
+	}
 
-	return NULL;
-}
+	return 0;
 
 /*
- * De-allocate all resources..
+ * error exits:
  */
-static void release_uhci(struct uhci *uhci)
-{
-	int i;
-#ifdef CONFIG_PROC_FS
-	char buf[8];
-#endif
-
-	if (uhci->irq >= 0) {
-		free_irq(uhci->irq, uhci);
-		uhci->irq = -1;
-	}
+err_start_root_hub:
+	free_irq(uhci->irq, uhci);
+	uhci->irq = -1;
 
+err_request_irq:
 	for (i = 0; i < UHCI_NUM_SKELQH; i++)
 		if (uhci->skelqh[i]) {
 			uhci_free_qh(uhci, uhci->skelqh[i]);
 			uhci->skelqh[i] = NULL;
 		}
 
+err_alloc_skelqh:
 	for (i = 0; i < UHCI_NUM_SKELTD; i++)
 		if (uhci->skeltd[i]) {
 			uhci_free_td(uhci, uhci->skeltd[i]);
 			uhci->skeltd[i] = NULL;
 		}
 
-	if (uhci->qh_pool) {
-		pci_pool_destroy(uhci->qh_pool);
-		uhci->qh_pool = NULL;
-	}
-
-	if (uhci->td_pool) {
-		pci_pool_destroy(uhci->td_pool);
-		uhci->td_pool = NULL;
-	}
-
-	if (uhci->fl) {
-		pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
-		uhci->fl = NULL;
-	}
+err_alloc_skeltd:
+	usb_free_dev(uhci->rh.dev);
+	uhci->rh.dev = NULL;
 
+err_alloc_root_hub:
 	usb_free_bus(uhci->bus);
+	uhci->bus = NULL;
 
-#ifdef CONFIG_PROC_FS
-	sprintf(buf, "hc%d", uhci->num);
-
-	remove_proc_entry(buf, uhci_proc_root);
-	uhci->proc_entry = NULL;
-#endif
-
-	kfree(uhci);
-}
-
-/*
- * If we've successfully found a UHCI, now is the time to return success..
- */
-static int setup_uhci(struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
-{
-	int retval;
-	struct uhci *uhci;
-	char buf[8], *bufp = buf;
-#ifdef CONFIG_PROC_FS
-	struct proc_dir_entry *ent;
-#endif
-
-#ifndef __sparc__
-	sprintf(buf, "%d", irq);
-#else
-	bufp = __irq_itoa(irq);
-#endif
-	printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n",
-		io_addr, bufp);
+err_alloc_bus:
+	pci_pool_destroy(uhci->qh_pool);
+	uhci->qh_pool = NULL;
 
-	uhci = alloc_uhci(dev, io_addr, io_size);
-	if (!uhci)
-		return -ENOMEM;
+err_create_qh_pool:
+	pci_pool_destroy(uhci->td_pool);
+	uhci->td_pool = NULL;
 
-	dev->driver_data = uhci;
+err_create_td_pool:
+	pci_free_consistent(uhci->dev, sizeof(*uhci->fl), uhci->fl, uhci->fl->dma_handle);
+	uhci->fl = NULL;
 
+err_alloc_fl:
 #ifdef CONFIG_PROC_FS
-	uhci->num = uhci_num++;
-
-	sprintf(buf, "hc%d", uhci->num);
-
-	ent = create_proc_entry(buf, S_IFREG|S_IRUGO|S_IWUSR, uhci_proc_root);
-	if (!ent)
-		return -ENOMEM;
+	remove_proc_entry(buf, uhci_proc_root);
+	uhci->proc_entry = NULL;
 
-	ent->data = uhci;
-	ent->proc_fops = &uhci_proc_operations;
-	ent->size = 0;
-	uhci->proc_entry = ent;
+err_create_proc_entry:
+	free_uhci(uhci);
 #endif
 
-	request_region(uhci->io_addr, io_size, "usb-uhci");
-
-	reset_hc(uhci);
+err_alloc_uhci:
 
-	usb_register_bus(uhci->bus);
-	start_hc(uhci);
+err_pci_set_dma_mask:
 
-	retval = -EBUSY;
-	if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci) == 0) {
-		uhci->irq = irq;
+err_enable_device:
 
-		pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+err_pci_dma_supported:
+	release_region(io_addr, io_size);
 
-		if (!uhci_start_root_hub(uhci))
-			return 0;
-	}
+err_invalid_irq:
 
-	/* Couldn't allocate IRQ if we got here */
-
-	reset_hc(uhci);
-	release_region(uhci->io_addr, uhci->io_size);
-	release_uhci(uhci);
+err_request_region:
 
 	return retval;
 }
@@ -2854,23 +2916,6 @@
 {
 	int i;
 
-	if (!pci_dma_supported(dev, 0xFFFFFFFF)) {
-		err("PCI subsystem doesn't support 32 bit addressing?");
-		return -ENODEV;
-	}
-	dev->dma_mask = 0xFFFFFFFF;
-
-	/* disable legacy emulation */
-	pci_write_config_word(dev, USBLEGSUP, 0);
-
-	if (pci_enable_device(dev) < 0)
-		return -ENODEV;
-
-	if (!dev->irq) {
-		err("found UHCI device with no IRQ assigned. check BIOS settings!");
-		return -ENODEV;
-	}
-
 	/* Search for the IO base address.. */
 	for (i = 0; i < 6; i++) {
 		unsigned int io_addr = pci_resource_start(dev, i);
@@ -2880,12 +2925,7 @@
 		if (!(pci_resource_flags(dev, i) & IORESOURCE_IO))
 			continue;
 
-		/* Is it already in use? */
-		if (check_region(io_addr, io_size))
-			break;
-
-		pci_set_master(dev);
-		return setup_uhci(dev, dev->irq, io_addr, io_size);
+		return alloc_uhci(dev, io_addr, io_size);
 	}
 
 	return -ENODEV;
@@ -2918,7 +2958,7 @@
 #ifdef CONFIG_PM
 static int uhci_pci_suspend(struct pci_dev *dev, u32 state)
 {
-	reset_hc((struct uhci *) dev->driver_data);
+	suspend_hc((struct uhci *) dev->driver_data);
 	return 0;
 }
 
@@ -2945,11 +2985,11 @@
 	}, { /* end: all zeroes */ }
 };
 
-MODULE_DEVICE_TABLE (pci, uhci_pci_ids);
+MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
 
 static struct pci_driver uhci_pci_driver = {
 	name:		"usb-uhci",
-	id_table:	&uhci_pci_ids [0],
+	id_table:	uhci_pci_ids,
 
 	probe:		uhci_pci_probe,
 	remove:		uhci_pci_remove,
@@ -2965,6 +3005,8 @@
 {
 	int retval = -ENOMEM;
 
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
 	if (debug) {
 		errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL);
 		if (!errbuf)
@@ -2986,8 +3028,6 @@
 	if (retval)
 		goto init_failed;
 
-	info(DRIVER_VERSION ":" DRIVER_DESC);
-
 	return 0;
 
 init_failed:
@@ -3009,7 +3049,7 @@
 	return retval;
 }
 
-static void __exit uhci_hcd_cleanup (void) 
+static void __exit uhci_hcd_cleanup(void) 
 {
 	pci_unregister_driver(&uhci_pci_driver);
 	
@@ -3027,6 +3067,6 @@
 module_init(uhci_hcd_init);
 module_exit(uhci_hcd_cleanup);
 
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)