From sailer@ife.ee.ethz.ch Fri Sep 9 02:24:34 2005 From: Thomas Sailer Cc: gregkh@suse.de, greg@kroah.com Subject: usb: fix uss720 schedule with interrupts off Date: Fri, 09 Sep 2005 10:43:50 +0200 Message-Id: <1126255430.4382.7.camel@playstation2.hb9jnx.ampr.org> This patch fixes the long standing schedule with interrupts off problem of the uss720 driver. The problem is caused by the parport layer calling the save and restore methods within a write_lock_irqsave guarded region. The fix is to issue the control transaction requests required by save and restore asynchronously. Signed-off-by: Thomas Sailer, Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/uss720.c | 393 +++++++++++++++++++++++++++++++--------------- 1 file changed, 270 insertions(+), 123 deletions(-) --- gregkh-2.6.orig/drivers/usb/misc/uss720.c 2005-09-12 11:10:10.000000000 -0700 +++ gregkh-2.6/drivers/usb/misc/uss720.c 2005-09-12 11:11:43.000000000 -0700 @@ -3,8 +3,8 @@ /* * uss720.c -- USS720 USB Parport Cable. * - * Copyright (C) 1999 - * Thomas Sailer (sailer@ife.ee.ethz.ch) + * Copyright (C) 1999, 2005 + * Thomas Sailer (t.sailer@alumni.ethz.ch) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,103 +23,240 @@ * Based on parport_pc.c * * History: - * 0.1 04.08.99 Created - * 0.2 07.08.99 Some fixes mainly suggested by Tim Waugh - * Interrupt handling currently disabled because - * usb_request_irq crashes somewhere within ohci.c - * for no apparent reason (that is for me, anyway) - * ECP currently untested - * 0.3 10.08.99 fixing merge errors - * 0.4 13.08.99 Added Vendor/Product ID of Brad Hard's cable - * 0.5 20.09.99 usb_control_msg wrapper used - * Nov01.00 usb_device_table support by Adam J. Richter - * 08.04.01 Identify version on module load. gb + * 0.1 04.08.1999 Created + * 0.2 07.08.1999 Some fixes mainly suggested by Tim Waugh + * Interrupt handling currently disabled because + * usb_request_irq crashes somewhere within ohci.c + * for no apparent reason (that is for me, anyway) + * ECP currently untested + * 0.3 10.08.1999 fixing merge errors + * 0.4 13.08.1999 Added Vendor/Product ID of Brad Hard's cable + * 0.5 20.09.1999 usb_control_msg wrapper used + * Nov01.2000 usb_device_table support by Adam J. Richter + * 08.04.2001 Identify version on module load. gb + * 0.6 02.09.2005 Fix "scheduling in interrupt" problem by making save/restore + * context asynchronous * */ /*****************************************************************************/ +#define DEBUG + #include #include #include #include #include #include +#include +#include /* * Version Information */ -#define DRIVER_VERSION "v0.5" -#define DRIVER_AUTHOR "Thomas M. Sailer, sailer@ife.ee.ethz.ch" +#define DRIVER_VERSION "v0.6" +#define DRIVER_AUTHOR "Thomas M. Sailer, t.sailer@alumni.ethz.ch" #define DRIVER_DESC "USB Parport Cable driver for Cables using the Lucent Technologies USS720 Chip" /* --------------------------------------------------------------------- */ struct parport_uss720_private { struct usb_device *usbdev; - void *irqhandle; - unsigned int irqpipe; - unsigned char reg[7]; /* USB registers */ + struct parport *pp; + struct kref ref_count; + __u8 reg[7]; /* USB registers */ + struct list_head asynclist; + spinlock_t asynclock; +}; + +struct uss720_async_request { + struct parport_uss720_private *priv; + struct kref ref_count; + struct list_head asynclist; + struct completion compl; + struct urb *urb; + struct usb_ctrlrequest dr; + __u8 reg[7]; }; /* --------------------------------------------------------------------- */ -static int get_1284_register(struct parport *pp, unsigned char reg, unsigned char *val) +static void destroy_priv(struct kref *kref) { - struct parport_uss720_private *priv = pp->private_data; - struct usb_device *usbdev = priv->usbdev; - static const unsigned char regindex[9] = { - 4, 0, 1, 5, 5, 0, 2, 3, 6 - }; - int ret; + struct parport_uss720_private *priv = container_of(kref, struct parport_uss720_private, ref_count); - if (!usbdev) - return -1; - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), 3, 0xc0, ((unsigned int)reg) << 8, 0, priv->reg, 7, 1000); - if (ret != 7) { - printk(KERN_DEBUG "uss720: get_1284_register(%d) failed, status 0x%x expected 7\n", - (unsigned int)reg, ret); - ret = -1; - } else { + usb_put_dev(priv->usbdev); + kfree(priv); + dbg("destroying priv datastructure"); +} + +static void destroy_async(struct kref *kref) +{ + struct uss720_async_request *rq = container_of(kref, struct uss720_async_request, ref_count); + struct parport_uss720_private *priv = rq->priv; + unsigned long flags; + + if (likely(rq->urb)) + usb_free_urb(rq->urb); + spin_lock_irqsave(&priv->asynclock, flags); + list_del_init(&rq->asynclist); + spin_unlock_irqrestore(&priv->asynclock, flags); + kfree(rq); + kref_put(&priv->ref_count, destroy_priv); +} + +/* --------------------------------------------------------------------- */ + +static void async_complete(struct urb *urb, struct pt_regs *ptregs) +{ + struct uss720_async_request *rq; + struct parport *pp; + struct parport_uss720_private *priv; + + rq = urb->context; + priv = rq->priv; + pp = priv->pp; + if (urb->status) { + err("async_complete: urb error %d", urb->status); + } else if (rq->dr.bRequest == 3) { + memcpy(priv->reg, rq->reg, sizeof(priv->reg)); #if 0 - printk(KERN_DEBUG "uss720: get_1284_register(%d) return %02x %02x %02x %02x %02x %02x %02x\n", - (unsigned int)reg, (unsigned int)priv->reg[0], (unsigned int)priv->reg[1], - (unsigned int)priv->reg[2], (unsigned int)priv->reg[3], (unsigned int)priv->reg[4], - (unsigned int)priv->reg[5], (unsigned int)priv->reg[6]); + dbg("async_complete regs %02x %02x %02x %02x %02x %02x %02x", + (unsigned int)priv->reg[0], (unsigned int)priv->reg[1], (unsigned int)priv->reg[2], + (unsigned int)priv->reg[3], (unsigned int)priv->reg[4], (unsigned int)priv->reg[5], + (unsigned int)priv->reg[6]); #endif /* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */ - if (priv->reg[2] & priv->reg[1] & 0x10) + if (rq->reg[2] & rq->reg[1] & 0x10 && pp) parport_generic_irq(0, pp, NULL); - ret = 0; } - if (val) - *val = priv->reg[(reg >= 9) ? 0 : regindex[reg]]; - return ret; + complete(&rq->compl); + kref_put(&rq->ref_count, destroy_async); } -static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val) +static struct uss720_async_request *submit_async_request(struct parport_uss720_private *priv, + __u8 request, __u8 requesttype, __u16 value, __u16 index, + unsigned int mem_flags) { - struct parport_uss720_private *priv = pp->private_data; - struct usb_device *usbdev = priv->usbdev; + struct usb_device *usbdev; + struct uss720_async_request *rq; + unsigned long flags; int ret; + if (!priv) + return NULL; + usbdev = priv->usbdev; if (!usbdev) - return -1; - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 4, 0x40, (((unsigned int)reg) << 8) | val, 0, NULL, 0, 1000); - if (ret) { - printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x) failed, status 0x%x\n", - (unsigned int)reg, (unsigned int)val, ret); - } else { -#if 0 - printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x)\n", - (unsigned int)reg, (unsigned int)val); -#endif + return NULL; + rq = kmalloc(sizeof(struct uss720_async_request), mem_flags); + if (!rq) { + err("submit_async_request out of memory"); + return NULL; + } + kref_init(&rq->ref_count); + INIT_LIST_HEAD(&rq->asynclist); + init_completion(&rq->compl); + kref_get(&priv->ref_count); + rq->priv = priv; + rq->urb = usb_alloc_urb(0, mem_flags); + if (!rq->urb) { + kref_put(&rq->ref_count, destroy_async); + err("submit_async_request out of memory"); + return NULL; + } + rq->dr.bRequestType = requesttype; + rq->dr.bRequest = request; + rq->dr.wValue = cpu_to_le16(value); + rq->dr.wIndex = cpu_to_le16(index); + rq->dr.wLength = cpu_to_le16((request == 3) ? sizeof(rq->reg) : 0); + usb_fill_control_urb(rq->urb, usbdev, (requesttype & 0x80) ? usb_rcvctrlpipe(usbdev, 0) : usb_sndctrlpipe(usbdev, 0), + (unsigned char *)&rq->dr, + (request == 3) ? rq->reg : NULL, (request == 3) ? sizeof(rq->reg) : 0, async_complete, rq); + /* rq->urb->transfer_flags |= URB_ASYNC_UNLINK; */ + spin_lock_irqsave(&priv->asynclock, flags); + list_add_tail(&rq->asynclist, &priv->asynclist); + spin_unlock_irqrestore(&priv->asynclock, flags); + ret = usb_submit_urb(rq->urb, mem_flags); + if (!ret) { + kref_get(&rq->ref_count); + return rq; + } + kref_put(&rq->ref_count, destroy_async); + err("submit_async_request submit_urb failed with %d", ret); + return NULL; +} + +static unsigned int kill_all_async_requests_priv(struct parport_uss720_private *priv) +{ + struct uss720_async_request *rq; + unsigned long flags; + unsigned int ret = 0; + + spin_lock_irqsave(&priv->asynclock, flags); + list_for_each_entry(rq, &priv->asynclist, asynclist) { + usb_unlink_urb(rq->urb); + ret++; } + spin_unlock_irqrestore(&priv->asynclock, flags); return ret; } /* --------------------------------------------------------------------- */ +static int get_1284_register(struct parport *pp, unsigned char reg, unsigned char *val, unsigned int mem_flags) +{ + struct parport_uss720_private *priv; + struct uss720_async_request *rq; + static const unsigned char regindex[9] = { + 4, 0, 1, 5, 5, 0, 2, 3, 6 + }; + int ret; + + if (!pp) + return -EIO; + priv = pp->private_data; + rq = submit_async_request(priv, 3, 0xc0, ((unsigned int)reg) << 8, 0, mem_flags); + if (!rq) { + err("get_1284_register(%u) failed", (unsigned int)reg); + return -EIO; + } + if (!val) { + kref_put(&rq->ref_count, destroy_async); + return 0; + } + if (wait_for_completion_timeout(&rq->compl, HZ)) { + ret = rq->urb->status; + *val = priv->reg[(reg >= 9) ? 0 : regindex[reg]]; + if (ret) + warn("get_1284_register: usb error %d", ret); + kref_put(&rq->ref_count, destroy_async); + return ret; + } + warn("get_1284_register timeout"); + kill_all_async_requests_priv(priv); + return -EIO; +} + +static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val, unsigned int mem_flags) +{ + struct parport_uss720_private *priv; + struct uss720_async_request *rq; + + if (!pp) + return -EIO; + priv = pp->private_data; + rq = submit_async_request(priv, 4, 0x40, (((unsigned int)reg) << 8) | val, 0, mem_flags); + if (!rq) { + err("set_1284_register(%u,%u) failed", (unsigned int)reg, (unsigned int)val); + return -EIO; + } + kref_put(&rq->ref_count, destroy_async); + return 0; +} + +/* --------------------------------------------------------------------- */ + /* ECR modes */ #define ECR_SPP 00 #define ECR_PS2 01 @@ -132,8 +269,9 @@ static int change_mode(struct parport *p { struct parport_uss720_private *priv = pp->private_data; int mode; + __u8 reg; - if (get_1284_register(pp, 6, NULL)) + if (get_1284_register(pp, 6, ®, GFP_KERNEL)) return -EIO; /* Bits <7:5> contain the mode. */ mode = (priv->reg[2] >> 5) & 0x7; @@ -153,7 +291,7 @@ static int change_mode(struct parport *p case ECR_ECP: /* ECP Parallel Port mode */ /* Poll slowly. */ for (;;) { - if (get_1284_register(pp, 6, NULL)) + if (get_1284_register(pp, 6, ®, GFP_KERNEL)) return -EIO; if (priv->reg[2] & 0x01) break; @@ -167,7 +305,9 @@ static int change_mode(struct parport *p } } /* Set the mode. */ - if (set_1284_register(pp, 6, m << 5)) + if (set_1284_register(pp, 6, m << 5, GFP_KERNEL)) + return -EIO; + if (get_1284_register(pp, 6, ®, GFP_KERNEL)) return -EIO; return 0; } @@ -179,7 +319,7 @@ static int clear_epp_timeout(struct parp { unsigned char stat; - if (get_1284_register(pp, 1, &stat)) + if (get_1284_register(pp, 1, &stat, GFP_KERNEL)) return 1; return stat & 1; } @@ -205,14 +345,14 @@ static int uss720_irq(int usbstatus, voi static void parport_uss720_write_data(struct parport *pp, unsigned char d) { - set_1284_register(pp, 0, d); + set_1284_register(pp, 0, d, GFP_KERNEL); } static unsigned char parport_uss720_read_data(struct parport *pp) { unsigned char ret; - if (get_1284_register(pp, 0, &ret)) + if (get_1284_register(pp, 0, &ret, GFP_KERNEL)) return 0; return ret; } @@ -222,7 +362,7 @@ static void parport_uss720_write_control struct parport_uss720_private *priv = pp->private_data; d = (d & 0xf) | (priv->reg[1] & 0xf0); - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -241,7 +381,7 @@ static unsigned char parport_uss720_frob mask &= 0x0f; val &= 0x0f; d = (priv->reg[1] & (~mask)) ^ val; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return 0; priv->reg[1] = d; return d & 0xf; @@ -251,7 +391,7 @@ static unsigned char parport_uss720_read { unsigned char ret; - if (get_1284_register(pp, 1, &ret)) + if (get_1284_register(pp, 1, &ret, GFP_KERNEL)) return 0; return ret & 0xf8; } @@ -262,7 +402,7 @@ static void parport_uss720_disable_irq(s unsigned char d; d = priv->reg[1] & ~0x10; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -273,7 +413,7 @@ static void parport_uss720_enable_irq(st unsigned char d; d = priv->reg[1] | 0x10; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -284,7 +424,7 @@ static void parport_uss720_data_forward unsigned char d; d = priv->reg[1] & ~0x20; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -295,7 +435,7 @@ static void parport_uss720_data_reverse unsigned char d; d = priv->reg[1] | 0x20; - if (set_1284_register(pp, 2, d)) + if (set_1284_register(pp, 2, d, GFP_KERNEL)) return; priv->reg[1] = d; } @@ -310,17 +450,23 @@ static void parport_uss720_save_state(st { struct parport_uss720_private *priv = pp->private_data; - if (get_1284_register(pp, 2, NULL)) +#if 0 + if (get_1284_register(pp, 2, NULL, GFP_ATOMIC)) return; +#endif s->u.pc.ctr = priv->reg[1]; s->u.pc.ecr = priv->reg[2]; } static void parport_uss720_restore_state(struct parport *pp, struct parport_state *s) { - set_1284_register(pp, 2, s->u.pc.ctr); - set_1284_register(pp, 6, s->u.pc.ecr); - get_1284_register(pp, 2, NULL); + struct parport_uss720_private *priv = pp->private_data; + + set_1284_register(pp, 2, s->u.pc.ctr, GFP_ATOMIC); + set_1284_register(pp, 6, s->u.pc.ecr, GFP_ATOMIC); + get_1284_register(pp, 2, NULL, GFP_ATOMIC); + priv->reg[1] = s->u.pc.ctr; + priv->reg[2] = s->u.pc.ecr; } static size_t parport_uss720_epp_read_data(struct parport *pp, void *buf, size_t length, int flags) @@ -331,7 +477,7 @@ static size_t parport_uss720_epp_read_da if (change_mode(pp, ECR_EPP)) return 0; for (; got < length; got++) { - if (get_1284_register(pp, 4, (char *)buf)) + if (get_1284_register(pp, 4, (char *)buf, GFP_KERNEL)) break; buf++; if (priv->reg[0] & 0x01) { @@ -352,10 +498,10 @@ static size_t parport_uss720_epp_write_d if (change_mode(pp, ECR_EPP)) return 0; for (; written < length; written++) { - if (set_1284_register(pp, 4, (char *)buf)) + if (set_1284_register(pp, 4, (char *)buf, GFP_KERNEL)) break; ((char*)buf)++; - if (get_1284_register(pp, 1, NULL)) + if (get_1284_register(pp, 1, NULL, GFP_KERNEL)) break; if (priv->reg[0] & 0x01) { clear_epp_timeout(pp); @@ -390,7 +536,7 @@ static size_t parport_uss720_epp_read_ad if (change_mode(pp, ECR_EPP)) return 0; for (; got < length; got++) { - if (get_1284_register(pp, 3, (char *)buf)) + if (get_1284_register(pp, 3, (char *)buf, GFP_KERNEL)) break; buf++; if (priv->reg[0] & 0x01) { @@ -410,10 +556,10 @@ static size_t parport_uss720_epp_write_a if (change_mode(pp, ECR_EPP)) return 0; for (; written < length; written++) { - if (set_1284_register(pp, 3, *(char *)buf)) + if (set_1284_register(pp, 3, *(char *)buf, GFP_KERNEL)) break; buf++; - if (get_1284_register(pp, 1, NULL)) + if (get_1284_register(pp, 1, NULL, GFP_KERNEL)) break; if (priv->reg[0] & 0x01) { clear_epp_timeout(pp); @@ -467,7 +613,7 @@ static size_t parport_uss720_ecp_write_a if (change_mode(pp, ECR_ECP)) return 0; for (; written < len; written++) { - if (set_1284_register(pp, 5, *(char *)buffer)) + if (set_1284_register(pp, 5, *(char *)buffer, GFP_KERNEL)) break; buffer++; } @@ -536,93 +682,91 @@ static struct parport_operations parport static int uss720_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *usbdev = interface_to_usbdev(intf); + struct usb_device *usbdev = usb_get_dev(interface_to_usbdev(intf)); struct usb_host_interface *interface; struct usb_host_endpoint *endpoint; struct parport_uss720_private *priv; struct parport *pp; + unsigned char reg; int i; - printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n", - le16_to_cpu(usbdev->descriptor.idVendor), - le16_to_cpu(usbdev->descriptor.idProduct)); + dbg("probe: vendor id 0x%x, device id 0x%x\n", + le16_to_cpu(usbdev->descriptor.idVendor), + le16_to_cpu(usbdev->descriptor.idProduct)); /* our known interfaces have 3 alternate settings */ - if (intf->num_altsetting != 3) + if (intf->num_altsetting != 3) { + usb_put_dev(usbdev); return -ENODEV; - + } i = usb_set_interface(usbdev, intf->altsetting->desc.bInterfaceNumber, 2); - printk(KERN_DEBUG "uss720: set inteface result %d\n", i); + dbg("set inteface result %d", i); interface = intf->cur_altsetting; /* * Allocate parport interface */ - printk(KERN_INFO "uss720: (C) 1999 by Thomas Sailer, \n"); - - if (!(priv = kmalloc(sizeof(struct parport_uss720_private), GFP_KERNEL))) + if (!(priv = kcalloc(sizeof(struct parport_uss720_private), 1, GFP_KERNEL))) { + usb_put_dev(usbdev); return -ENOMEM; + } + priv->pp = NULL; + priv->usbdev = usbdev; + kref_init(&priv->ref_count); + spin_lock_init(&priv->asynclock); + INIT_LIST_HEAD(&priv->asynclist); if (!(pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops))) { - printk(KERN_WARNING "usb-uss720: could not register parport\n"); + warn("could not register parport"); goto probe_abort; } + priv->pp = pp; pp->private_data = priv; - priv->usbdev = usbdev; pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_ECP | PARPORT_MODE_COMPAT; /* set the USS720 control register to manual mode, no ECP compression, enable all ints */ - set_1284_register(pp, 7, 0x00); - set_1284_register(pp, 6, 0x30); /* PS/2 mode */ - set_1284_register(pp, 2, 0x0c); + set_1284_register(pp, 7, 0x00, GFP_KERNEL); + set_1284_register(pp, 6, 0x30, GFP_KERNEL); /* PS/2 mode */ + set_1284_register(pp, 2, 0x0c, GFP_KERNEL); /* debugging */ - get_1284_register(pp, 0, NULL); - printk("uss720: reg: %02x %02x %02x %02x %02x %02x %02x\n", - priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]); + get_1284_register(pp, 0, ®, GFP_KERNEL); + dbg("reg: %02x %02x %02x %02x %02x %02x %02x", + priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]); endpoint = &interface->endpoint[2]; - printk(KERN_DEBUG "uss720: epaddr %d interval %d\n", endpoint->desc.bEndpointAddress, endpoint->desc.bInterval); -#if 0 - priv->irqpipe = usb_rcvctrlpipe(usbdev, endpoint->bEndpointAddress); - i = usb_request_irq(usbdev, priv->irqpipe, - uss720_irq, endpoint->bInterval, - pp, &priv->irqhandle); - if (i) { - printk (KERN_WARNING "usb-uss720: usb_request_irq failed (0x%x)\n", i); - goto probe_abort_port; - } -#endif + dbg("epaddr %d interval %d", endpoint->desc.bEndpointAddress, endpoint->desc.bInterval); parport_announce_port(pp); - usb_set_intfdata (intf, pp); + usb_set_intfdata(intf, pp); return 0; -#if 0 -probe_abort_port: - parport_put_port(pp); -#endif probe_abort: - kfree(priv); + kill_all_async_requests_priv(priv); + kref_put(&priv->ref_count, destroy_priv); return -ENODEV; } static void uss720_disconnect(struct usb_interface *intf) { - struct parport *pp = usb_get_intfdata (intf); + struct parport *pp = usb_get_intfdata(intf); struct parport_uss720_private *priv; + struct usb_device *usbdev; - usb_set_intfdata (intf, NULL); + dbg("disconnect"); + usb_set_intfdata(intf, NULL); if (pp) { priv = pp->private_data; - parport_remove_port(pp); -#if 0 - usb_release_irq(usbdev, priv->irqhandle, priv->irqpipe); -#endif + usbdev = priv->usbdev; priv->usbdev = NULL; + priv->pp = NULL; + dbg("parport_remove_port"); + parport_remove_port(pp); parport_put_port(pp); - kfree(priv); + kill_all_async_requests_priv(priv); + kref_put(&priv->ref_count, destroy_priv); } + dbg("disconnect done"); } /* table of cables that work through this driver */ @@ -647,8 +791,8 @@ static struct usb_driver uss720_driver = /* --------------------------------------------------------------------- */ -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static int __init uss720_init(void) @@ -659,6 +803,9 @@ static int __init uss720_init(void) goto out; info(DRIVER_VERSION ":" DRIVER_DESC); + info("NOTE: this is a special purpose driver to allow nonstandard"); + info("protocols (eg. bitbang) over USS720 usb to parallel cables"); + info("If you just want to connect to a printer, use usblp instead"); out: return retval; }