On Sat, 22 Apr 2000, Karl Heinz Kremer wrote:
> Thanks for the information regarding the HP4100. When you (or to
> be more accurate the HP backend) do the 8 byte reads, how much
> data are you requesting from the scanner?
Hi Karl,
Below is part of the output from enabling DEBUG in scanner.h. As you can
see, on several occasions I'm requesting X amount and returning either
0 bytes, less than X, or X bytes.
The '0' in the 'stats(0)'refers to the minor that I'm accessing. The
'this_read' refers to the number of bytes I've requested to read and
'partial' is the actual number of bytes returned.
Hmmm, you know, something looks awfully fishy here...two calls to
read_scanner() when only one should be done.....
Ok, I've attached a version of scanner.c that fixes that...and some other
things (in the comments for version 0.4.3). Please try it out and let me
know what happens..it's a drop in replacement for the old scanner.c.
NOTE: This is not meant for inclusion in the kernel. There are still some
things that need to be done before I release a patch to the linux-usb
list.
Regards,
/\/elson
0 - scanner.c: open_scanner: scn_minor:0
1 - scanner.c: write stats(0): result:0 this_write:2 partial:2
2 - scanner.c: write stats(0): result:0 this_write:7 partial:7
3 - scanner.c: read stats(0): result:0 this_read:16 partial:9
4 - scanner.c: read stats(0): result:0 this_read:7 partial:0
5 - scanner.c: write stats(0): result:0 this_write:5 partial:5
6 - scanner.c: read stats(0): result:0 this_read:24 partial:12
7 - scanner.c: read stats(0): result:0 this_read:12 partial:0
8 - scanner.c: write stats(0): result:0 this_write:6 partial:6
9 - scanner.c: read stats(0): result:0 this_read:24 partial:7
10 - scanner.c: read stats(0): result:0 this_read:17 partial:0
11 - scanner.c: write stats(0): result:0 this_write:5 partial:5
12 - scanner.c: read stats(0): result:0 this_read:24 partial:6
13 - scanner.c: read stats(0): result:0 this_read:18 partial:0
14 - scanner.c: write stats(0): result:0 this_write:6 partial:6
15 - scanner.c: read stats(0): result:0 this_read:24 partial:7
16 - scanner.c: read stats(0): result:0 this_read:17 partial:0
17 - scanner.c: write stats(0): result:0 this_write:6 partial:6
18 - scanner.c: read stats(0): result:0 this_read:24 partial:7
19 - scanner.c: read stats(0): result:0 this_read:17 partial:0
20 - scanner.c: write stats(0): result:0 this_write:6 partial:6
21 - scanner.c: read stats(0): result:0 this_read:24 partial:7
22 - scanner.c: read stats(0): result:0 this_read:17 partial:0
23 - scanner.c: write stats(0): result:0 this_write:6 partial:6
> Is it possible that the problems with my backend are caused by the
> fact that I'm reading just four bytes, regardless of how much data
> is available. These four bytes can either be all the scanner produced
> at this time, or just the header for more to come. When I first started
> out I read only one byte and based on this information the software then
> called different functions to handle the two cases. This caused some
> babble errors, so I modified the code to read four bytes instead and
> at least up to 2.3.39 this worked fine.
>
> Does this ring a bell?
>
> Karl Heinz
>
>
> On Fri, Apr 21, 2000 at 10:45:24PM -0500, David Nelson wrote:
> >
> > Hi Karl,
> >
> > I know I've replied to you privately but I want to reiterate here so that
> > more info is available to more folks in case it jogs somebody's memory.
> >
> > I just tested with linux-2.3.99-pre6-3, SANE 1.0.1, and my trusty hp-4100.
> > When SANE queries a scanner, several short messages are xfered in both
> > directions (usually in the neiborhood of 8 bytes...maybe more, maybe
> > less). Several hundred of these queries are performed. This evening, all
> > three drivers (ohci, uhci, and usb-uhci) were able to complete the task in
> > less than 3 seconds without any problems (which I think is fairly
> > resonable). Large data xfers are another matter...sloooowwwww. I'm not
> > sure what Karl is runing into here and without any hardware it's kinda
> > hard for him to try things and I can't reproduce them here. Ideas anyone?
> >
> > Regards,
> > /\/elson
> >
> > On Wed, 19 Apr 2000, Karl Heinz Kremer wrote:
> >
> > > Tap tap tap ... is this thing on?
> > >
> > > This problem was brought up here a few times before, but so far
> > > nobody seemed to care. With 2.4 comming up this will be a problem,
> > > people will no longer be able to use EPSON USB scanners with Linux.
> > >
> > > EPSON is a very good partner. They are releasing all information
> > > necessary to write drivers. I think it's important that their
> > > devices are supported under Linux.
> > >
> > > Regarding the SANE backend: I am not doing anything special, it's
> > > just reading and writing to the /dev/usbscanner device. The only
> > > thing that could be considered "special" is that I'm requesting
> > > only four bytes at the beginning of every read cycle, depending on
> > > the contents of these four bytes I then read the rest of the data
> > > that the scanner has available. In this four byte read attempt the
> > > communication with the scanner seems to hang with every USB software
> > > version after 2.3.39 (I'm not entirely sure if this really was the
> > > last version, it is for sure the last backport to 2.2.14 that worked).
> > >
> > > Can somebody please take a loot and either explain why it's not
> > > working (maybe it's something in the scanner, I doubt that however because
> > > it worked with 2.3.39) or give some advice about how to debug this
> > > problem. I don't have access to a USB scanner yet, but I am pretty
> > > sure I can relay some information and instructions to people who
> > > have access to such a device, and I'm pretty sure Marc Zyngier will
> > > be willing to help out as well.
> > >
> > > So please take a look,
> > >
> > > Karl Heinz
> > >
> > > Marc ZYNGIER <[EMAIL PROTECTED]> said:
> > >
> > > > Hi all.
> > > >
> > > > Can anyone with kernel USB experience have a look at this ? I'm quite
> > > > clueless on the subject.
> > > >
> > > > I'm experiencing problems with an Epson Perfection610 scanner. It
> > > > works fine with 2.2.14 + 2.3.39 backport, but fails with 2.3.50
> > > > backport and latests 2.3.99 kernels. System is a dual Celeron BP6
> > > > system (UHCI on 440BX chipset).
> > > >
> > > > Asking to Karl Heinz Kremer <[EMAIL PROTECTED]> (which maintains the Epson
> > > > Sane backend), it seems that is problem looks like being UHCI related.
> > > >
> > > > I have enabled debugs in usb-uhci.c, and traced a 'scanimage -L'.
> > > > Here's the result :
> > > >
> > > > open("/dev/usbscanner", O_RDWR|O_EXCL) = 4
> > > > write(4, "\33@", 2) = 2
> > > >
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: search_dev_ep:
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: submit_urb: scheduling c7cdb8e0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: uhci_submit_bulk_urb: urb
> > > c7cdb8e0, old 00000000, pipe c0010200, len 2
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: Allocated qh @ c58c68a0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: uhci_submit_bulk: qh=00000082,
> > > nqh=c03170de
> > > > Apr 16 08:59:52 hina kernel:
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: submit_urb: scheduled with ret: 0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: interrupt
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: process_transfer: len:2
> > > status:39000001 mapped:0 toggle:0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: clean transfer urb c7cdb8e0, qh
> > > c58c68a0, mode 1
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: process_transfer: (end) urb
> > > c7cdb8e0, wanted len 2, len 2 status 0 err 0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: dequeued urb: c7cdb8e0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: process_transfer: calling early
> > > completion
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c58c6820
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c58c6860
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c58c6a20
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912180
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c59121c0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912200
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912240
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912280
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c59122c0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912300
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912340
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912380
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c59123c0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912400
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912440
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912480
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c59124c0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912500
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912540
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912580
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c59125c0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912600
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912640
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912680
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c59126c0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912700
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912740
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912780
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c59127c0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912800
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912840
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912880
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c59128c0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c5912900
> > > >
> > > > Ok, the write has succeeded.
> > > >
> > > > read(4, 0x804d5d0, 4)
> > > >
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: search_dev_ep:
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: submit_urb: scheduling c7ad1cc0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: uhci_submit_bulk_urb: urb
> > > c7ad1cc0, old 00000000, pipe c0008280, len 4
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: Allocated qh @ c58c67a0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: uhci_submit_bulk: qh=00000082,
> > > nqh=c03170de
> > > > Apr 16 08:59:52 hina kernel:
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: submit_urb: scheduled with ret: 0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: interrupt
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: clean transfer urb c7ad1cc0, qh
> > > c58c67a0, mode 1
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: process_transfer: (end) urb
> > > c7ad1cc0, wanted len 4, len 1 status 0 err 0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: dequeued urb: c7ad1cc0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: process_transfer: calling early
> > > completion
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c58c6fa0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: interrupt
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: unlink td @ c58c6a20
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: search_dev_ep:
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: submit_urb: scheduling c7ad1cc0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: uhci_submit_bulk_urb: urb
> > > c7ad1cc0, old 00000000, pipe c0008280, len 3
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: Allocated qh @ c58c67a0
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: uhci_submit_bulk: qh=00000082,
> > > nqh=c03170de
> > > > Apr 16 08:59:52 hina kernel:
> > > > Apr 16 08:59:52 hina kernel: usb-uhci.c: submit_urb: scheduled with ret: 0
> > > >
> > > > <Here, the process pauses for 2 minutes, blocked in the read>
> > > >
> > > > read(4, 0x804d5d0, 4) = -1 ETIME (Timer expired)
> > > >
> > > > Apr 16 09:01:52 hina kernel: usb_control/bulk_msg: timeout
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: uhci_unlink_urb called for
> > > c7ad1cc0
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: clean transfer urb c7ad1cc0, qh
> > > c58c67a0, mode 1
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: unlink_urb: calling completion
> > > > Apr 16 09:01:52 hina kernel: scanner.c: read_scanner(0): NAK received
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: search_dev_ep:
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: submit_urb: scheduling c7ad15e0
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: uhci_submit_bulk_urb: urb
> > > c7ad15e0, old 00000000, pipe c0010200, len 2
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: Allocated qh @ c58c68a0
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: uhci_submit_bulk: qh=00000082,
> > > nqh=c03170de
> > > > Apr 16 09:01:52 hina kernel:
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: submit_urb: scheduled with ret: 0
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: interrupt
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: process_transfer: len:2
> > > status:39000001 mapped:0 toggle:1
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: clean transfer urb c7ad15e0, qh
> > > c58c68a0, mode 1
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: process_transfer: (end) urb
> > > c7ad15e0, wanted len 2, len 2 status 0 err 0
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: dequeued urb: c7ad15e0
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: process_transfer: calling early
> > > completion
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: unlink td @ c58c6a20
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: search_dev_ep:
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: submit_urb: scheduling c7ad1900
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: uhci_submit_bulk_urb: urb
> > > c7ad1900, old 00000000, pipe c0008280, len 4
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: Allocated qh @ c58c67a0
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: uhci_submit_bulk: qh=00000082,
> > > nqh=c03170de
> > > > Apr 16 09:01:52 hina kernel:
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: submit_urb: scheduled with ret: 0
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: interrupt
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: interrupt, status 3, frame# 1279
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: clean transfer urb c7ad1900, qh
> > > c58c67a0, mode 1
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: process_transfer: (end) urb
> > > c7ad1900, wanted len 4, len 0 status ffffffe0 err 1
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: dequeued urb: c7ad1900
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: process_transfer: calling early
> > > completion
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: unlink td @ c58c6fa0
> > > > Apr 16 09:01:52 hina kernel: scanner.c: read_scanner(0): funky result:-32.
> > > Please notify the maintainer.
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: interrupt
> > > > Apr 16 09:01:52 hina kernel: usb-uhci.c: unlink td @ c58c6a20
> > > >
> > > > I'll be very happy to test anything on that system. Note that it fails
> > > > with both usb-uhci and alternate uhci driver. Alas, I don't have
> > > > access to an OHCI system to test...
> > > >
> > > > Thanks a lot.
> > > >
> > > > Marc Zyngier.
> > > > --
> > > > Places change, faces change. Life is so very strange.
> > > >
> > > > ---------------------------------------------------------------------
> > > > To unsubscribe, e-mail: [EMAIL PROTECTED]
> > > > For additional commands, e-mail: [EMAIL PROTECTED]
> > > >
> > >
> > >
> > >
> >
> > --
> > David /\/elson
> > http://www.jump.net/~dnelson
> >
> > "Management is doing things right; leadership is doing the right things."
> > -- The 7 Habits Book
>
>
--
David /\/elson
http://www.jump.net/~dnelson
"Management is doing things right; leadership is doing the right things."
-- The 7 Habits Book
/* -*- linux-c -*- */
/*
* Driver for USB Scanners (linux-2.3.99-pre3-7)
*
* Copyright (C) 1999, 2000 David E. Nelson
*
* Portions may be copyright Brad Keryan and Michael Gee.
*
* David E. Nelson ([EMAIL PROTECTED])
*
* 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 the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Originally based upon mouse.c (Brad Keryan) and printer.c (Michael Gee).
*
* History
*
* 0.1 8/31/1999
*
* Developed/tested using linux-2.3.15 with minor ohci.c changes to
* support short packes during bulk xfer mode. Some testing was
* done with ohci-hcd but the performace was low. Very limited
* testing was performed with uhci but I was unable to get it to
* work. Initial relase to the linux-usb development effort.
*
*
* 0.2 10/16/1999
*
* - Device can't be opened unless a scanner is plugged into the USB.
* - Finally settled on a reasonable value for the I/O buffer's.
* - Cleaned up write_scanner()
* - Disabled read/write stats
* - A little more code cleanup
*
*
* 0.3 10/18/1999
*
* - Device registration changed to reflect new device
* allocation/registration for linux-2.3.22+.
* - Adopted David Brownell's <[EMAIL PROTECTED]> technique for
* assigning bulk endpoints.
* - Removed unnessesary #include's
* - Scanner model now reported via syslog INFO after being detected
* *and* configured.
* - Added user specified verdor:product USB ID's which can be passed
* as module parameters.
*
*
* 0.3.1
*
* - Applied patches for linux-2.3.25.
* - Error number reporting changed to reflect negative return codes.
*
*
* 0.3.2
*
* - Applied patches for linux-2.3.26 to scanner_init().
* - Debug read/write stats now report values as signed decimal.
*
*
* 0.3.3
*
* - Updated the bulk_msg() calls to usb usb_bulk_msg().
* - Added a small delay in the write_scanner() method to aid in
* avoiding NULL data reads on HP scanners. We'll see how this works.
* - Return values from usb_bulk_msg() now ignore positive values for
* use with the ohci driver.
* - Added conditional debugging instead of commenting/uncommenting
* all over the place.
* - kfree()'d the pointer after using usb_string() as documented in
* linux-usb-api.txt.
* - Added usb_set_configuration(). It got lost in version 0.3 -- ack!
* - Added the HP 5200C USB Vendor/Product ID's.
*
*
* 0.3.4 1/23/2000
*
* - Added Greg K-H's <[EMAIL PROTECTED]> patch for better handling of
* Product/Vendor detection.
* - The driver now autoconfigures its endpoints including interrupt
* endpoints if one is detected. The concept was originally based
* upon David Brownell's method.
* - Added some Seiko/Epson ID's. Thanks to Karl Heinz
* Kremer <[EMAIL PROTECTED]>.
* - Added some preliminary ioctl() calls for the PV8630 which is used
* by the HP4200. The ioctl()'s still have to be registered. Thanks
* to Adrian Perez Jorge <[EMAIL PROTECTED]>.
* - Moved/migrated stuff to scanner.h
* - Removed the usb_set_configuration() since this is handled by
* the usb_new_device() routine in usb.c.
* - Added the HP 3300C. Thanks to Bruce Tenison.
* - Changed user specified vendor/product id so that root hub doesn't
* get falsely attached to. Thanks to Greg K-H.
* - Added some Mustek ID's. Thanks to Gernot Hoyler
* <[EMAIL PROTECTED]>.
* - Modified the usb_string() reporting. See kfree() comment above.
* - Added Umax Astra 2000U. Thanks to Doug Alcorn <[EMAIL PROTECTED]>.
* - Updated the printk()'s to use the info/warn/dbg macros.
* - Updated usb_bulk_msg() argument types to fix gcc warnings.
*
*
* 0.4 2/4/2000
*
* - Removed usb_string() from probe_scanner since the core now does a
* good job of reporting what was connnected.
* - Finally, simultaneous multiple device attachment!
* - Fixed some potential memory freeing issues should memory allocation
* fail in probe_scanner();
* - Some fixes to disconnect_scanner().
* - Added interrupt endpoint support.
* - Added Agfa SnapScan Touch. Thanks to Jan Van den Bergh
* <[EMAIL PROTECTED]>.
* - Added Umax 1220U ID's. Thanks to Maciek Klimkowski
* <[EMAIL PROTECTED]>.
* - Fixed bug in write_scanner(). The buffer was not being properly
* updated for writes larger than OBUF_SIZE. Thanks to Henrik
* Johansson <[EMAIL PROTECTED]> for identifying it.
* - Added Microtek X6 ID's. Thanks to Oliver Neukum
* <[EMAIL PROTECTED]>.
*
*
* 0.4.1 2/15/2000
*
* - Fixed 'count' bug in read_scanner(). Thanks to Henrik
* Johansson <[EMAIL PROTECTED]> for identifying it. Amazing
* it has worked this long.
* - Fixed '>=' bug in both read/write_scanner methods.
* - Cleaned up both read/write_scanner() methods so that they are
* a little more readable.
* - Added a lot of Microtek ID's. Thanks to Adrian Perez Jorge.
* - Adopted the __initcall().
* - Added #include <linux/init.h> to scanner.h for __initcall().
* - Added one liner in irq_scanner() to keep gcc from complaining
* about an unused variable (data) if debugging was disabled
* in scanner.c.
* - Increased the timeout parameter in read_scanner() to 120 Secs.
*
*
* 0.4.2 3/23/2000
*
* - Added Umax 1236U ID. Thanks to Philipp Baer <[EMAIL PROTECTED]>.
* - Added Primax, ReadyScan, Visioneer, Colorado, and Genius ID's.
* Thanks to Adrian Perez Jorge <[EMAIL PROTECTED]>.
* - Fixed error number reported for non-existant devices. Thanks to
* Spyridon Papadimitriou <[EMAIL PROTECTED]>.
* - Added Acer Prisascan 620U ID's. Thanks to Joao <[EMAIL PROTECTED]>.
* - Replaced __initcall() with module_init()/module_exit(). Updates
* from patch-2.3.48.
* - Replaced file_operations structure with new syntax. Updates
* from patch-2.3.49.
* - Changed #include "usb.h" to #include <linux/usb.h>
* - Added #define SCN_IOCTL to exclude development areas
* since 2.4.x is about to be released. This mainly affects the
* ioctl() stuff. See scanner.h for more details.
* - Changed the return value for signal_pending() from -ERESTARTSYS to
* -EINTR.
*
*
* 0.4.3
*
* - Added Umax Astra 2200 ID. Thanks to Flynn Marquardt
* <[EMAIL PROTECTED]>.
* - Added iVina 1200U ID. Thanks to Dyson Lin <[EMAIL PROTECTED]>.
* vendor=0x0638 id=0x0268
* - Added access time update for the device file courtesy of Paul
* Mackerras <[EMAIL PROTECTED]>. This allows a user space daemon
* to turn the lamp off for a Umax 1220U scanner after a prescribed
* time.
* - Fixed HP S20 ID's. Thanks to Ruud Linders <[EMAIL PROTECTED]>.
* - Added Acer ScanPrisa 620U ID. Thanks to Oliver
* Schwartz <[EMAIL PROTECTED]> via sane-devel mail list.
* - Fixed bug in read_scanner for copy_to_user() function. The returned
* value should be 'partial' not 'this_read'.
* - Fixed bug in read_scanner. 'count' should be decremented
* by 'this_read' and not by 'partial'. This resulted in twice as many
* calls to read_scanner() for small amounts of data and possibly
* unexpected returns of '0'.
*
*
* TODO
*
* - Performance
* - Wait queues for the read_scanner
* - Select/poll methods
* - More testing
* - Proper registry/assignment for LM9830 ioctl's
*
*
* Thanks to:
*
* - All the folks on the linux-usb list who put up with me. :) This
* has been a great learning experience for me.
* - To Linus Torvalds for this great OS.
* - The GNU folks.
* - The folks that forwarded Vendor:Product ID's to me.
* - Johannes Erdfelt for the loaning of a USB analyzer for tracking an
* issue with HP-4100 and uhci.
* - Adolfo Montero for his assistance.
* - And anybody else who chimed in with reports and suggestions.
*
* Performance:
*
* System: Pentium 120, 80 MB RAM, OHCI, Linux 2.3.23, HP 4100C USB Scanner
* 300 dpi scan of the entire bed
* 24 Bit Color ~ 70 secs - 3.6 Mbit/sec
* 8 Bit Gray ~ 17 secs - 4.2 Mbit/sec
*/
#include "scanner.h"
static void
irq_scanner(struct urb *urb)
{
/*
* For the meantime, this is just a placeholder until I figure out what
* all I want to do with it.
*/
struct scn_usb_data *scn = urb->context;
unsigned char *data = &scn->button;
data += 0; /* Keep gcc from complaining about unused var */
if (urb->status) {
return;
}
dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data);
return;
}
static int
open_scanner(struct inode * inode, struct file * file)
{
struct scn_usb_data *scn;
struct usb_device *dev;
kdev_t scn_minor;
scn_minor = USB_SCN_MINOR(inode);
dbg("open_scanner: scn_minor:%d", scn_minor);
if (!p_scn_table[scn_minor]) {
err("open_scanner(%d): invalid scn_minor", scn_minor);
return -ENODEV;
}
scn = p_scn_table[scn_minor];
dev = scn->scn_dev;
if (!dev) {
return -ENODEV;
}
if (!scn->present) {
return -ENODEV;
}
if (scn->isopen) {
return -EBUSY;
}
scn->isopen = 1;
file->private_data = scn; /* Used by the read and write metheds */
MOD_INC_USE_COUNT;
return 0;
}
static int
close_scanner(struct inode * inode, struct file * file)
{
struct scn_usb_data *scn;
kdev_t scn_minor;
scn_minor = USB_SCN_MINOR (inode);
dbg("close_scanner: scn_minor:%d", scn_minor);
if (!p_scn_table[scn_minor]) {
err("close_scanner(%d): invalid scn_minor", scn_minor);
return -ENODEV;
}
scn = p_scn_table[scn_minor];
scn->isopen = 0;
file->private_data = NULL;
MOD_DEC_USE_COUNT;
return 0;
}
static ssize_t
write_scanner(struct file * file, const char * buffer,
size_t count, loff_t *ppos)
{
struct scn_usb_data *scn;
struct usb_device *dev;
ssize_t bytes_written = 0; /* Overall count of bytes written */
ssize_t ret = 0;
kdev_t scn_minor;
int this_write; /* Number of bytes to write */
int partial; /* Number of bytes successfully written */
int result = 0;
char *obuf;
scn = file->private_data;
scn_minor = scn->scn_minor;
obuf = scn->obuf;
dev = scn->scn_dev;
file->f_dentry->d_inode->i_atime = CURRENT_TIME;
while (count > 0) {
if (signal_pending(current)) {
ret = -EINTR;
break;
}
this_write = (count >= OBUF_SIZE) ? OBUF_SIZE : count;
if (copy_from_user(scn->obuf, buffer, this_write)) {
ret = -EFAULT;
break;
}
result = usb_bulk_msg(dev,usb_sndbulkpipe(dev, scn->bulk_out_ep),
obuf, this_write, &partial, 60*HZ);
dbg("write stats(%d): result:%d this_write:%d partial:%d", scn_minor,
result, this_write, partial);
if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */
warn("write_scanner: NAK recieved.");
ret = -ETIME;
break;
} else if (result < 0) { /* We should not get any I/O errors */
warn("write_scanner(%d): funky result: %d. Please notify the
maintainer.", scn_minor, result);
ret = -EIO;
break;
}
#ifdef WR_DATA_DUMP
if (partial) {
unsigned char cnt, cnt_max;
cnt_max = (partial > 24) ? 24 : partial;
printk(KERN_DEBUG "dump(%d): ", scn_minor);
for (cnt=0; cnt < cnt_max; cnt++) {
printk("%X ", obuf[cnt]);
}
printk("\n");
}
#endif
if (partial != this_write) { /* Unable to write all contents of obuf
*/
ret = -EIO;
break;
}
if (partial) { /* Data written */
buffer += partial;
count -= partial;
bytes_written += partial;
} else { /* No data written */
ret = 0;
bytes_written = 0;
break;
}
}
mdelay(5); /* This seems to help with SANE queries */
return ret ? ret : bytes_written;
}
static ssize_t
read_scanner(struct file * file, char * buffer,
size_t count, loff_t *ppos)
{
struct scn_usb_data *scn;
struct usb_device *dev;
ssize_t bytes_read; /* Overall count of bytes_read */
ssize_t ret;
kdev_t scn_minor;
int partial; /* Number of bytes successfully read */
int this_read; /* Max number of bytes to read */
int result;
char *ibuf;
scn = file->private_data;
scn_minor = scn->scn_minor;
ibuf = scn->ibuf;
dev = scn->scn_dev;
bytes_read = 0;
ret = 0;
file->f_dentry->d_inode->i_atime = CURRENT_TIME;
while (count > 0) {
if (signal_pending(current)) {
ret = -EINTR;
break;
}
this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep),
ibuf, this_read, &partial, 120*HZ);
dbg("read stats(%d): result:%d this_read:%d partial:%d count:%d",
scn_minor, result, this_read, partial, count);
if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */
warn("read_scanner(%d): NAK received", scn_minor);
ret = -ETIME;
break;
} else if ((result < 0) && (result != USB_ST_DATAUNDERRUN)) {
warn("read_scanner(%d): funky result:%d. Please notify the
maintainer.", scn_minor, (int)result);
ret = -EIO;
break;
}
#ifdef RD_DATA_DUMP
if (partial) {
unsigned char cnt, cnt_max;
cnt_max = (partial > 24) ? 24 : partial;
printk(KERN_DEBUG "dump(%d): ", scn_minor);
for (cnt=0; cnt < cnt_max; cnt++) {
printk("%X ", ibuf[cnt]);
}
printk("\n");
}
#endif
if (partial) { /* Data returned */
if (copy_to_user(buffer, ibuf, partial)) {
ret = -EFAULT;
break;
}
count -= this_read;
bytes_read += partial;
buffer += partial;
} else {
ret = 0;
break;
}
}
return ret ? ret : bytes_read;
}
static void *
probe_scanner(struct usb_device *dev, unsigned int ifnum)
{
struct scn_usb_data *scn;
struct usb_interface_descriptor *interface;
struct usb_endpoint_descriptor *endpoint;
int ep_cnt;
kdev_t scn_minor;
char valid_device = 0;
char have_bulk_in, have_bulk_out, have_intr;
if (vendor != -1 && product != -1) {
info("probe_scanner: User specified USB scanner -- Vendor:Product -
%x:%x", vendor, product);
}
dbg("probe_scanner: USB dev address:%p", dev);
dbg("probe_scanner: ifnum:%u", ifnum);
/*
* 1. Check Vendor/Product
* 2. Determine/Assign Bulk Endpoints
* 3. Determine/Assign Intr Endpoint
*/
/*
* There doesn't seem to be an imaging class defined in the USB
* Spec. (yet). If there is, HP isn't following it and it doesn't
* look like anybody else is either. Therefore, we have to test the
* Vendor and Product ID's to see what we have. Also, other scanners
* may be able to use this driver by specifying both vendor and
* product ID's as options to the scanner module in conf.modules.
*
* NOTE: Just because a product is supported here does not mean that
* applications exist that support the product. It's in the hopes
* that this will allow developers a means to produce applications
* that will support USB products.
*
* Until we detect a device which is pleasing, we silently punt.
*/
do {
if (dev->descriptor.idVendor == 0x03f0) { /* Hewlett Packard
*/
if (dev->descriptor.idProduct == 0x0205 || /* 3300C */
dev->descriptor.idProduct == 0x0101 || /* 4100C */
dev->descriptor.idProduct == 0x0105 || /* 4200C */
dev->descriptor.idProduct == 0x0202 || /* PhotoSmart S20
*/
dev->descriptor.idProduct == 0x0401 || /* 5200C */
dev->descriptor.idProduct == 0x0201 || /* 6200C */
dev->descriptor.idProduct == 0x0601) { /* 6300C */
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x06bd) { /* Agfa */
if (dev->descriptor.idProduct == 0x0001 || /* SnapScan 1212U
*/
dev->descriptor.idProduct == 0x2061 || /* Another SnapScan
1212U_2 (!) */
dev->descriptor.idProduct == 0x0100) { /* SnapScan Touch
*/
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x1606) { /* Umax */
if (dev->descriptor.idProduct == 0x0010 || /* Astra 1220U */
dev->descriptor.idProduct == 0x0030 || /* Astra 2000U */
dev->descriptor.idProduct == 0x0230 || /* Astra 2200U */
dev->descriptor.idProduct == 0x0002) { /* Astra 1236U */
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x04b8) { /* Seiko/Epson
Corp. */
if (dev->descriptor.idProduct == 0x0101 || /* Perfection 636U
and 636Photo */
dev->descriptor.idProduct == 0x0103 || /* Perfection 610
*/
dev->descriptor.idProduct == 0x0104) { /* Perfection 1200U
and 1200Photo */
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x055f) { /* Mustek */
if (dev->descriptor.idProduct == 0x0001) { /* 1200 CU */
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x05da) { /* Microtek */
if (dev->descriptor.idProduct == 0x0099 || /* ScanMaker X6 -
X6U */
dev->descriptor.idProduct == 0x0094 || /* Phantom 336CX -
C3 */
dev->descriptor.idProduct == 0x00a0 || /* Phantom 336CX -
C3 #2 */
dev->descriptor.idProduct == 0x009a || /* Phantom C6 */
dev->descriptor.idProduct == 0x00a3 || /* ScanMaker V6USL
*/
dev->descriptor.idProduct == 0x80a3 || /* ScanMaker V6USL
#2 */
dev->descriptor.idProduct == 0x80ac) { /* ScanMaker V6UL -
SpicyU */
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x0461) { /* Primax/Colorado
*/
if (dev->descriptor.idProduct == 0x0300 || /* G2-300 #1 */
dev->descriptor.idProduct == 0x0380 || /* G2-600 #1 */
dev->descriptor.idProduct == 0x0301 || /* G2E-300 */
dev->descriptor.idProduct == 0x0381 || /* ReadyScan 636i
*/
dev->descriptor.idProduct == 0x0302 || /* G2-300 #2 */
dev->descriptor.idProduct == 0x0382 || /* G2-600 #2 */
dev->descriptor.idProduct == 0x0303 || /* G2E-300 */
dev->descriptor.idProduct == 0x0383 || /* G2E-600 */
dev->descriptor.idProduct == 0x0340 || /* Colorado USB
9600 */
dev->descriptor.idProduct == 0x0360 || /* Colorado USB
19200 */
dev->descriptor.idProduct == 0x0341 || /* Colorado 600u */
dev->descriptor.idProduct == 0x0361) { /* Colorado 1200u
*/
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x04a7) { /* Visioneer */
if (dev->descriptor.idProduct == 0x0221 || /* OneTouch 5300 */
dev->descriptor.idProduct == 0x0221 || /* OneTouch 7600 */
dev->descriptor.idProduct == 0x0231) { /* 6100 */
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x0458) { /* Genius */
if(dev->descriptor.idProduct == 0x2001) { /* ColorPage-Vivid
Pro */
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x04a5) { /* Acer */
if(dev->descriptor.idProduct == 0x2060 || /* Prisa Acerscan
620U */
dev->descriptor.idProduct == 0x2040) { /* ScanPrisa 620U */
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == vendor && /* User specified */
dev->descriptor.idProduct == product) { /* User specified */
valid_device = 1;
break;
}
} while (0);
if (!valid_device)
return NULL; /* We didn't find anything pleasing */
/*
* After this point we can be a little noisy about what we are trying to
* configure.
*/
if (dev->descriptor.bNumConfigurations != 1) {
info("probe_scanner: Only one device configuration is supported.");
return NULL;
}
if (dev->config[0].bNumInterfaces != 1) {
info("probe_scanner: Only one device interface is supported.");
return NULL;
}
interface = dev->config[0].interface[ifnum].altsetting;
endpoint = interface[ifnum].endpoint;
/*
* Start checking for two bulk endpoints OR two bulk endpoints *and* one
* interrupt endpoint. If we have an interrupt endpoint go ahead and
* setup the handler. FIXME: This is a future enhancement...
*/
dbg("probe_scanner: Number of Endpoints:%d", (int) interface->bNumEndpoints);
if ((interface->bNumEndpoints != 2) && (interface->bNumEndpoints != 3)) {
info("probe_scanner: Only two or three endpoints supported.");
return NULL;
}
ep_cnt = have_bulk_in = have_bulk_out = have_intr = 0;
while (ep_cnt < interface->bNumEndpoints) {
if (!have_bulk_in && IS_EP_BULK_IN(endpoint[ep_cnt])) {
ep_cnt++;
have_bulk_in = ep_cnt;
dbg("probe_scanner: bulk_in_ep:%d", have_bulk_in);
continue;
}
if (!have_bulk_out && IS_EP_BULK_OUT(endpoint[ep_cnt])) {
ep_cnt++;
have_bulk_out = ep_cnt;
dbg("probe_scanner: bulk_out_ep:%d", have_bulk_out);
continue;
}
if (!have_intr && IS_EP_INTR(endpoint[ep_cnt])) {
ep_cnt++;
have_intr = ep_cnt;
dbg("probe_scanner: intr_ep:%d", have_intr);
continue;
}
info("probe_scanner: Undetected endpoint. Notify the maintainer.");
return NULL; /* Shouldn't ever get here unless we have something
weird */
}
/*
* Perform a quick check to make sure that everything worked as it
* should have.
*/
switch(interface->bNumEndpoints) {
case 2:
if (!have_bulk_in || !have_bulk_out) {
info("probe_scanner: Two bulk endpoints required.");
return NULL;
}
break;
case 3:
if (!have_bulk_in || !have_bulk_out || !have_intr) {
info("probe_scanner: Two bulk endpoints and one interrupt
endpoint required.");
return NULL;
}
break;
default:
info("probe_scanner: Endpoint determination failed. Notify the
maintainer.");
return NULL;
}
/*
* Determine a minor number and initialize the structure associated
* with it. The problem with this is that we are counting on the fact
* that the user will sequentially add device nodes for the scanner
* devices. */
for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) {
if (!p_scn_table[scn_minor])
break;
}
/* Check to make sure that the last slot isn't already taken */
if (p_scn_table[scn_minor]) {
err("probe_scanner: No more minor devices remaining.");
return NULL;
}
dbg("probe_scanner: Allocated minor:%d", scn_minor);
if (!(scn = kmalloc (sizeof (struct scn_usb_data), GFP_KERNEL))) {
err("probe_scanner: Out of memory.");
return NULL;
}
memset (scn, 0, sizeof(struct scn_usb_data));
dbg ("probe_scanner(%d): Address of scn:%p", scn_minor, scn);
/* Ok, if we detected an interrupt EP, setup a handler for it */
if (have_intr) {
dbg("probe_scanner(%d): Configuring IRQ handler for intr EP:%d",
scn_minor, have_intr);
FILL_INT_URB(&scn->scn_irq, dev,
usb_rcvintpipe(dev, have_intr),
&scn->button, 1, irq_scanner, scn,
// endpoint[(int)have_intr].bInterval);
250);
if (usb_submit_urb(&scn->scn_irq)) {
err("probe_scanner(%d): Unable to allocate INT URB.",
scn_minor);
kfree(scn);
return NULL;
}
}
/* Ok, now initialize all the relevant values */
if (!(scn->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) {
err("probe_scanner(%d): Not enough memory for the output buffer.",
scn_minor);
kfree(scn);
return NULL;
}
dbg("probe_scanner(%d): obuf address:%p", scn_minor, scn->obuf);
if (!(scn->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) {
err("probe_scanner(%d): Not enough memory for the input buffer.",
scn_minor);
kfree(scn->obuf);
kfree(scn);
return NULL;
}
dbg("probe_scanner(%d): ibuf address:%p", scn_minor, scn->ibuf);
scn->bulk_in_ep = have_bulk_in;
scn->bulk_out_ep = have_bulk_out;
scn->intr_ep = have_intr;
scn->present = 1;
scn->scn_dev = dev;
scn->scn_minor = scn_minor;
scn->isopen = 0;
return p_scn_table[scn_minor] = scn;
}
static void
disconnect_scanner(struct usb_device *dev, void *ptr)
{
struct scn_usb_data *scn = (struct scn_usb_data *) ptr;
if(scn->intr_ep) {
dbg("disconnect_scanner(%d): Unlinking IRQ URB", scn->scn_minor);
usb_unlink_urb(&scn->scn_irq);
}
usb_driver_release_interface(&scanner_driver,
&scn->scn_dev->actconfig->interface[scn->ifnum]);
kfree(scn->ibuf);
kfree(scn->obuf);
dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor);
p_scn_table[scn->scn_minor] = NULL;
kfree (scn);
}
#ifdef SCN_IOCTL
static int
ioctl_scanner(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct usb_device *dev;
int result;
kdev_t scn_minor;
scn_minor = USB_SCN_MINOR(inode);
if (!p_scn_table[scn_minor]) {
err("ioctl_scanner(%d): invalid scn_minor", scn_minor);
return -ENODEV;
}
dev = p_scn_table[scn_minor]->scn_dev;
switch (cmd)
{
case PV8630_IOCTL_INREQUEST :
{
struct {
__u8 data;
__u8 request;
__u16 value;
__u16 index;
} args;
if (copy_from_user(&args, (void *)arg, sizeof(args)))
return -EFAULT;
result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
args.request, USB_TYPE_VENDOR|
USB_RECIP_DEVICE|USB_DIR_IN,
args.value, args.index, &args.data,
1, HZ*5);
dbg("ioctl_scanner(%d): inreq: args.data:%x args.value:%x
args.index:%x args.request:%x\n", scn_minor, args.data, args.value, args.index,
args.request);
if (copy_to_user((void *)arg, &args, sizeof(args)))
return -EFAULT;
dbg("ioctl_scanner(%d): inreq: result:%d\n", scn_minor, result);
return result;
}
case PV8630_IOCTL_OUTREQUEST :
{
struct {
__u8 request;
__u16 value;
__u16 index;
} args;
if (copy_from_user(&args, (void *)arg, sizeof(args)))
return -EFAULT;
dbg("ioctl_scanner(%d): outreq: args.value:%x args.index:%x
args.request:%x\n", scn_minor, args.value, args.index, args.request);
result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
args.request, USB_TYPE_VENDOR|
USB_RECIP_DEVICE|USB_DIR_OUT,
args.value, args.index, NULL,
0, HZ*5);
dbg("ioctl_scanner(%d): outreq: result:%d\n", scn_minor, result);
return result;
}
default:
return -ENOIOCTLCMD;
}
return 0;
}
#endif /* SCN_IOCTL */
static struct
file_operations usb_scanner_fops = {
read: read_scanner,
write: write_scanner,
#ifdef SCN_IOCTL
ioctl: ioctl_scanner,
#endif /* SCN_IOCTL */
open: open_scanner,
release: close_scanner,
};
static struct
usb_driver scanner_driver = {
"usbscanner",
probe_scanner,
disconnect_scanner,
{ NULL, NULL },
&usb_scanner_fops,
SCN_BASE_MNR
};
void __exit
usb_scanner_exit(void)
{
usb_deregister(&scanner_driver);
}
int __init
usb_scanner_init (void)
{
if (usb_register(&scanner_driver) < 0)
return -1;
info("USB Scanner support registered.");
return 0;
}
module_init(usb_scanner_init);
module_exit(usb_scanner_exit);
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]