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]

Reply via email to