]> git.hungrycats.org Git - linux/commitdiff
v2.4.6.1 -> v2.4.6.2
authorLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 03:10:32 +0000 (19:10 -0800)
committerLinus Torvalds <torvalds@athlon.transmeta.com>
Tue, 5 Feb 2002 03:10:32 +0000 (19:10 -0800)
  - merge with Alan (USB, zoran, sony motion-eye, rio, dmi-scan)

113 files changed:
CREDITS
Documentation/sonypi.txt [new file with mode: 0644]
Documentation/video4linux/Zoran [new file with mode: 0644]
Documentation/video4linux/meye.txt [new file with mode: 0644]
Documentation/video4linux/w9966.txt [new file with mode: 0644]
Makefile
arch/arm/kernel/bios32.c
arch/arm/kernel/dec21285.c
arch/arm/kernel/dma-arc.c
arch/arm/kernel/head-armv.S
arch/arm/kernel/irq.c
arch/arm/kernel/oldlatches.c
arch/arm/kernel/plx90x0.c
arch/arm/kernel/sys_arm.c
arch/arm/mach-footbridge/cats-pci.c
arch/arm/mach-footbridge/ebsa285-pci.c
arch/arm/mach-footbridge/netwinder-pci.c
arch/arm/mach-footbridge/personal-pci.c
arch/arm/tools/mach-types
arch/i386/kernel/dmi_scan.c
arch/i386/kernel/pci-irq.c
arch/i386/kernel/process.c
arch/i386/kernel/setup.c
arch/i386/kernel/signal.c
arch/i386/math-emu/reg_u_div.S
drivers/block/ll_rw_blk.c
drivers/block/rd.c
drivers/char/Config.in
drivers/char/Makefile
drivers/char/dtlk.c
drivers/char/dz.c
drivers/char/generic_serial.c
drivers/char/h8.c
drivers/char/ip2/i2cmd.c
drivers/char/machzwd.c
drivers/char/moxa.c
drivers/char/rio/linux_compat.h
drivers/char/rio/rio_linux.c
drivers/char/rio/rio_linux.h
drivers/char/rio/rioboot.c
drivers/char/rio/riocmd.c
drivers/char/rio/riodrvr.h
drivers/char/rio/rioinit.c
drivers/char/rio/riotable.c
drivers/char/rio/riotty.c
drivers/char/ser_a2232.c [new file with mode: 0644]
drivers/char/ser_a2232.h [new file with mode: 0644]
drivers/char/ser_a2232fw.ax [new file with mode: 0644]
drivers/char/ser_a2232fw.h [new file with mode: 0644]
drivers/char/sonypi.c [new file with mode: 0644]
drivers/char/sonypi.h [new file with mode: 0644]
drivers/char/sx.c
drivers/char/w83877f_wdt.c [new file with mode: 0644]
drivers/ide/ide-tape.c
drivers/media/video/Config.in
drivers/media/video/Makefile
drivers/media/video/buz.c [deleted file]
drivers/media/video/buz.h [deleted file]
drivers/media/video/meye.c [new file with mode: 0644]
drivers/media/video/meye.h [new file with mode: 0644]
drivers/media/video/saa5249.c
drivers/media/video/zoran.h [new file with mode: 0644]
drivers/media/video/zoran_procfs.c [new file with mode: 0644]
drivers/media/video/zr36057.h
drivers/media/video/zr36060.h
drivers/media/video/zr36067.c [new file with mode: 0644]
drivers/net/acenic.c
drivers/net/arlan.c
drivers/net/au1000_eth.c [new file with mode: 0644]
drivers/net/au1000_eth.h [new file with mode: 0644]
drivers/net/dmfe.c
drivers/net/eql.c
drivers/net/eth16i.c
drivers/net/ioc3-eth.c
drivers/net/lp486e.c [new file with mode: 0644]
drivers/net/setup.c
drivers/pcmcia/yenta.c
drivers/pnp/isapnp.c
drivers/scsi/Config.in
drivers/scsi/osst.c
drivers/scsi/osst.h
drivers/scsi/sg.c
drivers/usb/acm.c
drivers/usb/devices.c
drivers/usb/inode.c
drivers/usb/mdc800.c
drivers/usb/storage/freecom.c
drivers/usb/uhci.c
drivers/usb/usb-skeleton.c [new file with mode: 0644]
drivers/usb/usb.c
drivers/video/tgafb.c
fs/locks.c
include/asm-arm/arch-ebsa285/io.h
include/asm-arm/arch-integrator/io.h
include/asm-arm/hardirq.h
include/asm-arm/mach/pci.h
include/asm-arm/softirq.h
include/asm-i386/processor.h
include/asm-mips64/ioctls.h
include/linux/meye.h [new file with mode: 0644]
include/linux/nls.h
include/linux/pci.h
include/linux/proc_fs.h
include/linux/sonypi.h [new file with mode: 0644]
include/linux/spinlock.h
include/linux/swap.h
include/linux/timex.h
include/linux/tty.h
include/linux/usb.h
include/linux/usbdevice_fs.h
include/linux/videodev.h
include/scsi/sg.h
init/main.c

diff --git a/CREDITS b/CREDITS
index 7cb7a78276869d42f79050c5c9948ada3cfe46a1..256c14d80fb4d8a6f297da1dbe04a512c98f628f 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -78,8 +78,8 @@ P: 1024/85AD9EED AD C0 49 08 91 67 DF D7  FA 04 1A EE 09 E8 44 B0
 D: Unix98 pty support.
 D: APM update to 1.2 spec.
 D: /devfs hacking.
-S: 322 N. Riverside Dr.
-S: Neptune, NJ 07753
+S: 7 Kiwi Loop
+S: Howell, NJ 07731
 S: USA
 
 N: Erik Andersen
@@ -679,6 +679,14 @@ S: Ottawa, Ontario
 S: K1N 6Z9
 S: CANADA
 
+N: Jeff Dike
+E: jdike@karaya.com
+W: http://user-mode-linux.sourceforge.net
+D: User mode kernel port
+S: RR1 Box 67C
+S: Deering NH 03244
+S: USA
+
 N: Eddie C. Dost
 E: ecd@skynet.be
 D: Linux/Sparc kernel hacker
@@ -1062,6 +1070,11 @@ S: PO-Box 297
 S: 2400 AG, Alphen aan den Rijn
 S: The Netherlands
 
+N: Enver Haase
+E: ehaase@inf.fu-berlin.de
+W: http://www.inf.fu-berlin.de/~ehaase
+D: Driver for the Commodore A2232 serial board
+
 N: Bruno Haible
 E: haible@ma2s2.mathematik.uni-karlsruhe.de
 D: SysV FS, shm swapping, memory management fixes
@@ -1275,12 +1288,11 @@ S: Pittsburgh, PA 15203-1250
 S: USA
 
 N: Gareth Hughes
-E: gareth@valinux.com
-E: gareth@precisioninsight.com
+E: gareth.hughes@acm.org
 D: Pentium III FXSR, SSE support
-S: 11/187 West Street
-S: Crows Nest NSW 2065
-S: Australia
+D: Author/maintainer of most DRM drivers (especially ATI, MGA)
+D: Core DRM templates, general DRM and 3D-related hacking
+S: No fixed address
 
 N: Kenn Humborg
 E: kenn@wombat.ie
@@ -2259,6 +2271,15 @@ N: Ken Pizzini
 E: ken@halcyon.com
 D: CDROM driver "sonycd535" (Sony CDU-535/531)
 
+N: Stelian Pop
+E: stelian.pop@fr.alcove.com
+P: 1024D/EDBB6147 7B36 0E07 04BC 11DC A7A0  D3F7 7185 9E7A EDBB 6147
+D: sonypi, meye drivers, mct_u232 usb serial hacks
+S: AlcĂ´ve
+S: 153, bd. Anatole France 
+S: 93200 Saint Denis
+S: France
+
 N: Frederic Potter 
 E: fpotter@cirpack.com
 D: Some PCI kernel support
@@ -2355,14 +2376,15 @@ S: 75019 Paris
 S: France
 
 N: Rik van Riel
-E: riel@nl.linux.org
-W: http://www.nl.linux.org/~riel/
+E: riel@conectiva.com.br
+W: http://www.surriel.com/
 D: Linux-MM site, Documentation/sysctl/*, swap/mm readaround
 D: clustering contributor, kswapd fixes, random kernel hacker,
-D: nl.linux.org maintainer, minor scheduler additions
-S: IJsselstraat 23a
-S: 9725 GA  Groningen
-S: The Netherlands
+D: nl.linux.org administrator, minor scheduler additions
+S: Conectiva S.A.
+S: R. Tocantins, 89 - Cristo Rei
+S: 80050-430 - Curitiba - Paraná
+S: Brazil
 
 N: Pekka Riikonen
 E: priikone@poseidon.pspt.fi
@@ -2868,6 +2890,7 @@ S: Belgium
 N: Petr Vandrovec
 E: vandrove@vc.cvut.cz
 D: Small contributions to ncpfs
+D: Matrox framebuffer driver
 S: Chudenicka 8
 S: 10200 Prague 10, Hostivar
 S: Czech Republic
diff --git a/Documentation/sonypi.txt b/Documentation/sonypi.txt
new file mode 100644 (file)
index 0000000..29156bf
--- /dev/null
@@ -0,0 +1,70 @@
+Sony Programmable I/O Control Device Driver Readme
+--------------------------------------------------
+       Copyright (C) 2001 Stelian Pop <stelian.pop@fr.alcove.com>, AlcĂ´ve
+       Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
+       Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
+       Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
+       Copyright (C) 2000 Andrew Tridgell <tridge@samba.org>
+
+This driver enables access to the Sony Programmable I/O Control Device which
+can be found in many (all ?) Sony Vaio laptops.
+
+It will give access (through a user space utility) to some events those laptops
+generate, like:
+       - jogdial events (the small wheel on the side of Vaios)
+       - capture button events (only on Vaio Picturebook series)
+       - Fn keys
+       - bluetooth button (only on C1VR model)
+
+Those events (see linux/sonypi.h) can be polled using the character device node
+/dev/sonypi (major 10, minor auto allocated or specified as a option).
+
+A simple daemon which translates the jogdial movements into mouse wheel events
+can be downloaded at: <http://www.alcove-labs.org/en/software/sonypi/>
+
+This driver supports also some ioctl commands for setting the LCD screen
+brightness (some more commands may be added in the future).
+
+This driver can also be used to set the camera controls on Picturebook series
+(brightness, contrast etc), and is used by the video4linux driver for the 
+Motion Eye camera.
+
+Please note that this driver was created by reverse engineering the Windows
+driver and the ACPI BIOS, because Sony doesn't agree to release any programming
+specs for its laptops. If someone convinces them to do so, drop me a note.
+
+Module options:
+---------------
+
+       minor:          minor number of the misc device /dev/sonypi, 
+                       default is -1 (automatic allocation, see /proc/misc
+                       or kernel logs)
+
+       camera:         if you have a PictureBook series Vaio (with the
+                       integrated MotionEye camera), set this parameter to 1
+                       in order to let the driver access to the camera
+
+       fnkeyinit:      on some Vaios (C1VE, C1VR etc), the Fn key events don't
+                       get enabled unless you set this parameter to 1
+
+       verbose:        print unknown events from the sonypi device
+
+Module use:
+-----------
+
+In order to automatically load the sonypi module on use, you can put those
+lines in your /etc/modules.conf file:
+
+       alias char-major-10-250 sonypi
+       options sonypi minor=250 fnkeyinit=1
+
+This supposes the use of minor 250 for the sonypi device:
+
+       # mknod /dev/sonypi c 10 250
+
+Bugs:
+-----
+
+       - since all development was done by reverse engineering, there is
+         _absolutely no guarantee_ that this driver will not crash your
+         laptop. Permanently.
diff --git a/Documentation/video4linux/Zoran b/Documentation/video4linux/Zoran
new file mode 100644 (file)
index 0000000..67d87ff
--- /dev/null
@@ -0,0 +1,517 @@
+DC10/DC10plus/LML33/Buz  Driver for Linux
+=========================================
+
+by Rainer Johanni <Rainer@Johanni.de> (for Iomega Buz Driver)
+
+Adapted for DC10/DC10plus by Wolfgang Scherr <scherr@net4you.net>
+
+Further changes for DC10/DC10plus and LML33 cards by
+Serguei Miridonov <mirsev@cicese.mx>
+
+Current homepage: http://www.cicese.mx/~mirsev/Linux/DC10plus/
+Current maintainer: Serguei Miridonov <mirsev@cicese.mx>
+
+  This is a driver for DC10plus capture cards from Pinnacle Systems
+  Inc., LML33 cards from Linux Media Labs and Buz from Iomega.
+  It also works with many old Miro DC10 cards with SAA7110A TV decoder
+  and ADV7176 TV encoder (please, make sure that your card has these
+  chips, otherwise the driver will not work).
+
+  The driver is Video4Linux compliant and contains extensions to
+  provide hardware support for full motion MJPEG compression and
+  decompression. Since this driver is a derivative from the driver for
+  Buz Iomega cards written by Dr. Rainer Johanni,
+  http://www.johanni.de/munich-vision/buz/ they both have compatible
+  API. I hope that this API will become a part of V4L standard.
+
+Copyright: This driver is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License. Please,
+check http://www.gnu.org/ for details.
+
+No warranty: This software is provided on AN "AS-IS" basis WITHOUT
+WARRANTY OF ANY KIND. YOU USE IT AT YOUR OWN RISK.
+
+
+
+CONTENTS
+~~~~~~~~
+
+Supported Formats
+Hardware compression
+Compiling and Loading the Driver
+Driver Options
+Tested applications
+Programming interface
+Features for testing
+Mailing lists
+Bug Reports
+
+
+Supported Formats
+=================
+
+Card:              DC10/DC10plus             LML33/Buz
+
+TV standard:       NTSC/PAL/SECAM(*)         NTSC/PAL
+
+Format:            Square pixel              CCIR.601
+                   640x480 NTSC              720x480 NTSC
+                   768x576 PAL/SECAM(*)      720x576 PAL
+
+Frame rates: 30 frames/60 fields per second NTSC
+             25 frames/50 fields per second PAL/SECAM(*)
+
+(*) - SECAM is supported for input only in DC10/DC10plus cards. The
+output of the recorded SECAM video stream will be in PAL standard.
+Also, please, note that monitoring of the SECAM input signal at the
+DC10/DC10plus analog output may not be available. Please, use
+appropriate application like XawTV to watch full color SECAM video at
+the card input.
+
+Hardware compression
+====================
+
+Since the card provides hardware compression, even low end machines can
+be successfully used for movie capture and playback. I'm testing the
+driver with with 2.2.16 kernel running on 233 MHz Pentium MMX with 64M
+RAM on 430TX motherboard and with 10GB IDE drive from Western Digital
+Corp.
+
+On one test run with DC10plus card I've got 0 frames dropped during
+about 20 minutes of full motion NTSC (I live in Mexico) video capture
+with fully synchronized audio. The command was
+
+  lavrec -fa -in -d1 -l -1 -q30 -w /dos/g/capture/Linux/test%03d.avi
+
+for recording, and
+
+  lavplay -n128 /dos/g/capture/Linux/test*.avi
+
+for playback. (See lavtools distribution for more information).
+
+Typical run of similar test can provide as few as 6-8 dropped frames per
+half of an hour. You mileage may vary, though.
+
+Compiling and Loading the Driver
+================================
+
+You should run a 2.2.x kernel in order to use this driver. The driver
+was also tested with 2.4-test6 kernel, so hopefully it will work
+with 2.4 kernels too.
+
+I would recommend to use only official kernels from www.kernel.org and
+its mirrors. Kernels supplied with some Linux distributions may be
+patched in some way to meet specific needs of particular Linux
+distributor and could be incompatible with this driver. As a driver
+maintainer, I am not able to follow every unofficial kernel release,
+and no unofficial kernels will be supported.
+
+Besides the files in this directory, the driver needs the 'videodev'
+and the 'i2c' module from the Linux kernel (i2c-old for 2.4 kernels).
+In order to get these modules available, enable module support for
+VIDEODEV and BTTV (which implies i2c) in your 2.2.x kernel
+configuration. You will find these devices in the menu "Character
+Devices" in your Kernel Configuration.
+
+In newer kernels (2.4) instead of BTTV you should enable support for
+Iomega Buz cards and for Zoran 36060/36067 chipset. This will include
+i2c or i2c-old modules and Buz/LML33 driver. However, instead of
+modules for Buz/LML33 driver from the kernel, use modules from _this_
+driver.
+
+To compile the driver, just type make.
+
+Before you load the driver you must have a video device at major device
+node 81. If you don't have it yet, do the following (as root!):
+
+cd /dev
+mknod video0 c 81 0
+ln -s video0 video
+
+If you have more than one card, add more nodes in /dev directory:
+
+mknod video1 c 81 1
+mknod video2 c 81 2
+...
+
+The driver should operate properly with several cards. It was tested
+with one DC10plus and one LML33 cards installed together and the driver
+correctly identifies both cards and works with both of them.
+
+Currently the driver does not support LML33 and Buz cards installed
+together in the same system. This will be fixed in future versions.
+
+Edit the 'update' script if you want to give the driver special options
+(see below for options descriptions) and then type (as root)
+
+./update <card_list>
+
+to insert all necessary modules into the kernel. <card_list> is a list of
+cards installed in your system separated by white space. Supported cards 
+are dc10, dc10plus, lml33, and buz. For example, if you have both dc10plus
+and lml33 cards, please type
+
+./update dc10 lml33
+
+If you want to make full use of the Video for Linux _uncompressed_
+grabbing facilities, you must either
+
+- obtain and install the "big_physarea patch" for your kernel and
+  set aside the necessary memory during boot time. There seem to be
+  several versions of this patch against various kernel versions
+  floating around in the net, you may obtain one e.g. from:
+  http://www.polyware.nl/~middelin/patch/bigphysarea-2.2.1.tar.gz You
+  also have to compile your driver AFTER installing that patch in order
+  to get it working
+
+  or
+
+- start your kernel with the mem=xxx option, where xxx is your
+  real memory minus the memory needed for the buffers.
+  For doing this add an entry in lilo.conf (if you use lilo):
+    append "mem=xxxM"
+  or add a line in your linux.par file (if you use loadlin):
+    mem=xxxM
+
+The second method is by far easier, however it is dangerous if more
+than one driver at a time has the idea to use the memory leftover by
+setting the mem=xxx parameter below the actual memory size.
+
+Read also below how to use this memory!
+
+
+  If you use only MJPEG compressed capture provided by the driver, you
+  should not need large memory areas for DMA. In this case, you will be
+  able to capture and playback movies with lavtools, however you will
+  not be able to use capture features of XawTV and other similar
+  programs (you can still watch video on the screen).
+
+
+
+Driver Options
+==============
+
+You are able to customize the behavior of the driver by giving
+it some options at start time.
+
+default_input, default_norm
+---------------------------
+
+As soon as the driver is loaded, the Buz samples video signals
+from one of its input ports and displays it on its output.
+The driver uses the Composite Input and the video norm PAL for this.
+If you want to change this default behavior, set default_input=1
+(for S-VHS input) or default_norm=1 for NTSC or default_norm=2
+for SECAM (DC10/DC10plus only).
+
+lock_norm
+---------
+
+This option was introduced to disable norm (TV standard) change by some
+not well behaving programs. For example, if you have some application
+which was written by somebody who lives in a country with PAL standard,
+this program may not have NTSC option and may always try to set the
+driver to PAL. In this case, you may load the driver with
+default_norm=1 and lock_norm=1 and the card will be forced to work in
+NTSC standard only.
+
+  Options:
+  
+  lock_norm=0   default, TV standard change is enabled;
+  lock_norm=1   TV standard change is disabled but the driver
+                will not notify the application about any error;
+  lock_norm=2   TV standard change is disabled and the driver
+                will notify the program that TV standards other
+                than set by default_norm=X option are not 
+                supported.
+
+pass_through
+------------
+
+When the driver is not in use (device is not opened by any program) and
+pass_through=0 (default) the driver will set the TV encoder to produce
+color bar signal at the output. If the driver was loaded with
+pass_through=1, the color bar will be disabled and input signal will be
+sent to the output even if the driver not in use. If you have LML33 card
+and wish the color bar signal at the output, you will also need to set
+lml33dpath=1 (please, see next section).
+
+lml33dpath
+----------
+
+LML33 card normally (lml33dpath=0) connects its output to the input
+using analog switch. Additionally, it also allows real-time monitoring
+of digitized video using TV monitor connected to the output. This
+"digital path" option can be enabled setting lml33dpath=1. In this
+mode, the input is connected only to the TV decoder, digital video data
+is sent via internal video bus to the TV encoder and resulting analog
+signal is sent to the output. This mode could be very useful for testing and
+picture adjustment while watching video at the TV monitor connected to
+the output. However, because of lack of 75 ohm terminating resistors at
+TV decoder input, the signal will suffer serious distortions.
+
+# These distortions could be eliminated by soldering two 75 ohm resistors
+# in LML33 card: in parallel to capacitors C73 and C82 (see schematics of
+# H33 board available at www.linuxmedialabs.com and www.zoran.com). Be
+# aware, however, that doing so will void card warranty and the card,
+# after this change, must always be used with loading option lml33dpath=1.
+# 
+# WARNING: I DID NOT TRY THIS CARD CHANGE YET, THIS IS JUST AN ASSUMPTION
+# AND I WILL NOT BE RESPONSIBLE FOR ANY DAMAGE ASSOCIATED WITH THIS
+# CHANGE. IF YOU WISH TO TRY IT, DO IT AT YOUR OWN RISK.
+
+Please, note that DC10/DC10plus cards always use "digital path" for
+signal monitoring. Its input and output are both properly terminated
+and the digitized signal quality does not depend on the connection of
+the output load.
+
+
+v4l_nbufs, v4l_bufsize
+----------------------
+
+In order to make to make full use of the Video for Linux uncompressed
+picture grabbing facilities of the driver (which are needed by many
+Video for Linux applications), the driver needs a set of physically
+contiguous buffers for grabbing. These parameters determine how many
+buffers of which size the driver will allocate at open (the open will
+fail if it is unable to do so!).
+
+These values do not affect the MJPEG grabbing facilities of the driver,
+they are needed for uncompressed image grabbing only!!!
+
+v4l_nbufs is the number of buffers to allocate, a value of 2 (the default)
+should be sufficient in almost all cases. Only special applications
+(streaming captures) will need more buffers and then mostly the
+MJPEG capturing features of the Buz will be more appropriate.
+So leave this parameter at it's default unless you know what you do.
+
+The things for v4l_bufsize are more complicated: v4l_bufsize is set by
+default to 128 [KB] which is the maximum amount of physically
+contiguous memory Linux is able to allocate without kernel changes.
+This is sufficient for grabbing 24 bit color images up to sizes of
+approx. 240x180 pixels (240*180*3 = 129600, 128 KB = 131072).
+
+In order to be able to capture bigger images you have either to
+- obtain and install the "big_physarea patch" and set aside
+  the necessary memory during boot time or
+- start your kernel with the mem=xxx option, where xxx is your
+  real memory minus the memory needed for the buffers.
+In that case, useful settings for v4l_bufsize are
+- 1296 [Kb] for grabbing 24 bit images of max size 768*576
+- 1728 [Kb] for 32bit images of same size (4*768*576 = 1728 Kb!)
+You may reduce these numbers accordingly if you know you are only
+grabbing 720 pixels wide images or NTSC images (max height 480).
+
+In some cases it may happen that Linux isn't even able to obtain
+the default 128 KB buffers. If you don't need uncompressed image
+grabbing at all, set v4l_bufsize to an arbitrary small value (e.g. 4)
+in order to be able to open the video device.
+
+triton, natoma
+--------------
+
+The driver tries to detect if you have a triton or natoma chipset
+in order to take special measures for these chipsets.
+If this detection fails but you are sure you have such a chipset,
+set the corresponding variable to 1.
+This is a very special option and may go away in the future.
+
+
+Tested applications
+===================
+
+  XawTV         to watch video on your computer monitor.
+
+  kwintv        the same (you might need to use option lock_norm=1).
+  
+  lavtools      To record and playback AVI or Quicktime files. Note: you
+                will need patched version, lavtools-1.2p2 to support new
+                features of this driver. Please visit driver homepage for
+                more info.
+
+  Broadcast2000 reportedly (I didn't try that) can accept movies recorded
+                by lavrec in Quicktime format for editing and then edited
+                movie can be played back by lavplay program.
+
+  MainActor 3.5x also can accept movies recorded by lavrec for editing.
+
+
+The driver can to be used by two programs at the same time
+(please, see warning note below regarding this feature). Using XawTV
+you can watch what you are recording or playing back with lavtools.
+I've tested the following sequence and it worked for me:
+
+* start xawtv and switch inputs, TV standards, and adjust video
+  (contrast, saturation, etc.). You may also run your favorite
+  audio mixer application to adjust audio inputs.
+
+* run lavrec with options: 
+
+    -i<set your input and norm here> (to choose proper input
+                                      and TV standard)
+
+    -l -1  (to use audio mixer settings)
+
+    Other lavrec option can be added at your choice.
+
+* watch the movie in xawtv window while recording it as AVI or
+  Quicktime file.
+
+* when recording is finished, run lavplay or xlav and watch your
+  clip in xawtv window.
+
+* Note: you should not quit xawtv during recording or playing back.
+  If you quit xawtv during recording or playback, another lavtools
+  program will stop and may even crash.
+
+I'm not sure that the same will work for you. You can try but,
+please, be careful.
+
+WARNING! This is an experimental feature and I'm not sure if it will be
+supported in the future. The original driver was not designed to be
+used like this and it has no protection against any interference
+between two running programs. THEREFORE, IT IS POTENTIALLY DANGEROUS
+AND SINCE THE DRIVER OPERATES IN KERNEL SPACE, USING THIS FEATURE MAY
+CRASH YOUR ENTIRE SYSTEM.
+
+
+Programming interface
+=====================
+
+This driver should be fully compliant to Video for Linux, so all
+tools working with Video for Linux should work with (hopefully)
+no problems.
+
+A description of the Video for Linux programming interface can be found at:
+http://roadrunner.swansea.linux.org.uk/v4lapi.shtml
+
+Besides the Video for Linux interface, the driver has a "proprietary"
+interface for accessing the Buz's MJPEG capture and playback facilities.
+
+For a full description of all members and ioctls see "zoran.h" (used to
+be buz.h or dc10.h in previous versions, so, please, update your
+programs accordingly).
+
+The ioctls for that interface are as follows:
+
+BUZIOC_G_PARAMS
+BUZIOC_S_PARAMS
+
+Get and set the parameters of the buz. The user should always do a
+BUZIOC_G_PARAMS (with a struct buz_params) to obtain the default
+settings, change what he likes and then make a BUZIOC_S_PARAMS call.
+
+BUZIOC_REQBUFS
+
+Before being able to capture/playback, the user has to request
+the buffers he is wanting to use. Fill the structure
+zoran_requestbuffers with the size (recommended: 256*1024) and
+the number (recommended 32 up to 256). There are no such restrictions
+as for the Video for Linux buffers, you should LEAVE SUFFICIENT
+MEMORY for your system however, else strange things will happen ....
+On return, the zoran_requestbuffers structure contains number and
+size of the actually allocated buffers.
+You should use these numbers for doing a mmap of the buffers
+into the user space.
+The BUZIOC_REQBUFS ioctl also makes it happen, that the next mmap
+maps the MJPEG buffer instead of the V4L buffers.
+
+BUZIOC_QBUF_CAPT
+BUZIOC_QBUF_PLAY
+
+Queue a buffer for capture or playback. The first call also starts
+streaming capture. When streaming capture is going on, you may
+only queue further buffers or issue syncs until streaming
+capture is switched off again with a argument of -1 to
+a BUZIOC_QBUF_CAPT/BUZIOC_QBUF_PLAY ioctl.
+
+BUZIOC_SYNC
+
+Issue this ioctl when all buffers are queued. This ioctl will
+block until the first buffer becomes free for saving its
+data to disk (after BUZIOC_QBUF_CAPT) or for reuse (after BUZIOC_QBUF_PLAY).
+
+BUZIOC_G_STATUS
+
+Get the status of the input lines (video source connected/norm).
+This ioctl may be subject to change.
+
+For programming example, please, look at lavrec.c and lavplay.c code in
+lavtools-1.2p2 package (URL: http://www.cicese.mx/~mirsev/DC10plus/)
+and the 'examples' directory in the original Buz driver distribution.
+
+Additional notes for software developers:
+
+   The driver returns maxwidth and maxheight parameters according to
+   the current TV standard (norm). Therefore, the software which
+   communicates with the driver and "asks" for these parameters should
+   first set the correct norm. Well, it seems logically correct: TV
+   standard is "more constant" for current country than geometry
+   settings of a variety of TV capture cards which may work in ITU or
+   square pixel format. Remember that users now can lock the norm to
+   avoid any ambiguity.
+
+Features for testing
+====================
+
+When loaded, the driver creates a /proc/zoranX entry for each card:
+using 'cat /proc/zoran0' for your first card you can see the contents
+of ZR36057/67 chip registers. It is also possible to modify the
+contents of some registers directly. WARNING: modified contents is not
+stored in the driver memory, if you restart any program which uses this
+driver or even change position or cause redraw of a window of xawtv or
+other program, the original registers contents will be restored by the
+driver. However, it can be used to change ZR36067 registers on the fly
+for fine tuning and then to include these changes into driver code.
+This feature is very limited and still requires some documentation.
+However, if you are impatient, look at zoran_procfs.c code and
+(IMPORTANT!) read ZR36057/67 manual. To set TopField bit, for example,
+you need to type as root:
+
+echo TopField=1 > /proc/zoranX # change X to 0 for your first card,
+                               # 1 for second and so on...
+
+If you use this feature and have found some interesting result, please, let
+me know.
+
+Mailing lists
+=============
+
+There are two mailing lists available to discuss application issues and
+suggest driver improvements:
+
+1. A mailing list buz-linux was set up to discuss Iomega Buz driver.
+Since this driver is derivative of that driver, you can also post your
+questions and suggestions there. Subscribe with a message (with
+"subscribe" in the subject) to  buz-linux-subscribe@webmages.com.
+Unsubscribe with a message (with "unsubscribe" in the subject) to
+buz-linux-unsubscribe@webmages.com. The mailing list archive can be
+found at http://buz.webmages.com/list/. 
+
+2. Video4Linux mailing list is set for more general discussions related
+to uncompressed video capture, V4L and V4L2 API, many Video4Linux
+applications, etc. to subscribe to this mailing list, please, visit
+https://listman.redhat.com/mailman/listinfo/video4linux-list
+
+Bug Reports
+===========
+
+If you have found a bug, please, do the following:
+
+1. Edit first line of zoran.c file and set DEBUGLEVEL to 3;
+2. Recompile the driver and install it running update script
+   in the driver directory;
+3. Run the application(s) which you used when you had found a
+   suspisious behavior;
+4. When application stops, look at you /var/log/messages file
+   (or whatever file you use to log kernel messages) and copy
+   all lines related to the driver activity to a separate file
+   in the same order of their appearence in your log file.
+5. Mail a message to <mirsev@cicese.mx> with a subject
+   "Linux DC10(plus)/LML33/Buz driver bug report" with a detailed
+   description of your problem, kernel version, application name and
+   attach that file with kernel messages as plain text (please, don't
+   attach it using base64, uuencode, or any other encoding).
+   
+   If you have a Buz card, please, also mail the same message to
+   Wolfgang Scherr <scherr@net4you.net>
diff --git a/Documentation/video4linux/meye.txt b/Documentation/video4linux/meye.txt
new file mode 100644 (file)
index 0000000..0001c32
--- /dev/null
@@ -0,0 +1,102 @@
+Vaio Picturebook Motion Eye Camera Driver Readme
+------------------------------------------------
+       Copyright (C) 2001 Stelian Pop <stelian.pop@fr.alcove.com>, AlcĂ´ve
+       Copyright (C) 2000 Andrew Tridgell <tridge@samba.org>
+
+This driver enable the use of video4linux compatible applications with the
+Motion Eye camera.
+
+It can do at maximum 30 fps @ 320x240 or 15 fps @ 640x480.
+
+Grabbing is supported in packed YUV colorspace only.
+
+MJPEG hardware grabbing is supported via a private API (see below).
+
+Module options:
+---------------
+
+       gbuffers:       number of capture buffers, default is 2 (32 max)
+
+       gbufsize:       size of each capture buffer, default is 614400
+
+       video_nr:       video device to register (0 = /dev/video0, etc)
+
+Module use:
+-----------
+
+In order to automatically load the meye module on use, you can put those lines
+in your /etc/modules.conf file:
+
+       alias char-major-81 videodev
+       alias char-major-81-0 meye
+       options meye gbuffers=32
+
+Usage:
+------
+
+       xawtv >= 3.49 (<http://bytesex.org/xawtv/>)
+               for display and uncompressed video capture:
+
+                       xawtv -c /dev/video0 -geometry 640x480
+                               or
+                       xawtv -c /dev/video0 -geometry 320x240
+
+       motioneye (<http://www.alcove-labs.org/en/software/meye/>)
+               for getting ppm or jpg snapshots, mjpeg video
+
+Private API:
+------------
+
+       The driver supports frame grabbing with the video4linux API, so
+       all video4linux tools (like xawtv) should work with this driver.
+
+       Besides the video4linux interface, the driver has a private interface
+       for accessing the Motion Eye extended parameters (camera sharpness,
+       agc, video framerate), the shapshot and the MJPEG capture facilities.
+
+       This interface consists of several ioctls (prototypes and structures
+       can be found in include/linux/meye.h):
+
+       MEYEIOC_G_PARAMS
+       MEYEIOC_S_PARAMS
+               Get and set the extended parameters of the motion eye camera.
+               The user should always query the current parameters with
+               MEYEIOC_G_PARAMS, change what he likes and then issue the
+               MEYEIOC_S_PARAMS call (checking for -EINVAL). The extended
+               parameters are described by the meye_params structure.
+
+
+       MEYEIOC_QBUF_CAPT
+               Queue a buffer for capture (the buffers must have been
+               obtained with a VIDIOCGMBUF call and mmap'ed by the
+               application). The argument to MEYEIOC_QBUF_CAPT is the
+               buffer number to queue (or -1 to end capture). The first
+               call to MEYEIOC_QBUF_CAPT starts the streaming capture.
+
+       MEYEIOC_SYNC
+               Takes as an argument the buffer number you want to sync.
+               This ioctl blocks untils the buffer is filled and ready
+               for the application to use. It returns the buffer size.
+
+       MEYEIOC_STILLCAPT
+       MEYEIOC_STILLJCAPT
+               Takes a snapshot in an uncompressed or compressed jpeg format.
+               This ioctl blocks until the snapshot is done and returns (for
+               jpeg snapshot) the size of the image. The image data is 
+               available from the first mmap'ed buffer.
+
+       Look at the 'motioneye' application code for an actual example.
+
+Bugs / Todo:
+------------
+
+       - overlay output is not supported (although the camera is capable of).
+               (it should not be too hard to to it, provided we found how...)
+               
+       - mjpeg hardware playback doesn't work (depends on overlay...)
+
+       - rewrite the driver to use some commun video4linux API for snapshot
+         and mjpeg capture. Unfortunately, video4linux1 does not permit it,
+         the BUZ API seems to be targeted to TV cards only. The video4linux 2
+         API may be an option, if it goes into the kernel (maybe 2.5 
+         material ?).
diff --git a/Documentation/video4linux/w9966.txt b/Documentation/video4linux/w9966.txt
new file mode 100644 (file)
index 0000000..d132c43
--- /dev/null
@@ -0,0 +1,37 @@
+
+W9966 Camera driver, written by Jakob Kemi (jakob.kemi@post.utfors.se)
+
+Ok, after a lot of work in softice, wdasm, reading pdf-files
+and trial-and-error work I've finally got everything to work.
+Since I needed some vision for a robotics project I borrowed
+this camera from a friend and started hacking. Anyway I've
+converted my original code from the AVR 8bit RISC C/asm
+into a working linux driver. I would really appreciate _any_
+kind of feedback regarding this driver.
+
+To get it working quickly configure your kernel
+to support parport, ieee1284, video4linux, experimental drivers
+and w9966
+
+If w9966 is statically linked it will perform aggressive probing
+for the camera. If built as a module you'll have more configuration options.
+
+Options:
+modprobe w9966.o pardev=parport0(or whatever) parmode=0 (0=auto, 1=ecp, 2=epp)
+voila!
+
+you can also type 'modinfo -p w9966.o' for option usage
+(or checkout w9966.c)
+
+I've only tested it with custom built testprograms
+(http://hem.fyristorg.com/mogul/w9966.html) and with gqcam.
+(you'll need to tweak the code to qcam a bit to make it work,
+dimensions and such)
+
+The slow framerate is due to missing DMA ECP read support in the 
+parport drivers. I might add working EPP support later.
+
+Good luck!
+
+    /Jakob
index 25fe9ab7f290127c0389475fccedc116124dc463..c28985e62dd5907b05444f1207a41f40d2a17321 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 7
-EXTRAVERSION =-pre1
+EXTRAVERSION =-pre2
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
index f0db4578310e36a4457b0f82569604c4700cf1cb..a8bd061246538a80e8547cbe886e83963bb3e7df 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
+#include <linux/slab.h>
 #include <linux/init.h>
 
 #include <asm/page.h> /* for BUG() */
 static int debug_pci;
 int have_isa_bridge;
 
+struct pci_sys_data {
+       /*
+        * The hardware we are attached to
+        */
+       struct hw_pci   *hw;
+
+       unsigned long   mem_offset;
+
+       /*
+        * These are the resources for the root bus.
+        */
+       struct resource *resource[3];
+};
+
 void pcibios_report_status(u_int status_mask, int warn)
 {
        struct pci_dev *dev;
@@ -118,19 +133,22 @@ static void __init pci_fixup_unassign(struct pci_dev *dev)
 }
 
 /*
- * Prevent the PCI layer from seeing the resources
- * allocated to this device.  These resources are
- * of no consequence to the PCI layer (they are
- * handled elsewhere).
+ * Prevent the PCI layer from seeing the resources allocated to this device
+ * if it is the host bridge by marking it as such.  These resources are of
+ * no consequence to the PCI layer (they are handled elsewhere).
  */
-static void __init pci_fixup_disable(struct pci_dev *dev)
+static void __init pci_fixup_dec21285(struct pci_dev *dev)
 {
        int i;
 
-       for (i = 0; i < PCI_NUM_RESOURCES; i++) {
-               dev->resource[i].start = 0;
-               dev->resource[i].end   = 0;
-               dev->resource[i].flags = 0;
+       if (dev->devfn == 0) {
+               dev->class &= 0xff;
+               dev->class |= PCI_CLASS_BRIDGE_HOST << 8;
+               for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+                       dev->resource[i].start = 0;
+                       dev->resource[i].end   = 0;
+                       dev->resource[i].flags = 0;
+               }
        }
 }
 
@@ -167,7 +185,7 @@ struct pci_fixup pcibios_fixups[] = {
        {
                PCI_FIXUP_HEADER,
                PCI_VENDOR_ID_DEC,      PCI_DEVICE_ID_DEC_21285,
-               pci_fixup_disable
+               pci_fixup_dec21285
        }, {
                PCI_FIXUP_HEADER,
                PCI_VENDOR_ID_WINBOND,  PCI_DEVICE_ID_WINBOND_83C553,
@@ -187,27 +205,11 @@ struct pci_fixup pcibios_fixups[] = {
        }, { 0 }
 };
 
-/*
- * Allocate resources for all PCI devices that have been enabled.
- * We need to do that before we try to fix up anything.
- */
-static void __init pcibios_claim_resources(void)
-{
-       struct pci_dev *dev;
-       int idx;
-
-       pci_for_each_dev(dev) {
-               for (idx = 0; idx < PCI_NUM_RESOURCES; idx++)
-                       if (dev->resource[idx].flags &&
-                           dev->resource[idx].start)
-                               pci_claim_resource(dev, idx);
-       }
-}
-
 void __init
 pcibios_update_resource(struct pci_dev *dev, struct resource *root,
                        struct resource *res, int resource)
 {
+       struct pci_sys_data *sys = dev->sysdata;
        u32 val, check;
        int reg;
 
@@ -216,12 +218,9 @@ pcibios_update_resource(struct pci_dev *dev, struct resource *root,
                        res->flags & IORESOURCE_IO ? "IO" : "MEM",
                        res->start, dev->name);
 
-       val = res->start | (res->flags & PCI_REGION_FLAG_MASK);
        if (resource < 6) {
                reg = PCI_BASE_ADDRESS_0 + 4*resource;
        } else if (resource == PCI_ROM_RESOURCE) {
-               res->flags |= PCI_ROM_ADDRESS_ENABLE;
-               val |= PCI_ROM_ADDRESS_ENABLE;
                reg = dev->rom_base_reg;
        } else {
                /* Somebody might have asked allocation of a
@@ -229,6 +228,12 @@ pcibios_update_resource(struct pci_dev *dev, struct resource *root,
                 */
                return;
        }
+
+       val = res->start;
+       if (res->flags & IORESOURCE_MEM)
+               val -= sys->mem_offset;
+       val |= res->flags & PCI_REGION_FLAG_MASK;
+
        pci_write_config_dword(dev, reg, val);
        pci_read_config_dword(dev, reg, &check);
        if ((val ^ check) & ((val & PCI_BASE_ADDRESS_SPACE_IO) ?
@@ -246,77 +251,93 @@ void __init pcibios_update_irq(struct pci_dev *dev, int irq)
        pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
 }
 
-/**
- * pcibios_fixup_bus - Called after each bus is probed, but before its children
- * are examined.
+/*
+ * If the bus contains any of these devices, then we must not turn on
+ * parity checking of any kind.  Currently this is CyberPro 20x0 only.
  */
-void __init pcibios_fixup_bus(struct pci_bus *bus)
+static inline int pdev_bad_for_parity(struct pci_dev *dev)
 {
-       struct list_head *walk = &bus->devices;
-       struct arm_pci_sysdata *sysdata =
-                       (struct arm_pci_sysdata *)bus->sysdata;
-       struct arm_bus_sysdata *busdata;
+       return (dev->vendor == PCI_VENDOR_ID_INTERG &&
+               (dev->device == PCI_DEVICE_ID_INTERG_2000 ||
+                dev->device == PCI_DEVICE_ID_INTERG_2010));
+}
 
-       if (bus->number >= MAX_NR_BUS)
-               BUG();
+/*
+ * Adjust the device resources from bus-centric to Linux-centric.
+ */
+static void __init
+pdev_fixup_device_resources(struct pci_sys_data *root, struct pci_dev *dev)
+{
+       int i;
 
-       if (bus->self) {
-               struct pci_dev *dev = bus->self;
-               int i;
+       for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+               if (dev->resource[i].start == 0)
+                       continue;
+               if (dev->resource[i].flags & IORESOURCE_MEM) {
+                       dev->resource[i].start += root->mem_offset;
+                       dev->resource[i].end   += root->mem_offset;
+               }
+       }
+}
+
+static void __init
+pbus_assign_bus_resources(struct pci_bus *bus, struct pci_sys_data *root)
+{
+       struct pci_dev *dev = bus->self;
+       int i;
 
+       if (dev) {
                for (i = 0; i < 3; i++) {
-                       bus->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES + i];
-                       bus->resource[i]->name = bus->name;
+                       bus->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i];
+                       bus->resource[i]->name  = bus->name;
                }
-               bus->resource[0]->start = ioport_resource.start;
-               bus->resource[0]->end   = ioport_resource.end;
                bus->resource[0]->flags |= pci_bridge_check_io(dev);
-               bus->resource[1]->start = iomem_resource.start;
-               bus->resource[1]->end   = iomem_resource.end;
                bus->resource[1]->flags |= IORESOURCE_MEM;
 
-               /* Turn off downsteam prefetchable memory address range */
-               bus->resource[2]->start = 1024*1024;
-               bus->resource[2]->end   = bus->resource[2]->start - 1;
+               if (root->resource[2])
+                       bus->resource[2]->flags = root->resource[2]->flags;
+               else {
+                       /* no prefetchable memory region - disable it */
+                       bus->resource[2]->start = 1024*1024;
+                       bus->resource[2]->end   = bus->resource[2]->start - 1;
+               }
+       } else {
+               /*
+                * Assign root bus resources.
+                */
+               for (i = 0; i < 3; i++)
+                       bus->resource[i] = root->resource[i];
        }
+}
 
-       busdata = sysdata->bus + bus->number;
-       busdata->max_lat = 255;
+/*
+ * pcibios_fixup_bus - Called after each bus is probed,
+ * but before its children are examined.
+ */
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+       struct pci_sys_data *root = bus->sysdata;
+       struct list_head *walk;
+       u16 features = PCI_COMMAND_SERR | PCI_COMMAND_PARITY;
+       u16 all_status = -1;
+
+       pbus_assign_bus_resources(bus, root);
 
        /*
         * Walk the devices on this bus, working out what we can
         * and can't support.
         */
-       for (walk = walk->next; walk != &bus->devices; walk = walk->next) {
+       for (walk = bus->devices.next; walk != &bus->devices; walk = walk->next) {
                struct pci_dev *dev = pci_dev_b(walk);
                u16 status;
-               u8 max_lat, min_gnt;
 
-               pci_read_config_word(dev, PCI_STATUS, &status);
+               pdev_fixup_device_resources(root, dev);
 
-               /*
-                * If this device does not support fast back to back
-                * transfers, the bus as a whole cannot support them.
-                */
-               if (!(status & PCI_STATUS_FAST_BACK))
-                       busdata->features &= ~PCI_COMMAND_FAST_BACK;
-
-               /*
-                * If we encounter a CyberPro 2000, then we disable
-                * SERR and PERR reporting - this chip doesn't drive the
-                * parity line correctly.
-                */
-               if (dev->vendor == PCI_VENDOR_ID_INTERG &&
-                   (dev->device == PCI_DEVICE_ID_INTERG_2000 ||
-                    dev->device == PCI_DEVICE_ID_INTERG_2010))
-                       busdata->features &= ~(PCI_COMMAND_SERR |
-                                              PCI_COMMAND_PARITY);
+               pci_read_config_word(dev, PCI_STATUS, &status);
+               all_status &= status;
 
-               /*
-                * Calculate the maximum devsel latency.
-                */
-               if (busdata->maxdevsel < (status & PCI_STATUS_DEVSEL_MASK))
-                       busdata->maxdevsel = (status & PCI_STATUS_DEVSEL_MASK);
+               if (pdev_bad_for_parity(dev))
+                       features &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
 
                /*
                 * If this device is an ISA bridge, set the have_isa_bridge
@@ -326,63 +347,48 @@ void __init pcibios_fixup_bus(struct pci_bus *bus)
                if (dev->class >> 8 == PCI_CLASS_BRIDGE_ISA ||
                    dev->class >> 8 == PCI_CLASS_BRIDGE_EISA)
                        have_isa_bridge = !0;
-
-               /*
-                * Calculate the maximum latency on this bus.  Note
-                * that we ignore any device which reports its max
-                * latency is the same as its use.
-                */
-               pci_read_config_byte(dev, PCI_MAX_LAT, &max_lat);
-               pci_read_config_byte(dev, PCI_MIN_GNT, &min_gnt);
-               if (max_lat && max_lat != min_gnt && max_lat < busdata->max_lat)
-                       busdata->max_lat = max_lat;
        }
 
+       /*
+        * If any device on this bus does not support fast back to back
+        * transfers, then the bus as a whole is not able to support them.
+        * Having fast back to back transfers on saves us one PCI cycle
+        * per transaction.
+        */
+       if (all_status & PCI_STATUS_FAST_BACK)
+               features |= PCI_COMMAND_FAST_BACK;
+
        /*
         * Now walk the devices again, this time setting them up.
         */
-       walk = &bus->devices;
-       for (walk = walk->next; walk != &bus->devices; walk = walk->next) {
+       for (walk = bus->devices.next; walk != &bus->devices; walk = walk->next) {
                struct pci_dev *dev = pci_dev_b(walk);
                u16 cmd;
-               u8 min_gnt, latency;
 
-               /*
-                * Calculate this masters latency timer value.
-                * This is rather primitive - it does not take
-                * account of the number of masters in a system
-                * wanting to use the bus.
-                */
-               pci_read_config_byte(dev, PCI_MIN_GNT, &min_gnt);
-               if (min_gnt) {
-                       if (min_gnt > busdata->max_lat)
-                               min_gnt = busdata->max_lat;
-
-                       latency = (int)min_gnt * 25 / 3;
-               } else
-                       latency = 32; /* 1us */
-
-               pci_write_config_byte(dev, PCI_LATENCY_TIMER, latency);
-
-               /*
-                * Set the cache line size to 32 bytes.
-                * Also, set system error enable, parity error enable.
-                * Disable ROM.
-                */
-               pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 8);
                pci_read_config_word(dev, PCI_COMMAND, &cmd);
-
-               cmd |= busdata->features;
-
+               cmd |= features;
                pci_write_config_word(dev, PCI_COMMAND, cmd);
-               pci_read_config_word(dev, PCI_COMMAND, &cmd);
-               pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0);
        }
+
+       /*
+        * Report what we did for this bus
+        */
+       printk(KERN_INFO "PCI: bus%d: Fast back to back transfers %sabled\n",
+               bus->number, (features & PCI_COMMAND_FAST_BACK) ? "en" : "dis");
 }
 
+/*
+ * Convert from Linux-centric to bus-centric addresses for bridge devices.
+ */
 void __init
 pcibios_fixup_pbus_ranges(struct pci_bus *bus, struct pbus_set_ranges_data *ranges)
 {
+       struct pci_sys_data *root = bus->sysdata;
+
+       ranges->mem_start -= root->mem_offset;
+       ranges->mem_end -= root->mem_offset;
+       ranges->prefetch_start -= root->mem_offset;
+       ranges->prefetch_end -= root->mem_offset;
 }
 
 u8 __init no_swizzle(struct pci_dev *dev, u8 *pin)
@@ -400,81 +406,90 @@ extern struct hw_pci integrator_pci;
 
 void __init pcibios_init(void)
 {
-       struct hw_pci *hw_pci = NULL;
-       struct arm_pci_sysdata sysdata;
-       int i;
+       struct pci_sys_data *root;
+       struct hw_pci *hw = NULL;
 
        do {
 #ifdef CONFIG_ARCH_EBSA285
                if (machine_is_ebsa285()) {
-                       hw_pci = &ebsa285_pci;
+                       hw = &ebsa285_pci;
                        break;
                }
 #endif
 #ifdef CONFIG_ARCH_SHARK
                if (machine_is_shark()) {
-                       hw_pci = &shark_pci;
+                       hw = &shark_pci;
                        break;
                }
 #endif
 #ifdef CONFIG_ARCH_CATS
                if (machine_is_cats()) {
-                       hw_pci = &cats_pci;
+                       hw = &cats_pci;
                        break;
                }
 #endif
 #ifdef CONFIG_ARCH_NETWINDER
                if (machine_is_netwinder()) {
-                       hw_pci = &netwinder_pci;
+                       hw = &netwinder_pci;
                        break;
                }
 #endif
 #ifdef CONFIG_ARCH_PERSONAL_SERVER
                if (machine_is_personal_server()) {
-                       hw_pci = &personal_server_pci;
+                       hw = &personal_server_pci;
                        break;
                }
 #endif
 #ifdef CONFIG_ARCH_FTVPCI
                if (machine_is_ftvpci()) {
-                       hw_pci = &ftv_pci;
+                       hw = &ftv_pci;
                        break;
                }
 #endif
 #ifdef CONFIG_ARCH_INTEGRATOR
                if (machine_is_integrator()) {
-                       hw_pci = &integrator_pci;
+                       hw = &integrator_pci;
                        break;
                }
 #endif
        } while (0);
 
-       if (hw_pci == NULL)
+       if (hw == NULL)
                return;
 
-       for (i = 0; i < MAX_NR_BUS; i++) {
-               sysdata.bus[i].features  = PCI_COMMAND_FAST_BACK |
-                                          PCI_COMMAND_SERR |
-                                          PCI_COMMAND_PARITY;
-               sysdata.bus[i].maxdevsel = PCI_STATUS_DEVSEL_FAST;
-       }
+       root = kmalloc(sizeof(*root), GFP_KERNEL);
+       if (!root)
+               panic("PCI: unable to allocate root data!");
+
+       root->hw = hw;
+       root->mem_offset = hw->mem_offset;
+
+       memset(root->resource, 0, sizeof(root->resource));
 
        /*
-        * Set up the host bridge, and scan the bus.
+        * Setup the resources for this bus.
+        *   resource[0] - IO ports
+        *   resource[1] - non-prefetchable memory
+        *   resource[2] - prefetchable memory
         */
-       hw_pci->init(&sysdata);
+       if (root->hw->setup_resources)
+               root->hw->setup_resources(root->resource);
+       else {
+               root->resource[0] = &ioport_resource;
+               root->resource[1] = &iomem_resource;
+               root->resource[2] = NULL;
+       }
 
        /*
-        * Claim the currently allocated resources.  This ensures
-        * that we will not allocate an already inuse region.
+        * Set up the host bridge, and scan the bus.
         */
-       pcibios_claim_resources();
+       root->hw->init(root);
 
        /*
         * Assign any unassigned resources.
         */
        pci_assign_unassigned_resources();
-       pci_fixup_irqs(hw_pci->swizzle, hw_pci->map_irq);
+       pci_fixup_irqs(root->hw->swizzle, root->hw->map_irq);
 }
 
 char * __init pcibios_setup(char *str)
@@ -499,7 +514,7 @@ char * __init pcibios_setup(char *str)
  * is reserved for motherboard devices that decode all 16
  * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
  * but we want to try to avoid allocating at 0x2900-0x2bff
- * which might have be mirrored at 0x0100-0x03ff..
+ * which might be mirrored at 0x0100-0x03ff..
  */
 void pcibios_align_resource(void *data, struct resource *res, unsigned long size)
 {
index bf265fab3fe92f9e2a9ddec613048e9cda774dc2..3d08c2e7fee85feba77dda8092c09a70c2f85dd3 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/ptrace.h>
 #include <linux/interrupt.h>
 #include <linux/mm.h>
+#include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
 
 
 #define MAX_SLOTS              21
 
+#define PCICMD_ERROR_BITS ((PCI_STATUS_DETECTED_PARITY | \
+                       PCI_STATUS_REC_MASTER_ABORT | \
+                       PCI_STATUS_REC_TARGET_ABORT | \
+                       PCI_STATUS_PARITY) << 16)
+
 extern int setup_arm_irq(int, struct irqaction *);
 extern void pcibios_report_status(u_int status_mask, int warn);
 extern void register_isa_ports(unsigned int, unsigned int, unsigned int);
@@ -255,12 +261,33 @@ static void dc21285_parity_irq(int irq, void *dev_id, struct pt_regs *regs)
        add_timer(timer);
 }
 
-void __init dc21285_init(struct arm_pci_sysdata *sysdata)
+void __init dc21285_setup_resources(struct resource **resource)
+{
+       struct resource *busmem, *busmempf;
+
+       busmem = kmalloc(sizeof(*busmem), GFP_KERNEL);
+       busmempf = kmalloc(sizeof(*busmempf), GFP_KERNEL);
+       memset(busmem, 0, sizeof(*busmem));
+       memset(busmempf, 0, sizeof(*busmempf));
+
+       busmem->flags = IORESOURCE_MEM;
+       busmem->name  = "Footbridge non-prefetch";
+       busmempf->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
+       busmempf->name  = "Footbridge prefetch";
+
+       allocate_resource(&iomem_resource, busmempf, 0x20000000,
+                         0x80000000, 0xffffffff, 0x20000000, NULL, NULL);
+       allocate_resource(&iomem_resource, busmem, 0x40000000,
+                         0x80000000, 0xffffffff, 0x40000000, NULL, NULL);
+
+       resource[0] = &ioport_resource;
+       resource[1] = busmem;
+       resource[2] = busmempf;
+}
+
+void __init dc21285_init(void *sysdata)
 {
-       unsigned long cntl;
        unsigned int mem_size, mem_mask;
-       unsigned int pci_cmd = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
-                               PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
        int cfn_mode;
 
        mem_size = (unsigned int)high_memory - PAGE_OFFSET;
@@ -286,26 +313,17 @@ void __init dc21285_init(struct arm_pci_sysdata *sysdata)
                "central function" : "addin");
 
        if (cfn_mode) {
-               static struct resource csrmem, csrio, busmem, busmempf;
-               struct pci_bus *bus;
+               static struct resource csrmem, csrio;
 
-               csrio.flags = IORESOURCE_IO;
-               csrio.name  = "Footbridge";
+               csrio.flags  = IORESOURCE_IO;
+               csrio.name   = "Footbridge";
                csrmem.flags = IORESOURCE_MEM;
                csrmem.name  = "Footbridge";
-               busmem.flags = IORESOURCE_MEM;
-               busmem.name  = "Footbridge non-prefetch";
-               busmempf.flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
-               busmempf.name  = "Footbridge prefetch";
 
                allocate_resource(&ioport_resource, &csrio, 128,
                                  0xff00, 0xffff, 128, NULL, NULL);
                allocate_resource(&iomem_resource, &csrmem, 128,
                                  0xf4000000, 0xf8000000, 128, NULL, NULL);
-               allocate_resource(&iomem_resource, &busmempf, 0x20000000,
-                                 0x00000000, 0x80000000, 0x20000000, NULL, NULL);
-               allocate_resource(&iomem_resource, &busmem, 0x40000000,
-                                 0x00000000, 0x80000000, 0x40000000, NULL, NULL);
 
                /*
                 * Map our SDRAM at a known address in PCI space, just in case
@@ -313,37 +331,23 @@ void __init dc21285_init(struct arm_pci_sysdata *sysdata)
                 * necessary, since some VGA cards forcefully use PCI addresses
                 * in the range 0x000a0000 to 0x000c0000. (eg, S3 cards).
                 */
-               *CSR_PCICACHELINESIZE = 0x00002008;
                *CSR_PCICSRBASE       = csrmem.start;
                *CSR_PCICSRIOBASE     = csrio.start;
                *CSR_PCISDRAMBASE     = __virt_to_bus(PAGE_OFFSET);
                *CSR_PCIROMBASE       = 0;
-               *CSR_PCICMD           = pci_cmd |
-                               (1 << 31) | (1 << 29) | (1 << 28) | (1 << 24);
-
-               bus = pci_scan_bus(0, &dc21285_ops, sysdata);
-               /*
-                * bus->resource[0] is the IO resource for this bus
-                * bus->resource[1] is the mem resource for this bus
-                * bus->resource[2] is the prefetch mem resource for this bus
-                */
-               bus->resource[1] = &busmem;
-               bus->resource[2] = &busmempf;
-
-               pci_cmd |= sysdata->bus[0].features;
+               *CSR_PCICMD = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+                             PCI_COMMAND_INVALIDATE | PCICMD_ERROR_BITS;
 
-               printk("PCI: Fast back to back transfers %sabled\n",
-                      (sysdata->bus[0].features & PCI_COMMAND_FAST_BACK) ?
-                       "en" : "dis");
+               pci_scan_bus(0, &dc21285_ops, sysdata);
 
                /*
                 * Clear any existing errors - we aren't
                 * interested in historical data...
                 */
-               cntl            = *CSR_SA110_CNTL & 0xffffde07;
-               *CSR_SA110_CNTL = cntl | SA110_CNTL_RXSERR;
-               *CSR_PCICMD     = pci_cmd | 1 << 31 | 1 << 29 | 1 << 28 | 1 << 24;
-       } else {
+               *CSR_SA110_CNTL = (*CSR_SA110_CNTL & 0xffffde07) |
+                                 SA110_CNTL_RXSERR;
+               *CSR_PCICMD = (*CSR_PCICMD & 0xffff) | PCICMD_ERROR_BITS;
+       } else if (footbridge_cfn_mode() != 0) {
                /*
                 * If we are not compiled to accept "add-in" mode, then
                 * we are using a constant virt_to_bus translation which
index ad9ca3de5ade3be9d9510e34455dd3479b4537b9..50c057e73f1ba96e0ec519e3eefb11abcfd83e60 100644 (file)
@@ -155,7 +155,7 @@ static void a5k_floppy_enable_dma(dmach_t channel, dma_t *dma)
        }
        memcpy((void *)0x1c, fiqhandler_start, fiqhandler_length);
        regs.ARM_r9 = dma->buf.length;
-       regs.ARM_r10 = dma->buf.address;
+       regs.ARM_r10 = (unsigned long)dma->buf.address;
        regs.ARM_fp = FLOPPYDMA_BASE;
        set_fiq_regs(&regs);
        enable_fiq(dma->dma_irq);
index a969a5b576404787bbb821982f072571fe172c01..b8078ddd70586efe6bad721c9a1e435b4e93ecad 100644 (file)
@@ -79,7 +79,7 @@ ENTRY(stext)
  *     ideal, but in this case, it should ONLY set r0 and r1 to the
  *     appropriate value.
  */
-#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_INTEGRATOR)
+#if defined(CONFIG_ARCH_NETWINDER)
 /*
  * Compatability cruft for old NetWinder NeTTroms.  This
  * code is currently scheduled for destruction in 2.5.xx
@@ -124,8 +124,6 @@ __entry:
  * FIXME - No bootloader, so manually set 'r1' with our architecture number.
  */
                mov     r1, #MACH_TYPE_L7200
-#elif defined(CONFIG_ARCH_INTEGRATOR)
-               mov     r1, #MACH_TYPE_INTEGRATOR
 #endif
 
                mov     r0, #F_BIT | I_BIT | MODE_SVC   @ make sure svc mode
@@ -236,14 +234,16 @@ __create_page_tables:
                 * nearest megabyte boundary.
                 */
                add     r0, r4, #(TEXTADDR & 0xff000000) >> 18 @ start of kernel
+               bic     r2, r3, #0x00f00000
+               str     r2, [r0]                        @ PAGE_OFFSET + 0MB
                add     r0, r0, #(TEXTADDR & 0x00f00000) >> 18
-               str     r3, [r0], #4                    @ PAGE_OFFSET + 0MB
+               str     r3, [r0], #4                    @ KERNEL + 0MB
                add     r3, r3, #1 << 20
-               str     r3, [r0], #4                    @ PAGE_OFFSET + 1MB
+               str     r3, [r0], #4                    @ KERNEL + 1MB
                add     r3, r3, #1 << 20
-               str     r3, [r0], #4                    @ PAGE_OFFSET + 2MB
+               str     r3, [r0], #4                    @ KERNEL + 2MB
                add     r3, r3, #1 << 20
-               str     r3, [r0], #4                    @ PAGE_OFFSET + 3MB
+               str     r3, [r0], #4                    @ KERNEL + 3MB
 
                /*
                 * Ensure that the first section of RAM is present.
index 75ba611b1f92e10b674504d126fbe5706f47e9f7..168470eb42c32bde46b2e0e405aff043eb0c3b6b 100644 (file)
@@ -216,7 +216,7 @@ asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
 
        irq_exit(cpu, irq);
 
-       if (softirq_active(cpu) & softirq_mask(cpu))
+       if (softirq_pending(cpu))
                do_softirq();
        return;
 
index fd2f9269b2313be95fac7e0443d6cbb17c064973..a057e47dec440ff90658788eb010232ad6d83cff 100644 (file)
@@ -50,13 +50,14 @@ void oldlatch_bupdate(unsigned char mask,unsigned char newdata)
                BUG();
 }
 
-static void __init oldlatch_init(void)
+static int __init oldlatch_init(void)
 {
        if (machine_is_archimedes()) {
                oldlatch_aupdate(0xff, 0xff);
                /* Thats no FDC reset...*/
                oldlatch_bupdate(0xff, LATCHB_FDCRESET);
        }
+       return 0;
 }
 
 __initcall(oldlatch_init);
index 47ec9a8d23f129b0ba4b9a93af97771801384608..bee4330b9c4e3f8dfbbdc7076324fd622b69d83e 100644 (file)
@@ -129,8 +129,6 @@ plx90x0_init(struct arm_sysdata *sysdata)
        static const unsigned long int base = PLX_BASE;
        char *what;
        unsigned long bar = (unsigned long)virt_to_bus((void *)PAGE_OFFSET);
-       unsigned int pci_cmd = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
-                               PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
 
        /* Have a sniff around and see which PLX device is present. */
        unsigned long id = __raw_readl(base + 0xf0);
@@ -189,10 +187,4 @@ plx90x0_init(struct arm_sysdata *sysdata)
                    "system error", NULL);
 
        pci_scan_bus(0, &plx90x0_ops, sysdata);
-
-       pci_cmd |= sysdata->bus[0].features;
-
-       printk("PCI: Fast back to back transfers %sabled\n",
-              (sysdata->bus[0].features & PCI_COMMAND_FAST_BACK) ?
-              "en" : "dis");
 }
index 453598e5ef0664c6854528517f2ff9eeb9ee3c69..34023b9c3f66acd015ffbc3454af496db96f00a0 100644 (file)
@@ -28,9 +28,9 @@
 #include <asm/uaccess.h>
 #include <asm/ipc.h>
 
-/*
- * Constant strings used in inlined functions in header files
- */
+extern unsigned long do_mremap(unsigned long addr, unsigned long old_len,
+                              unsigned long new_len, unsigned long flags,
+                              unsigned long new_addr);
 
 /*
  * sys_pipe() is the normal C calling standard for creating
index 1f9e198bac88556a63a5d28684e21432270640c1..9b12f731431f70fb084ca160d60dc941e1f889d3 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <asm/irq.h>
 #include <asm/mach/pci.h>
+#include <asm/hardware/dec21285.h>
 
 /* cats host-specific stuff */
 static int irqmap_cats[] __initdata = { IRQ_PCI, IRQ_IN0, IRQ_IN1, IRQ_IN3 };
@@ -31,7 +32,9 @@ static int __init cats_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
 }
 
 struct hw_pci cats_pci __initdata = {
-       init:           dc21285_init,
-       swizzle:        no_swizzle,
-       map_irq:        cats_map_irq,
+       setup_resources:        dc21285_setup_resources,
+       init:                   dc21285_init,
+       mem_offset:             DC21285_PCI_MEM,
+       swizzle:                no_swizzle,
+       map_irq:                cats_map_irq,
 };
index 304079040b824835dcab5970a2af26636bf5c15d..47a5cf27173e67717063ac481a965c4a6b8a8b2e 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <asm/irq.h>
 #include <asm/mach/pci.h>
+#include <asm/hardware/dec21285.h>
 
 static int irqmap_ebsa285[] __initdata = { IRQ_IN3, IRQ_IN1, IRQ_IN0, IRQ_PCI };
 
@@ -33,7 +34,9 @@ static int __init ebsa285_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
 }
 
 struct hw_pci ebsa285_pci __initdata = {
-       init:           dc21285_init,
-       swizzle:        ebsa285_swizzle,
-       map_irq:        ebsa285_map_irq,
+       setup_resources:        dc21285_setup_resources,
+       init:                   dc21285_init,
+       mem_offset:             DC21285_PCI_MEM,
+       swizzle:                ebsa285_swizzle,
+       map_irq:                ebsa285_map_irq,
 };
index 6bad74680c162493a755c48e12204e4dc24f5d89..4fe7436ced8122d3bfcf83e304e9b7f2f2e234ee 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <asm/irq.h>
 #include <asm/mach/pci.h>
+#include <asm/hardware/dec21285.h>
 
 /* netwinder host-specific stuff */
 static int __init netwinder_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
@@ -48,7 +49,9 @@ static int __init netwinder_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
 }
 
 struct hw_pci netwinder_pci __initdata = {
-       init:           dc21285_init,
-       swizzle:        no_swizzle,
-       map_irq:        netwinder_map_irq,
+       setup_resources:        dc21285_setup_resources,
+       init:                   dc21285_init,
+       mem_offset:             DC21285_PCI_MEM,
+       swizzle:                no_swizzle,
+       map_irq:                netwinder_map_irq,
 };
index 8d810a22f32a5b20d27c61a8495dd8e11bd653b8..2f4029c0fe272f11134775f512e3f7ae123650ab 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <asm/irq.h>
 #include <asm/mach/pci.h>
+#include <asm/hardware/dec21285.h>
 
 static int irqmap_personal_server[] __initdata = {
        IRQ_IN0, IRQ_IN1, IRQ_IN2, IRQ_IN3, 0, 0, 0,
@@ -37,7 +38,9 @@ static int __init personal_server_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
 }
 
 struct hw_pci personal_server_pci __initdata = {
-       init:           dc21285_init,
-       swizzle:        no_swizzle,
-       map_irq:        personal_server_map_irq,
+       setup_resources:        dc21285_setup_resources,
+       init:                   dc21285_init,
+       mem_offset:             DC21285_PCI_MEM,
+       swizzle:                no_swizzle,
+       map_irq:                personal_server_map_irq,
 };
index 0bd20fe44e12f6f31b8dad80a3b5873814af0cb1..9c1fd64c362973a9b324814ca2ff58d4c2d66d6d 100644 (file)
@@ -6,7 +6,7 @@
 # To add an entry into this database, please see Documentation/arm/README,
 # or contact rmk@arm.linux.org.uk
 #
-# Last update: Sun Jun 17 00:53:17 2001
+# Last update: Wed Jul 4 20:04:11 2001
 #
 # machine_is_xxx       CONFIG_xxxx             MACH_TYPE_xxx           number
 #
@@ -91,12 +91,17 @@ mercury                     ARCH_MERCURY            MERCURY                 78
 empeg                  SA1100_EMPEG            EMPEG                   79
 adi_eb                 ARCH_I80200FCC          I80200FCC               80
 itt_cpb                        SA1100_ITT_CPB          ITT_CPB                 81
-sa1110_svc             ARCH_SA1110_SVC         SA1110_SVC              82
+svc                    SA1100_SVC              SVC                     82
 alpha2                 SA1100_ALPHA2           ALPHA2                  84
 alpha1                 SA1100_ALPHA1           ALPHA1                  85
 netarm                 ARCH_NETARM             NETARM                  86
 simpad                 SA1100_SIMPAD           SIMPAD                  87
 pda1                   ARCH_PDA1               PDA1                    88
 lubbock                        ARCH_LUBBOCK            LUBBOCK                 89
+aniko                  ARCH_ANIKO              ANIKO                   90
+clep7212               ARCH_CLEP7212           CLEP7212                91
+cs89712                        ARCH_CS89712            CS89712                 92
+weararm                        SA1100_WEARARM          WEARARM                 93
+possio_px              SA1100_POSSIO_PX        POSSIO_PX               94
 
 # The following are unallocated
index 4029a8d1457001c854e01b53e8a549698ef25a78..533c84f683092b9d5610919595913f9255740ae3 100644 (file)
@@ -1,8 +1,10 @@
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/init.h>
 #include <linux/apm_bios.h>
+#include <linux/slab.h>
 #include <asm/io.h>
 
 struct dmi_header
@@ -13,6 +15,7 @@ struct dmi_header
 };
 
 #define dmi_printk(x)
+//#define dmi_printk(x) printk(x)
 
 static char * __init dmi_string(struct dmi_header *dm, u8 s)
 {
@@ -90,9 +93,254 @@ int __init dmi_iterate(void (*decode)(struct dmi_header *))
 }
 
 
+enum
+{
+       DMI_BIOS_VENDOR,
+       DMI_BIOS_VERSION,
+       DMI_BIOS_DATE,
+       DMI_SYS_VENDOR,
+       DMI_PRODUCT_NAME,
+       DMI_PRODUCT_VERSION,
+       DMI_BOARD_VENDOR,
+       DMI_BOARD_NAME,
+       DMI_BOARD_VERSION,
+       DMI_STRING_MAX
+};
+
+static char *dmi_ident[DMI_STRING_MAX];
+
+/*
+ *     Save a DMI string
+ */
+static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string)
+{
+       char *d = (char*)dm;
+       char *p = dmi_string(dm, d[string]);
+       if(p==NULL || *p == 0)
+               return;
+       if (dmi_ident[slot])
+               return;
+       dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL);
+       if(dmi_ident[slot])
+               strcpy(dmi_ident[slot], p);
+       else
+               printk(KERN_ERR "dmi_save_ident: out of memory.\n");
+}
+
+/*
+ *     DMI callbacks for problem boards
+ */
+
+struct dmi_strmatch
+{
+       u8 slot;
+       char *substr;
+};
+
+#define NONE   255
+
+struct dmi_blacklist
+{
+       int (*callback)(struct dmi_blacklist *);
+       char *ident;
+       struct dmi_strmatch matches[4];
+};
+
+#define NO_MATCH       { NONE, NULL}
+#define MATCH(a,b)     { a, b }
+
+/*
+ *     We have problems with IDE DMA on some platforms. In paticular the
+ *     KT7 series. On these it seems the newer BIOS has fixed them. The
+ *     rule needs to be improved to match specific BIOS revisions with
+ *     corruption problems
+ */ 
+static __init int disable_ide_dma(struct dmi_blacklist *d)
+{
+#ifdef CONFIG_BLK_DEV_IDE
+       extern int noautodma;
+       if(noautodma == 0)
+       {
+               noautodma = 1;
+               printk(KERN_INFO "%s series board detected. Disabling IDE DMA.\n", d->ident);
+       }
+#endif 
+       return 0;
+}
+
+/* 
+ * Some machines require the "reboot=b"  commandline option, this quirk makes that automatic.
+ */
+static __init int set_bios_reboot(struct dmi_blacklist *d)
+{
+       extern int reboot_thru_bios;
+       if (reboot_thru_bios == 0)
+       {
+               reboot_thru_bios = 1;
+               printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident);
+       }
+       return 0;
+}
+
+/*
+ * Some bioses have a broken protected mode poweroff and need to use realmode
+ */
+
+static __init int set_realmode_power_off(struct dmi_blacklist *d)
+{
+       if (apm_info.realmode_power_off == 0)
+       {
+               apm_info.realmode_power_off = 1;
+               printk(KERN_INFO "%s bios detected. Using realmode poweroff only.\n", d->ident);
+       }
+       return 0;
+}
+
+
+/* 
+ * Some laptops require interrupts to be enabled during APM calls 
+ */
+
+static __init int set_apm_ints(struct dmi_blacklist *d)
+{
+       if (apm_info.allow_ints == 0)
+       {
+               apm_info.allow_ints = 1;
+               printk(KERN_INFO "%s machine detected. Enabling interrupts during APM calls.\n", d->ident);
+       }
+       return 0;
+}
+
+/* 
+ * Some APM bioses corrupt memory or just plain do not work
+ */
+
+static __init int apm_is_horked(struct dmi_blacklist *d)
+{
+       if (apm_info.disabled == 0)
+       {
+               apm_info.disabled = 1;
+               printk(KERN_INFO "%s machine detected. Disabling APM.\n", d->ident);
+       }
+       return 0;
+}
+
+
+/*
+ *  Check for clue free BIOS implementations who use
+ *  the following QA technique
+ *
+ *      [ Write BIOS Code ]<------
+ *               |                ^
+ *      < Does it Compile >----N--
+ *               |Y               ^
+ *     < Does it Boot Win98 >-N--
+ *               |Y
+ *           [Ship It]
+ *
+ *     Phoenix A04  08/24/2000 is known bad (Dell Inspiron 5000e)
+ *     Phoenix A07  09/29/2000 is known good (Dell Inspiron 5000)
+ */
+
+static __init int broken_apm_power(struct dmi_blacklist *d)
+{
+       apm_info.get_power_status_broken = 1;
+       printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n");
+       return 0;
+}              
+
+/*
+ *     Process the DMI blacklists
+ */
+
+/*
+ *     This will be expanded over time to force things like the APM 
+ *     interrupt mask settings according to the laptop
+ */
+static __initdata struct dmi_blacklist dmi_blacklist[]={
+#if 0
+       { disable_ide_dma, "KT7", {     /* Overbroad right now - kill DMA on problem KT7 boards */
+                       MATCH(DMI_PRODUCT_NAME, "KT7-RAID"),
+                       NO_MATCH, NO_MATCH, NO_MATCH
+                       } },
+#endif                 
+       { broken_apm_power, "Dell Inspiron 5000e", {    /* Handle problems with APM on Inspiron 5000e */
+                       MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
+                       MATCH(DMI_BIOS_VERSION, "A04"),
+                       MATCH(DMI_BIOS_DATE, "08/24/2000"), NO_MATCH
+                       } },
+       { set_realmode_power_off, "Award Software v4.60 PGMA", {        /* broken PM poweroff bios */
+                       MATCH(DMI_BIOS_VENDOR, "Award Software International, Inc."),
+                       MATCH(DMI_BIOS_VERSION, "4.60 PGMA"),
+                       MATCH(DMI_BIOS_DATE, "134526184"), NO_MATCH
+                       } },
+       { set_bios_reboot, "PowerEdge 1300/500", {      /* Handle problems with rebooting on Dell 1300's */
+                       MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+                       MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/500"),
+                       NO_MATCH, NO_MATCH
+                       } },
+       { set_bios_reboot, "PowerEdge 1300/550", {      /* Handle problems with rebooting on Dell 1300's */
+                       MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+                       MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/550"),
+                       NO_MATCH, NO_MATCH
+                       } },
+       { set_apm_ints, "IBM", {        /* Allow interrupts during suspend on IBM laptops */
+                       MATCH(DMI_SYS_VENDOR, "IBM"),
+                       NO_MATCH, NO_MATCH, NO_MATCH
+                       } },
+       { set_apm_ints, "ASUSTeK", {   /* Allow interrupts during APM or the clock goes slow */
+                       MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+                       MATCH(DMI_PRODUCT_NAME, "L8400K series Notebook PC"),
+                       NO_MATCH, NO_MATCH
+                       } },                                    
+       { apm_is_horked, "Trigem Delhi3", { /* APM crashes */
+                       MATCH(DMI_SYS_VENDOR, "TriGem Computer, Inc"),
+                       MATCH(DMI_PRODUCT_NAME, "Delhi3"),
+                       NO_MATCH, NO_MATCH,
+                       } },
+       { NULL, }
+};
+       
+       
+/*
+ *     Walk the blacklist table running matching functions until someone 
+ *     returns 1 or we hit the end.
+ */
+static __init void dmi_check_blacklist(void)
+{
+       struct dmi_blacklist *d;
+       int i;
+               
+       d=&dmi_blacklist[0];
+       while(d->callback)
+       {
+               for(i=0;i<4;i++)
+               {
+                       int s = d->matches[i].slot;
+                       if(s==NONE)
+                               continue;
+                       if(dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr))
+                               continue;
+                       /* No match */
+                       goto fail;
+               }
+               if(d->callback(d))
+                       return;
+fail:                  
+               d++;
+       }
+}
+
+       
+
 /*
  *     Process a DMI table entry. Right now all we care about are the BIOS
- *     and machine entries. For 2.4 we should pull the smbus controller info
+ *     and machine entries. For 2.5 we should pull the smbus controller info
  *     out of here.
  */
 
@@ -105,66 +353,47 @@ static void __init dmi_decode(struct dmi_header *dm)
        {
                case  0:
                        p=dmi_string(dm,data[4]);
-
-                       if(*p && *p!=' ')
+                       if(*p)
                        {
                                dmi_printk(("BIOS Vendor: %s\n", p));
+                               dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);
                                dmi_printk(("BIOS Version: %s\n", 
                                        dmi_string(dm, data[5])));
+                               dmi_save_ident(dm, DMI_BIOS_VERSION, 5);
                                dmi_printk(("BIOS Release: %s\n",
                                        dmi_string(dm, data[8])));
-                       }
-                               
-                       /*
-                        *  Check for clue free BIOS implementations who use
-                        *  the following QA technique
-                        *
-                        *      [ Write BIOS Code ]<------
-                        *               |                ^
-                        *      < Does it Compile >----N--
-                        *               |Y               ^
-                        *      < Does it Boot Win98 >-N--
-                        *               |Y
-                        *           [Ship It]
-                        *
-                        *      Phoenix A04  08/24/2000 is known bad (Dell Inspiron 5000e)
-                        *      Phoenix A07  09/29/2000 is known good (Dell Inspiron 5000)
-                        */
-                        
-                       if(strcmp(dmi_string(dm, data[4]), "Phoenix Technologies LTD")==0)
-                       {
-                               if(strcmp(dmi_string(dm, data[5]), "A04")==0 
-                                       && strcmp(dmi_string(dm, data[8]), "08/24/2000")==0)
-                               {
-                                       apm_info.get_power_status_broken = 1;
-                                       printk(KERN_WARNING "BIOS strings suggest APM bugs, disabling power status reporting.\n");
-                               }
+                               dmi_save_ident(dm, DMI_BIOS_DATE, 8);
                        }
                        break;
+                       
                case 1:
                        p=dmi_string(dm,data[4]);
-
-                       if(*p && *p!=' ')
+                       if(*p)
                        {
                                dmi_printk(("System Vendor: %s.\n",p));
+                               dmi_save_ident(dm, DMI_SYS_VENDOR, 4);
                                dmi_printk(("Product Name: %s.\n",
                                        dmi_string(dm, data[5])));
+                               dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
                                dmi_printk(("Version %s.\n",
                                        dmi_string(dm, data[6])));
+                               dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
                                dmi_printk(("Serial Number %s.\n",
                                        dmi_string(dm, data[7])));
                        }
                        break;
                case 2:
                        p=dmi_string(dm,data[4]);
-
-                       if(*p && *p!=' ')
+                       if(*p)
                        {
                                dmi_printk(("Board Vendor: %s.\n",p));
+                               dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
                                dmi_printk(("Board Name: %s.\n",
                                        dmi_string(dm, data[5])));
+                               dmi_save_ident(dm, DMI_BOARD_NAME, 5);
                                dmi_printk(("Board Version: %s.\n",
                                        dmi_string(dm, data[6])));
+                               dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
                        }
                        break;
                case 3:
@@ -177,7 +406,10 @@ static void __init dmi_decode(struct dmi_header *dm)
 
 static int __init dmi_scan_machine(void)
 {
-       return dmi_iterate(dmi_decode);
+       int err = dmi_iterate(dmi_decode);
+       if(err == 0)
+               dmi_check_blacklist();
+       return err;
 }
 
 module_init(dmi_scan_machine);
index 15284083ae97c0b1d79cfe7c48fb1a52541d89ec..22bcc1ea732ee1050224b398f6339c24fd1dea5d 100644 (file)
@@ -391,6 +391,38 @@ static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int
        return 1;
 }
 
+/* Support for AMD756 PCI IRQ Routing
+ * Jhon H. Caicedo <jhcaiced@osso.org.co>
+ * Jun/21/2001 0.2.0 Release, fixed to use "nybble" functions... (jhcaiced)
+ * Jun/19/2001 Alpha Release 0.1.0 (jhcaiced)
+ * The AMD756 pirq rules are nibble-based
+ * offset 0x56 0-3 PIRQA  4-7  PIRQB
+ * offset 0x57 0-3 PIRQC  4-7  PIRQD
+ */
+static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+{
+       u8 irq;
+       irq = 0;
+       if (pirq <= 4)
+       {
+               irq = read_config_nybble(router, 0x56, pirq - 1);
+       }
+       printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n",
+               dev->vendor, dev->device, pirq, irq);
+       return irq;
+}
+
+static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+{
+       printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n", 
+               dev->vendor, dev->device, pirq, irq);
+       if (pirq <= 4)
+       {
+               write_config_nybble(router, 0x56, pirq - 1, irq);
+       }
+       return 1;
+}
+
 #ifdef CONFIG_PCI_BIOS
 
 static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
@@ -426,6 +458,8 @@ static struct irq_router pirq_routers[] = {
        { "VLSI 82C534", PCI_VENDOR_ID_VLSI, PCI_DEVICE_ID_VLSI_82C534, pirq_vlsi_get, pirq_vlsi_set },
        { "ServerWorks", PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4,
          pirq_serverworks_get, pirq_serverworks_set },
+       { "AMD756 VIPER", PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_740B,
+               pirq_amd756_get, pirq_amd756_set },
 
        { "default", 0, 0, NULL, NULL }
 };
index 65f51796605a5090f2d7002e3fae81f4cac5bca4..c8458853e22aaf3e8cce54597df0a3fd1bce6b5a 100644 (file)
@@ -152,7 +152,7 @@ __setup("idle=", idle_setup);
 
 static long no_idt[2];
 static int reboot_mode;
-static int reboot_thru_bios;
+int reboot_thru_bios;
 
 static int __init reboot_setup(char *str)
 {
index 2b24458f5cc742033cd0bd1dd3a320f7f8d24648..9d349da28d322c7757b2cb94da951d36f76718f8 100644 (file)
@@ -1438,8 +1438,16 @@ static void __init init_cyrix(struct cpuinfo_x86 *c)
                break;
 
         case 5: /* 6x86MX/M II */
-               if (dir1 > 7) dir0_msn++;  /* M II */
-               else c->coma_bug = 1;      /* 6x86MX, it has the bug. */
+               if (dir1 > 7)
+               {
+                       dir0_msn++;  /* M II */
+                       /* Enable MMX extensions (App note 108) */
+                       setCx86(CX86_CCR7, getCx86(CX86_CCR7)|1);
+               }
+               else
+               {
+                       c->coma_bug = 1;      /* 6x86MX, it has the bug. */
+               }
                tmp = (!(dir0_lsn & 7) || dir0_lsn & 1) ? 2 : 0;
                Cx86_cb[tmp] = cyrix_model_mult2[dir0_lsn & 7];
                p = Cx86_cb+tmp;
index 7b096279fc114f2d2ffcfdf8a183d5dcb208529a..3c69c51dc0306525874f1860079604170a68ec53 100644 (file)
@@ -370,7 +370,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
 
        /* This is the X/Open sanctioned signal stack switching.  */
        if (ka->sa.sa_flags & SA_ONSTACK) {
-               if (! on_sig_stack(esp))
+               if (sas_ss_flags(esp) == 0)
                        esp = current->sas_ss_sp + current->sas_ss_size;
        }
 
index 9f08e64537578f5977701a4c4b024f9f6af9a1b2..cc00654b6f9ada84e99f6eda6c35091b47a7854a 100644 (file)
@@ -89,10 +89,8 @@ ENTRY(FPU_u_div)
        movl    REGB,%ebx
        movl    DEST,%edi
 
-       movw    EXP(%esi),%dx
-       movw    EXP(%ebx),%ax
-       .byte   0x0f,0xbf,0xc0  /* movsx        %ax,%eax */
-       .byte   0x0f,0xbf,0xd2  /* movsx        %dx,%edx */
+       movswl  EXP(%esi),%edx
+       movswl  EXP(%ebx),%eax
        subl    %eax,%edx
        addl    EXP_BIAS,%edx
 
index 3422c0e563fd8486f427683901973492f35b3564..52472167fcf1ad501a8d10c3044c0a462486e679 100644 (file)
@@ -365,6 +365,11 @@ static void blk_init_free_list(request_queue_t *q)
         */
        for (i = 0; i < queue_nr_requests; i++) {
                rq = kmem_cache_alloc(request_cachep, SLAB_KERNEL);
+               if (rq == NULL) {
+                       /* We'll get a `leaked requests' message from blk_cleanup_queue */
+                       printk(KERN_EMERG "blk_init_free_list: error allocating requests\n");
+                       break;
+               }
                memset(rq, 0, sizeof(struct request));
                rq->rq_status = RQ_INACTIVE;
                list_add(&rq->table, &q->request_freelist[i & 1]);
index 503913bf32288b568c4e06da3c392cd7eec8a84e..35d57134497cf5fe191e0976d0a5351dae183b39 100644 (file)
@@ -466,7 +466,7 @@ MODULE_PARM_DESC(rd_blocksize, "Blocksize of each RAM disk in bytes.");
  *     romfs
  *     gzip
  */
-int __init 
+static int __init 
 identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)
 {
        const int size = 512;
index 0c8a7091be7608130d70d994584cbad23bfa3af2..44df530ec97e4a7d76e80ee90c6f3514384ce612 100644 (file)
@@ -42,8 +42,8 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       dep_tristate '  Multi-Tech multiport card support (EXPERIMENTAL)' CONFIG_ISI m
    fi
-   dep_tristate '  Microgate SyncLink card support' CONFIG_SYNCLINK m
-   dep_tristate '  HDLC line discipline support' CONFIG_N_HDLC m
+   tristate '  Microgate SyncLink card support' CONFIG_SYNCLINK
+   tristate '  HDLC line discipline support' CONFIG_N_HDLC
    tristate '  SDL RISCom/8 card support' CONFIG_RISCOM8
    tristate '  Specialix IO8+ card support' CONFIG_SPECIALIX
    if [ "$CONFIG_SPECIALIX" != "n" ]; then
@@ -60,6 +60,9 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
      tristate '    Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
    fi
 fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_ZORRO" = "y" ]; then
+   tristate 'Commodore A2232 serial support (EXPERIMENTAL)' CONFIG_A2232
+fi
 if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
    bool 'DC21285 serial port support' CONFIG_SERIAL_21285
    if [ "$CONFIG_SERIAL_21285" = "y" ]; then
@@ -135,6 +138,7 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
    tristate '  Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
    tristate '  Advantech SBC Watchdog Timer' CONFIG_ADVANTECH_WDT
    tristate '  SBC-60XX Watchdog Timer' CONFIG_60XX_WDT
+   tristate '  W83877F (EMACS) Watchdog Timer' CONFIG_W83877F_WDT
    tristate '  Mixcom Watchdog' CONFIG_MIXCOMWD 
    tristate '  Intel i810 TCO timer / Watchdog' CONFIG_I810_TCO
    if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
@@ -169,6 +173,9 @@ fi
 tristate 'Double Talk PC internal speech card support' CONFIG_DTLK
 tristate 'Siemens R3964 line discipline' CONFIG_R3964
 tristate 'Applicom intelligent fieldbus card support' CONFIG_APPLICOM
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Sony Vaio Programmable I/O Control Device support' CONFIG_SONYPI $CONFIG_PCI
+fi
 
 mainmenu_option next_comment
 comment 'Ftape, the floppy tape device driver'
index 6aa94907cc43917100e114ed4db21a66badd7709..1fa4054ed9c99e50859e3d5d804dc8e405377f06 100644 (file)
@@ -23,7 +23,7 @@ obj-y  += mem.o tty_io.o n_tty.o tty_ioctl.o raw.o pty.o misc.o random.o
 
 export-objs     :=     busmouse.o console.o keyboard.o sysrq.o \
                        misc.o pty.o random.o selection.o serial.o \
-                       tty_io.o
+                       sonypi.o tty_io.o tty_ioctl.o
 
 mod-subdirs    :=      joystick ftape drm pcmcia
 
@@ -48,6 +48,20 @@ ifeq ($(ARCH),s390x)
   SERIAL   =
 endif
 
+ifeq ($(ARCH),s390)
+  KEYMAP   =
+  KEYBD    =
+  CONSOLE  =
+  SERIAL   =
+endif
+
+ifeq ($(ARCH),s390x)
+  KEYMAP   =
+  KEYBD    =
+  CONSOLE  =
+  SERIAL   =
+endif
+
 ifeq ($(ARCH),m68k)
    ifdef CONFIG_AMIGA
       KEYBD = amikeyb.o
@@ -139,6 +153,7 @@ obj-$(CONFIG_SYNCLINK) += synclink.o
 obj-$(CONFIG_N_HDLC) += n_hdlc.o
 obj-$(CONFIG_SPECIALIX) += specialix.o
 obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
+obj-$(CONFIG_A2232) += ser_a2232.o generic_serial.o
 obj-$(CONFIG_SX) += sx.o generic_serial.o
 obj-$(CONFIG_RIO) += rio/rio.o generic_serial.o
 obj-$(CONFIG_SH_SCI) += sh-sci.o generic_serial.o
@@ -162,6 +177,7 @@ obj-$(CONFIG_BUSMOUSE) += busmouse.o
 obj-$(CONFIG_DTLK) += dtlk.o
 obj-$(CONFIG_R3964) += n_r3964.o
 obj-$(CONFIG_APPLICOM) += applicom.o
+obj-$(CONFIG_SONYPI) += sonypi.o
 obj-$(CONFIG_MS_BUSMOUSE) += msbusmouse.o
 obj-$(CONFIG_82C710_MOUSE) += qpmouse.o
 obj-$(CONFIG_AMIGAMOUSE) += amigamouse.o
index e24f0ce846510d55bae839c66086accada092b38..dbad0a536a5376843749150e62b992006587ee05 100644 (file)
@@ -128,7 +128,7 @@ static ssize_t dtlk_read(struct file *file, char *buf,
 {
        unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev);
        char ch;
-       int retval, i = 0, retries;
+       int i = 0, retries;
 
        /* Can't seek (pread) on the DoubleTalk.  */
        if (ppos != &file->f_pos)
@@ -144,8 +144,8 @@ static ssize_t dtlk_read(struct file *file, char *buf,
                while (i < count && dtlk_readable()) {
                        ch = dtlk_read_lpc();
                        /*        printk("dtlk_read() reads 0x%02x\n", ch); */
-                       if ((retval = put_user(ch, buf++)))
-                               return retval;
+                       if (put_user(ch, buf++))
+                               return -EFAULT;
                        i++;
                }
                if (i)
@@ -163,7 +163,7 @@ static ssize_t dtlk_read(struct file *file, char *buf,
 static ssize_t dtlk_write(struct file *file, const char *buf,
                          size_t count, loff_t * ppos)
 {
-       int i = 0, retries = 0, err, ch;
+       int i = 0, retries = 0, ch;
 
        TRACE_TEXT("(dtlk_write");
 #ifdef TRACING
@@ -171,7 +171,8 @@ static ssize_t dtlk_write(struct file *file, const char *buf,
        {
                int i, ch;
                for (i = 0; i < count; i++) {
-                       err = get_user(ch, buf + i);
+                       if (get_user(ch, buf + i))
+                               return -EFAULT;
                        if (' ' <= ch && ch <= '~')
                                printk("%c", ch);
                        else
@@ -189,7 +190,7 @@ static ssize_t dtlk_write(struct file *file, const char *buf,
                return -EINVAL;
 
        while (1) {
-               while (i < count && (err = get_user(ch, buf)) == 0 &&
+               while (i < count && !get_user(ch, buf) &&
                       (ch == DTLK_CLEAR || dtlk_writeable())) {
                        dtlk_write_tts(ch);
                        buf++;
@@ -279,7 +280,6 @@ static int dtlk_ioctl(struct inode *inode,
                      unsigned long arg)
 {
        struct dtlk_settings *sp;
-       int err;
        char portval;
        TRACE_TEXT(" dtlk_ioctl");
 
@@ -287,9 +287,8 @@ static int dtlk_ioctl(struct inode *inode,
 
        case DTLK_INTERROGATE:
                sp = dtlk_interrogate();
-               err = copy_to_user((char *) arg, (char *) sp,
-                                  sizeof(struct dtlk_settings));
-               if (err)
+               if (copy_to_user((char *) arg, (char *) sp,
+                                  sizeof(struct dtlk_settings)))
                        return -EINVAL;
                return 0;
 
index 3ee3026aa54bf45a50f8463a166586cab2943bd2..94876285307cfd8fb795dde9de6cf0c9d75ddfed 100644 (file)
@@ -14,6 +14,8 @@
  *    after patches by harald to irq code.  
  * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout
  *            field from "current" - somewhere between 2.1.121 and 2.1.131
+Qua Jun 27 15:02:26 BRT 2001
+ * [27-JUN-2001] Arnaldo Carvalho de Melo <acme@conectiva.com.br> - cleanups
  *  
  * Parts (C) 1999 David Airlie, airlied@linux.ie 
  * [07-SEP-99] Bugfixes 
@@ -835,7 +837,7 @@ static int get_serial_info (struct dz_serial *info, struct serial_struct *retinf
   tmp.close_delay = info->close_delay;
   tmp.closing_wait = info->closing_wait;
   
-  return copy_to_user (retinfo, &tmp, sizeof(*retinfo));
+  return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
 }
 
 static int set_serial_info (struct dz_serial *info, struct serial_struct *new_info)
@@ -919,9 +921,9 @@ static void send_break (struct dz_serial *info, int duration)
   restore_flags (flags);
 }
 
-static int dz_ioctl (struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+static int dz_ioctl (struct tty_struct *tty, struct file *file,
+                    unsigned int cmd, unsigned long arg)
 {
-  int error;
   struct dz_serial * info = (struct dz_serial *)tty->driver_data;
   int retval;
 
@@ -951,41 +953,26 @@ static int dz_ioctl (struct tty_struct *tty, struct file *file, unsigned int cmd
     return 0;
 
   case TIOCGSOFTCAR:
-    error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(long));
-    if (error)
-      return error;
-    put_user (C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg);
-    return 0;
+    return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg);
 
   case TIOCSSOFTCAR:
-    error = get_user (arg, (unsigned long *)arg);
-    if (error)
-      return error;
+    if (get_user (arg, (unsigned long *)arg))
+      return -EFAULT;
     tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0));
     return 0;
 
   case TIOCGSERIAL:
-    error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(struct serial_struct));
-    if (error)
-      return error;
     return get_serial_info (info, (struct serial_struct *)arg);
 
   case TIOCSSERIAL:
     return set_serial_info (info, (struct serial_struct *) arg);
 
   case TIOCSERGETLSR: /* Get line status register */
-    error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(unsigned int));
-    if (error)
-      return error;
-    else
-      return get_lsr_info (info, (unsigned int *)arg);
+    return get_lsr_info (info, (unsigned int *)arg);
 
   case TIOCSERGSTRUCT:
-    error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(struct dz_serial));
-    if (error)
-      return error;
-    copy_to_user((struct dz_serial *)arg, info, sizeof(struct dz_serial));
-    return 0;
+    return copy_to_user((struct dz_serial *)arg, info,
+                       sizeof(struct dz_serial)) ? -EFAULT : 0;
     
   default:
     return -ENOIOCTLCMD;
@@ -1436,7 +1423,7 @@ static void dz_console_put_char (unsigned char ch)
  * dz_console_print ()
  *
  * dz_console_print is registered for printk.
- * The console_lock must be held when we get here.
+ * The console must be locked when we get here.
  * ------------------------------------------------------------------- 
  */
 static void dz_console_print (struct console *cons, 
index acd3402f89c2429f2cb2f4948b4c8748a801123a..01099383cf61e1f171e832ae8d5388d54840bfe0 100644 (file)
@@ -344,7 +344,7 @@ static int gs_wait_tx_flushed (void * ptr, int timeout)
        struct gs_port *port = ptr;
        long end_jiffies;
        int jiffies_to_transmit, charsleft = 0, rv = 0;
-       int to, rcib;
+       int rcib;
 
        func_enter();
 
@@ -368,6 +368,7 @@ static int gs_wait_tx_flushed (void * ptr, int timeout)
                return rv;
        }
        /* stop trying: now + twice the time it would normally take +  seconds */
+       if (timeout == 0) timeout = MAX_SCHEDULE_TIMEOUT;
        end_jiffies  = jiffies; 
        if (timeout !=  MAX_SCHEDULE_TIMEOUT)
                end_jiffies += port->baud?(2 * rcib * 10 * HZ / port->baud):0;
@@ -376,11 +377,9 @@ static int gs_wait_tx_flushed (void * ptr, int timeout)
        gs_dprintk (GS_DEBUG_FLUSH, "now=%lx, end=%lx (%ld).\n", 
                    jiffies, end_jiffies, end_jiffies-jiffies); 
 
-       to = 100;
        /* the expression is actually jiffies < end_jiffies, but that won't
           work around the wraparound. Tricky eh? */
-       while (to-- &&
-              (charsleft = gs_real_chars_in_buffer (port->tty)) &&
+       while ((charsleft = gs_real_chars_in_buffer (port->tty)) &&
                time_after (end_jiffies, jiffies)) {
                /* Units check: 
                   chars * (bits/char) * (jiffies /sec) / (bits/sec) = jiffies!
@@ -1059,6 +1058,19 @@ void gs_getserial(struct gs_port *port, struct serial_struct *sp)
        copy_to_user(sp, &sio, sizeof(struct serial_struct));
 }
 
+
+void gs_got_break(struct gs_port *port)
+{
+       if (port->flags & ASYNC_SAK) {
+               do_SAK (port->tty);
+       }
+       *(port->tty->flip.flag_buf_ptr) = TTY_BREAK;
+       port->tty->flip.flag_buf_ptr++;
+       port->tty->flip.char_buf_ptr++;
+       port->tty->flip.count++;
+}
+
+
 EXPORT_SYMBOL(gs_put_char);
 EXPORT_SYMBOL(gs_write);
 EXPORT_SYMBOL(gs_write_room);
@@ -1075,4 +1087,4 @@ EXPORT_SYMBOL(gs_set_termios);
 EXPORT_SYMBOL(gs_init_port);
 EXPORT_SYMBOL(gs_setserial);
 EXPORT_SYMBOL(gs_getserial);
-
+EXPORT_SYMBOL(gs_got_break);
index 8a4b4de5120edec2e91d5deef14a869db9273605..14eda87b3c72945d6b803916ad3ff0ae5adde031 100644 (file)
@@ -54,9 +54,9 @@
 /*
  * Forward declarations.
  */
-static int   h8_init(void);
-int          h8_display_blank(void);
-int          h8_display_unblank(void);
+static int  h8_init(void);
+static int  h8_display_blank(void);
+static int  h8_display_unblank(void);
 
 static void  h8_intr(int irq, void *dev_id, struct pt_regs *regs);
 
@@ -106,10 +106,10 @@ static int h8_monitor_timer_active = 0;
 
 static char  driver_version[] = "X0.0";/* no spaces */
 
-union  intr_buf intrbuf;
-int    intr_buf_ptr;
-union   intr_buf xx;   
-u_char  last_temp;
+static union   intr_buf intrbuf;
+static int     intr_buf_ptr;
+static union   intr_buf xx;    
+static u_char  last_temp;
 
 /*
  * I/O Macros for register reads and writes.
@@ -122,40 +122,40 @@ u_char  last_temp;
 #define WRITE_DATA(d)  H8_WRITE((d), h8_base + H8_DATA_REG_OFF)
 #define WRITE_CMD(d)   H8_WRITE((d), h8_base + H8_CMD_REG_OFF)
 
-unsigned int h8_base = H8_BASE_ADDR;
-unsigned int h8_irq = H8_IRQ;
-unsigned int h8_state = H8_IDLE;
-unsigned int h8_index = -1;
-unsigned int h8_enabled = 0;
+static unsigned int h8_base = H8_BASE_ADDR;
+static unsigned int h8_irq = H8_IRQ;
+static unsigned int h8_state = H8_IDLE;
+static unsigned int h8_index = -1;
+static unsigned int h8_enabled = 0;
 
-LIST_HEAD(h8_actq);
-LIST_HEAD(h8_cmdq);
-LIST_HEAD(h8_freeq);
+static LIST_HEAD(h8_actq);
+static LIST_HEAD(h8_cmdq);
+static LIST_HEAD(h8_freeq);
 
 /* 
  * Globals used in thermal control of Alphabook1.
  */
-int cpu_speed_divisor = -1;                    
-int h8_event_mask = 0;                 
-DECLARE_WAIT_QUEUE_HEAD(h8_monitor_wait);
-unsigned int h8_command_mask = 0;
-int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD;
-int h8_uthermal_window = UTH_HYSTERESIS;                     
-int h8_debug = 0xfffffdfc;
-int h8_ldamp = MHZ_115;
-int h8_udamp = MHZ_57;
-u_char h8_current_temp = 0;
-u_char h8_system_temp = 0;
-int h8_sync_channel = 0;
-DECLARE_WAIT_QUEUE_HEAD(h8_sync_wait);
-int h8_init_performed;
+static int cpu_speed_divisor = -1;                     
+static int h8_event_mask = 0;                  
+static DECLARE_WAIT_QUEUE_HEAD(h8_monitor_wait);
+static unsigned int h8_command_mask = 0;
+static int h8_uthermal_threshold = DEFAULT_UTHERMAL_THRESHOLD;
+static int h8_uthermal_window = UTH_HYSTERESIS;                      
+static int h8_debug = 0xfffffdfc;
+static int h8_ldamp = MHZ_115;
+static int h8_udamp = MHZ_57;
+static u_char h8_current_temp = 0;
+static u_char h8_system_temp = 0;
+static int h8_sync_channel = 0;
+static DECLARE_WAIT_QUEUE_HEAD(h8_sync_wait);
+static int h8_init_performed;
 
 /* CPU speeds and clock divisor values */
-int speed_tab[6] = {230, 153, 115, 57, 28, 14};
+static int speed_tab[6] = {230, 153, 115, 57, 28, 14};
   
 /*
  * H8 interrupt handler
- */
 */
 static void h8_intr(int irq, void *dev_id, struct pt_regs *regs)
 {
        u_char  stat_reg, data_reg;
@@ -377,7 +377,7 @@ static int h8_get_info(char *buf, char **start, off_t fpos, int length)
 }
 
 /* Called from console driver -- must make sure h8_enabled. */
-int h8_display_blank(void)
+static int h8_display_blank(void)
 {
 #ifdef CONFIG_H8_DISPLAY_BLANK
         int     error;
@@ -393,7 +393,7 @@ int h8_display_blank(void)
 }
 
 /* Called from console driver -- must make sure h8_enabled. */
-int h8_display_unblank(void)
+static int h8_display_unblank(void)
 {
 #ifdef CONFIG_H8_DISPLAY_BLANK
         int error;
@@ -408,8 +408,7 @@ int h8_display_unblank(void)
         return 0;
 }
 
-int
-h8_alloc_queues(void)
+static int h8_alloc_queues(void)
 {
         h8_cmd_q_t *qp;
        unsigned long flags;
@@ -419,7 +418,7 @@ h8_alloc_queues(void)
                                   GFP_KERNEL);
 
         if (!qp) {
-                printk("H8: could not allocate memory for command queue\n");
+                printk(KERN_ERR "H8: could not allocate memory for command queue\n");
                 return(0);
         }
         /* add to the free queue */
@@ -560,14 +559,14 @@ h8_read_event_status(void)
 {
 
         if(h8_debug & 0x200)
-                printk("h8_read_event_status: value 0x%x\n", intrbuf.word);
+                printk(KERN_DEBUG "h8_read_event_status: value 0x%x\n", intrbuf.word);
 
         /*
          * Power related items
          */
         if (intrbuf.word & H8_DC_CHANGE) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: DC_CHANGE\n");
+                   printk(KERN_DEBUG "h8_read_event_status: DC_CHANGE\n");
                 /* see if dc added or removed, set batt/dc flag, send event */
 
                 h8_set_event_mask(H8_MANAGE_BATTERY);
@@ -575,7 +574,7 @@ h8_read_event_status(void)
         }
 
         if (intrbuf.word & H8_POWER_BUTTON) {
-                printk("Power switch pressed - please wait - preparing to power 
+                printk(KERN_CRIT "Power switch pressed - please wait - preparing to power 
 off\n");
                 h8_set_event_mask(H8_POWER_BUTTON);
                 wake_up(&h8_monitor_wait);
@@ -586,7 +585,7 @@ off\n");
          */
         if (intrbuf.word & H8_THERMAL_THRESHOLD) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: THERMAL_THRESHOLD\n");
+                   printk(KERN_DEBUG "h8_read_event_status: THERMAL_THRESHOLD\n");
                 h8_set_event_mask(H8_MANAGE_UTHERM);
                 wake_up(&h8_monitor_wait);
         }
@@ -596,67 +595,67 @@ off\n");
          */
         if (intrbuf.word & H8_DOCKING_STATION_STATUS) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: DOCKING_STATION_STATUS\n");
+                   printk(KERN_DEBUG "h8_read_event_status: DOCKING_STATION_STATUS\n");
                 /* read_ext_status */
         }
         if (intrbuf.word & H8_EXT_BATT_STATUS) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: EXT_BATT_STATUS\n");
+                   printk(KERN_DEBUG "h8_read_event_status: EXT_BATT_STATUS\n");
 
         }
         if (intrbuf.word & H8_EXT_BATT_CHARGE_STATE) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: EXT_BATT_CHARGE_STATE\n");
+                   printk(KERN_DEBUG "h8_read_event_status: EXT_BATT_CHARGE_STATE\n");
 
         }
         if (intrbuf.word & H8_BATT_CHANGE_OVER) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: BATT_CHANGE_OVER\n");
+                   printk(KERN_DEBUG "h8_read_event_status: BATT_CHANGE_OVER\n");
 
         }
         if (intrbuf.word & H8_WATCHDOG) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: WATCHDOG\n");
+                   printk(KERN_DEBUG "h8_read_event_status: WATCHDOG\n");
                 /* nop */
         }
         if (intrbuf.word & H8_SHUTDOWN) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: SHUTDOWN\n");
+                   printk(KERN_DEBUG "h8_read_event_status: SHUTDOWN\n");
                 /* nop */
         }
         if (intrbuf.word & H8_KEYBOARD) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: KEYBOARD\n");
+                   printk(KERN_DEBUG "h8_read_event_status: KEYBOARD\n");
                 /* nop */
         }
         if (intrbuf.word & H8_EXT_MOUSE_OR_CASE_SWITCH) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n");
+                   printk(KERN_DEBUG "h8_read_event_status: EXT_MOUSE_OR_CASE_SWITCH\n");
                 /* read_ext_status*/
         }
         if (intrbuf.word & H8_INT_BATT_LOW) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: INT_BATT_LOW\n");
-                /* post event, warn user */
+                   printk(KERN_DEBUG "h8_read_event_status: INT_BATT_LOW\n"); post
+                /* event, warn user */
         }
         if (intrbuf.word & H8_INT_BATT_CHARGE_STATE) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: INT_BATT_CHARGE_STATE\n");
+                   printk(KERN_DEBUG "h8_read_event_status: INT_BATT_CHARGE_STATE\n");
                 /* nop - happens often */
         }
         if (intrbuf.word & H8_INT_BATT_STATUS) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: INT_BATT_STATUS\n");
+                   printk(KERN_DEBUG "h8_read_event_status: INT_BATT_STATUS\n");
 
         }
         if (intrbuf.word & H8_INT_BATT_CHARGE_THRESHOLD) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n");
+                   printk(KERN_DEBUG "h8_read_event_status: INT_BATT_CHARGE_THRESHOLD\n");
                 /* nop - happens often */
         }
         if (intrbuf.word & H8_EXT_BATT_LOW) {
                if(h8_debug & 0x4)
-                   printk("h8_read_event_status: EXT_BATT_LOW\n");
+                   printk(KERN_DEBUG "h8_read_event_status: EXT_BATT_LOW\n");
                 /*if no internal, post event, warn user */
                 /* else nop */
         }
@@ -667,7 +666,7 @@ off\n");
 /*
  * Function called when H8 has performed requested command.
  */
-void
+static void
 h8_cmd_done(h8_cmd_q_t *qp)
 {
 
@@ -675,14 +674,14 @@ h8_cmd_done(h8_cmd_q_t *qp)
         switch (qp->cmdbuf[0]) {
        case H8_SYNC:
            if (h8_debug & 0x40000) 
-               printk("H8: Sync command done - byte returned was 0x%x\n", 
+               printk(KERN_DEBUG "H8: Sync command done - byte returned was 0x%x\n", 
                       qp->rcvbuf[0]);
            list_add(&qp->link, &h8_freeq);
            break;
 
        case H8_RD_SN:
        case H8_RD_ENET_ADDR:
-           printk("H8: read Ethernet address: command done - address: %x - %x - %x - %x - %x - %x \n", 
+           printk(KERN_DEBUG "H8: read Ethernet address: command done - address: %x - %x - %x - %x - %x - %x \n", 
                   qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2],
                   qp->rcvbuf[3], qp->rcvbuf[4], qp->rcvbuf[5]);
            list_add(&qp->link, &h8_freeq);
@@ -691,13 +690,13 @@ h8_cmd_done(h8_cmd_q_t *qp)
        case H8_RD_HW_VER:
        case H8_RD_MIC_VER:
        case H8_RD_MAX_TEMP:
-           printk("H8: Max recorded CPU temp %d, Sys temp %d\n",
+           printk(KERN_DEBUG "H8: Max recorded CPU temp %d, Sys temp %d\n",
                   qp->rcvbuf[0], qp->rcvbuf[1]);
            list_add(&qp->link, &h8_freeq);
            break;
 
        case H8_RD_MIN_TEMP:
-           printk("H8: Min recorded CPU temp %d, Sys temp %d\n",
+           printk(KERN_DEBUG "H8: Min recorded CPU temp %d, Sys temp %d\n",
                   qp->rcvbuf[0], qp->rcvbuf[1]);
            list_add(&qp->link, &h8_freeq);
            break;
@@ -712,11 +711,11 @@ h8_cmd_done(h8_cmd_q_t *qp)
 
        case H8_RD_SYS_VARIENT:
        case H8_RD_PWR_ON_CYCLES:
-           printk(" H8: RD_PWR_ON_CYCLES command done\n");
+           printk(KERN_DEBUG " H8: RD_PWR_ON_CYCLES command done\n");
            break;
 
        case H8_RD_PWR_ON_SECS:
-           printk("H8: RD_PWR_ON_SECS command done\n");
+           printk(KERN_DEBUG "H8: RD_PWR_ON_SECS command done\n");
            break;
 
        case H8_RD_RESET_STATUS:
@@ -741,7 +740,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
        case H8_RD_NEW_BUSY_SPEED:
        case H8_RD_CONFIG_INTERFACE:
        case H8_RD_INT_BATT_STATUS:
-           printk("H8: Read int batt status cmd done - returned was %x %x %x\n",
+           printk(KERN_DEBUG "H8: Read int batt status cmd done - returned was %x %x %x\n",
                   qp->rcvbuf[0], qp->rcvbuf[1], qp->rcvbuf[2]);
            list_add(&qp->link, &h8_freeq);
            break;
@@ -752,7 +751,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
        case H8_CTL_EMU_BITPORT:
        case H8_DEVICE_CONTROL:
            if(h8_debug & 0x20000) {
-               printk("H8: Device control cmd done - byte returned was 0x%x\n",
+               printk(KERN_DEBUG "H8: Device control cmd done - byte returned was 0x%x\n",
                       qp->rcvbuf[0]);
            }
            list_add(&qp->link, &h8_freeq);
@@ -767,7 +766,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
        case H8_CTL_MOUSE_SENSITIVITY:
        case H8_CTL_DIAG_MODE:
        case H8_CTL_IDLE_AND_BUSY_SPDS:
-           printk("H8: Idle and busy speed command done\n");
+           printk(KERN_DEBUG "H8: Idle and busy speed command done\n");
            break;
 
        case H8_CTL_TFT_BRT_BATT:
@@ -808,7 +807,7 @@ h8_cmd_done(h8_cmd_q_t *qp)
        case H8_BQ_RD_REG:
        case H8_BQ_WRT_REG:
        case H8_PWR_OFF:
-           printk ("H8: misc command completed\n");
+           printk (KERN_DEBUG "H8: misc command completed\n");
            break;
         }
         return;
@@ -948,7 +947,7 @@ h8_monitor_thread(void * unused)
          */
         h8_get_curr_temp(curr_temp);
 
-        printk("H8: Initial CPU temp: %d\n", curr_temp[0]);
+        printk(KERN_INFO "H8: Initial CPU temp: %d\n", curr_temp[0]);
 
         if(curr_temp[0] >= h8_uthermal_threshold) {
                 h8_set_event_mask(H8_MANAGE_UTHERM);
@@ -965,7 +964,7 @@ h8_monitor_thread(void * unused)
                sleep_on(&h8_monitor_wait);
 
                 if(h8_debug & 0x2)
-                        printk("h8_monitor_thread awakened, mask:%x\n",
+                        printk(KERN_DEBUG "h8_monitor_thread awakened, mask:%x\n",
                                 h8_event_mask);
 
                 if (h8_event_mask & (H8_MANAGE_UTHERM|H8_MANAGE_LTHERM)) {
@@ -1008,7 +1007,7 @@ h8_manage_therm(void)
         if(h8_event_mask & H8_MANAGE_UTHERM) {
                /* Upper thermal interrupt received, need to cool down. */
                if(h8_debug & 0x10)
-                        printk("H8: Thermal threshold %d F reached\n",
+                        printk(KERN_WARNING "H8: Thermal threshold %d F reached\n",
                               h8_uthermal_threshold);
                h8_set_cpu_speed(h8_udamp); 
                 h8_clear_event_mask(H8_MANAGE_UTHERM);
@@ -1027,7 +1026,7 @@ h8_manage_therm(void)
                         h8_set_upper_therm_thold(h8_uthermal_threshold);
                         h8_set_cpu_speed(h8_ldamp); /* adjustable */ 
                         if(h8_debug & 0x10)
-                            printk("H8: CPU cool, applying cpu_divisor: %d \n",
+                            printk(KERN_WARNING "H8: CPU cool, applying cpu_divisor: %d \n",
                                   h8_ldamp);
                         h8_clear_event_mask(H8_MANAGE_LTHERM);
                 }
@@ -1082,7 +1081,7 @@ h8_set_cpu_speed(int speed_divisor)
 #endif /* NOT_YET */
 
         if(h8_debug & 0x8)
-                printk("H8: Setting CPU speed to %d MHz\n",
+                printk(KERN_DEBUG "H8: Setting CPU speed to %d MHz\n",
                       speed_tab[speed_divisor]); 
 
          /* Make the actual speed change */
@@ -1125,7 +1124,7 @@ h8_get_cpu_speed(void)
                 break;
         }
         if(h8_debug & 0x8)
-                printk("H8: CPU speed current setting: %d MHz\n", speed); 
+                printk(KERN_DEBUG "H8: CPU speed current setting: %d MHz\n", speed); 
 #endif  /* NOT_YET */
        return speed;
 }
index 77fd8543efc3dd0f972b0fbc5c352cb20264c0f6..ce42166318860c39729a48708ebcf836769993e9 100644 (file)
@@ -139,7 +139,7 @@ static UCHAR ct79[] = { 2, BYP,     0x4F,0                   }; // XMIT_NOW
 //static UCHAR ct86[]={ 2, BTH,     0x56,0                   }; // RCV_ENABLE
 static UCHAR ct87[] = { 1, BYP,     0x57                     }; // HW_TEST
 //static UCHAR ct88[]={ 3, BTH,     0x58,0,0                 }; // RCV_THRESHOLD
-static UCHAR ct89[]={ 1, BYP,     0x59                     }; // DSS_NOW
+//static UCHAR ct89[]={ 1, BYP,     0x59                     }; // DSS_NOW
 //static UCHAR ct90[]={ 3, BYP,     0x5A,0,0                 }; // Set SILO
 //static UCHAR ct91[]={ 2, BYP,     0x5B,0                   }; // timed break
 
index b1dbb359491901e4f4070a286a7323ae8a6962d1..9050eec10cd9ce02ee5ee20655c24783f1f3203b 100644 (file)
@@ -124,6 +124,7 @@ static int zf_action = GEN_RESET;
 static int zf_is_open = 0;
 static int zf_expect_close = 0;
 static spinlock_t zf_lock;
+static spinlock_t zf_port_lock;
 static struct timer_list zf_timer;
 static unsigned long next_heartbeat = 0;
 
@@ -140,7 +141,7 @@ static unsigned long next_heartbeat = 0;
 #ifndef ZF_DEBUG
 #      define dprintk(format, args...)
 #else
-#      define dprintk(format, args...) printk(KERN_DEBUG PFX ":" __FUNCTION__ ":%d: " format, __LINE__ , ## args)
+#      define dprintk(format, args...) printk(KERN_DEBUG PFX; ":" __FUNCTION__ ":%d: " format, __LINE__ , ## args)
 #endif
 
 
@@ -175,7 +176,7 @@ static inline void zf_set_control(unsigned short new)
  *     Just get current counter value
  */
 
-inline unsigned short zf_get_timer(unsigned char n)
+static inline unsigned short zf_get_timer(unsigned char n)
 {
        switch(n){
                case WD1:
@@ -209,17 +210,20 @@ static inline void zf_set_timer(unsigned short new, unsigned char n)
 static void zf_timer_off(void)
 {
        unsigned int ctrl_reg = 0;
+       unsigned long flags;
 
        /* stop internal ping */
-       del_timer(&zf_timer);
+       del_timer_sync(&zf_timer);
 
+       spin_lock_irqsave(&zf_port_lock, flags);
        /* stop watchdog timer */       
        ctrl_reg = zf_get_control();
        ctrl_reg |= (ENABLE_WD1|ENABLE_WD2);    /* disable wd1 and wd2 */
        ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2);
        zf_set_control(ctrl_reg);
+       spin_unlock_irqrestore(&zf_port_lock, flags);
 
-       printk(PFX ": Watchdog timer is now disabled\n");
+       printk(KERN_INFO PFX ": Watchdog timer is now disabled\n");
 }
 
 
@@ -229,6 +233,9 @@ static void zf_timer_off(void)
 static void zf_timer_on(void)
 {
        unsigned int ctrl_reg = 0;
+       unsigned long flags;
+       
+       spin_lock_irqsave(&zf_port_lock, flags);
 
        zf_writeb(PULSE_LEN, 0xff);
 
@@ -246,15 +253,17 @@ static void zf_timer_on(void)
        ctrl_reg = zf_get_control();
        ctrl_reg |= (ENABLE_WD1|zf_action);
        zf_set_control(ctrl_reg);
+       spin_unlock_irqrestore(&zf_port_lock, flags);
 
-       printk(PFX ": Watchdog timer is now enabled\n");
+       printk(KERN_INFO PFX ": Watchdog timer is now enabled\n");
 }
 
 
 static void zf_ping(unsigned long data)
 {
        unsigned int ctrl_reg = 0;
-
+       unsigned long flags;
+               
        zf_writeb(COUNTER_2, 0xff);
 
        if(time_before(jiffies, next_heartbeat)){
@@ -265,6 +274,8 @@ static void zf_ping(unsigned long data)
                 * reset event is activated by transition from 0 to 1 on
                 * RESET_WD1 bit and we assume that it is already zero...
                 */
+
+               spin_lock_irqsave(&zf_port_lock, flags);
                ctrl_reg = zf_get_control();    
                ctrl_reg |= RESET_WD1;          
                zf_set_control(ctrl_reg);       
@@ -272,11 +283,12 @@ static void zf_ping(unsigned long data)
                /* ...and nothing changes until here */
                ctrl_reg &= ~(RESET_WD1);
                zf_set_control(ctrl_reg);               
+               spin_unlock_irqrestore(&zf_port_lock, flags);
 
                zf_timer.expires = jiffies + ZF_HW_TIMEO;
                add_timer(&zf_timer);
        }else{
-               printk(PFX ": I will reset your machine\n");
+               printk(KERN_CRIT PFX ": I will reset your machine\n");
        }
 }
 
@@ -396,7 +408,7 @@ static int zf_close(struct inode *inode, struct file *file)
                        zf_timer_off();
                } else {
                        del_timer(&zf_timer);
-                       printk(PFX ": device file closed unexpectedly. Will not stop the WDT!\n");
+                       printk(KERN_ERR PFX ": device file closed unexpectedly. Will not stop the WDT!\n");
                }
                
                spin_lock(&zf_lock);
@@ -427,9 +439,7 @@ static int zf_notify_sys(struct notifier_block *this, unsigned long code,
 
 
 static struct file_operations zf_fops = {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,34)
        owner:          THIS_MODULE,
-#endif
        read:           zf_read,
        write:          zf_write,
        ioctl:          zf_ioctl,
@@ -458,19 +468,19 @@ static void __init zf_show_action(int act)
 {
        char *str[] = { "RESET", "SMI", "NMI", "SCI" };
        
-       printk(PFX ": Watchdog using action = %s\n", str[act]);
+       printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]);
 }
 
-int __init zf_init(void)
+static int __init zf_init(void)
 {
        int ret;
        
-       printk(PFX ": MachZ ZF-Logic Watchdog driver initializing.\n");
+       printk(KERN_INFO PFX ": MachZ ZF-Logic Watchdog driver initializing.\n");
 
        ret = zf_get_ZFL_version();
        printk("%#x\n", ret);
        if((!ret) || (ret != 0xffff)){
-               printk(PFX ": no ZF-Logic found\n");
+               printk(KERN_WARNING PFX ": no ZF-Logic found\n");
                return -ENODEV;
        }
 
@@ -482,6 +492,7 @@ int __init zf_init(void)
        zf_show_action(action);
 
        spin_lock_init(&zf_lock);
+       spin_lock_init(&zf_port_lock);
        
        ret = misc_register(&zf_miscdev);
        if (ret){
@@ -490,23 +501,13 @@ int __init zf_init(void)
                goto out;
        }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,3)
-       if(check_region(ZF_IOBASE, 3)){
-#else
        if(!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")){
-#endif
-
                printk(KERN_ERR "cannot reserve I/O ports at %d\n",
                                                        ZF_IOBASE);
                ret = -EBUSY;
                goto no_region;
        }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,3)
-       request_region(ZF_IOBASE, 3, "MachZ ZFL WDT");
-#define __exit
-#endif
-
        ret = register_reboot_notifier(&zf_notifier);
        if(ret){
                printk(KERN_ERR "can't register reboot notifier (err=%d)\n",
index 027ed59156d303ddde1f432c124f1aa384af72e7..49b220a9ed07652a477784dbd4fc0136559e876d 100644 (file)
@@ -2916,6 +2916,8 @@ static int moxaload320b(int cardno, unsigned char * tmp, int len)
        unsigned long baseAddr;
        int i;
 
+       if(len > sizeof(moxaBuff))
+               return -EINVAL;
        if(copy_from_user(moxaBuff, tmp, len))
                return -EFAULT;
        baseAddr = moxaBaseAddr[cardno];
index ca1649f1efb386d5862cbdc01699732d2c0e4f40..beb075780fa5ec115c695225cd52d815fa51d1f8 100644 (file)
  *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <asm/hardirq.h>
+
 
 #define disable(oldspl) save_flags (oldspl)
 #define restore(oldspl) restore_flags (oldspl)
 
-#define sysbrk(x) kmalloc ((x), GFP_KERNEL)
+#define sysbrk(x) kmalloc ((x),in_interrupt()? GFP_ATOMIC : GFP_KERNEL)
 #define sysfree(p,size) kfree ((p))
 
 #define WBYTE(p,v) writeb(v, &p)
index 00c2d3c5bf889bc27f4bd5b0bb9045954d1c0baa..5e4741556c9f103dcd5112cc68ddd70b30117ce3 100644 (file)
@@ -58,6 +58,7 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/miscdevice.h>
+#include <linux/init.h>
 
 #include <linux/compatmac.h>
 #include <linux/generic_serial.h>
@@ -165,7 +166,7 @@ RIOConf =
   /* startuptime */     HZ*2,           /* how long to wait for card to run */
   /* slowcook */        0,              /* TRUE -> always use line disc. */
   /* intrpolltime */    1,              /* The frequency of OUR polls */
-  /* breakinterval */   25,             /* x10 mS */
+  /* breakinterval */   25,             /* x10 mS XXX: units seem to be 1ms not 10! -- REW*/
   /* timer */           10,             /* mS */
   /* RtaLoadBase */     0x7000,
   /* HostLoadBase */    0x7C00,
@@ -203,11 +204,8 @@ static int rio_fw_ioctl (struct inode *inode, struct file *filp,
                         unsigned int cmd, unsigned long arg);
 static int rio_init_drivers(void);
 
-
 void my_hd (void *addr, int len);
 
-
-
 static struct tty_driver rio_driver, rio_callout_driver;
 static struct tty_driver rio_driver2, rio_callout_driver2;
 
@@ -248,14 +246,12 @@ int rio_probe_addrs[]= {0xc0000, 0xd0000, 0xe0000};
 long rio_irqmask = -1;
 
 #ifndef TWO_ZERO
-#ifdef MODULE
 MODULE_AUTHOR("Rogier Wolff <R.E.Wolff@bitwizard.nl>, Patrick van de Lageweg <patrick@bitwizard.nl>");
 MODULE_DESCRIPTION("RIO driver");
 MODULE_PARM(rio_poll, "i");
 MODULE_PARM(rio_debug, "i");
 MODULE_PARM(rio_irqmask, "i");
 #endif
-#endif
 
 static struct real_driver rio_real_driver = {
   rio_disable_tx_interrupts,
@@ -383,8 +379,8 @@ int rio_minor (kdev_t device)
 
 int rio_ismodem (kdev_t device)
 {
-  return (MAJOR (device) != RIO_NORMAL_MAJOR0) &&
-         (MAJOR (device) != RIO_NORMAL_MAJOR1);
+  return (MAJOR (device) == RIO_NORMAL_MAJOR0) ||
+         (MAJOR (device) == RIO_NORMAL_MAJOR1);
 }
 
 
@@ -455,7 +451,6 @@ static void rio_interrupt (int irq, void *ptr, struct pt_regs *regs)
   func_enter ();
 
   HostP = (struct Host*)ptr; /* &p->RIOHosts[(long)ptr]; */
-  
   rio_dprintk (RIO_DEBUG_IFLOW, "rio: enter rio_interrupt (%d/%d)\n", 
                irq, HostP->Ivec); 
 
@@ -624,8 +619,12 @@ static int rio_chars_in_buffer (void * ptr)
 /* Nothing special here... */
 static void rio_shutdown_port (void * ptr) 
 {
+  struct Port *PortP;
+
   func_enter();
 
+  PortP = (struct Port *)ptr;
+  PortP->gs.tty = NULL;
 #if 0
   port->gs.flags &= ~ GS_ACTIVE;
   if (!port->gs.tty) {
@@ -654,8 +653,14 @@ static void rio_shutdown_port (void * ptr)
    exit minicom.  I expect an "oops".  -- REW */
 static void rio_hungup (void *ptr)
 {
-  func_enter ();
+  struct Port *PortP;
+
+  func_enter();
+  
+  PortP = (struct Port *)ptr;
+  PortP->gs.tty = NULL;
   rio_dec_mod_count (); 
+
   func_exit ();
 }
 
@@ -679,9 +684,8 @@ static void rio_close (void *ptr)
     PortP->gs.count = 0; 
   }                
 
-
+  PortP->gs.tty = NULL;
   rio_dec_mod_count ();
-
   func_exit ();
 }
 
@@ -700,24 +704,28 @@ static int rio_fw_ioctl (struct inode *inode, struct file *filp,
   return rc;
 }
 
+extern int RIOShortCommand(struct rio_info *p, struct Port *PortP,
+               int command, int len, int arg);
 
 static int rio_ioctl (struct tty_struct * tty, struct file * filp, 
                      unsigned int cmd, unsigned long arg)
 {
-#if 0
   int rc;
-  struct rio_port *port = tty->driver_data;
+  struct Port *PortP;
   int ival;
 
-  /* func_enter2(); */
+  func_enter();
 
+  PortP = (struct Port *)tty->driver_data;
 
   rc  = 0;
   switch (cmd) {
+#if 0
   case TIOCGSOFTCAR:
     rc = Put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0),
                   (unsigned int *) arg);
     break;
+#endif
   case TIOCSSOFTCAR:
     if ((rc = verify_area(VERIFY_READ, (void *) arg,
                           sizeof(int))) == 0) {
@@ -730,13 +738,39 @@ static int rio_ioctl (struct tty_struct * tty, struct file * filp,
   case TIOCGSERIAL:
     if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
                           sizeof(struct serial_struct))) == 0)
-      gs_getserial(&port->gs, (struct serial_struct *) arg);
+      gs_getserial(&PortP->gs, (struct serial_struct *) arg);
+    break;
+  case TCSBRK:
+    if ( PortP->State & RIO_DELETED ) {
+      rio_dprintk (RIO_DEBUG_TTY, "BREAK on deleted RTA\n");
+      rc = -EIO;      
+    } else {
+      if (RIOShortCommand(p, PortP, SBREAK, 2, 250) == RIO_FAIL) {
+         rio_dprintk (RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n");
+         rc = -EIO;
+      }          
+    }
+    break;
+  case TCSBRKP:
+    if ( PortP->State & RIO_DELETED ) {
+      rio_dprintk (RIO_DEBUG_TTY, "BREAK on deleted RTA\n");
+      rc = -EIO;      
+    } else {
+      int l;
+      l = arg?arg*100:250;
+      if (l > 255) l = 255;
+      if (RIOShortCommand(p, PortP, SBREAK, 2, arg?arg*100:250) == RIO_FAIL) {
+         rio_dprintk (RIO_DEBUG_INTR, "SBREAK RIOShortCommand failed\n");
+         rc = -EIO;
+      }          
+    }
     break;
   case TIOCSSERIAL:
     if ((rc = verify_area(VERIFY_READ, (void *) arg,
                           sizeof(struct serial_struct))) == 0)
-      rc = gs_setserial(&port->gs, (struct serial_struct *) arg);
+      rc = gs_setserial(&PortP->gs, (struct serial_struct *) arg);
     break;
+#if 0
   case TIOCMGET:
     if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
                           sizeof(unsigned int))) == 0) {
@@ -768,17 +802,13 @@ static int rio_ioctl (struct tty_struct * tty, struct file * filp,
                            ((ival & TIOCM_RTS) ? 1 : 0));
     }
     break;
-
+#endif
   default:
     rc = -ENOIOCTLCMD;
     break;
   }
-  /* func_exit(); */
+  func_exit();
   return rc;
-#else
-  return -ENOIOCTLCMD;
-#endif
-
 }
 
 
@@ -1029,17 +1059,15 @@ static int rio_init_datastructures (void)
  free4:kfree (rio_termios);
  free3:kfree (p->RIOPortp);
  free2:kfree (p->RIOHosts);
- free1:kfree (p);
- free0:
-  if (p) {
-       rio_dprintk (RIO_DEBUG_INIT, "Not enough memory! %p %p %p %p %p\n", 
+ free1:
+  rio_dprintk (RIO_DEBUG_INIT, "Not enough memory! %p %p %p %p %p\n", 
                       p, p->RIOHosts, p->RIOPortp, rio_termios, rio_termios);
-  }
+  kfree(p);                  
+ free0:
   return -ENOMEM;
 }
 
-#ifdef MODULE
-static void rio_release_drivers(void)
+static void  __exit rio_release_drivers(void)
 {
   func_enter();
   tty_unregister_driver (&rio_callout_driver2);
@@ -1048,7 +1076,6 @@ static void rio_release_drivers(void)
   tty_unregister_driver (&rio_driver);
   func_exit();
 }
-#endif 
 
 #ifdef TWO_ZERO
 #define PDEV unsigned char pci_bus, unsigned pci_fun
@@ -1101,11 +1128,7 @@ void fix_rio_pci (PDEV)
 #endif
 
 
-#ifdef MODULE
-#define rio_init init_module
-#endif
-
-int rio_init(void) 
+static int __init rio_init(void) 
 {
   int found = 0;
   int i;
@@ -1255,6 +1278,7 @@ int rio_init(void)
       hp->Type  = RIO_PCI;
       hp->Copy  = rio_pcicopy;
       hp->Mode  = RIO_PCI_BOOT_FROM_RAM;
+      hp->HostLock = SPIN_LOCK_UNLOCKED;
 
       rio_dprintk (RIO_DEBUG_PROBE, "Ivec: %x\n", hp->Ivec);
       rio_dprintk (RIO_DEBUG_PROBE, "Mode: %x\n", hp->Mode);
@@ -1310,6 +1334,7 @@ int rio_init(void)
                              * Moreover, the ISA card will work with the 
                              * special PCI copy anyway. -- REW */
     hp->Mode = 0;
+    hp->HostLock = SPIN_LOCK_UNLOCKED;
 
     vpdp = get_VPD_PROM (hp);
     rio_dprintk (RIO_DEBUG_PROBE, "Got VPD ROM\n");
@@ -1388,8 +1413,7 @@ int rio_init(void)
 }
 
 
-#ifdef MODULE
-void cleanup_module(void)
+static void __exit rio_exit (void)
 {
   int i; 
   struct Host *hp;
@@ -1424,9 +1448,9 @@ void cleanup_module(void)
 
   func_exit();
 }
-#endif
-
 
+module_init(rio_init);
+module_exit(rio_exit);
 
 /*
  * Anybody who knows why this doesn't work for me, please tell me -- REW.
index 4242197e5e226a023ae5c65349f2f524cf02b488..23d8951c6ae041585ab98c32092b6a7aac29c7b5 100644 (file)
@@ -95,28 +95,27 @@ void rio_inc_mod_count (void);
 #if 1
 #define rio_spin_lock_irqsave(sem, flags) do { \
        rio_dprintk (RIO_DEBUG_SPINLOCK, "spinlockirqsave: %p %s:%d\n", \
-                                       sem, __FILE__, __LINE__);\
-        spin_lock_irqsave(sem, flags);\
-        } while (0)
+                                       sem, __FILE__, __LINE__);\
+       spin_lock_irqsave(sem, flags);\
+       } while (0)
 
 #define rio_spin_unlock_irqrestore(sem, flags) do { \
        rio_dprintk (RIO_DEBUG_SPINLOCK, "spinunlockirqrestore: %p %s:%d\n",\
-                                       sem, __FILE__, __LINE__);\
-        spin_unlock_irqrestore(sem, flags);\
-        } while (0)
-
+                                       sem, __FILE__, __LINE__);\
+       spin_unlock_irqrestore(sem, flags);\
+       } while (0)
 
 #define rio_spin_lock(sem) do { \
        rio_dprintk (RIO_DEBUG_SPINLOCK, "spinlock: %p %s:%d\n",\
-                                       sem, __FILE__, __LINE__);\
-        spin_lock(sem);\
-        } while (0)
+                                       sem, __FILE__, __LINE__);\
+       spin_lock(sem);\
+       } while (0)
 
 #define rio_spin_unlock(sem) do { \
        rio_dprintk (RIO_DEBUG_SPINLOCK, "spinunlock: %p %s:%d\n",\
-                                       sem, __FILE__, __LINE__);\
-        spin_unlock(sem);\
-        } while (0)
+                                       sem, __FILE__, __LINE__);\
+       spin_unlock(sem);\
+       } while (0)
 #else
 #define rio_spin_lock_irqsave(sem, flags) \
             spin_lock_irqsave(sem, flags)
@@ -165,7 +164,7 @@ static inline void *rio_memcpy_fromio (void *dest, void *source, int n)
 #define rio_memcpy_fromio                      memcpy_fromio
 #endif
 
-#define DEBUG
+#define DEBUG 1
 
 
 /* 
index c1e2ca81899fa34256cca6a66303972858d62404..4266c4ff2b89e36b1a52fd27f0cd7839741fdb27 100644 (file)
@@ -38,6 +38,7 @@ static char *_rioboot_c_sccs_ = "@(#)rioboot.c        1.3";
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
+#include <linux/interrupt.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/string.h>
index f651ed9e8ac5e3cbad86e65bbefd5e1bd13b8012..7b3345242e65d086f8dd606dc7b7878b896e5daf 100644 (file)
@@ -474,17 +474,18 @@ PKT *PacketP;
        rio_spin_lock_irqsave(&PortP->portSem, flags);
        switch( RBYTE(PktCmdP->Command) ) {
                case BREAK_RECEIVED:
-               rio_dprintk (RIO_DEBUG_CMD, "Received a break!\n");
+                       rio_dprintk (RIO_DEBUG_CMD, "Received a break!\n");
                        /* If the current line disc. is not multi-threading and
                                the current processor is not the default, reset rup_intr
                                and return FALSE to ensure that the command packet is
                                not freed. */
                        /* Call tmgr HANGUP HERE */
                        /* Fix this later when every thing works !!!! RAMRAJ */
+                       gs_got_break (PortP);
                        break;
 
                case COMPLETE:
-               rio_dprintk (RIO_DEBUG_CMD, "Command complete on phb %d host %d\n",
+                       rio_dprintk (RIO_DEBUG_CMD, "Command complete on phb %d host %d\n",
                             RBYTE(PktCmdP->PhbNum), HostP-p->RIOHosts);
                        subCommand = 1;
                        switch (RBYTE(PktCmdP->SubCommand)) {
@@ -549,6 +550,8 @@ PKT *PacketP;
                                */
                                if (PortP->gs.tty == NULL)
                                        break;
+                               if (PortP->gs.tty->termios == NULL)
+                                       break;
                          
                                if (!(PortP->gs.tty->termios->c_cflag & CLOCAL) &&
                                ((PortP->State & (RIO_MOPEN|RIO_WOPEN)))) {
@@ -623,7 +626,8 @@ RIOGetCmdBlk()
        struct CmdBlk *CmdBlkP;
 
        CmdBlkP = (struct CmdBlk *)sysbrk(sizeof(struct CmdBlk));
-       bzero(CmdBlkP, sizeof(struct CmdBlk));
+       if (CmdBlkP)
+               bzero(CmdBlkP, sizeof(struct CmdBlk));
 
        return CmdBlkP;
 }
index 9a429e16bb41bfbe8f516435a620e4617a8be83b..bc38ac5dfbdef05928dc0179a0020fe5b02f305e 100644 (file)
 #ifndef __riodrvr_h
 #define __riodrvr_h
 
+#include <asm/param.h> /* for HZ */
+
 #ifdef SCCS_LABELS
 static char *_riodrvr_h_sccs_ = "@(#)riodrvr.h 1.3";
 #endif
 
 #define MEMDUMP_SIZE   32
-#define HZ                             100
 #define        MOD_DISABLE     (RIO_NOREAD|RIO_NOWRITE|RIO_NOXPRINT)
 
 
index 6ab8175c7d663f267d16cab0ac208c6d5b82db82..888dddce2006c129c2b6dab26682cf7c3d546042 100644 (file)
@@ -1446,7 +1446,7 @@ struct rio_info   * p;
                                }
                                RIODefaultName(p, HostP, rup);
                        }
-                       HostP->UnixRups[rup].RupLock = -1;
+                       HostP->UnixRups[rup].RupLock = SPIN_LOCK_UNLOCKED;
                }
        }
 }
index 51f87de11171484a5aed43c99ecf9b19d1f2dd53..9970ecbcf7e78870586951b951b8fd59553d3fa1 100644 (file)
@@ -37,6 +37,8 @@ static char *_riotable_c_sccs_ = "@(#)riotable.c      1.2";
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
+#include <linux/interrupt.h>
+
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/string.h>
@@ -499,7 +501,7 @@ struct Map *MapP;
        struct Map *HostMapP;
        struct Port *PortP;
        int work_done = 0;
-       unsigned long flags;
+       unsigned long lock_flags, sem_flags;
 
        rio_dprintk (RIO_DEBUG_TABLE, "Delete entry on host %x, rta %x\n",
                                                                MapP->HostUniqueNum, MapP->RtaUniqueNum);
@@ -507,10 +509,10 @@ struct Map *MapP;
        for ( host=0; host < p->RIONumHosts; host++ ) {
                HostP = &p->RIOHosts[host];
 
-               rio_spin_lock_irqsave( &HostP->HostLock, flags );
+               rio_spin_lock_irqsave( &HostP->HostLock, lock_flags );
 
                if ( (HostP->Flags & RUN_STATE) != RC_RUNNING ) {
-                       rio_spin_unlock_irqrestore(&HostP->HostLock, flags);
+                       rio_spin_unlock_irqrestore(&HostP->HostLock, lock_flags);
                        continue;
                }
 
@@ -527,7 +529,7 @@ struct Map *MapP;
                                        if ( HostMapP->Topology[link].Unit != ROUTE_DISCONNECT ) {
                                                rio_dprintk (RIO_DEBUG_TABLE, "Entry is in use and cannot be deleted!\n");
                                                p->RIOError.Error = UNIT_IS_IN_USE;
-                                               rio_spin_unlock_irqrestore( &HostP->HostLock, flags);
+                                               rio_spin_unlock_irqrestore( &HostP->HostLock, lock_flags);
                                                return EBUSY;
                                        }
                                }
@@ -542,7 +544,7 @@ struct Map *MapP;
                                                PortP = p->RIOPortp[port];
                                                rio_dprintk (RIO_DEBUG_TABLE, "Unmap port\n");
 
-                                               rio_spin_lock_irqsave( &PortP->portSem, flags );
+                                               rio_spin_lock_irqsave( &PortP->portSem, sem_flags );
 
                                                PortP->Mapped = 0;
 
@@ -600,7 +602,7 @@ struct Map *MapP;
                                                        WWORD(PortP->PhbP->destination,
                                                         dest_unit + (dest_port << 8));
                                                }
-                                               rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+                                               rio_spin_unlock_irqrestore(&PortP->portSem, sem_flags);
                                        }
                                }
                                rio_dprintk (RIO_DEBUG_TABLE, "Entry nulled.\n");
@@ -608,7 +610,7 @@ struct Map *MapP;
                                work_done++;
                        }
                }
-               rio_spin_unlock_irqrestore(&HostP->HostLock, flags);
+               rio_spin_unlock_irqrestore(&HostP->HostLock, lock_flags);
        }
 
        /* XXXXX lock me up */
index aaa7a1f327966a8357f331286dfa5407db4553e9..15868bfbb285c3a0a6306e6fee1ad73eae98c3a3 100644 (file)
@@ -96,7 +96,7 @@ static void ttyseth_pv(struct Port *, struct ttystatics *,
 #endif
 
 static void RIOClearUp(struct Port *PortP);
-static int RIOShortCommand(struct rio_info *p, struct Port *PortP, 
+int RIOShortCommand(struct rio_info *p, struct Port *PortP, 
                           int command, int len, int arg);
 
 
@@ -452,8 +452,11 @@ bombout:
                                PortP->gs.tty->termios->c_state |= WOPEN;
                                */
                                PortP->State |= RIO_WOPEN;
+                               rio_spin_unlock_irqrestore(&PortP->portSem, flags);
+                               if (RIODelay (PortP, HUNDRED_MS) == RIO_FAIL)
 #if 0
                                if ( sleep((caddr_t)&tp->tm.c_canqo, TTIPRI|PCATCH))
+#endif
                                {
                                        /*
                                        ** ACTION: verify that this is a good thing
@@ -471,7 +474,6 @@ bombout:
                                        func_exit ();
                                        return -EINTR;
                                }
-#endif
                        }
                        PortP->State &= ~RIO_WOPEN;
                }
@@ -527,8 +529,10 @@ riotclose(void  *ptr)
 #endif
        struct Port *PortP =ptr;        /* pointer to the port structure */
        int deleted = 0;
-       int     try = 25;
-       int     repeat_this = 0xff;
+       int     try = -1; /* Disable the timeouts by setting them to -1 */
+       int     repeat_this = -1; /* Congrats to those having 15 years of 
+                                    uptime! (You get to break the driver.) */
+       long end_time;
        struct tty_struct * tty;
        unsigned long flags;
        int Modem;
@@ -541,6 +545,12 @@ riotclose(void  *ptr)
        /* tp = PortP->TtyP;*/                  /* Get tty */
        tty = PortP->gs.tty;
        rio_dprintk (RIO_DEBUG_TTY, "TTY is at address 0x%x\n",(int)tty);
+
+       if (PortP->gs.closing_wait) 
+               end_time = jiffies + PortP->gs.closing_wait;
+       else 
+               end_time = jiffies + MAX_SCHEDULE_TIMEOUT;
+
        Modem = rio_ismodem(tty->device);
 #if 0
        /* What F.CKING cache? Even then, a higly idle multiprocessor,
@@ -573,7 +583,8 @@ riotclose(void  *ptr)
        ** clear the open bits for this device
        */
        PortP->State &= (Modem ? ~RIO_MOPEN : ~RIO_LOPEN);
-
+       PortP->State &= ~RIO_CARR_ON;
+       PortP->ModemState &= ~MSVR1_CD;
        /*
        ** If the device was open as both a Modem and a tty line
        ** then we need to wimp out here, as the port has not really
@@ -605,7 +616,6 @@ riotclose(void  *ptr)
        */
        rio_dprintk (RIO_DEBUG_TTY, "Timeout 1 starts\n");
 
-#if 0
        if (!deleted)
        while ( (PortP->InUse != NOT_INUSE) && !p->RIOHalted && 
                (PortP->TxBufferIn != PortP->TxBufferOut) ) {
@@ -626,7 +636,7 @@ riotclose(void  *ptr)
                }
                rio_spin_lock_irqsave(&PortP->portSem, flags);
        }
-#endif
+
        PortP->TxBufferIn = PortP->TxBufferOut = 0;
        repeat_this = 0xff;
 
@@ -662,7 +672,7 @@ riotclose(void  *ptr)
        if (!deleted)
          while (try && (PortP->PortState & PORT_ISOPEN)) {
                try--;
-               if (try == 0) {
+               if (time_after (jiffies, end_time)) {
                  rio_dprintk (RIO_DEBUG_TTY, "Run out of tries - force the bugger shut!\n" );
                  RIOPreemptiveCmd(p, PortP,FCLOSE);
                  break;
@@ -674,7 +684,11 @@ riotclose(void  *ptr)
                        RIOClearUp( PortP );
                        goto close_end;
                }
-               RIODelay_ni(PortP, HUNDRED_MS);
+               if (RIODelay(PortP, HUNDRED_MS) == RIO_FAIL) {
+                       rio_dprintk (RIO_DEBUG_TTY, "RTA EINTR in delay \n");
+                       RIOPreemptiveCmd(p, PortP,FCLOSE);
+                       break;
+               }
        }
        rio_spin_lock_irqsave(&PortP->portSem, flags);
        rio_dprintk (RIO_DEBUG_TTY, "Close: try was %d on completion\n", try );
@@ -779,7 +793,7 @@ struct Port *PortP;
 ** Other values of len aren't allowed, and will cause
 ** a panic.
 */
-static int RIOShortCommand(struct rio_info *p, struct Port *PortP,
+int RIOShortCommand(struct rio_info *p, struct Port *PortP,
                int command, int len, int arg)
 {
        PKT *PacketP;
diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c
new file mode 100644 (file)
index 0000000..631814c
--- /dev/null
@@ -0,0 +1,880 @@
+/* drivers/char/ser_a2232.c */
+
+/* $Id: ser_a2232.c,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */
+
+/* Linux serial driver for the Amiga A2232 board */
+
+/* This driver is MAINTAINED. Before applying any changes, please contact
+ * the author.
+ */
+
+/* Copyright (c) 2000-2001 Enver Haase    <ehaase@inf.fu-berlin.de>
+ *                   alias The A2232 driver project <A2232@gmx.net>
+ * All rights reserved.
+ *
+ *   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.
+ *
+ */
+/***************************** Documentation ************************/
+/*
+ * This driver is in EXPERIMENTAL state. That means I could not find
+ * someone with five A2232 boards with 35 ports running at 19200 bps
+ * at the same time and test the machine's behaviour.
+ * However, I know that you can performance-tweak this driver (see
+ * the source code).
+ * One thing to consider is the time this driver consumes during the
+ * Amiga's vertical blank interrupt. Everything that is to be done
+ * _IS DONE_ when entering the vertical blank interrupt handler of
+ * this driver.
+ * However, it would be more sane to only do the job for only ONE card
+ * instead of ALL cards at a time; or, more generally, to handle only
+ * SOME ports instead of ALL ports at a time.
+ * However, as long as no-one runs into problems I guess I shouldn't
+ * change the driver as it runs fine for me :) .
+ *
+ * Version history of this file:
+ * 0.4 Resolved licensing issues.
+ * 0.3 Inclusion in the Linux/m68k tree, small fixes.
+ * 0.2 Added documentation, minor typo fixes.
+ * 0.1 Initial release.
+ *
+ * TO DO:
+ * -   Handle incoming BREAK events. I guess "Stevens: Advanced
+ *     Programming in the UNIX(R) Environment" is a good reference
+ *     on what is to be done.
+ * -   When installing as a module, don't simply 'printk' text, but
+ *     send it to the TTY used by the user.
+ *
+ * THANKS TO:
+ * -   Jukka Marin (65EC02 code).
+ * -   The other NetBSD developers on whose A2232 driver I had a
+ *     pretty close look. However, I didn't copy any code so it
+ *     is okay to put my code under the GPL and include it into
+ *     Linux.
+ */
+/***************************** End of Documentation *****************/
+
+/***************************** Defines ******************************/
+/*
+ * Enables experimental 115200 (normal) 230400 (turbo) baud rate.
+ * The A2232 specification states it can only operate at speeds up to
+ * 19200 bits per second, and I was not able to send a file via
+ * "sz"/"rz" and a null-modem cable from one A2232 port to another
+ * at 115200 bits per second.
+ * However, this might work for you.
+ */
+#undef A2232_SPEEDHACK
+/*
+ * Default is not to use RTS/CTS so you could be talked to death.
+ */
+#define A2232_SUPPRESS_RTSCTS_WARNING
+/************************* End of Defines ***************************/
+
+/***************************** Includes *****************************/
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include <asm/setup.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <linux/zorro.h>
+#include <asm/irq.h>
+#include <asm/semaphore.h>
+
+#include <linux/delay.h>
+
+#include <linux/serial.h>
+#include <linux/generic_serial.h>
+
+#include "ser_a2232.h"
+#include "ser_a2232fw.h"
+/************************* End of Includes **************************/
+
+/***************************** Prototypes ***************************/
+/* Helper functions */
+static __inline__ volatile struct a2232status *a2232stat(unsigned int board,
+                                               unsigned int portonboard);
+static __inline__ volatile struct a2232memory *a2232mem (unsigned int board); 
+static __inline__ void a2232_receive_char(     struct a2232_port *port,
+                                               int ch, int err );
+/* The interrupt service routine */
+static void a2232_vbl_inter(int irq, void *data, struct pt_regs *fp);
+/* Initialize the port structures */
+static void a2232_init_portstructs(void);
+/* Initialize and register TTY drivers. */
+/* returns 0 IFF successful */
+static int a2232_init_drivers(void); 
+/* Initialize all A2232 boards; main entry point. */
+int a2232board_init(void);
+
+/* BEGIN GENERIC_SERIAL PROTOTYPES */
+static void a2232_disable_tx_interrupts(void *ptr);
+static void a2232_enable_tx_interrupts(void *ptr);
+static void a2232_disable_rx_interrupts(void *ptr);
+static void a2232_enable_rx_interrupts(void *ptr);
+static int  a2232_get_CD(void *ptr);
+static void a2232_shutdown_port(void *ptr);
+static int  a2232_set_real_termios(void *ptr);
+static int  a2232_chars_in_buffer(void *ptr);
+static void a2232_close(void *ptr);
+static void a2232_hungup(void *ptr);
+/* static void a2232_getserial (void *ptr, struct serial_struct *sp); */
+/* END GENERIC_SERIAL PROTOTYPES */
+
+/* Functions that the TTY driver struct expects */
+static int  a2232_ioctl(struct tty_struct *tty, struct file *file,
+                                                                               unsigned int cmd, unsigned long arg);
+static void a2232_throttle(struct tty_struct *tty);
+static void a2232_unthrottle(struct tty_struct *tty);
+static int  a2232_open(struct tty_struct * tty, struct file * filp);
+/************************* End of Prototypes ************************/
+
+/***************************** Global variables *********************/
+/*---------------------------------------------------------------------------
+ * Interface from generic_serial.c back here
+ *--------------------------------------------------------------------------*/
+static struct real_driver a2232_real_driver = {
+        a2232_disable_tx_interrupts,
+        a2232_enable_tx_interrupts,
+        a2232_disable_rx_interrupts,
+        a2232_enable_rx_interrupts,
+        a2232_get_CD,
+        a2232_shutdown_port,
+        a2232_set_real_termios,
+        a2232_chars_in_buffer,
+        a2232_close,
+        a2232_hungup,
+       NULL    /* a2232_getserial */
+};
+
+static void *a2232_driver_ID = &a2232_driver_ID; // Some memory address WE own.
+
+/* Ports structs */
+static struct a2232_port a2232_ports[MAX_A2232_BOARDS*NUMLINES];
+
+/* TTY driver structs */
+static struct tty_driver a2232_driver;
+static struct tty_driver a2232_callout_driver;
+
+/* Variables used by the TTY driver */
+static int a2232_refcount;
+static struct tty_struct *a2232_table[MAX_A2232_BOARDS*NUMLINES] = { NULL, };
+static struct termios *a2232_termios[MAX_A2232_BOARDS*NUMLINES];
+static struct termios *a2232_termios_locked[MAX_A2232_BOARDS*NUMLINES];
+
+/* nr of cards completely (all ports) and correctly configured */
+static int nr_a2232; 
+
+/* zorro_dev structs for the A2232's */
+static struct zorro_dev *zd_a2232[MAX_A2232_BOARDS]; 
+/***************************** End of Global variables **************/
+
+/***************************** Functions ****************************/
+/*** BEGIN OF REAL_DRIVER FUNCTIONS ***/
+
+static void a2232_disable_tx_interrupts(void *ptr)
+{
+       struct a2232_port *port;
+       volatile struct a2232status *stat;
+       unsigned long flags;
+  
+       port = ptr;
+       stat = a2232stat(port->which_a2232, port->which_port_on_a2232);
+       stat->OutDisable = -1;
+
+       /* Does this here really have to be? */
+       save_flags(flags);
+       cli(); 
+       port->gs.flags &= ~GS_TX_INTEN;
+       restore_flags(flags);
+}
+
+static void a2232_enable_tx_interrupts(void *ptr)
+{
+       struct a2232_port *port;
+       volatile struct a2232status *stat;
+       unsigned long flags;
+
+       port = ptr;
+       stat = a2232stat(port->which_a2232, port->which_port_on_a2232);
+       stat->OutDisable = 0;
+
+       /* Does this here really have to be? */
+       save_flags(flags);
+       cli();
+       port->gs.flags |= GS_TX_INTEN;
+       restore_flags(flags);
+}
+
+static void a2232_disable_rx_interrupts(void *ptr)
+{
+       struct a2232_port *port;
+       port = ptr;
+       port->disable_rx = -1;
+}
+
+static void a2232_enable_rx_interrupts(void *ptr)
+{
+       struct a2232_port *port;
+       port = ptr;
+       port->disable_rx = 0;
+}
+
+static int  a2232_get_CD(void *ptr)
+{
+       return ((struct a2232_port *) ptr)->cd_status;
+}
+
+static void a2232_shutdown_port(void *ptr)
+{
+       struct a2232_port *port;
+       volatile struct a2232status *stat;
+       unsigned long flags;
+
+       port = ptr;
+       stat = a2232stat(port->which_a2232, port->which_port_on_a2232);
+
+       save_flags(flags);
+       cli();
+
+       port->gs.flags &= ~GS_ACTIVE;
+       
+       if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) {
+               /* Set DTR and RTS to Low, flush output.
+                  The NetBSD driver "msc.c" does it this way. */
+               stat->Command = (       (stat->Command & ~A2232CMD_CMask) | 
+                                       A2232CMD_Close );
+               stat->OutFlush = -1;
+               stat->Setup = -1;
+       }
+
+       restore_flags(flags);
+       
+       /* After analyzing control flow, I think a2232_shutdown_port
+               is actually the last call from the system when at application
+               level someone issues a "echo Hello >>/dev/ttyY0".
+               Therefore I think the MOD_DEC_USE_COUNT should be here and
+               not in "a2232_close()". See the comment in "sx.c", too.
+               If you run into problems, compile this driver into the
+               kernel instead of compiling it as a module. */
+       MOD_DEC_USE_COUNT;
+}
+
+static int  a2232_set_real_termios(void *ptr)
+{
+       unsigned int cflag, baud, chsize, stopb, parity, softflow;
+       int rate;
+       int a2232_param, a2232_cmd;
+       unsigned long flags;
+       unsigned int i;
+       struct a2232_port *port = ptr;
+       volatile struct a2232status *status;
+       volatile struct a2232memory *mem;
+
+       if (!port->gs.tty || !port->gs.tty->termios) return 0;
+
+       status = a2232stat(port->which_a2232, port->which_port_on_a2232);
+       mem = a2232mem(port->which_a2232);
+       
+       a2232_param = a2232_cmd = 0;
+
+       // get baud rate
+       baud = port->gs.baud;
+       if (baud == 0) {
+               /* speed == 0 -> drop DTR, do nothing else */
+               save_flags(flags);
+               cli();
+               // Clear DTR (and RTS... mhhh).
+               status->Command = (     (status->Command & ~A2232CMD_CMask) |
+                                       A2232CMD_Close );
+               status->OutFlush = -1;
+               status->Setup = -1;
+               
+               restore_flags(flags);
+               return 0;
+       }
+       
+       rate = A2232_BAUD_TABLE_NOAVAIL;
+       for (i=0; i < A2232_BAUD_TABLE_NUM_RATES * 3; i += 3){
+               if (a2232_baud_table[i] == baud){
+                       if (mem->Common.Crystal == A2232_TURBO) rate = a2232_baud_table[i+2];
+                       else                                    rate = a2232_baud_table[i+1];
+               }
+       }
+       if (rate == A2232_BAUD_TABLE_NOAVAIL){
+               printk("a2232: Board %d Port %d unsupported baud rate: %d baud. Using another.\n",port->which_a2232,port->which_port_on_a2232,baud);
+               // This is useful for both (turbo or normal) Crystal versions.
+               rate = A2232PARAM_B9600;
+       }
+       a2232_param |= rate;
+
+       cflag  = port->gs.tty->termios->c_cflag;
+
+       // get character size
+       chsize = cflag & CSIZE;
+       switch (chsize){
+               case CS8:       a2232_param |= A2232PARAM_8Bit; break;
+               case CS7:       a2232_param |= A2232PARAM_7Bit; break;
+               case CS6:       a2232_param |= A2232PARAM_6Bit; break;
+               case CS5:       a2232_param |= A2232PARAM_5Bit; break;
+               default:        printk("a2232: Board %d Port %d unsupported character size: %d. Using 8 data bits.\n",
+                                       port->which_a2232,port->which_port_on_a2232,chsize);
+                               a2232_param |= A2232PARAM_8Bit; break;
+       }
+
+       // get number of stop bits
+       stopb  = cflag & CSTOPB;
+       if (stopb){ // two stop bits instead of one
+               printk("a2232: Board %d Port %d 2 stop bits unsupported. Using 1 stop bit.\n",
+                       port->which_a2232,port->which_port_on_a2232);
+       }
+
+       // Warn if RTS/CTS not wanted
+       if (!(cflag & CRTSCTS)){
+#ifndef A2232_SUPPRESS_RTSCTS_WARNING
+               printk("a2232: Board %d Port %d cannot switch off firmware-implemented RTS/CTS hardware flow control.\n",
+                       port->which_a2232,port->which_port_on_a2232);
+#endif
+       }
+
+       /*      I think this is correct.
+               However, IXOFF means _input_ flow control and I wonder
+               if one should care about IXON _output_ flow control,
+               too. If this makes problems, one should turn the A2232
+               firmware XON/XOFF "SoftFlow" flow control off and use
+               the conventional way of inserting START/STOP characters
+               by hand in throttle()/unthrottle().
+       */
+       softflow = !!( port->gs.tty->termios->c_iflag & IXOFF );
+
+       // get Parity (Enabled/Disabled? If Enabled, Odd or Even?)
+       parity = cflag & (PARENB | PARODD);
+       if (parity & PARENB){
+               if (parity & PARODD){
+                       a2232_cmd |= A2232CMD_OddParity;
+               }
+               else{
+                       a2232_cmd |= A2232CMD_EvenParity;
+               }
+       }
+       else a2232_cmd |= A2232CMD_NoParity;
+
+
+       /*      Hmm. Maybe an own a2232_port structure
+               member would be cleaner?        */
+       if (cflag & CLOCAL)
+               port->gs.flags &= ~ASYNC_CHECK_CD;
+       else
+               port->gs.flags |= ASYNC_CHECK_CD;
+
+
+       /* Now we have all parameters and can go to set them: */
+       save_flags(flags);
+       cli();
+
+       status->Param = a2232_param | A2232PARAM_RcvBaud;
+       status->Command = a2232_cmd | A2232CMD_Open |  A2232CMD_Enable;
+       status->SoftFlow = softflow;
+       status->OutDisable = 0;
+       status->Setup = -1;
+
+       restore_flags(flags);
+       return 0;
+}
+
+static int  a2232_chars_in_buffer(void *ptr)
+{
+       struct a2232_port *port;
+       volatile struct a2232status *status; 
+       unsigned char ret; /* we need modulo-256 arithmetics */
+       port = ptr;
+       status = a2232stat(port->which_a2232, port->which_port_on_a2232);
+#if A2232_IOBUFLEN != 256
+#error "Re-Implement a2232_chars_in_buffer()!"
+#endif
+       ret = (status->OutHead - status->OutTail);
+       return ret;
+}
+
+static void a2232_close(void *ptr)
+{
+       a2232_disable_tx_interrupts(ptr);
+       a2232_disable_rx_interrupts(ptr);
+       /* see the comment in a2232_shutdown_port above. */
+       /* MOD_DEC_USE_COUNT; */
+}
+
+static void a2232_hungup(void *ptr)
+{
+       a2232_close(ptr);
+}
+/*** END   OF REAL_DRIVER FUNCTIONS ***/
+
+/*** BEGIN  FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/
+static int a2232_ioctl(        struct tty_struct *tty, struct file *file,
+                       unsigned int cmd, unsigned long arg)
+{
+       return -ENOIOCTLCMD;
+}
+
+static void a2232_throttle(struct tty_struct *tty)
+{
+/* Throttle: System cannot take another chars: Drop RTS or
+             send the STOP char or whatever.
+   The A2232 firmware does RTS/CTS anyway, and XON/XOFF
+   if switched on. So the only thing we can do at this
+   layer here is not taking any characters out of the
+   A2232 buffer any more. */
+       struct a2232_port *port = (struct a2232_port *) tty->driver_data;
+       port->throttle_input = -1;
+}
+
+static void a2232_unthrottle(struct tty_struct *tty)
+{
+/* Unthrottle: dual to "throttle()" above. */
+       struct a2232_port *port = (struct a2232_port *) tty->driver_data;
+       port->throttle_input = 0;
+}
+
+static int  a2232_open(struct tty_struct * tty, struct file * filp)
+{
+/* More or less stolen from other drivers. */
+       int line;
+       int retval;
+       struct a2232_port *port;
+
+       line = MINOR(tty->device);
+       port = &a2232_ports[line];
+       
+       tty->driver_data = port;
+       port->gs.tty = tty;
+       port->gs.count++;
+       retval = gs_init_port(&port->gs);
+       if (retval) {
+               port->gs.count--;
+               return retval;
+       }
+       port->gs.flags |= GS_ACTIVE;
+       if (port->gs.count == 1) {
+               MOD_INC_USE_COUNT;
+       }
+       retval = gs_block_til_ready(port, filp);
+
+       if (retval) {
+               MOD_DEC_USE_COUNT;
+               port->gs.count--;
+               return retval;
+       }
+
+       if ((port->gs.count == 1) && (port->gs.flags & ASYNC_SPLIT_TERMIOS)){
+               if (tty->driver.subtype == A2232_TTY_SUBTYPE_NORMAL)
+                       *tty->termios = port->gs.normal_termios;
+               else 
+                       *tty->termios = port->gs.callout_termios;
+               a2232_set_real_termios (port);
+       }
+
+       port->gs.session = current->session;
+       port->gs.pgrp = current->pgrp;
+
+       a2232_enable_rx_interrupts(port);
+       
+       return 0;
+}
+/*** END OF FUNCTIONS EXPECTED BY TTY DRIVER STRUCTS ***/
+
+static __inline__ volatile struct a2232status *a2232stat(unsigned int board, unsigned int portonboard)
+{
+       volatile struct a2232memory *mem = a2232mem(board);
+       return &(mem->Status[portonboard]);
+}
+
+static __inline__ volatile struct a2232memory *a2232mem (unsigned int board)
+{
+       return (volatile struct a2232memory *) ZTWO_VADDR( zd_a2232[board]->resource.start );
+}
+
+static __inline__ void a2232_receive_char(     struct a2232_port *port,
+                                               int ch, int err )
+{
+/*     Mostly stolen from other drivers.
+       Maybe one could implement a more efficient version by not only
+       transferring one character at a time.
+*/
+       struct tty_struct *tty = port->gs.tty;
+       
+       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+               return;
+
+       tty->flip.count++;
+
+#if 0
+       switch(err) {
+       case TTY_BREAK:
+               break;
+       case TTY_PARITY:
+               break;
+       case TTY_OVERRUN:
+               break;
+       case TTY_FRAME:
+               break;
+       }
+#endif
+
+       *tty->flip.flag_buf_ptr++ = err;
+       *tty->flip.char_buf_ptr++ = ch;
+       tty_flip_buffer_push(tty);
+}
+
+static void a2232_vbl_inter(int irq, void *data, struct pt_regs *fp)
+{
+#if A2232_IOBUFLEN != 256
+#error "Re-Implement a2232_vbl_inter()!"
+#endif
+
+struct a2232_port *port;
+volatile struct a2232memory *mem;
+volatile struct a2232status *status;
+unsigned char newhead;
+unsigned char bufpos; /* Must be unsigned char. We need the modulo-256 arithmetics */
+unsigned char ncd, ocd, ccd; /* names consistent with the NetBSD driver */
+volatile u_char *ibuf, *cbuf, *obuf;
+int ch, err, n, p;
+       for (n = 0; n < nr_a2232; n++){         /* for every completely initialized A2232 board */
+               mem = a2232mem(n);
+               for (p = 0; p < NUMLINES; p++){ /* for every port on this board */
+                       err = 0;
+                       port = &a2232_ports[n*NUMLINES+p];
+                       if ( port->gs.flags & GS_ACTIVE ){ /* if the port is used */
+
+                               status = a2232stat(n,p);
+
+                               if (!port->disable_rx && !port->throttle_input){ /* If input is not disabled */
+                                       newhead = status->InHead;               /* 65EC02 write pointer */
+                                       bufpos = status->InTail;
+
+                                       /* check for input for this port */
+                                       if (newhead != bufpos) {
+                                               /* buffer for input chars/events */
+                                               ibuf = mem->InBuf[p];
+                                               /* data types of bytes in ibuf */
+                                               cbuf = mem->InCtl[p];
+                                               /* do for all chars */
+                                               while (bufpos != newhead) {
+                                                       /* which type of input data? */
+                                                       switch (cbuf[bufpos]) {
+                                                               /* switch on input event (CD, BREAK, etc.) */
+                                                       case A2232INCTL_EVENT:
+                                                               switch (ibuf[bufpos++]) {
+                                                               case A2232EVENT_Break:
+                                                                       /* TODO: Handle BREAK signal */
+                                                                       break;
+                                                                       /*      A2232EVENT_CarrierOn and A2232EVENT_CarrierOff are
+                                                                               handled in a separate queue and should not occur here. */
+                                                               case A2232EVENT_Sync:
+                                                                       printk("A2232: 65EC02 software sent SYNC event, don't know what to do. Ignoring.");
+                                                                       break;
+                                                               default:
+                                                                       printk("A2232: 65EC02 software broken, unknown event type %d occured.\n",ibuf[bufpos-1]);
+                                                               } /* event type switch */
+                                                               break;
+                                                       case A2232INCTL_CHAR:
+                                                               /* Receive incoming char */
+                                                               a2232_receive_char(port, ibuf[bufpos], err);
+                                                               bufpos++;
+                                                               break;
+                                                       default:
+                                                               printk("A2232: 65EC02 software broken, unknown data type %d occured.\n",cbuf[bufpos]);
+                                                               bufpos++;
+                                                       } /* switch on input data type */
+                                               } /* while there's something in the buffer */
+
+                                               status->InTail = bufpos;            /* tell 65EC02 what we've read */
+                                               
+                                       } /* if there was something in the buffer */                          
+                               } /* If input is not disabled */
+
+                               /* Now check if there's something to output */
+                               obuf = mem->OutBuf[p];
+                               bufpos = status->OutHead;
+                               while ( (port->gs.xmit_cnt > 0)         &&
+                                       (!port->gs.tty->stopped)        &&
+                                       (!port->gs.tty->hw_stopped) ){  /* While there are chars to transmit */
+                                       if (((bufpos+1) & A2232_IOBUFLENMASK) != status->OutTail) { /* If the A2232 buffer is not full */
+                                               ch = port->gs.xmit_buf[port->gs.xmit_tail];                                     /* get the next char to transmit */
+                                               port->gs.xmit_tail = (port->gs.xmit_tail+1) & (SERIAL_XMIT_SIZE-1); /* modulo-addition for the gs.xmit_buf ring-buffer */
+                                               obuf[bufpos++] = ch;                                                                                                                            /* put it into the A2232 buffer */
+                                               port->gs.xmit_cnt--;
+                                       }
+                                       else{                                                                                                                                                                                                   /* If A2232 the buffer is full */
+                                               break;                                                                                                                                                                                  /* simply stop filling it. */
+                                       }                                                                                                       
+                               }                                       
+                               status->OutHead = bufpos;
+                                       
+                               /* WakeUp if output buffer runs low */
+                               if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) {
+                                       if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && port->gs.tty->ldisc.write_wakeup){
+                                               (port->gs.tty->ldisc.write_wakeup)(port->gs.tty);
+                                       }
+                                       wake_up_interruptible(&port->gs.tty->write_wait);
+                               }
+                       } // if the port is used
+               } // for every port on the board
+                       
+               /* Now check the CD message queue */
+               newhead = mem->Common.CDHead;
+               bufpos = mem->Common.CDTail;
+               if (newhead != bufpos){                         /* There are CD events in queue */
+                       ocd = mem->Common.CDStatus;             /* get old status bits */
+                       while (newhead != bufpos){              /* read all events */
+                               ncd = mem->CDBuf[bufpos++];     /* get one event */
+                               ccd = ncd ^ ocd;                /* mask of changed lines */
+                               ocd = ncd;                      /* save new status bits */
+                               for(p=0; p < NUMLINES; p++){    /* for all ports */
+                                       if (ccd & 1){           /* this one changed */
+
+                                               struct a2232_port *port = &a2232_ports[n*7+p];
+                                               port->cd_status = !(ncd & 1); /* ncd&1 <=> CD is now off */
+
+                                               if (!(port->gs.flags & ASYNC_CHECK_CD))
+                                                       ;       /* Don't report DCD changes */
+                                               else if (port->cd_status) { // if DCD on: DCD went UP!
+                                                       if (~(port->gs.flags & ASYNC_NORMAL_ACTIVE) ||
+                                                           ~(port->gs.flags & ASYNC_CALLOUT_ACTIVE)) {
+                                                               /* Are we blocking in open?*/
+                                                               wake_up_interruptible(&port->gs.open_wait);
+                                                       }
+                                               }
+                                               else { // if DCD off: DCD went DOWN!
+                                                       if (!((port->gs.flags & ASYNC_CALLOUT_ACTIVE) &&
+                                                             (port->gs.flags & ASYNC_CALLOUT_NOHUP))) {
+                                                               if (port->gs.tty)
+                                                                       tty_hangup (port->gs.tty);
+                                                       }
+                                               }
+                                               
+                                       } // if CD changed for this port
+                                       ccd >>= 1;
+                                       ncd >>= 1;                                                                      /* Shift bits for next line */
+                               } // for every port
+                       } // while CD events in queue
+                       mem->Common.CDStatus = ocd; /* save new status */
+                       mem->Common.CDTail = bufpos; /* remove events */
+               } // if events in CD queue
+               
+       } // for every completely initialized A2232 board
+}
+
+static void a2232_init_portstructs(void)
+{
+       struct a2232_port *port;
+       int i;
+
+       for (i = 0; i < MAX_A2232_BOARDS*NUMLINES; i++) {
+               port = a2232_ports + i;
+               port->which_a2232 = i/NUMLINES;
+               port->which_port_on_a2232 = i%NUMLINES;
+               port->disable_rx = port->throttle_input = port->cd_status = 0;
+               port->gs.callout_termios = tty_std_termios;
+               port->gs.normal_termios = tty_std_termios;
+               port->gs.magic = A2232_MAGIC;
+               port->gs.close_delay = HZ/2;
+               port->gs.closing_wait = 30 * HZ;
+               port->gs.rd = &a2232_real_driver;
+#ifdef NEW_WRITE_LOCKING
+               init_MUTEX(&(port->gs.port_write_sem));
+#endif
+               init_waitqueue_head(&port->gs.open_wait);
+               init_waitqueue_head(&port->gs.close_wait);
+       }
+}
+
+static int a2232_init_drivers(void)
+{
+       int error;
+
+       memset(&a2232_driver, 0, sizeof(a2232_driver));
+       a2232_driver.magic = TTY_DRIVER_MAGIC;
+       a2232_driver.driver_name = "commodore_a2232";
+       a2232_driver.name = "ttyY";
+       a2232_driver.major = A2232_NORMAL_MAJOR;
+       a2232_driver.num = NUMLINES * nr_a2232;
+       a2232_driver.type = TTY_DRIVER_TYPE_SERIAL;
+       a2232_driver.subtype = A2232_TTY_SUBTYPE_NORMAL;
+       a2232_driver.init_termios = tty_std_termios;
+       a2232_driver.init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       a2232_driver.flags = TTY_DRIVER_REAL_RAW;
+       a2232_driver.refcount = &a2232_refcount;
+       a2232_driver.table = a2232_table;
+       a2232_driver.termios = a2232_termios;
+       a2232_driver.termios_locked = a2232_termios_locked;
+
+       a2232_driver.open = a2232_open;
+       a2232_driver.close = gs_close;
+       a2232_driver.write = gs_write;
+       a2232_driver.put_char = gs_put_char;
+       a2232_driver.flush_chars = gs_flush_chars;
+       a2232_driver.write_room = gs_write_room;
+       a2232_driver.chars_in_buffer = gs_chars_in_buffer;
+       a2232_driver.flush_buffer = gs_flush_buffer;
+       a2232_driver.ioctl = a2232_ioctl;
+       a2232_driver.throttle = a2232_throttle;
+       a2232_driver.unthrottle = a2232_unthrottle;
+       a2232_driver.set_termios = gs_set_termios;
+       a2232_driver.stop = gs_stop;
+       a2232_driver.start = gs_start;
+       a2232_driver.hangup = gs_hangup;
+
+       a2232_callout_driver = a2232_driver;
+       a2232_callout_driver.name = "cuy";
+       a2232_callout_driver.major = A2232_CALLOUT_MAJOR;
+       a2232_callout_driver.subtype = A2232_TTY_SUBTYPE_CALLOUT;
+
+       if ((error = tty_register_driver(&a2232_driver))) {
+               printk(KERN_ERR "A2232: Couldn't register A2232 driver, error = %d\n",
+                      error);
+               return 1;
+       }
+       if ((error = tty_register_driver(&a2232_callout_driver))) {
+               tty_unregister_driver(&a2232_driver);
+               printk(KERN_ERR "A2232: Couldn't register A2232 callout driver, error = %d\n",
+                      error);
+               return 1;
+       }
+       return 0;
+}
+
+int a2232board_init(void)
+{
+       struct zorro_dev *z;
+
+       unsigned int boardaddr;
+       int bcount;
+       short start;
+       u_char *from;
+       volatile u_char *to;
+       volatile struct a2232memory *mem;
+
+#ifdef __SMP__
+       return -ENODEV; /* This driver is not SMP aware. Is there an SMP ZorroII-bus-machine? */
+#endif
+
+       if (!MACH_IS_AMIGA){
+               return -ENODEV;
+       }
+
+       printk("Commodore A2232 driver initializing.\n"); /* Say that we're alive. */
+
+       z = NULL;
+       nr_a2232 = 0;
+       while ( (z = zorro_find_device(ZORRO_WILDCARD, z)) ){
+               if (    (z->id != ZORRO_PROD_CBM_A2232_PROTOTYPE) && 
+                       (z->id != ZORRO_PROD_CBM_A2232) ){
+                       continue;       // The board found was no A2232
+               }
+               if (!zorro_request_device(z,"A2232 driver"))
+                       continue;
+
+               printk("Commodore A2232 found (#%d).\n",nr_a2232);
+
+               zd_a2232[nr_a2232] = z;
+
+               boardaddr = ZTWO_VADDR( z->resource.start );
+               printk("Board is located at address 0x%x, size is 0x%x.\n", boardaddr, (unsigned int) ((z->resource.end+1) - (z->resource.start)));
+
+               mem = (volatile struct a2232memory *) boardaddr;
+
+               (void) mem->Enable6502Reset;   /* copy the code across to the board */
+               to = (u_char *)mem;  from = a2232_65EC02code; bcount = sizeof(a2232_65EC02code) - 2;
+               start = *(short *)from;
+               from += sizeof(start);
+               to += start;
+               while(bcount--) *to++ = *from++;
+               printk("65EC02 software uploaded to the A2232 memory.\n");
+  
+               mem->Common.Crystal = A2232_UNKNOWN;  /* use automatic speed check */
+  
+               /* start 6502 running */
+               (void) mem->ResetBoard;
+               printk("A2232's 65EC02 CPU up and running.\n");
+  
+               /* wait until speed detector has finished */
+               for (bcount = 0; bcount < 2000; bcount++) {
+                       udelay(1000);
+                       if (mem->Common.Crystal)
+                               break;
+               }
+               printk((mem->Common.Crystal?"A2232 oscillator crystal detected by 65EC02 software: ":"65EC02 software could not determine A2232 oscillator crystal: "));
+               switch (mem->Common.Crystal){
+               case A2232_UNKNOWN:
+                       printk("Unknown crystal.\n");
+                       break;
+               case A2232_NORMAL:
+                       printk ("Normal crystal.\n");
+                       break;
+               case A2232_TURBO:
+                       printk ("Turbo crystal.\n");
+                       break;
+               default:
+                       printk ("0x%x. Huh?\n",mem->Common.Crystal);
+               }
+
+               nr_a2232++;
+
+       }       
+
+       printk("Total: %d A2232 boards initialized.\n.", nr_a2232); /* Some status report if no card was found */
+
+       a2232_init_portstructs();
+
+       /*
+               a2232_init_drivers also registers the drivers. Must be here because all boards
+               have to be detected first.
+       */
+       if (a2232_init_drivers()) return -ENODEV; // maybe we should use a different -Exxx?
+
+       request_irq(IRQ_AMIGA_VERTB, a2232_vbl_inter, 0, "A2232 serial VBL", a2232_driver_ID);
+       return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+       return a2232board_init();
+}
+
+void cleanup_module(void)
+{
+       int i;
+
+       for (i = 0; i < nr_a2232; i++) {
+               zorro_release_device(zd_a2232[i]);
+       }
+
+       tty_unregister_driver(&a2232_driver);
+       tty_unregister_driver(&a2232_callout_driver);
+       free_irq(IRQ_AMIGA_VERTB, a2232_driver_ID);
+}
+#endif
+/***************************** End of Functions *********************/
diff --git a/drivers/char/ser_a2232.h b/drivers/char/ser_a2232.h
new file mode 100644 (file)
index 0000000..d2b17b4
--- /dev/null
@@ -0,0 +1,206 @@
+/* drivers/char/ser_a2232.h */
+
+/* $Id: ser_a2232.h,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */
+
+/* Linux serial driver for the Amiga A2232 board */
+
+/* This driver is MAINTAINED. Before applying any changes, please contact
+ * the author.
+ */
+   
+/* Copyright (c) 2000-2001 Enver Haase    <ehaase@inf.fu-berlin.de>
+ *                   alias The A2232 driver project <A2232@gmx.net>
+ * All rights reserved.
+ *
+ *   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.
+ *  
+ */
+
+#ifndef _SER_A2232_H_
+#define _SER_A2232_H_
+
+/*
+       How many boards are to be supported at maximum;
+       "up to five A2232 Multiport Serial Cards may be installed in a
+       single Amiga 2000" states the A2232 User's Guide. If you have
+       more slots available, you might want to change the value below.
+*/
+#define MAX_A2232_BOARDS 5
+
+#ifndef A2232_NORMAL_MAJOR
+/* This allows overriding on the compiler commandline, or in a "major.h" 
+   include or something like that */
+#define A2232_NORMAL_MAJOR  224        /* /dev/ttyY* */
+#define A2232_CALLOUT_MAJOR 225        /* /dev/cuy*  */
+#endif
+
+/* Some magic is always good - Who knows :) */
+#define A2232_MAGIC 0x000a2232
+
+/* for the tty_struct subtype field */
+#define A2232_TTY_SUBTYPE_NORMAL       1
+#define A2232_TTY_SUBTYPE_CALLOUT      2
+
+/* A2232 port structure to keep track of the
+   status of every single line used */
+struct a2232_port{
+       struct gs_port gs;
+       unsigned int which_a2232;
+       unsigned int which_port_on_a2232;
+       short disable_rx;
+       short throttle_input;
+       short cd_status;
+};
+
+#define        NUMLINES                7       /* number of lines per board */
+#define        A2232_IOBUFLEN          256     /* number of bytes per buffer */
+#define        A2232_IOBUFLENMASK      0xff    /* mask for maximum number of bytes */
+
+
+#define        A2232_UNKNOWN   0       /* crystal not known */
+#define        A2232_NORMAL    1       /* normal A2232 (1.8432 MHz oscillator) */
+#define        A2232_TURBO     2       /* turbo A2232 (3.6864 MHz oscillator) */
+
+
+struct a2232common {
+       char   Crystal; /* normal (1) or turbo (2) board? */
+       u_char Pad_a;
+       u_char TimerH;  /* timer value after speed check */
+       u_char TimerL;
+       u_char CDHead;  /* head pointer for CD message queue */
+       u_char CDTail;  /* tail pointer for CD message queue */
+       u_char CDStatus;
+       u_char Pad_b;
+};
+
+struct a2232status {
+       u_char InHead;          /* input queue head */
+       u_char InTail;          /* input queue tail */
+       u_char OutDisable;      /* disables output */
+       u_char OutHead;         /* output queue head */
+       u_char OutTail;         /* output queue tail */
+       u_char OutCtrl;         /* soft flow control character to send */
+       u_char OutFlush;        /* flushes output buffer */
+       u_char Setup;           /* causes reconfiguration */
+       u_char Param;           /* parameter byte - see A2232PARAM */
+       u_char Command;         /* command byte - see A2232CMD */
+       u_char SoftFlow;        /* enables xon/xoff flow control */
+       /* private 65EC02 fields: */
+       u_char XonOff;          /* stores XON/XOFF enable/disable */
+};
+
+#define        A2232_MEMPAD1   \
+       (0x0200 - NUMLINES * sizeof(struct a2232status) -       \
+       sizeof(struct a2232common))
+#define        A2232_MEMPAD2   (0x2000 - NUMLINES * A2232_IOBUFLEN - A2232_IOBUFLEN)
+
+struct a2232memory {
+       struct a2232status Status[NUMLINES];    /* 0x0000-0x006f status areas */
+       struct a2232common Common;              /* 0x0070-0x0077 common flags */
+       u_char Dummy1[A2232_MEMPAD1];           /* 0x00XX-0x01ff */
+       u_char OutBuf[NUMLINES][A2232_IOBUFLEN];/* 0x0200-0x08ff output bufs */
+       u_char InBuf[NUMLINES][A2232_IOBUFLEN]; /* 0x0900-0x0fff input bufs */
+       u_char InCtl[NUMLINES][A2232_IOBUFLEN]; /* 0x1000-0x16ff control data */
+       u_char CDBuf[A2232_IOBUFLEN];           /* 0x1700-0x17ff CD event buffer */
+       u_char Dummy2[A2232_MEMPAD2];           /* 0x1800-0x2fff */
+       u_char Code[0x1000];                    /* 0x3000-0x3fff code area */
+       u_short InterruptAck;                   /* 0x4000        intr ack */
+       u_char Dummy3[0x3ffe];                  /* 0x4002-0x7fff */
+       u_short Enable6502Reset;                /* 0x8000 Stop board, */
+                                               /*  6502 RESET line held low */
+       u_char Dummy4[0x3ffe];                  /* 0x8002-0xbfff */
+       u_short ResetBoard;                     /* 0xc000 reset board & run, */
+                                               /*  6502 RESET line held high */
+};
+
+#undef A2232_MEMPAD1
+#undef A2232_MEMPAD2
+
+#define        A2232INCTL_CHAR         0       /* corresponding byte in InBuf is a character */
+#define        A2232INCTL_EVENT        1       /* corresponding byte in InBuf is an event */
+
+#define        A2232EVENT_Break        1       /* break set */
+#define        A2232EVENT_CarrierOn    2       /* carrier raised */
+#define        A2232EVENT_CarrierOff   3       /* carrier dropped */
+#define A2232EVENT_Sync                4       /* don't know, defined in 2232.ax */
+
+#define        A2232CMD_Enable         0x1     /* enable/DTR bit */
+#define        A2232CMD_Close          0x2     /* close the device */
+#define        A2232CMD_Open           0xb     /* open the device */
+#define        A2232CMD_CMask          0xf     /* command mask */
+#define        A2232CMD_RTSOff         0x0     /* turn off RTS */
+#define        A2232CMD_RTSOn          0x8     /* turn on RTS */
+#define        A2232CMD_Break          0xd     /* transmit a break */
+#define        A2232CMD_RTSMask        0xc     /* mask for RTS stuff */
+#define        A2232CMD_NoParity       0x00    /* don't use parity */
+#define        A2232CMD_OddParity      0x20    /* odd parity */
+#define        A2232CMD_EvenParity     0x60    /* even parity */
+#define        A2232CMD_ParityMask     0xe0    /* parity mask */
+
+#define        A2232PARAM_B115200      0x0     /* baud rates */
+#define        A2232PARAM_B50          0x1
+#define        A2232PARAM_B75          0x2
+#define        A2232PARAM_B110         0x3
+#define        A2232PARAM_B134         0x4
+#define        A2232PARAM_B150         0x5
+#define        A2232PARAM_B300         0x6
+#define        A2232PARAM_B600         0x7
+#define        A2232PARAM_B1200        0x8
+#define        A2232PARAM_B1800        0x9
+#define        A2232PARAM_B2400        0xa
+#define        A2232PARAM_B3600        0xb
+#define        A2232PARAM_B4800        0xc
+#define        A2232PARAM_B7200        0xd
+#define        A2232PARAM_B9600        0xe
+#define        A2232PARAM_B19200       0xf
+#define        A2232PARAM_BaudMask     0xf     /* baud rate mask */
+#define        A2232PARAM_RcvBaud      0x10    /* enable receive baud rate */
+#define        A2232PARAM_8Bit         0x00    /* numbers of bits */
+#define        A2232PARAM_7Bit         0x20
+#define        A2232PARAM_6Bit         0x40
+#define        A2232PARAM_5Bit         0x60
+#define        A2232PARAM_BitMask      0x60    /* numbers of bits mask */
+
+
+/* Standard speeds tables, -1 means unavailable, -2 means 0 baud: switch off line */
+#define A2232_BAUD_TABLE_NOAVAIL -1
+#define A2232_BAUD_TABLE_NUM_RATES (18)
+static int a2232_baud_table[A2232_BAUD_TABLE_NUM_RATES*3] = {
+       //Baud  //Normal                        //Turbo
+       50,     A2232PARAM_B50,                 A2232_BAUD_TABLE_NOAVAIL,
+       75,     A2232PARAM_B75,                 A2232_BAUD_TABLE_NOAVAIL,
+       110,    A2232PARAM_B110,                A2232_BAUD_TABLE_NOAVAIL,
+       134,    A2232PARAM_B134,                A2232_BAUD_TABLE_NOAVAIL,
+       150,    A2232PARAM_B150,                A2232PARAM_B75,
+       200,    A2232_BAUD_TABLE_NOAVAIL,       A2232_BAUD_TABLE_NOAVAIL,
+       300,    A2232PARAM_B300,                A2232PARAM_B150,
+       600,    A2232PARAM_B600,                A2232PARAM_B300,
+       1200,   A2232PARAM_B1200,               A2232PARAM_B600,
+       1800,   A2232PARAM_B1800,               A2232_BAUD_TABLE_NOAVAIL,
+       2400,   A2232PARAM_B2400,               A2232PARAM_B1200,
+       4800,   A2232PARAM_B4800,               A2232PARAM_B2400,
+       9600,   A2232PARAM_B9600,               A2232PARAM_B4800,
+       19200,  A2232PARAM_B19200,              A2232PARAM_B9600,
+       38400,  A2232_BAUD_TABLE_NOAVAIL,       A2232PARAM_B19200,
+       57600,  A2232_BAUD_TABLE_NOAVAIL,       A2232_BAUD_TABLE_NOAVAIL,
+#ifdef A2232_SPEEDHACK
+       115200, A2232PARAM_B115200,             A2232_BAUD_TABLE_NOAVAIL,
+       230400, A2232_BAUD_TABLE_NOAVAIL,       A2232PARAM_B115200
+#else
+       115200, A2232_BAUD_TABLE_NOAVAIL,       A2232_BAUD_TABLE_NOAVAIL,
+       230400, A2232_BAUD_TABLE_NOAVAIL,       A2232_BAUD_TABLE_NOAVAIL
+#endif
+};
+#endif
diff --git a/drivers/char/ser_a2232fw.ax b/drivers/char/ser_a2232fw.ax
new file mode 100644 (file)
index 0000000..7364380
--- /dev/null
@@ -0,0 +1,529 @@
+;.lib "axm"
+;
+;begin
+;title "A2232 serial board driver"
+;
+;set modules "2232"
+;set executable "2232.bin"
+;
+;;;;set nolink
+;
+;set temporary directory "t:"
+;
+;set assembly options "-m6502 -l60:t:list"
+;set link options "bin"; loadadr"
+;;;bin2c 2232.bin msc6502.h msc6502code
+;end
+;
+;
+; ### Commodore A2232 serial board driver for NetBSD by JM v1.3 ###
+;
+; - Created 950501 by JM -
+;
+;
+; Serial board driver software.
+;
+;
+% Copyright (c) 1995 Jukka Marin <jmarin@jmp.fi>.
+% All rights reserved.
+%
+% Redistribution and use in source and binary forms, with or without
+% modification, are permitted provided that the following conditions
+% are met:
+% 1. Redistributions of source code must retain the above copyright
+%    notice, and the entire permission notice in its entirety,
+%    including the disclaimer of warranties.
+% 2. Redistributions in binary form must reproduce the above copyright
+%    notice, this list of conditions and the following disclaimer in the
+%    documentation and/or other materials provided with the distribution.
+% 3. The name of the author may not be used to endorse or promote
+%    products derived from this software without specific prior
+%    written permission.
+%
+% ALTERNATIVELY, this product may be distributed under the terms of
+% the GNU General Public License, in which case the provisions of the
+% GPL are required INSTEAD OF the above restrictions.  (This clause is
+% necessary due to a potential bad interaction between the GPL and
+% the restrictions contained in a BSD-style copyright.)
+%
+% THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+% WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+% OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+% DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+% INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+% (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+% HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+% STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+% OF THE POSSIBILITY OF SUCH DAMAGE.
+;
+;
+; Bugs:
+;
+; - Can't send a break yet
+;
+;
+;
+; Edited:
+;
+; - 950501 by JM -> v0.1       - Created this file.
+; - 951029 by JM -> v1.3       - Carrier Detect events now queued in a separate
+;                                queue.
+;
+;
+
+
+CODE           equ     $3800           ; start address for program code
+
+
+CTL_CHAR       equ     $00             ; byte in ibuf is a character
+CTL_EVENT      equ     $01             ; byte in ibuf is an event
+
+EVENT_BREAK    equ     $01
+EVENT_CDON     equ     $02
+EVENT_CDOFF    equ     $03
+EVENT_SYNC     equ     $04
+
+XON            equ     $11
+XOFF           equ     $13
+
+
+VARBASE                macro   *starting_address       ; was VARINIT
+_varbase       set     \1
+               endm
+
+VARDEF         macro   *name space_needs
+\1             equ     _varbase
+_varbase       set     _varbase+\2
+               endm
+
+
+stz            macro   * address
+                db     $64,\1
+               endm
+
+stzax          macro   * address
+                db     $9e,<\1,>\1
+               endm
+
+
+biti           macro   * immediate value
+               db      $89,\1
+               endm
+
+smb0           macro   * address
+               db      $87,\1
+               endm
+smb1           macro   * address
+               db      $97,\1
+               endm
+smb2           macro   * address
+               db      $a7,\1
+               endm
+smb3           macro   * address
+               db      $b7,\1
+               endm
+smb4           macro   * address
+               db      $c7,\1
+               endm
+smb5           macro   * address
+               db      $d7,\1
+               endm
+smb6           macro   * address
+               db      $e7,\1
+               endm
+smb7           macro   * address
+               db      $f7,\1
+               endm
+
+
+
+;-----------------------------------------------------------------------;
+;                                                                      ;
+; stuff common for all ports, non-critical (run once / loop)           ;
+;                                                                      ;
+DO_SLOW                macro   * port_number                                   ;
+               .local                  ;                               ;
+               lda     CIA+C_PA        ; check all CD inputs           ;
+               cmp     CommonCDo       ; changed from previous accptd? ;
+               beq     =over           ; nope, do nothing else here    ;
+                                       ;                               ;
+               cmp     CommonCDb       ; bouncing?                     ;
+               beq     =nobounce       ; nope ->                       ;
+                                       ;                               ;
+               sta     CommonCDb       ; save current state            ;
+               lda     #64             ; reinitialize counter          ;
+               sta     CommonCDc       ;                               ;
+               jmp     =over           ; skip CD save                  ;
+                                       ;                               ;
+=nobounce      dec     CommonCDc       ; no, decrement bounce counter  ;
+               bpl     =over           ; not done yet, so skip CD save ;
+                                       ;                               ;
+=saveCD                ldx     CDHead          ; get write index               ;
+               sta     cdbuf,x         ; save status in buffer         ;
+               inx                     ;                               ;
+               cpx     CDTail          ; buffer full?                  ;
+               .if     ne              ; no: preserve status:          ;
+                stx    CDHead          ; update index in RAM           ;
+                sta    CommonCDo       ; save state for the next check ;
+               .end                    ;                               ;
+=over          .end    local                                           ;
+               endm                                                    ;
+                                                                       ;
+;-----------------------------------------------------------------------;
+
+
+; port specific stuff (no data transfer)
+
+DO_PORT                macro   * port_number
+               .local                  ;                               ;
+               lda     SetUp\1         ; reconfiguration request?      ;
+               .if     ne              ; yes:                          ;
+                lda    SoftFlow\1      ; get XON/XOFF flag             ;
+                sta    XonOff\1        ; save it                       ;
+                lda    Param\1         ; get parameter                 ;
+                ora    #%00010000      ; use baud generator for Rx     ;
+                sta    ACIA\1+A_CTRL   ; store in control register     ;
+                stz    OutDisable\1    ; enable transmit output        ;
+                stz    SetUp\1         ; no reconfiguration no more    ;
+               .end                    ;                               ;
+                                       ;                               ;
+               lda     InHead\1        ; get write index               ;
+               sbc     InTail\1        ; buffer full soon?             ;
+               cmp     #200            ; 200 chars or more in buffer?  ;
+               lda     Command\1       ; get Command reg value         ;
+               and     #%11110011      ; turn RTS OFF by default       ;
+               .if     cc              ; still room in buffer:         ;
+                ora    #%00001000      ; turn RTS ON                   ;
+               .end                    ;                               ;
+               sta     ACIA\1+A_CMD    ; set/clear RTS                 ;
+                                       ;                               ;
+               lda     OutFlush\1      ; request to flush output buffer;
+               .if     ne              ; yessh!                        ;
+                lda    OutHead\1       ; get head                      ;
+                sta    OutTail\1       ; save as tail                  ;
+                stz    OutDisable\1    ; enable transmit output        ;
+                stz    OutFlush\1      ; clear request                 ;
+               .end
+               .end    local
+               endm
+
+
+DO_DATA                macro   * port number
+               .local
+               lda     ACIA\1+A_SR     ; read ACIA status register     ;
+               biti    [1<<3]          ; something received?           ;
+               .if     ne              ; yes:                          ;
+                biti   [1<<1]          ; framing error?                ;
+                .if    ne              ; yes:                          ;
+                 lda   ACIA\1+A_DATA   ; read received character       ;
+                 bne   =SEND           ; not break -> ignore it        ;
+                 ldx   InHead\1        ; get write pointer             ;
+                 lda   #CTL_EVENT      ; get type of byte              ;
+                 sta   ictl\1,x        ; save it in InCtl buffer       ;
+                 lda   #EVENT_BREAK    ; event code                    ;
+                 sta   ibuf\1,x        ; save it as well               ;
+                 inx                   ;                               ;
+                 cpx   InTail\1        ; still room in buffer?         ;
+                 .if   ne              ; absolutely:                   ;
+                  stx  InHead\1        ; update index in memory        ;
+                 .end                  ;                               ;
+                 jmp   =SEND           ; go check if anything to send  ;
+                .end                   ;                               ;
+                                       ; normal char received:         ;
+                ldx    InHead\1        ; get write index               ;
+                lda    ACIA\1+A_DATA   ; read received character       ;
+                sta    ibuf\1,x        ; save char in buffer           ;
+                stzax  ictl\1          ; set type to CTL_CHAR          ;
+                inx                    ;                               ;
+                cpx    InTail\1        ; buffer full?                  ;
+                .if    ne              ; no: preserve character:       ;
+                 stx   InHead\1        ; update index in RAM           ;
+                .end                   ;                               ;
+                and    #$7f            ; mask off parity if any        ;
+                cmp    #XOFF           ; XOFF from remote host?        ;
+                .if    eq              ; yes:                          ;
+                 lda   XonOff\1        ; if XON/XOFF handshaking..     ;
+                 sta   OutDisable\1    ; ..disable transmitter         ;
+                .end                   ;                               ;
+               .end                    ;                               ;
+                                       ;                               ;
+                                       ; BUFFER FULL CHECK WAS HERE    ;
+                                       ;                               ;
+=SEND          lda     ACIA\1+A_SR     ; transmit register empty?      ;
+               and     #[1<<4]         ;                               ;
+               .if     ne              ; yes:                          ;
+                ldx    OutCtrl\1       ; sending out XON/XOFF?         ;
+                .if    ne              ; yes:                          ;
+                 lda   CIA+C_PB        ; check CTS signal              ;
+                 and   #[1<<\1]        ; (for this port only)          ;
+                 bne   =DONE           ; not allowed to send -> done   ;
+                 stx   ACIA\1+A_DATA   ; transmit control char         ;
+                 stz   OutCtrl\1       ; clear flag                    ;
+                 jmp   =DONE           ; and we're done                ;
+                .end                   ;                               ;
+                                       ;                               ;
+                ldx    OutTail\1       ; anything to transmit?         ;
+                cpx    OutHead\1       ;                               ;
+                .if    ne              ; yes:                          ;
+                 lda   OutDisable\1    ; allowed to transmit?          ;
+                 .if   eq              ; yes:                          ;
+                  lda  CIA+C_PB        ; check CTS signal              ;
+                  and  #[1<<\1]        ; (for this port only)          ;
+                  bne  =DONE           ; not allowed to send -> done   ;
+                  lda  obuf\1,x        ; get a char from buffer        ;
+                  sta  ACIA\1+A_DATA   ; send it away                  ;
+                  inc  OutTail\1       ; update read index             ;
+                 .end                  ;                               ;
+                .end                   ;                               ;
+               .end                    ;                               ;
+=DONE          .end    local
+               endm
+
+
+
+PORTVAR                macro   * port number
+               VARDEF  InHead\1 1
+               VARDEF  InTail\1 1
+               VARDEF  OutDisable\1 1
+               VARDEF  OutHead\1 1
+               VARDEF  OutTail\1 1
+               VARDEF  OutCtrl\1 1
+               VARDEF  OutFlush\1 1
+               VARDEF  SetUp\1 1
+               VARDEF  Param\1 1
+               VARDEF  Command\1 1
+               VARDEF  SoftFlow\1 1
+               ; private:
+               VARDEF  XonOff\1 1
+               endm
+
+
+ VARBASE 0     ; start variables at address $0000
+ PORTVAR 0     ; define variables for port 0
+ PORTVAR 1     ; define variables for port 1
+ PORTVAR 2     ; define variables for port 2
+ PORTVAR 3     ; define variables for port 3
+ PORTVAR 4     ; define variables for port 4
+ PORTVAR 5     ; define variables for port 5
+ PORTVAR 6     ; define variables for port 6
+
+
+
+ VARDEF        Crystal 1       ; 0 = unknown, 1 = normal, 2 = turbo
+ VARDEF        Pad_a   1
+ VARDEF        TimerH  1
+ VARDEF        TimerL  1
+ VARDEF        CDHead  1
+ VARDEF        CDTail  1
+ VARDEF        CDStatus 1
+ VARDEF        Pad_b   1
+
+ VARDEF        CommonCDo 1     ; for carrier detect optimization
+ VARDEF        CommonCDc 1     ; for carrier detect debouncing
+ VARDEF        CommonCDb 1     ; for carrier detect debouncing
+
+
+ VARBASE $0200
+ VARDEF        obuf0 256       ; output data (characters only)
+ VARDEF        obuf1 256
+ VARDEF        obuf2 256
+ VARDEF        obuf3 256
+ VARDEF        obuf4 256
+ VARDEF        obuf5 256
+ VARDEF        obuf6 256
+
+ VARDEF        ibuf0 256       ; input data (characters, events etc - see ictl)
+ VARDEF        ibuf1 256
+ VARDEF        ibuf2 256
+ VARDEF        ibuf3 256
+ VARDEF        ibuf4 256
+ VARDEF        ibuf5 256
+ VARDEF        ibuf6 256
+
+ VARDEF        ictl0 256       ; input control information (type of data in ibuf)
+ VARDEF        ictl1 256
+ VARDEF        ictl2 256
+ VARDEF        ictl3 256
+ VARDEF        ictl4 256
+ VARDEF        ictl5 256
+ VARDEF        ictl6 256
+
+ VARDEF        cdbuf 256       ; CD event queue
+
+
+ACIA0          equ     $4400
+ACIA1          equ     $4c00
+ACIA2          equ     $5400
+ACIA3          equ     $5c00
+ACIA4          equ     $6400
+ACIA5          equ     $6c00
+ACIA6          equ     $7400
+
+A_DATA         equ     $00
+A_SR           equ     $02
+A_CMD          equ     $04
+A_CTRL         equ     $06
+;  00  write transmit data     read received data
+;  02  reset ACIA              read status register
+;  04  write command register  read command register
+;  06  write control register  read control register
+
+CIA            equ     $7c00           ; 8520 CIA
+C_PA           equ     $00             ; port A data register
+C_PB           equ     $02             ; port B data register
+C_DDRA         equ     $04             ; data direction register for port A
+C_DDRB         equ     $06             ; data direction register for port B
+C_TAL          equ     $08             ; timer A
+C_TAH          equ     $0a
+C_TBL          equ     $0c             ; timer B
+C_TBH          equ     $0e
+C_TODL         equ     $10             ; TOD LSB
+C_TODM         equ     $12             ; TOD middle byte
+C_TODH         equ     $14             ; TOD MSB
+C_DATA         equ     $18             ; serial data register
+C_INTCTRL      equ     $1a             ; interrupt control register
+C_CTRLA                equ     $1c             ; control register A
+C_CTRLB                equ     $1e             ; control register B
+
+
+
+
+
+               section main,code,CODE-2
+
+               db      >CODE,<CODE
+
+;-----------------------------------------------------------------------;
+; here's the initialization code:                                      ;
+;                                                                      ;
+R_RESET                ldx     #$ff                                            ;
+               txs                     ; initialize stack pointer      ;
+               cld                     ; in case a 6502 is used...     ;
+               ldx     #0              ;                               ;
+               lda     #0              ;                               ;
+               ldy     #Crystal        ; this many bytes to clear      ;
+clr_loop       sta     0,x             ; clear zero page variables     ;
+               inx                     ;                               ;
+               dey                     ;                               ;
+               bne     clr_loop        ;                               ;
+                                       ;                               ;
+               stz     CommonCDo       ; force CD test at boot         ;
+               stz     CommonCDb       ;                               ;
+               stz     CDHead          ; clear queue                   ;
+               stz     CDTail          ;                               ;
+                                       ;                               ;
+               lda     #0              ;                               ;
+               sta     Pad_a           ;                               ;
+               lda     #170            ; test cmp                      ;
+               cmp     #100            ;                               ;
+               .if     cs              ;                               ;
+                inc    Pad_a           ; C was set                     ;
+               .end                    ;                               ;
+                                                                       ;
+;-----------------------------------------------------------------------;
+; Speed check                                                          ;
+;-----------------------------------------------------------------------;
+                                                                       ;
+               lda     Crystal         ; speed already set?            ;
+               beq     DoSpeedy        ;                               ;
+               jmp     LOOP            ; yes, skip speed test          ;
+                                       ;                               ;
+DoSpeedy       lda     #%10011000      ; 8N1, 1200/2400 bps            ;
+               sta     ACIA0+A_CTRL    ;                               ;
+               lda     #%00001011      ; enable DTR                    ;
+               sta     ACIA0+A_CMD     ;                               ;
+               lda     ACIA0+A_SR      ; read status register          ;
+                                       ;                               ;
+               lda     #%10000000      ; disable all ints (unnecessary);
+               sta     CIA+C_INTCTRL   ;                               ;
+               lda     #255            ; program the timer             ;
+               sta     CIA+C_TAL       ;                               ;
+               sta     CIA+C_TAH       ;                               ;
+                                       ;                               ;
+               ldx     #0              ;                               ;
+               stx     ACIA0+A_DATA    ; transmit a zero               ;
+               nop                     ;                               ;
+               nop                     ;                               ;
+               lda     ACIA0+A_SR      ; read status                   ;
+               nop                     ;                               ;
+               nop                     ;                               ;
+               stx     ACIA0+A_DATA    ; transmit a zero               ;
+Speedy1                lda     ACIA0+A_SR      ; read status                   ;
+               and     #[1<<4]         ; transmit data reg empty?      ;
+               beq     Speedy1         ; not yet, wait more            ;
+                                       ;                               ;
+               lda     #%00010001      ; load & start the timer        ;
+               stx     ACIA0+A_DATA    ; transmit one more zero        ;
+               sta     CIA+C_CTRLA     ;                               ;
+Speedy2                lda     ACIA0+A_SR      ; read status                   ;
+               and     #[1<<4]         ; transmit data reg empty?      ;
+               beq     Speedy2         ; not yet, wait more            ;
+               stx     CIA+C_CTRLA     ; stop the timer                ;
+                                       ;                               ;
+               lda     CIA+C_TAL       ; copy timer value for 68k      ;
+               sta     TimerL          ;                               ;
+               lda     CIA+C_TAH       ;                               ;
+               sta     TimerH          ;                               ;
+               cmp     #$d0            ; turbo or normal?              ;
+               .if     cs              ;                               ;
+                lda    #2              ; turbo! :-)                    ;
+               .else                   ;                               ;
+                lda    #1              ; normal :-(                    ;
+               .end                    ;                               ;
+               sta     Crystal         ;                               ;
+               lda     #0              ;                               ;
+               sta     ACIA0+A_SR      ;                               ;
+               sta     ACIA0+A_CTRL    ; reset UART                    ;
+               sta     ACIA0+A_CMD     ;                               ;
+                                                                       ;
+               jmp     LOOP                                            ;
+                                                                       ;
+;                                                                      ;
+;-----------------------------------------------------------------------;
+;                                                                      ;
+; The Real Thing:                                                      ;
+;                                                                      ;
+LOOP           DO_SLOW                 ; do non-critical things        ;
+               jsr     do_input        ; check for received data
+               DO_PORT 0
+               jsr     do_input
+               DO_PORT 1
+               jsr     do_input
+               DO_PORT 2
+               jsr     do_input
+               DO_PORT 3
+               jsr     do_input
+               DO_PORT 4
+               jsr     do_input
+               DO_PORT 5
+               jsr     do_input
+               DO_PORT 6
+               jsr     do_input
+               jmp     LOOP
+
+
+do_input       DO_DATA 0
+               DO_DATA 1
+               DO_DATA 2
+               DO_DATA 3
+               DO_DATA 4
+               DO_DATA 5
+               DO_DATA 6
+               rts
+
+
+;-----------------------------------------------------------------------;
+               section vectors,data,$3ffa
+               dw      $d0d0
+               dw      R_RESET
+               dw      $c0ce
+;-----------------------------------------------------------------------;
+
+
+
+               end
+
+
+
diff --git a/drivers/char/ser_a2232fw.h b/drivers/char/ser_a2232fw.h
new file mode 100644 (file)
index 0000000..e09a30a
--- /dev/null
@@ -0,0 +1,306 @@
+/* drivers/char/ser_a2232fw.h */
+
+/* $Id: ser_a2232fw.h,v 0.4 2000/01/25 12:00:00 ehaase Exp $ */
+
+/*
+ * Copyright (c) 1995 Jukka Marin <jmarin@jmp.fi>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * ALTERNATIVELY, this product may be distributed under the terms of
+ * the GNU Public License, in which case the provisions of the GPL are
+ * required INSTEAD OF the above restrictions.  (This clause is
+ * necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+                                      
+/* This is the 65EC02 code by Jukka Marin that is executed by
+   the A2232's 65EC02 processor (base address: 0x3800)
+   Source file:        ser_a2232fw.ax
+   Version:    1.3 (951029)
+   Known Bugs: Cannot send a break yet
+*/
+static unsigned char a2232_65EC02code[] = {
+       0x38, 0x00, 0xA2, 0xFF, 0x9A, 0xD8, 0xA2, 0x00, 
+       0xA9, 0x00, 0xA0, 0x54, 0x95, 0x00, 0xE8, 0x88, 
+       0xD0, 0xFA, 0x64, 0x5C, 0x64, 0x5E, 0x64, 0x58, 
+       0x64, 0x59, 0xA9, 0x00, 0x85, 0x55, 0xA9, 0xAA, 
+       0xC9, 0x64, 0x90, 0x02, 0xE6, 0x55, 0xA5, 0x54, 
+       0xF0, 0x03, 0x4C, 0x92, 0x38, 0xA9, 0x98, 0x8D, 
+       0x06, 0x44, 0xA9, 0x0B, 0x8D, 0x04, 0x44, 0xAD, 
+       0x02, 0x44, 0xA9, 0x80, 0x8D, 0x1A, 0x7C, 0xA9, 
+       0xFF, 0x8D, 0x08, 0x7C, 0x8D, 0x0A, 0x7C, 0xA2, 
+       0x00, 0x8E, 0x00, 0x44, 0xEA, 0xEA, 0xAD, 0x02, 
+       0x44, 0xEA, 0xEA, 0x8E, 0x00, 0x44, 0xAD, 0x02, 
+       0x44, 0x29, 0x10, 0xF0, 0xF9, 0xA9, 0x11, 0x8E, 
+       0x00, 0x44, 0x8D, 0x1C, 0x7C, 0xAD, 0x02, 0x44, 
+       0x29, 0x10, 0xF0, 0xF9, 0x8E, 0x1C, 0x7C, 0xAD, 
+       0x08, 0x7C, 0x85, 0x57, 0xAD, 0x0A, 0x7C, 0x85, 
+       0x56, 0xC9, 0xD0, 0x90, 0x05, 0xA9, 0x02, 0x4C, 
+       0x82, 0x38, 0xA9, 0x01, 0x85, 0x54, 0xA9, 0x00, 
+       0x8D, 0x02, 0x44, 0x8D, 0x06, 0x44, 0x8D, 0x04, 
+       0x44, 0x4C, 0x92, 0x38, 0xAD, 0x00, 0x7C, 0xC5, 
+       0x5C, 0xF0, 0x1F, 0xC5, 0x5E, 0xF0, 0x09, 0x85, 
+       0x5E, 0xA9, 0x40, 0x85, 0x5D, 0x4C, 0xB8, 0x38, 
+       0xC6, 0x5D, 0x10, 0x0E, 0xA6, 0x58, 0x9D, 0x00, 
+       0x17, 0xE8, 0xE4, 0x59, 0xF0, 0x04, 0x86, 0x58, 
+       0x85, 0x5C, 0x20, 0x23, 0x3A, 0xA5, 0x07, 0xF0, 
+       0x0F, 0xA5, 0x0A, 0x85, 0x0B, 0xA5, 0x08, 0x09, 
+       0x10, 0x8D, 0x06, 0x44, 0x64, 0x02, 0x64, 0x07, 
+       0xA5, 0x00, 0xE5, 0x01, 0xC9, 0xC8, 0xA5, 0x09, 
+       0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, 
+       0x44, 0xA5, 0x06, 0xF0, 0x08, 0xA5, 0x03, 0x85, 
+       0x04, 0x64, 0x02, 0x64, 0x06, 0x20, 0x23, 0x3A, 
+       0xA5, 0x13, 0xF0, 0x0F, 0xA5, 0x16, 0x85, 0x17, 
+       0xA5, 0x14, 0x09, 0x10, 0x8D, 0x06, 0x4C, 0x64, 
+       0x0E, 0x64, 0x13, 0xA5, 0x0C, 0xE5, 0x0D, 0xC9, 
+       0xC8, 0xA5, 0x15, 0x29, 0xF3, 0xB0, 0x02, 0x09, 
+       0x08, 0x8D, 0x04, 0x4C, 0xA5, 0x12, 0xF0, 0x08, 
+       0xA5, 0x0F, 0x85, 0x10, 0x64, 0x0E, 0x64, 0x12, 
+       0x20, 0x23, 0x3A, 0xA5, 0x1F, 0xF0, 0x0F, 0xA5, 
+       0x22, 0x85, 0x23, 0xA5, 0x20, 0x09, 0x10, 0x8D, 
+       0x06, 0x54, 0x64, 0x1A, 0x64, 0x1F, 0xA5, 0x18, 
+       0xE5, 0x19, 0xC9, 0xC8, 0xA5, 0x21, 0x29, 0xF3, 
+       0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, 0x54, 0xA5, 
+       0x1E, 0xF0, 0x08, 0xA5, 0x1B, 0x85, 0x1C, 0x64, 
+       0x1A, 0x64, 0x1E, 0x20, 0x23, 0x3A, 0xA5, 0x2B, 
+       0xF0, 0x0F, 0xA5, 0x2E, 0x85, 0x2F, 0xA5, 0x2C, 
+       0x09, 0x10, 0x8D, 0x06, 0x5C, 0x64, 0x26, 0x64, 
+       0x2B, 0xA5, 0x24, 0xE5, 0x25, 0xC9, 0xC8, 0xA5, 
+       0x2D, 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, 
+       0x04, 0x5C, 0xA5, 0x2A, 0xF0, 0x08, 0xA5, 0x27, 
+       0x85, 0x28, 0x64, 0x26, 0x64, 0x2A, 0x20, 0x23, 
+       0x3A, 0xA5, 0x37, 0xF0, 0x0F, 0xA5, 0x3A, 0x85, 
+       0x3B, 0xA5, 0x38, 0x09, 0x10, 0x8D, 0x06, 0x64, 
+       0x64, 0x32, 0x64, 0x37, 0xA5, 0x30, 0xE5, 0x31, 
+       0xC9, 0xC8, 0xA5, 0x39, 0x29, 0xF3, 0xB0, 0x02, 
+       0x09, 0x08, 0x8D, 0x04, 0x64, 0xA5, 0x36, 0xF0, 
+       0x08, 0xA5, 0x33, 0x85, 0x34, 0x64, 0x32, 0x64, 
+       0x36, 0x20, 0x23, 0x3A, 0xA5, 0x43, 0xF0, 0x0F, 
+       0xA5, 0x46, 0x85, 0x47, 0xA5, 0x44, 0x09, 0x10, 
+       0x8D, 0x06, 0x6C, 0x64, 0x3E, 0x64, 0x43, 0xA5, 
+       0x3C, 0xE5, 0x3D, 0xC9, 0xC8, 0xA5, 0x45, 0x29, 
+       0xF3, 0xB0, 0x02, 0x09, 0x08, 0x8D, 0x04, 0x6C, 
+       0xA5, 0x42, 0xF0, 0x08, 0xA5, 0x3F, 0x85, 0x40, 
+       0x64, 0x3E, 0x64, 0x42, 0x20, 0x23, 0x3A, 0xA5, 
+       0x4F, 0xF0, 0x0F, 0xA5, 0x52, 0x85, 0x53, 0xA5, 
+       0x50, 0x09, 0x10, 0x8D, 0x06, 0x74, 0x64, 0x4A, 
+       0x64, 0x4F, 0xA5, 0x48, 0xE5, 0x49, 0xC9, 0xC8, 
+       0xA5, 0x51, 0x29, 0xF3, 0xB0, 0x02, 0x09, 0x08, 
+       0x8D, 0x04, 0x74, 0xA5, 0x4E, 0xF0, 0x08, 0xA5, 
+       0x4B, 0x85, 0x4C, 0x64, 0x4A, 0x64, 0x4E, 0x20, 
+       0x23, 0x3A, 0x4C, 0x92, 0x38, 0xAD, 0x02, 0x44, 
+       0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, 
+       0xAD, 0x00, 0x44, 0xD0, 0x32, 0xA6, 0x00, 0xA9, 
+       0x01, 0x9D, 0x00, 0x10, 0xA9, 0x01, 0x9D, 0x00, 
+       0x09, 0xE8, 0xE4, 0x01, 0xF0, 0x02, 0x86, 0x00, 
+       0x4C, 0x65, 0x3A, 0xA6, 0x00, 0xAD, 0x00, 0x44, 
+       0x9D, 0x00, 0x09, 0x9E, 0x00, 0x10, 0xE8, 0xE4, 
+       0x01, 0xF0, 0x02, 0x86, 0x00, 0x29, 0x7F, 0xC9, 
+       0x13, 0xD0, 0x04, 0xA5, 0x0B, 0x85, 0x02, 0xAD, 
+       0x02, 0x44, 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x05, 
+       0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x01, 0xD0, 
+       0x21, 0x8E, 0x00, 0x44, 0x64, 0x05, 0x4C, 0x98, 
+       0x3A, 0xA6, 0x04, 0xE4, 0x03, 0xF0, 0x13, 0xA5, 
+       0x02, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x01, 
+       0xD0, 0x08, 0xBD, 0x00, 0x02, 0x8D, 0x00, 0x44, 
+       0xE6, 0x04, 0xAD, 0x02, 0x4C, 0x89, 0x08, 0xF0, 
+       0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, 0x4C, 
+       0xD0, 0x32, 0xA6, 0x0C, 0xA9, 0x01, 0x9D, 0x00, 
+       0x11, 0xA9, 0x01, 0x9D, 0x00, 0x0A, 0xE8, 0xE4, 
+       0x0D, 0xF0, 0x02, 0x86, 0x0C, 0x4C, 0xDA, 0x3A, 
+       0xA6, 0x0C, 0xAD, 0x00, 0x4C, 0x9D, 0x00, 0x0A, 
+       0x9E, 0x00, 0x11, 0xE8, 0xE4, 0x0D, 0xF0, 0x02, 
+       0x86, 0x0C, 0x29, 0x7F, 0xC9, 0x13, 0xD0, 0x04, 
+       0xA5, 0x17, 0x85, 0x0E, 0xAD, 0x02, 0x4C, 0x29, 
+       0x10, 0xF0, 0x2C, 0xA6, 0x11, 0xF0, 0x0F, 0xAD, 
+       0x02, 0x7C, 0x29, 0x02, 0xD0, 0x21, 0x8E, 0x00, 
+       0x4C, 0x64, 0x11, 0x4C, 0x0D, 0x3B, 0xA6, 0x10, 
+       0xE4, 0x0F, 0xF0, 0x13, 0xA5, 0x0E, 0xD0, 0x0F, 
+       0xAD, 0x02, 0x7C, 0x29, 0x02, 0xD0, 0x08, 0xBD, 
+       0x00, 0x03, 0x8D, 0x00, 0x4C, 0xE6, 0x10, 0xAD, 
+       0x02, 0x54, 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, 
+       0xF0, 0x1B, 0xAD, 0x00, 0x54, 0xD0, 0x32, 0xA6, 
+       0x18, 0xA9, 0x01, 0x9D, 0x00, 0x12, 0xA9, 0x01, 
+       0x9D, 0x00, 0x0B, 0xE8, 0xE4, 0x19, 0xF0, 0x02, 
+       0x86, 0x18, 0x4C, 0x4F, 0x3B, 0xA6, 0x18, 0xAD, 
+       0x00, 0x54, 0x9D, 0x00, 0x0B, 0x9E, 0x00, 0x12, 
+       0xE8, 0xE4, 0x19, 0xF0, 0x02, 0x86, 0x18, 0x29, 
+       0x7F, 0xC9, 0x13, 0xD0, 0x04, 0xA5, 0x23, 0x85, 
+       0x1A, 0xAD, 0x02, 0x54, 0x29, 0x10, 0xF0, 0x2C, 
+       0xA6, 0x1D, 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 
+       0x04, 0xD0, 0x21, 0x8E, 0x00, 0x54, 0x64, 0x1D, 
+       0x4C, 0x82, 0x3B, 0xA6, 0x1C, 0xE4, 0x1B, 0xF0, 
+       0x13, 0xA5, 0x1A, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 
+       0x29, 0x04, 0xD0, 0x08, 0xBD, 0x00, 0x04, 0x8D, 
+       0x00, 0x54, 0xE6, 0x1C, 0xAD, 0x02, 0x5C, 0x89, 
+       0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, 
+       0x00, 0x5C, 0xD0, 0x32, 0xA6, 0x24, 0xA9, 0x01, 
+       0x9D, 0x00, 0x13, 0xA9, 0x01, 0x9D, 0x00, 0x0C, 
+       0xE8, 0xE4, 0x25, 0xF0, 0x02, 0x86, 0x24, 0x4C, 
+       0xC4, 0x3B, 0xA6, 0x24, 0xAD, 0x00, 0x5C, 0x9D, 
+       0x00, 0x0C, 0x9E, 0x00, 0x13, 0xE8, 0xE4, 0x25, 
+       0xF0, 0x02, 0x86, 0x24, 0x29, 0x7F, 0xC9, 0x13, 
+       0xD0, 0x04, 0xA5, 0x2F, 0x85, 0x26, 0xAD, 0x02, 
+       0x5C, 0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x29, 0xF0, 
+       0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x08, 0xD0, 0x21, 
+       0x8E, 0x00, 0x5C, 0x64, 0x29, 0x4C, 0xF7, 0x3B, 
+       0xA6, 0x28, 0xE4, 0x27, 0xF0, 0x13, 0xA5, 0x26, 
+       0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x08, 0xD0, 
+       0x08, 0xBD, 0x00, 0x05, 0x8D, 0x00, 0x5C, 0xE6, 
+       0x28, 0xAD, 0x02, 0x64, 0x89, 0x08, 0xF0, 0x3B, 
+       0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, 0x64, 0xD0, 
+       0x32, 0xA6, 0x30, 0xA9, 0x01, 0x9D, 0x00, 0x14, 
+       0xA9, 0x01, 0x9D, 0x00, 0x0D, 0xE8, 0xE4, 0x31, 
+       0xF0, 0x02, 0x86, 0x30, 0x4C, 0x39, 0x3C, 0xA6, 
+       0x30, 0xAD, 0x00, 0x64, 0x9D, 0x00, 0x0D, 0x9E, 
+       0x00, 0x14, 0xE8, 0xE4, 0x31, 0xF0, 0x02, 0x86, 
+       0x30, 0x29, 0x7F, 0xC9, 0x13, 0xD0, 0x04, 0xA5, 
+       0x3B, 0x85, 0x32, 0xAD, 0x02, 0x64, 0x29, 0x10, 
+       0xF0, 0x2C, 0xA6, 0x35, 0xF0, 0x0F, 0xAD, 0x02, 
+       0x7C, 0x29, 0x10, 0xD0, 0x21, 0x8E, 0x00, 0x64, 
+       0x64, 0x35, 0x4C, 0x6C, 0x3C, 0xA6, 0x34, 0xE4, 
+       0x33, 0xF0, 0x13, 0xA5, 0x32, 0xD0, 0x0F, 0xAD, 
+       0x02, 0x7C, 0x29, 0x10, 0xD0, 0x08, 0xBD, 0x00, 
+       0x06, 0x8D, 0x00, 0x64, 0xE6, 0x34, 0xAD, 0x02, 
+       0x6C, 0x89, 0x08, 0xF0, 0x3B, 0x89, 0x02, 0xF0, 
+       0x1B, 0xAD, 0x00, 0x6C, 0xD0, 0x32, 0xA6, 0x3C, 
+       0xA9, 0x01, 0x9D, 0x00, 0x15, 0xA9, 0x01, 0x9D, 
+       0x00, 0x0E, 0xE8, 0xE4, 0x3D, 0xF0, 0x02, 0x86, 
+       0x3C, 0x4C, 0xAE, 0x3C, 0xA6, 0x3C, 0xAD, 0x00, 
+       0x6C, 0x9D, 0x00, 0x0E, 0x9E, 0x00, 0x15, 0xE8, 
+       0xE4, 0x3D, 0xF0, 0x02, 0x86, 0x3C, 0x29, 0x7F, 
+       0xC9, 0x13, 0xD0, 0x04, 0xA5, 0x47, 0x85, 0x3E, 
+       0xAD, 0x02, 0x6C, 0x29, 0x10, 0xF0, 0x2C, 0xA6, 
+       0x41, 0xF0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x20, 
+       0xD0, 0x21, 0x8E, 0x00, 0x6C, 0x64, 0x41, 0x4C, 
+       0xE1, 0x3C, 0xA6, 0x40, 0xE4, 0x3F, 0xF0, 0x13, 
+       0xA5, 0x3E, 0xD0, 0x0F, 0xAD, 0x02, 0x7C, 0x29, 
+       0x20, 0xD0, 0x08, 0xBD, 0x00, 0x07, 0x8D, 0x00, 
+       0x6C, 0xE6, 0x40, 0xAD, 0x02, 0x74, 0x89, 0x08, 
+       0xF0, 0x3B, 0x89, 0x02, 0xF0, 0x1B, 0xAD, 0x00, 
+       0x74, 0xD0, 0x32, 0xA6, 0x48, 0xA9, 0x01, 0x9D, 
+       0x00, 0x16, 0xA9, 0x01, 0x9D, 0x00, 0x0F, 0xE8, 
+       0xE4, 0x49, 0xF0, 0x02, 0x86, 0x48, 0x4C, 0x23, 
+       0x3D, 0xA6, 0x48, 0xAD, 0x00, 0x74, 0x9D, 0x00, 
+       0x0F, 0x9E, 0x00, 0x16, 0xE8, 0xE4, 0x49, 0xF0, 
+       0x02, 0x86, 0x48, 0x29, 0x7F, 0xC9, 0x13, 0xD0, 
+       0x04, 0xA5, 0x53, 0x85, 0x4A, 0xAD, 0x02, 0x74, 
+       0x29, 0x10, 0xF0, 0x2C, 0xA6, 0x4D, 0xF0, 0x0F, 
+       0xAD, 0x02, 0x7C, 0x29, 0x40, 0xD0, 0x21, 0x8E, 
+       0x00, 0x74, 0x64, 0x4D, 0x4C, 0x56, 0x3D, 0xA6, 
+       0x4C, 0xE4, 0x4B, 0xF0, 0x13, 0xA5, 0x4A, 0xD0, 
+       0x0F, 0xAD, 0x02, 0x7C, 0x29, 0x40, 0xD0, 0x08, 
+       0xBD, 0x00, 0x08, 0x8D, 0x00, 0x74, 0xE6, 0x4C, 
+       0x60, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
+       0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0xD0, 0x00, 0x38, 
+       0xCE, 0xC0, 
+};
diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c
new file mode 100644 (file)
index 0000000..04b888a
--- /dev/null
@@ -0,0 +1,716 @@
+/* 
+ * Sony Programmable I/O Control Device driver for VAIO
+ *
+ * Copyright (C) 2001 Stelian Pop <stelian.pop@fr.alcove.com>, AlcĂ´ve
+ *
+ * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
+ *
+ * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
+ *
+ * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include "sonypi.h"
+#include <linux/sonypi.h>
+
+static struct sonypi_device sonypi_device;
+static int minor = -1;
+static int verbose; /* = 0 */
+static int fnkeyinit; /* = 0 */
+static int camera; /* = 0 */
+
+/* Inits the queue */
+static inline void sonypi_initq(void) {
+        sonypi_device.queue.head = sonypi_device.queue.tail = 0;
+       sonypi_device.queue.len = 0;
+       sonypi_device.queue.s_lock = (spinlock_t)SPIN_LOCK_UNLOCKED;
+       init_waitqueue_head(&sonypi_device.queue.proc_list);
+}
+
+/* Pulls an event from the queue */
+static inline unsigned char sonypi_pullq(void) {
+        unsigned char result;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sonypi_device.queue.s_lock, flags);
+       if (!sonypi_device.queue.len) {
+               spin_unlock_irqrestore(&sonypi_device.queue.s_lock, flags);
+               return 0;
+       }
+       result = sonypi_device.queue.buf[sonypi_device.queue.head];
+        sonypi_device.queue.head++;
+       sonypi_device.queue.head &= (SONYPI_BUF_SIZE - 1);
+       sonypi_device.queue.len--;
+       spin_unlock_irqrestore(&sonypi_device.queue.s_lock, flags);
+        return result;
+}
+
+/* Pushes an event into the queue */
+static inline void sonypi_pushq(unsigned char event) {
+       unsigned long flags;
+
+       spin_lock_irqsave(&sonypi_device.queue.s_lock, flags);
+       if (sonypi_device.queue.len == SONYPI_BUF_SIZE) {
+               /* remove the first element */
+               sonypi_device.queue.head++;
+               sonypi_device.queue.head &= (SONYPI_BUF_SIZE - 1);
+               sonypi_device.queue.len--;
+       }
+       sonypi_device.queue.buf[sonypi_device.queue.tail] = event;
+       sonypi_device.queue.tail++;
+       sonypi_device.queue.tail &= (SONYPI_BUF_SIZE - 1);
+       sonypi_device.queue.len++;
+
+       kill_fasync(&sonypi_device.queue.fasync, SIGIO, POLL_IN);
+       wake_up_interruptible(&sonypi_device.queue.proc_list);
+       spin_unlock_irqrestore(&sonypi_device.queue.s_lock, flags);
+}
+
+/* Tests if the queue is empty */
+static inline int sonypi_emptyq(void) {
+        int result;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sonypi_device.queue.s_lock, flags);
+        result = (sonypi_device.queue.len == 0);
+       spin_unlock_irqrestore(&sonypi_device.queue.s_lock, flags);
+        return result;
+}
+
+static void sonypi_ecrset(u16 addr, u16 value) {
+       int n = 100;
+
+       while (n-- && (inw_p(SONYPI_CST_IOPORT) & 3))
+               udelay(1);
+       outw_p(0x81, SONYPI_CST_IOPORT);
+       while (inw_p(SONYPI_CST_IOPORT) & 2)
+               udelay(1);
+       outw_p(addr, SONYPI_DATA_IOPORT);
+       while (inw_p(SONYPI_CST_IOPORT) & 2)
+               udelay(1);
+       outw_p(value, SONYPI_DATA_IOPORT);
+       while (inw_p(SONYPI_CST_IOPORT) & 2)
+               udelay(1);
+}
+
+static u16 sonypi_ecrget(u16 addr) {
+       int n = 100;
+
+       while (n-- && (inw_p(SONYPI_CST_IOPORT) & 3))
+               udelay(1);
+       outw_p(0x80, SONYPI_CST_IOPORT);
+       while (inw_p(SONYPI_CST_IOPORT) & 2)
+               udelay(1);
+       outw_p(addr, SONYPI_DATA_IOPORT);
+       while (inw_p(SONYPI_CST_IOPORT) & 2)
+               udelay(1);
+       return inw_p(SONYPI_DATA_IOPORT);
+}
+
+/* Initializes the device - this comes from the AML code in the ACPI bios */
+static void __devinit sonypi_normal_srs(void) {
+       u32 v;
+
+       pci_read_config_dword(sonypi_device.dev, SONYPI_G10A, &v);
+       v = (v & 0xFFFF0000) | ((u32)sonypi_device.ioport1);
+       pci_write_config_dword(sonypi_device.dev, SONYPI_G10A, v);
+
+       pci_read_config_dword(sonypi_device.dev, SONYPI_G10A, &v);
+       v = (v & 0xFFF0FFFF) | 
+           (((u32)sonypi_device.ioport1 ^ sonypi_device.ioport2) << 16);
+       pci_write_config_dword(sonypi_device.dev, SONYPI_G10A, v);
+
+       v = inl(SONYPI_IRQ_PORT);
+       v &= ~(((u32)0x3) << SONYPI_IRQ_SHIFT);
+       v |= (((u32)sonypi_device.bits) << SONYPI_IRQ_SHIFT);
+       outl(v, SONYPI_IRQ_PORT);
+
+       pci_read_config_dword(sonypi_device.dev, SONYPI_G10A, &v);
+       v = (v & 0xFF1FFFFF) | 0x00C00000;
+       pci_write_config_dword(sonypi_device.dev, SONYPI_G10A, v);
+}
+
+static void __devinit sonypi_r505_srs(void) {
+       sonypi_ecrset(SONYPI_SHIB, (sonypi_device.ioport1 & 0xFF00) >> 8);
+       sonypi_ecrset(SONYPI_SLOB,  sonypi_device.ioport1 & 0x00FF);
+       sonypi_ecrset(SONYPI_SIRQ,  sonypi_device.bits);
+       udelay(10);
+}
+
+/* Disables the device - this comes from the AML code in the ACPI bios */
+static void __devexit sonypi_normal_dis(void) {
+       u32 v;
+
+       pci_read_config_dword(sonypi_device.dev, SONYPI_G10A, &v);
+       v = v & 0xFF3FFFFF;
+       pci_write_config_dword(sonypi_device.dev, SONYPI_G10A, v);
+
+       v = inl(SONYPI_IRQ_PORT);
+       v |= (0x3 << SONYPI_IRQ_SHIFT);
+       outl(v, SONYPI_IRQ_PORT);
+}
+
+static void __devexit sonypi_r505_dis(void) {
+       sonypi_ecrset(SONYPI_SHIB, 0);
+       sonypi_ecrset(SONYPI_SLOB, 0);
+       sonypi_ecrset(SONYPI_SIRQ, 0);
+}
+
+static u8 sonypi_call1(u8 dev) {
+       u8 v1, v2;
+
+       while (inb_p(sonypi_device.ioport2) & 2)
+               udelay(1);
+       outb(dev, sonypi_device.ioport2);
+       v1 = inb_p(sonypi_device.ioport2);
+       v2 = inb_p(sonypi_device.ioport1);
+       return v2;
+}
+
+static u8 sonypi_call2(u8 dev, u8 fn) {
+       u8 v1;
+
+       while (inb_p(sonypi_device.ioport2) & 2)
+               udelay(1);
+       outb(dev, sonypi_device.ioport2);
+
+       while (inb_p(sonypi_device.ioport2) & 2)
+               udelay(1);
+       outb(fn, sonypi_device.ioport1);
+
+       v1 = inb_p(sonypi_device.ioport1);
+       return v1;
+}
+
+static u8 sonypi_call3(u8 dev, u8 fn, u8 v) {
+       u8 v1;
+
+       while (inb_p(sonypi_device.ioport2) & 2)
+               udelay(1);
+       outb(dev, sonypi_device.ioport2);
+
+       while (inb_p(sonypi_device.ioport2) & 2)
+               udelay(1);
+       outb(fn, sonypi_device.ioport1);
+
+       while (inb_p(sonypi_device.ioport2) & 2)
+               udelay(1);
+       outb(v, sonypi_device.ioport1);
+
+       v1 = inb_p(sonypi_device.ioport1);
+       return v1;
+}
+
+static u8 sonypi_read(u8 fn) {
+       u8 v1, v2;
+       int n = 100;
+
+       while (n--) {
+               v1 = sonypi_call2(0x8f, fn);
+               v2 = sonypi_call2(0x8f, fn);
+               if (v1 == v2 && v1 != 0xff)
+                       return v1;
+       }
+       return 0xff;
+}
+
+/* Set brightness, hue etc */
+static void sonypi_set(u8 fn, u8 v) {
+       int n = 100;
+       
+       while (n--)
+               if (sonypi_call3(0x90, fn, v) == 0) 
+                       break;
+}
+
+/* Tests if the camera is ready */
+static int sonypi_camera_ready(void) {
+       u8 v;
+
+       v = sonypi_call2(0x8f, SONYPI_CAMERA_STATUS);
+       return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
+}
+
+/* Turns the camera off */
+static void sonypi_camera_off(void) {
+
+       sonypi_set(SONYPI_CAMERA_PICTURE, SONYPI_CAMERA_MUTE_MASK);
+
+       if (!sonypi_device.camera_power)
+               return;
+
+       sonypi_call2(0x91, 0); 
+       sonypi_device.camera_power = 0;
+}
+
+/* Turns the camera on */
+static void sonypi_camera_on(void) {
+       int i, j;
+
+       if (sonypi_device.camera_power)
+               return;
+
+       for (j = 5; j > 0; j--) {
+
+               while (sonypi_call2(0x91, 0x1) != 0) {
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule_timeout(1);
+               }
+               sonypi_call1(0x93);
+
+               for (i = 400; i > 0; i--) {
+                       if (sonypi_camera_ready())
+                               break;
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule_timeout(1);
+               }
+               if (i != 0)
+                       break;
+       }
+       
+       if (j == 0) {
+               printk(KERN_WARNING "sonypi: failed to power on camera\n");
+               return;
+       }
+
+       sonypi_set(0x10, 0x5a);
+       sonypi_device.camera_power = 1;
+}
+
+/* Interrupt handler: some event is available */
+void sonypi_irq(int irq, void *dev_id, struct pt_regs *regs) {
+       u8 v1, v2, event = 0;
+       int i;
+       u8 sonypi_jogger_ev, sonypi_fnkey_ev;
+
+       if (sonypi_device.model == SONYPI_DEVICE_MODEL_R505) {
+               sonypi_jogger_ev = SONYPI_R505_JOGGER_EV;
+               sonypi_fnkey_ev = SONYPI_R505_FNKEY_EV;
+       }
+       else {
+               sonypi_jogger_ev = SONYPI_NORMAL_JOGGER_EV;
+               sonypi_fnkey_ev = SONYPI_NORMAL_FNKEY_EV;
+       }
+
+       v1 = inb_p(sonypi_device.ioport1);
+       v2 = inb_p(sonypi_device.ioport2);
+
+       if ((v2 & sonypi_jogger_ev) == sonypi_jogger_ev) {
+               for (i = 0; sonypi_joggerev[i].event; i++)
+                       if (sonypi_joggerev[i].data == v1) {
+                               event = sonypi_joggerev[i].event;
+                               goto found;
+                       }
+       }
+       if ((v2 & SONYPI_CAPTURE_EV) == SONYPI_CAPTURE_EV) {
+               for (i = 0; sonypi_captureev[i].event; i++)
+                       if (sonypi_captureev[i].data == v1) {
+                               event = sonypi_captureev[i].event;
+                               goto found;
+                       }
+       }
+       if ((v2 & sonypi_fnkey_ev) == sonypi_fnkey_ev) {
+               for (i = 0; sonypi_fnkeyev[i].event; i++)
+                       if (sonypi_fnkeyev[i].data == v1) {
+                               event = sonypi_fnkeyev[i].event;
+                               goto found;
+                       }
+       }
+       if ((v2 & SONYPI_BLUETOOTH_EV) == SONYPI_BLUETOOTH_EV) {
+               for (i = 0; sonypi_blueev[i].event; i++)
+                       if (sonypi_blueev[i].data == v1) {
+                               event = sonypi_blueev[i].event;
+                               goto found;
+                       }
+       }
+       if (verbose)
+               printk(KERN_WARNING 
+                      "sonypi: unknown event port1=0x%x,port2=0x%x\n",v1,v2);
+       return;
+
+found:
+       sonypi_pushq(event);
+}
+
+/* External camera command (exported to the motion eye v4l driver) */
+u8 sonypi_camera_command(int command, u8 value) {
+       u8 ret = 0;
+
+       if (!camera)
+               return 0;
+
+       down(&sonypi_device.lock);
+
+       switch(command) {
+               case SONYPI_COMMAND_GETCAMERA:
+                       ret = sonypi_camera_ready();
+                       break;
+               case SONYPI_COMMAND_SETCAMERA:
+                       if (value)
+                               sonypi_camera_on();
+                       else
+                               sonypi_camera_off();
+                       break;
+               case SONYPI_COMMAND_GETCAMERABRIGHTNESS:
+                       ret = sonypi_read(SONYPI_CAMERA_BRIGHTNESS);
+                       break;
+               case SONYPI_COMMAND_SETCAMERABRIGHTNESS:
+                       sonypi_set(SONYPI_CAMERA_BRIGHTNESS, value);
+                       break;
+               case SONYPI_COMMAND_GETCAMERACONTRAST:
+                       ret = sonypi_read(SONYPI_CAMERA_CONTRAST);
+                       break;
+               case SONYPI_COMMAND_SETCAMERACONTRAST:
+                       sonypi_set(SONYPI_CAMERA_CONTRAST, value);
+                       break;
+               case SONYPI_COMMAND_GETCAMERAHUE:
+                       ret = sonypi_read(SONYPI_CAMERA_HUE);
+                       break;
+               case SONYPI_COMMAND_SETCAMERAHUE:
+                       sonypi_set(SONYPI_CAMERA_HUE, value);
+                       break;
+               case SONYPI_COMMAND_GETCAMERACOLOR:
+                       ret = sonypi_read(SONYPI_CAMERA_COLOR);
+                       break;
+               case SONYPI_COMMAND_SETCAMERACOLOR:
+                       sonypi_set(SONYPI_CAMERA_COLOR, value);
+                       break;
+               case SONYPI_COMMAND_GETCAMERASHARPNESS:
+                       ret = sonypi_read(SONYPI_CAMERA_SHARPNESS);
+                       break;
+               case SONYPI_COMMAND_SETCAMERASHARPNESS:
+                       sonypi_set(SONYPI_CAMERA_SHARPNESS, value);
+                       break;
+               case SONYPI_COMMAND_GETCAMERAPICTURE:
+                       ret = sonypi_read(SONYPI_CAMERA_PICTURE);
+                       break;
+               case SONYPI_COMMAND_SETCAMERAPICTURE:
+                       sonypi_set(SONYPI_CAMERA_PICTURE, value);
+                       break;
+               case SONYPI_COMMAND_GETCAMERAAGC:
+                       ret = sonypi_read(SONYPI_CAMERA_AGC);
+                       break;
+               case SONYPI_COMMAND_SETCAMERAAGC:
+                       sonypi_set(SONYPI_CAMERA_AGC, value);
+                       break;
+               case SONYPI_COMMAND_GETCAMERADIRECTION:
+                       ret = sonypi_read(SONYPI_CAMERA_STATUS);
+                       ret &= SONYPI_DIRECTION_BACKWARDS;
+                       break;
+               case SONYPI_COMMAND_GETCAMERAROMVERSION:
+                       ret = sonypi_read(SONYPI_CAMERA_ROMVERSION);
+                       break;
+               case SONYPI_COMMAND_GETCAMERAREVISION:
+                       ret = sonypi_read(SONYPI_CAMERA_REVISION);
+                       break;
+       }
+       up(&sonypi_device.lock);
+       return ret;
+}
+
+static int sonypi_misc_fasync(int fd, struct file *filp, int on) {
+       int retval;
+
+       retval = fasync_helper(fd, filp, on, &sonypi_device.queue.fasync);
+       if (retval < 0)
+               return retval;
+       return 0;
+}
+
+static int sonypi_misc_release(struct inode * inode, struct file * file) {
+       sonypi_misc_fasync(-1, file, 0);
+       down(&sonypi_device.lock);
+       sonypi_device.open_count--;
+       up(&sonypi_device.lock);
+       return 0;
+}
+
+static int sonypi_misc_open(struct inode * inode, struct file * file) {
+       down(&sonypi_device.lock);
+       if (sonypi_device.open_count)
+               goto out;
+       sonypi_device.open_count++;
+       /* Flush input queue */
+       sonypi_initq();
+out:
+       up(&sonypi_device.lock);
+       return 0;
+}
+
+static ssize_t sonypi_misc_read(struct file * file, char * buf, 
+                               size_t count, loff_t *pos) {
+       DECLARE_WAITQUEUE(wait, current);
+       ssize_t i = count;
+       unsigned char c;
+
+       if (sonypi_emptyq()) {
+               if (file->f_flags & O_NONBLOCK)
+                       return -EAGAIN;
+               add_wait_queue(&sonypi_device.queue.proc_list, &wait);
+repeat:
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (sonypi_emptyq() && !signal_pending(current)) {
+                       schedule();
+                       goto repeat;
+               }
+               current->state = TASK_RUNNING;
+               remove_wait_queue(&sonypi_device.queue.proc_list, &wait);
+       }
+       while (i > 0 && !sonypi_emptyq()) {
+               c = sonypi_pullq();
+               put_user(c, buf++);
+               i--;
+        }
+       if (count - i) {
+               file->f_dentry->d_inode->i_atime = CURRENT_TIME;
+               return count-i;
+       }
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+       return 0;
+}
+
+static unsigned int sonypi_misc_poll(struct file *file, poll_table * wait) {
+       poll_wait(file, &sonypi_device.queue.proc_list, wait);
+       if (!sonypi_emptyq())
+               return POLLIN | POLLRDNORM;
+       return 0;
+}
+
+static int sonypi_misc_ioctl(struct inode *ip, struct file *fp, 
+                            unsigned int cmd, unsigned long arg) {
+       int ret = 0;
+       u8 val;
+
+       down(&sonypi_device.lock);
+       switch (cmd) {
+               case SONYPI_IOCGBRT:
+                       val = sonypi_ecrget(0x96) & 0xff;
+                       if (copy_to_user((u8 *)arg, &val, sizeof(val))) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       break;
+               case SONYPI_IOCSBRT:
+                       if (copy_from_user(&val, (u8 *)arg, sizeof(val))) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       sonypi_ecrset(0x96, val);
+                       break;
+       default:
+               ret = -EINVAL;
+       }
+out:
+       up(&sonypi_device.lock);
+       return ret;
+}
+
+static struct file_operations sonypi_misc_fops = {
+       owner:          THIS_MODULE,
+       read:           sonypi_misc_read,
+       poll:           sonypi_misc_poll,
+       open:           sonypi_misc_open,
+       release:        sonypi_misc_release,
+       fasync:         sonypi_misc_fasync,
+       ioctl:          sonypi_misc_ioctl,
+};
+
+struct miscdevice sonypi_misc_device = {
+       -1, "sonypi", &sonypi_misc_fops
+};
+
+static int __devinit sonypi_probe(struct pci_dev *pcidev, 
+                                 const struct pci_device_id *ent) {
+       int i, ret;
+       struct sonypi_ioport_list *ioport_list;
+       struct sonypi_irq_list *irq_list;
+
+       if (sonypi_device.dev) {
+               printk(KERN_ERR "sonypi: only one device allowed!\n"),
+               ret = -EBUSY;
+               goto out1;
+       }
+       sonypi_device.dev = pcidev;
+       sonypi_device.model = (int)ent->driver_data;
+       sonypi_initq();
+       init_MUTEX(&sonypi_device.lock);
+       
+       if (pci_enable_device(pcidev)) {
+               printk(KERN_ERR "sonypi: pci_enable_device failed\n");
+               ret = -EIO;
+               goto out1;
+       }
+
+       sonypi_misc_device.minor = (minor == -1) ? 
+               MISC_DYNAMIC_MINOR : minor;
+       if ((ret = misc_register(&sonypi_misc_device))) {
+               printk(KERN_ERR "sonypi: misc_register failed\n");
+               goto out1;
+       }
+
+       if (sonypi_device.model == SONYPI_DEVICE_MODEL_R505) {
+               ioport_list = sonypi_r505_ioport_list;
+               sonypi_device.region_size = SONYPI_R505_REGION_SIZE;
+               irq_list = sonypi_r505_irq_list;
+       }
+       else {
+               ioport_list = sonypi_normal_ioport_list;
+               sonypi_device.region_size = SONYPI_NORMAL_REGION_SIZE;
+               irq_list = sonypi_normal_irq_list;
+       }
+
+       for (i = 0; ioport_list[i].port1; i++) {
+               if (request_region(ioport_list[i].port1, 
+                                  sonypi_device.region_size, 
+                                  "Sony Programable I/O Device")) {
+                       /* get the ioport */
+                       sonypi_device.ioport1 = ioport_list[i].port1;
+                       sonypi_device.ioport2 = ioport_list[i].port2;
+                       break;
+               }
+       }
+       if (!sonypi_device.ioport1) {
+               printk(KERN_ERR "sonypi: request_region failed\n");
+               ret = -ENODEV;
+               goto out2;
+       }
+
+       for (i = 0; irq_list[i].irq; i++) {
+               if (!request_irq(irq_list[i].irq, sonypi_irq, 
+                                SA_INTERRUPT, "sonypi", sonypi_irq)) {
+                       sonypi_device.irq = irq_list[i].irq;
+                       sonypi_device.bits = irq_list[i].bits;
+                       break;
+               }
+       }
+       if (!sonypi_device.irq ) {
+               printk(KERN_ERR "sonypi: request_irq failed\n");
+               ret = -ENODEV;
+               goto out3;
+       }
+
+       if (fnkeyinit)
+               outb(0xf0, 0xb2);
+
+       if (sonypi_device.model == SONYPI_DEVICE_MODEL_R505)
+               sonypi_r505_srs();
+       else
+               sonypi_normal_srs();
+
+       sonypi_call1(0x82);
+       sonypi_call2(0x81, 0xff);
+       sonypi_call1(0x92); 
+
+       printk(KERN_INFO "sonypi: Sony Programmable I/O Controller Driver v%d.%d.\n",
+              SONYPI_DRIVER_MAJORVERSION,
+              SONYPI_DRIVER_MINORVERSION);
+       printk(KERN_INFO "sonypi: detected %s model, camera = %s\n",
+              (sonypi_device.model == SONYPI_DEVICE_MODEL_NORMAL) ?
+              "normal" : "R505",
+              camera ? "on" : "off");
+       printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n",
+              sonypi_device.irq, 
+              sonypi_device.ioport1, sonypi_device.ioport2);
+       if (minor == -1)
+               printk(KERN_INFO "sonypi: device allocated minor is %d\n",
+                      sonypi_misc_device.minor);
+
+       return 0;
+
+out3:
+       release_region(sonypi_device.ioport1, sonypi_device.region_size);
+out2:
+       misc_deregister(&sonypi_misc_device);
+out1:
+       return ret;
+}
+
+static void __devexit sonypi_remove(struct pci_dev *pcidev) {
+       sonypi_call2(0x81, 0); /* make sure we don't get any more events */
+       if (camera)
+               sonypi_camera_off();
+       if (sonypi_device.model == SONYPI_DEVICE_MODEL_R505)
+               sonypi_r505_dis();
+       else
+               sonypi_normal_dis();
+       free_irq(sonypi_device.irq, sonypi_irq);
+       release_region(sonypi_device.ioport1, sonypi_device.region_size);
+       misc_deregister(&sonypi_misc_device);
+       printk(KERN_INFO "sonypi: removed.\n");
+}
+
+static struct pci_device_id sonypi_id_tbl[] __devinitdata = {
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, 
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+         (unsigned long) SONYPI_DEVICE_MODEL_NORMAL },
+       { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+         (unsigned long) SONYPI_DEVICE_MODEL_R505 },
+       { }
+};
+
+MODULE_DEVICE_TABLE(pci, sonypi_id_tbl);
+
+static struct pci_driver sonypi_driver = {
+       name:           "sonypi",
+       id_table:       sonypi_id_tbl,
+       probe:          sonypi_probe,
+       remove:         sonypi_remove,
+};
+
+static int __init sonypi_init_module(void) {
+       return pci_module_init(&sonypi_driver);
+}
+
+static void __exit sonypi_cleanup_module(void) {
+       pci_unregister_driver(&sonypi_driver);
+}
+
+/* Module entry points */
+module_init(sonypi_init_module);
+module_exit(sonypi_cleanup_module);
+
+MODULE_AUTHOR("Stelian Pop <stelian.pop@fr.alcove.com>");
+MODULE_DESCRIPTION("Sony Programmable I/O Control Device driver");
+
+MODULE_PARM(minor,"i");
+MODULE_PARM_DESC(minor, "minor number of the misc device, default is -1 (automatic)");
+MODULE_PARM(verbose,"i");
+MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
+MODULE_PARM(fnkeyinit,"i");
+MODULE_PARM_DESC(fnkeyinit, "set this if your Fn keys do not generate any event");
+MODULE_PARM(camera,"i");
+MODULE_PARM_DESC(camera, "set this if you have a MotionEye camera (PictureBook series)");
+
+EXPORT_SYMBOL(sonypi_camera_command);
diff --git a/drivers/char/sonypi.h b/drivers/char/sonypi.h
new file mode 100644 (file)
index 0000000..9160f0f
--- /dev/null
@@ -0,0 +1,228 @@
+/* 
+ * Sony Programmable I/O Control Device driver for VAIO
+ *
+ * Copyright (C) 2001 Stelian Pop <stelian.pop@fr.alcove.com>, AlcĂ´ve
+ *
+ * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
+ *
+ * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
+ *
+ * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ * 
+ * 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.
+ *
+ */
+
+#ifndef _SONYPI_PRIV_H_ 
+#define _SONYPI_PRIV_H_
+
+#ifdef __KERNEL__
+
+#define SONYPI_DRIVER_MAJORVERSION     1
+#define SONYPI_DRIVER_MINORVERSION     2
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include "linux/sonypi.h"
+
+/* Normal models use those */
+#define SONYPI_IRQ_PORT                        0x8034
+#define SONYPI_IRQ_SHIFT               22
+#define SONYPI_BASE                    0x50
+#define SONYPI_G10A                    (SONYPI_BASE+0x14)
+#define SONYPI_NORMAL_REGION_SIZE      0x08
+
+/* R505 series specifics */
+#define SONYPI_SIRQ            0x9b
+#define SONYPI_SLOB            0x9c
+#define SONYPI_SHIB            0x9d
+#define SONYPI_R505_REGION_SIZE        0x20
+
+/* ioports used for brightness and R505 events */
+#define SONYPI_DATA_IOPORT     0x62
+#define SONYPI_CST_IOPORT      0x66
+
+/* The set of possible ioports */
+struct sonypi_ioport_list {
+       u16     port1;
+       u16     port2;
+};
+
+static struct sonypi_ioport_list sonypi_normal_ioport_list[] = {
+       { 0x10c0, 0x10c4 },     /* looks like the default on C1Vx */
+       { 0x1080, 0x1084 },
+       { 0x1090, 0x1094 },
+       { 0x10a0, 0x10a4 },
+       { 0x10b0, 0x10b4 },
+       { 0x0, 0x0 }
+};
+
+static struct sonypi_ioport_list sonypi_r505_ioport_list[] = {
+       { 0x1080, 0x1084 },
+       { 0x10a0, 0x10a4 },
+       { 0x10c0, 0x10c4 },
+       { 0x10e0, 0x10e4 },
+       { 0x0, 0x0 }
+};
+
+/* The set of possible interrupts */
+struct sonypi_irq_list {
+       u16     irq;
+       u16     bits;
+};
+
+static struct sonypi_irq_list sonypi_normal_irq_list[] = {
+       { 11, 0x2 },    /* IRQ 11, GO22=0,GO23=1 in AML */
+       { 10, 0x1 },    /* IRQ 10, GO22=1,GO23=0 in AML */
+       {  5, 0x0 },    /* IRQ  5, GO22=0,GO23=0 in AML */
+       {  0, 0x3 }     /* no IRQ, GO22=1,GO23=1 in AML */
+};
+
+static struct sonypi_irq_list sonypi_r505_irq_list[] = {
+       { 11, 0x80 },   /* IRQ 11, 0x80 in SIRQ in AML */
+       { 10, 0x40 },   /* IRQ 10, 0x40 in SIRQ in AML */
+       {  9, 0x20 },   /* IRQ  9, 0x20 in SIRQ in AML */
+       {  6, 0x10 },   /* IRQ  6, 0x10 in SIRQ in AML */
+       {  0, 0x00 }    /* no IRQ, 0x00 in SIRQ in AML */
+};
+
+#define SONYPI_CAMERA_BRIGHTNESS               0
+#define SONYPI_CAMERA_CONTRAST                 1
+#define SONYPI_CAMERA_HUE                      2
+#define SONYPI_CAMERA_COLOR                    3
+#define SONYPI_CAMERA_SHARPNESS                        4
+
+#define SONYPI_CAMERA_PICTURE                  5
+#define SONYPI_CAMERA_EXPOSURE_MASK            0xC
+#define SONYPI_CAMERA_WHITE_BALANCE_MASK       0x3
+#define SONYPI_CAMERA_PICTURE_MODE_MASK                0x30
+#define SONYPI_CAMERA_MUTE_MASK                        0x40
+
+/* the rest don't need a loop until not 0xff */
+#define SONYPI_CAMERA_AGC                      6
+#define SONYPI_CAMERA_AGC_MASK                 0x30
+#define SONYPI_CAMERA_SHUTTER_MASK             0x7
+
+#define SONYPI_CAMERA_SHUTDOWN_REQUEST         7
+#define SONYPI_CAMERA_CONTROL                  0x10
+
+#define SONYPI_CAMERA_STATUS                   7
+#define SONYPI_CAMERA_STATUS_READY             0x2
+#define SONYPI_CAMERA_STATUS_POSITION          0x4
+
+#define SONYPI_DIRECTION_BACKWARDS             0x4
+
+#define SONYPI_CAMERA_REVISION                         8
+#define SONYPI_CAMERA_ROMVERSION               9
+
+/* key press event data (ioport2) */
+#define SONYPI_NORMAL_JOGGER_EV        0x10
+#define SONYPI_R505_JOGGER_EV  0x08
+#define SONYPI_CAPTURE_EV      0x60
+#define SONYPI_NORMAL_FNKEY_EV 0x20
+#define SONYPI_R505_FNKEY_EV   0x08
+#define SONYPI_BLUETOOTH_EV    0x30
+
+struct sonypi_event {
+       u8      data;
+       u8      event;
+};
+
+/* The set of possible jogger events  */
+static struct sonypi_event sonypi_joggerev[] = {
+       { 0x1f, SONYPI_EVENT_JOGDIAL_UP },
+       { 0x01, SONYPI_EVENT_JOGDIAL_DOWN },
+       { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED },
+       { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED },
+       { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED },
+       { 0x00, SONYPI_EVENT_JOGDIAL_RELEASED },
+       { 0x00, 0x00 }
+};
+
+/* The set of possible capture button events */
+static struct sonypi_event sonypi_captureev[] = {
+       { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED },
+       { 0x07, SONYPI_EVENT_CAPTURE_PRESSED },
+       { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED },
+       { 0x00, SONYPI_EVENT_CAPTURE_RELEASED },
+       { 0x00, 0x00 }
+};
+
+/* The set of possible fnkeys events */
+static struct sonypi_event sonypi_fnkeyev[] = {
+       { 0x10, SONYPI_EVENT_FNKEY_ESC },
+       { 0x11, SONYPI_EVENT_FNKEY_F1 },
+       { 0x12, SONYPI_EVENT_FNKEY_F2 },
+       { 0x13, SONYPI_EVENT_FNKEY_F3 },
+       { 0x14, SONYPI_EVENT_FNKEY_F4 },
+       { 0x15, SONYPI_EVENT_FNKEY_F5 },
+       { 0x16, SONYPI_EVENT_FNKEY_F6 },
+       { 0x17, SONYPI_EVENT_FNKEY_F7 },
+       { 0x18, SONYPI_EVENT_FNKEY_F8 },
+       { 0x19, SONYPI_EVENT_FNKEY_F9 },
+       { 0x1a, SONYPI_EVENT_FNKEY_F10 },
+       { 0x1b, SONYPI_EVENT_FNKEY_F11 },
+       { 0x1c, SONYPI_EVENT_FNKEY_F12 },
+       { 0x21, SONYPI_EVENT_FNKEY_1 },
+       { 0x22, SONYPI_EVENT_FNKEY_2 },
+       { 0x31, SONYPI_EVENT_FNKEY_D },
+       { 0x32, SONYPI_EVENT_FNKEY_E },
+       { 0x33, SONYPI_EVENT_FNKEY_F },
+       { 0x34, SONYPI_EVENT_FNKEY_S },
+       { 0x35, SONYPI_EVENT_FNKEY_B },
+       { 0x00, 0x00 }
+};
+
+/* The set of possible bluetooth events */
+static struct sonypi_event sonypi_blueev[] = {
+       { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
+       { 0x00, 0x00 }
+};
+
+#define SONYPI_BUF_SIZE        128
+struct sonypi_queue {
+       unsigned long head;
+       unsigned long tail;
+       unsigned long len;
+       spinlock_t s_lock;
+       wait_queue_head_t proc_list;
+       struct fasync_struct *fasync;
+       unsigned char buf[SONYPI_BUF_SIZE];
+};
+
+#define SONYPI_DEVICE_MODEL_NORMAL     1
+#define SONYPI_DEVICE_MODEL_R505       2
+
+struct sonypi_device {
+       struct pci_dev *dev;
+       u16 irq;
+       u16 bits;
+       u16 ioport1;
+       u16 ioport2;
+       u16 region_size;
+       int camera_power;
+       struct semaphore lock;
+       struct sonypi_queue queue;
+       int open_count;
+       int model;
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _SONYPI_PRIV_H_ */
index e528417d4c924350d9d205211efed93834253509..9458888ee323ed0a25a4194af22b0933b8581c15 100644 (file)
@@ -918,7 +918,6 @@ static int sx_set_real_termios (void *ptr)
                               SP_DCEN);
 
        sx_write_channel_byte (port, hi_break, 
-                              I_OTHER(port->gs.tty) ? 0:
                               (I_IGNBRK(port->gs.tty)?BR_IGN:0 |
                                I_BRKINT(port->gs.tty)?BR_INT:0));
 
@@ -1140,9 +1139,7 @@ static inline void sx_check_modem_signals (struct sx_port *port)
                sx_dprintk (SX_DEBUG_MODEMSIGNALS, "got a break.\n");
 
                sx_write_channel_byte (port, hi_state, hi_state);
-               if (port->gs.flags & ASYNC_SAK) {
-                       do_SAK (port->gs.tty);
-               }
+               gs_got_break (port);
        }
        if (hi_state & ST_DCD) {
                hi_state &= ~ST_DCD;
diff --git a/drivers/char/w83877f_wdt.c b/drivers/char/w83877f_wdt.c
new file mode 100644 (file)
index 0000000..610c903
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ *     W83877F Computer Watchdog Timer driver for Linux 2.4.x
+ *
+ *      Based on acquirewdt.c by Alan Cox,
+ *           and sbc60xxwdt.c by Jakob Oestergaard <jakob@ostenfeld.dk>
+ *     
+ *     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.
+ *     
+ *     The authors do NOT admit liability nor provide warranty for 
+ *     any of this software. This material is provided "AS-IS" in 
+ *      the hope that it may be useful for others.
+ *
+ *     (c) Copyright 2001    Scott Jennings <management@oro.net>
+ *
+ *           4/19 - 2001      [Initial revision]
+ *
+ *
+ *  Theory of operation:
+ *  A Watchdog Timer (WDT) is a hardware circuit that can 
+ *  reset the computer system in case of a software fault.
+ *  You probably knew that already.
+ *
+ *  Usually a userspace daemon will notify the kernel WDT driver
+ *  via the /proc/watchdog special device file that userspace is
+ *  still alive, at regular intervals.  When such a notification
+ *  occurs, the driver will usually tell the hardware watchdog
+ *  that everything is in order, and that the watchdog should wait
+ *  for yet another little while to reset the system.
+ *  If userspace fails (RAM error, kernel bug, whatever), the
+ *  notifications cease to occur, and the hardware watchdog will
+ *  reset the system (causing a reboot) after the timeout occurs.
+ *
+ *  This WDT driver is different from most other Linux WDT
+ *  drivers in that the driver will ping the watchdog by itself,
+ *  because this particular WDT has a very short timeout (1.6
+ *  seconds) and it would be insane to count on any userspace
+ *  daemon always getting scheduled within that time frame.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/smp_lock.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+
+#define OUR_NAME "w83877f_wdt"
+
+#define ENABLE_W83877F_PORT 0x3F0
+#define ENABLE_W83877F 0x87
+#define DISABLE_W83877F 0xAA
+#define WDT_PING 0x443
+#define WDT_REGISTER 0x14
+#define WDT_ENABLE 0x9C
+#define WDT_DISABLE 0x8C
+
+/*
+ * The W83877F seems to be fixed at 1.6s timeout (at least on the
+ * EMACS PC-104 board I'm using). If we reset the watchdog every
+ * ~250ms we should be safe.  */
+
+#define WDT_INTERVAL (HZ/4+1)
+
+/*
+ * We must not require too good response from the userspace daemon.
+ * Here we require the userspace daemon to send us a heartbeat
+ * char to /dev/watchdog every 30 seconds.
+ */
+
+#define WDT_HEARTBEAT (HZ * 30)
+
+static void wdt_timer_ping(unsigned long);
+static struct timer_list timer;
+static unsigned long next_heartbeat;
+static int wdt_is_open;
+static int wdt_expect_close;
+
+/*
+ *     Whack the dog
+ */
+
+static void wdt_timer_ping(unsigned long data)
+{
+       /* If we got a heartbeat pulse within the WDT_US_INTERVAL
+        * we agree to ping the WDT 
+        */
+       if(time_before(jiffies, next_heartbeat)) 
+       {
+               /* Ping the WDT by reading from WDT_PING */
+               inb_p(WDT_PING);
+               /* Re-set the timer interval */
+               timer.expires = jiffies + WDT_INTERVAL;
+               add_timer(&timer);
+       } else {
+               printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n");
+       }
+}
+
+/* 
+ * Utility routines
+ */
+
+static void wdt_change(int writeval)
+{
+       /* buy some time */
+       inb_p(WDT_PING);
+
+       /* make W83877F available */
+       outb_p(ENABLE_W83877F,ENABLE_W83877F_PORT);
+       outb_p(ENABLE_W83877F,ENABLE_W83877F_PORT);
+
+       /* enable watchdog */
+       outb_p(WDT_REGISTER,ENABLE_W83877F_PORT);
+       outb_p(writeval,ENABLE_W83877F_PORT+1);
+
+       /* lock the W8387FF away */
+       outb_p(DISABLE_W83877F,ENABLE_W83877F_PORT);
+}
+
+static void wdt_startup(void)
+{
+       next_heartbeat = jiffies + WDT_HEARTBEAT;
+
+       /* Start the timer */
+       timer.expires = jiffies + WDT_INTERVAL; 
+       add_timer(&timer);
+
+       wdt_change(WDT_ENABLE);
+
+       printk(OUR_NAME ": Watchdog timer is now enabled.\n");  
+}
+
+static void wdt_turnoff(void)
+{
+       /* Stop the timer */
+       del_timer(&timer);
+
+       wdt_change(WDT_DISABLE);
+
+       printk(OUR_NAME ": Watchdog timer is now disabled...\n");
+}
+
+
+/*
+ * /dev/watchdog handling
+ */
+
+static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
+{
+       /* We can't seek */
+       if(ppos != &file->f_pos)
+               return -ESPIPE;
+
+       /* See if we got the magic character */
+       if(count) 
+       {
+               size_t ofs;
+
+               /* note: just in case someone wrote the magic character
+                * five months ago... */
+               wdt_expect_close = 0;
+
+               /* now scan */
+               for(ofs = 0; ofs != count; ofs++)
+                 if(buf[ofs] == 'V')
+                               wdt_expect_close = 1;
+
+               /* someone wrote to us, we should restart timer */
+               next_heartbeat = jiffies + WDT_HEARTBEAT;
+               return 1;
+       };
+       return 0;
+}
+
+static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos)
+{
+       /* No can do */
+       return -EINVAL;
+}
+
+static int fop_open(struct inode * inode, struct file * file)
+{
+       switch(MINOR(inode->i_rdev)) 
+       {
+               case WATCHDOG_MINOR:
+                       /* Just in case we're already talking to someone... */
+                       if(wdt_is_open)
+                               return -EBUSY;
+                       /* Good, fire up the show */
+                       wdt_is_open = 1;
+                       wdt_startup();
+                       return 0;
+
+               default:
+                       return -ENODEV;
+       }
+}
+
+static int fop_close(struct inode * inode, struct file * file)
+{
+       lock_kernel();
+       if(MINOR(inode->i_rdev) == WATCHDOG_MINOR) 
+       {
+               if(wdt_expect_close)
+                       wdt_turnoff();
+               else {
+                       del_timer(&timer);
+                       printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n");
+               }
+       }
+       wdt_is_open = 0;
+       unlock_kernel();
+       return 0;
+}
+
+static long long fop_llseek(struct file *file, long long offset, int origin)
+{
+       return -ESPIPE;
+}
+
+static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+       unsigned long arg)
+{
+       static struct watchdog_info ident=
+       {
+               0,
+               1,
+               "W83877F"
+       };
+       
+       switch(cmd)
+       {
+               default:
+                       return -ENOIOCTLCMD;
+               case WDIOC_GETSUPPORT:
+                       return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
+               case WDIOC_KEEPALIVE:
+                       next_heartbeat = jiffies + WDT_HEARTBEAT;
+                       return 0;
+       }
+}
+
+static struct file_operations wdt_fops = {
+       owner:          THIS_MODULE,
+       llseek:         fop_llseek,
+       read:           fop_read,
+       write:          fop_write,
+       open:           fop_open,
+       release:        fop_close,
+       ioctl:          fop_ioctl
+};
+
+static struct miscdevice wdt_miscdev = {
+       WATCHDOG_MINOR,
+       "watchdog",
+       &wdt_fops
+};
+
+/*
+ *     Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+       void *unused)
+{
+       if(code==SYS_DOWN || code==SYS_HALT) 
+               wdt_turnoff();
+       return NOTIFY_DONE;
+}
+/*
+ *     The WDT needs to learn about soft shutdowns in order to
+ *     turn the timebomb registers off. 
+ */
+static struct notifier_block wdt_notifier=
+{
+       wdt_notify_sys,
+       0,
+       0
+};
+
+static void __exit w83877f_wdt_unload(void)
+{
+       wdt_turnoff();
+
+       /* Deregister */
+       misc_deregister(&wdt_miscdev);
+
+       unregister_reboot_notifier(&wdt_notifier);
+       release_region(WDT_PING,1);
+       release_region(ENABLE_W83877F_PORT,2);
+}
+
+static int __init w83877f_wdt_init(void)
+{
+       int rc = -EBUSY;
+
+       if (!request_region(ENABLE_W83877F_PORT, 2, "W83877F WDT"))
+               goto err_out;
+       if (!request_region(WDT_PING, 1, "W8387FF WDT"))
+               goto err_out_region1;
+
+       init_timer(&timer);
+       timer.function = wdt_timer_ping;
+       timer.data = 0;
+
+       rc = misc_register(&wdt_miscdev);
+       if (rc)
+               goto err_out_region2;
+
+       rc = register_reboot_notifier(&wdt_notifier);
+       if (rc)
+               goto err_out_miscdev;
+
+       printk(KERN_INFO OUR_NAME ": WDT driver for W83877F initialised.\n");
+       
+       return 0;
+
+err_out_miscdev:
+       misc_deregister(&wdt_miscdev);
+err_out_region2:
+       release_region(WDT_PING,1);
+err_out_region1:
+       release_region(ENABLE_W83877F_PORT,2);
+err_out:
+       return rc;
+}
+
+module_init(w83877f_wdt_init);
+module_exit(w83877f_wdt_unload);
index 63fb9f4ec93488c23f318ef71d4d0517f87ae978..91f24bac280b1bb50da39d67d8768eebc55babef 100644 (file)
@@ -1454,7 +1454,7 @@ char *idetape_command_key_verbose (byte idetape_command_key)
                case IDETAPE_WRITE_FILEMARK_CMD:        return("WRITE_FILEMARK_CMD");
                case IDETAPE_SPACE_CMD:                 return("SPACE_CMD");
                case IDETAPE_INQUIRY_CMD:               return("INQUIRY_CMD");
-               case IDETAPE_ERASE_CMD:                 return("ERASE_CMD")
+               case IDETAPE_ERASE_CMD:                 return("ERASE_CMD");
                case IDETAPE_MODE_SENSE_CMD:            return("MODE_SENSE_CMD");
                case IDETAPE_MODE_SELECT_CMD:           return("MODE_SELECT_CMD");
                case IDETAPE_LOAD_UNLOAD_CMD:           return("LOAD_UNLOAD_CMD");
index f5fa400ef2e3c5d852c058d1abe35c4696c2d5f8..2286ed5a6e2f082ad2d5d19d03c4d0c2d50771e9 100644 (file)
@@ -21,6 +21,13 @@ if [ "$CONFIG_PARPORT" != "n" ]; then
       dep_tristate '  QuickCam Colour Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT
    fi
 fi
+if [ "$CONFIG_PARPORT" != "n" ]; then
+   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      if [ "$CONFIG_PARPORT_1284" != "n" ]; then
+         dep_tristate '  Winbond W9966CF Webcam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_W9966 $CONFIG_VIDEO_DEV $CONFIG_PARPORT
+      fi
+   fi
+fi
 dep_tristate '  CPiA Video For Linux' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV
 if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then
   if [ "$CONFIG_PARPORT_1284" != "n" ]; then
@@ -39,7 +46,9 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
    dep_tristate '  Stradis 4:2:2 MPEG-2 video driver  (EXPERIMENTAL)' CONFIG_VIDEO_STRADIS $CONFIG_VIDEO_DEV $CONFIG_PCI
 fi
 dep_tristate '  Zoran ZR36057/36060 Video For Linux' CONFIG_VIDEO_ZORAN $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C
-dep_tristate '    Include support for Iomega Buz' CONFIG_VIDEO_BUZ $CONFIG_VIDEO_ZORAN
 dep_tristate '  Zoran ZR36120/36125 Video For Linux' CONFIG_VIDEO_ZR36120 $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_I2C
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  dep_tristate '  Sony Vaio Picturebook Motion Eye Video For Linux' CONFIG_VIDEO_MEYE $CONFIG_VIDEO_DEV $CONFIG_PCI $CONFIG_SONYPI
+fi
 
 endmenu
index 182a992667e61cb8c2c2b5b770c9d40fb4a6a591..9b0e25fb6730308221aed0958be26ea1fb5f517f 100644 (file)
@@ -38,12 +38,13 @@ obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \
        tda7432.o tda9875.o tuner.o
 obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
 
-obj-$(CONFIG_VIDEO_ZR36120) += zoran.o i2c-old.o tuner.o saa7110.o saa7111.o saa7185.o 
+obj-$(CONFIG_VIDEO_ZR36120) += zoran.o
 obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o i2c-old.o
 obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o i2c-old.o
 obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
 obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o
-obj-$(CONFIG_VIDEO_ZORAN) += buz.o i2c-old.o saa7110.o saa7111.o saa7185.o 
+obj-$(CONFIG_VIDEO_W9966) += w9966.o
+obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o i2c-old.o saa7110.o saa7111.o saa7185.o adv7175.o bt819.o bt856.o 
 obj-$(CONFIG_VIDEO_LML33) += bt856.o bt819.o
 obj-$(CONFIG_VIDEO_PMS) += pms.o
 obj-$(CONFIG_VIDEO_PLANB) += planb.o
@@ -52,6 +53,7 @@ obj-$(CONFIG_VIDEO_STRADIS) += stradis.o
 obj-$(CONFIG_VIDEO_CPIA) += cpia.o
 obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
 obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
+obj-$(CONFIG_VIDEO_MEYE) += meye.o
 obj-$(CONFIG_TUNER_3036) += tuner-3036.o
 
 # Extract lists of the multi-part drivers.
diff --git a/drivers/media/video/buz.c b/drivers/media/video/buz.c
deleted file mode 100644 (file)
index e106305..0000000
+++ /dev/null
@@ -1,3463 +0,0 @@
-/*
-   buz - Iomega Buz driver version 1.0
-
-   Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
-
-   based on
-
-   buz.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net>
-
-   and
-
-   bttv - Bt848 frame grabber driver
-
-   Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
-   & Marcus Metzler (mocm@thp.uni-koeln.de)
-
-   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.
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/pci.h>
-#include <linux/signal.h>
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
-#include <linux/sched.h>
-#include <asm/segment.h>
-#include <linux/types.h>
-#include <linux/wrapper.h>
-#include <linux/spinlock.h>
-#include <linux/vmalloc.h>
-
-#include <linux/videodev.h>
-
-#include <linux/version.h>
-#include <asm/uaccess.h>
-
-#include <linux/i2c-old.h>
-#include "buz.h"
-#include <linux/video_decoder.h>
-#include <linux/video_encoder.h>
-
-#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | /* ZR36057_ISR_GIRQ1 | ZR36057_ISR_CodRepIRQ | */ ZR36057_ISR_JPEGRepIRQ )
-#define GPIO_MASK 0xdf
-
-/*
- BUZ
-   GPIO0 = 1, take board out of reset
-   GPIO1 = 1, take JPEG codec out of sleep mode
-   GPIO3 = 1, deassert FRAME# to 36060
-   
-
-   GIRQ0 signals a vertical sync of the video signal
-   GIRQ1 signals that ZR36060's DATERR# line is asserted.
-
-   SAA7111A
-
-   In their infinite wisdom, the Iomega engineers decided to
-   use the same input line for composite and S-Video Color,
-   although there are two entries not connected at all!
-   Through this ingenious strike, it is not possible to
-   keep two running video sources connected at the same time
-   to Composite and S-VHS input!
-
-   mode 0 - N/C
-   mode 1 - S-Video Y
-   mode 2 - noise or something I don't know
-   mode 3 - Composite and S-Video C
-   mode 4 - N/C
-   mode 5 - S-Video (gain C independently selectable of gain Y)
-   mode 6 - N/C
-   mode 7 - S-Video (gain C adapted to gain Y)
- */
-
-#define MAJOR_VERSION 1                /* driver major version */
-#define MINOR_VERSION 0                /* driver minor version */
-
-#define BUZ_NAME      "Iomega BUZ V-1.0"       /* name of the driver */
-
-#define DEBUG(x)               /* Debug driver */
-#define IDEBUG(x)              /* Debug interrupt handler */
-#define IOCTL_DEBUG(x)
-
-
-/* The parameters for this driver */
-
-/*
-   The video mem address of the video card.
-   The driver has a little database for some videocards
-   to determine it from there. If your video card is not in there
-   you have either to give it to the driver as a parameter
-   or set in in a VIDIOCSFBUF ioctl
- */
-
-static unsigned long vidmem;   /* Video memory base address (default 0) */
-
-/* Special purposes only: */
-
-static int triton;             /* 0=no (default), 1=yes */
-static int natoma;             /* 0=no (default), 1=yes */
-
-/*
-   Number and size of grab buffers for Video 4 Linux
-   The vast majority of applications should not need more than 2,
-   the very popular BTTV driver actually does ONLY have 2.
-   Time sensitive applications might need more, the maximum
-   is VIDEO_MAX_FRAME (defined in <linux/videodev.h>).
-
-   The size is set so that the maximum possible request
-   can be satisfied. Decrease  it, if bigphys_area alloc'd
-   memory is low. If you don't have the bigphys_area patch,
-   set it to 128 KB. Will you allow only to grab small
-   images with V4L, but that's better than nothing.
-
-   v4l_bufsize has to be given in KB !
-
- */
-
-static int v4l_nbufs = 2;
-static int v4l_bufsize = 128;  /* Everybody should be able to work with this setting */
-
-/*
-   Default input and video norm at startup of the driver.
- */
-
-static int default_input;      /* 0=Composite (default), 1=S-VHS */
-static int default_norm;       /* 0=PAL (default), 1=NTSC */
-
-MODULE_PARM(vidmem, "i");
-MODULE_PARM(triton, "i");
-MODULE_PARM(natoma, "i");
-MODULE_PARM(v4l_nbufs, "i");
-MODULE_PARM(v4l_bufsize, "i");
-MODULE_PARM(default_input, "i");
-MODULE_PARM(default_norm, "i");
-
-/* Anybody who uses more than four? */
-#define BUZ_MAX 4
-
-static int zoran_num;          /* number of Buzs in use */
-static struct zoran zoran[BUZ_MAX];
-
-/* forward references */
-
-static void v4l_fbuffer_free(struct zoran *zr);
-static void jpg_fbuffer_free(struct zoran *zr);
-static void zoran_feed_stat_com(struct zoran *zr);
-
-
-
-/*
- *   Allocate the V4L grab buffers
- *
- *   These have to be pysically contiguous.
- *   If v4l_bufsize <= KMALLOC_MAXSIZE we use kmalloc
- */
-
-static int v4l_fbuffer_alloc(struct zoran *zr)
-{
-       int i, off;
-       unsigned char *mem;
-
-       for (i = 0; i < v4l_nbufs; i++) {
-               if (zr->v4l_gbuf[i].fbuffer)
-                       printk(KERN_WARNING "%s: v4l_fbuffer_alloc: buffer %d allready allocated ?\n", zr->name, i);
-
-               if (v4l_bufsize <= KMALLOC_MAXSIZE) {
-                       /* Use kmalloc */
-
-                       mem = (unsigned char *) kmalloc(v4l_bufsize, GFP_KERNEL);
-                       if (mem == 0) {
-                               printk(KERN_ERR "%s: kmalloc for V4L bufs failed\n", zr->name);
-                               v4l_fbuffer_free(zr);
-                               return -ENOBUFS;
-                       }
-                       zr->v4l_gbuf[i].fbuffer = mem;
-                       zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem);
-                       zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem);
-                       for (off = 0; off < v4l_bufsize; off += PAGE_SIZE)
-                               mem_map_reserve(virt_to_page(mem + off));
-                       DEBUG(printk(BUZ_INFO ": V4L frame %d mem 0x%x (bus: 0x%x=%d)\n", i, mem, virt_to_bus(mem), virt_to_bus(mem)));
-               } else {
-                       v4l_fbuffer_free(zr);
-                       return -ENOBUFS;
-               }
-       }
-
-       return 0;
-}
-
-/* free the V4L grab buffers */
-static void v4l_fbuffer_free(struct zoran *zr)
-{
-       int i, off;
-       unsigned char *mem;
-
-       for (i = 0; i < v4l_nbufs; i++) {
-               if (!zr->v4l_gbuf[i].fbuffer)
-                       continue;
-
-               mem = zr->v4l_gbuf[i].fbuffer;
-               for (off = 0; off < v4l_bufsize; off += PAGE_SIZE)
-                       mem_map_unreserve(virt_to_page(mem + off));
-               kfree((void *) zr->v4l_gbuf[i].fbuffer);
-               zr->v4l_gbuf[i].fbuffer = NULL;
-       }
-}
-
-/*
- *   Allocate the MJPEG grab buffers.
- *
- *   If the requested buffer size is smaller than KMALLOC_MAXSIZE,
- *   kmalloc is used to request a physically contiguous area,
- *   else we allocate the memory in framgents with get_free_page.
- *
- *   If a Natoma chipset is present and this is a revision 1 zr36057,
- *   each MJPEG buffer needs to be physically contiguous.
- *   (RJ: This statement is from Dave Perks' original driver,
- *   I could never check it because I have a zr36067)
- *   The driver cares about this because it reduces the buffer
- *   size to KMALLOC_MAXSIZE in that case (which forces contiguous allocation).
- *
- *   RJ: The contents grab buffers needs never be accessed in the driver.
- *       Therefore there is no need to allocate them with vmalloc in order
- *       to get a contiguous virtual memory space.
- *       I don't understand why many other drivers first allocate them with
- *       vmalloc (which uses internally also get_free_page, but delivers you
- *       virtual addresses) and then again have to make a lot of efforts
- *       to get the physical address.
- *
- */
-
-static int jpg_fbuffer_alloc(struct zoran *zr)
-{
-       int i, j, off, alloc_contig;
-       unsigned long mem;
-
-       /* Decide if we should alloc contiguous or fragmented memory */
-       /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */
-
-       alloc_contig = (zr->jpg_bufsize < KMALLOC_MAXSIZE);
-
-       for (i = 0; i < zr->jpg_nbufs; i++) {
-               if (zr->jpg_gbuf[i].frag_tab)
-                       printk(KERN_WARNING "%s: jpg_fbuffer_alloc: buffer %d allready allocated ???\n", zr->name, i);
-
-               /* Allocate fragment table for this buffer */
-
-               mem = get_free_page(GFP_KERNEL);
-               if (mem == 0) {
-                       printk(KERN_ERR "%s: jpg_fbuffer_alloc: get_free_page (frag_tab) failed for buffer %d\n", zr->name, i);
-                       jpg_fbuffer_free(zr);
-                       return -ENOBUFS;
-               }
-               memset((void *) mem, 0, PAGE_SIZE);
-               zr->jpg_gbuf[i].frag_tab = (u32 *) mem;
-               zr->jpg_gbuf[i].frag_tab_bus = virt_to_bus((void *) mem);
-
-               if (alloc_contig) {
-                       mem = (unsigned long) kmalloc(zr->jpg_bufsize, GFP_KERNEL);
-                       if (mem == 0) {
-                               jpg_fbuffer_free(zr);
-                               return -ENOBUFS;
-                       }
-                       zr->jpg_gbuf[i].frag_tab[0] = virt_to_bus((void *) mem);
-                       zr->jpg_gbuf[i].frag_tab[1] = ((zr->jpg_bufsize / 4) << 1) | 1;
-                       for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE)
-                               mem_map_reserve(virt_to_page(mem + off));
-               } else {
-                       /* jpg_bufsize is alreay page aligned */
-                       for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) {
-                               mem = get_free_page(GFP_KERNEL);
-                               if (mem == 0) {
-                                       jpg_fbuffer_free(zr);
-                                       return -ENOBUFS;
-                               }
-                               zr->jpg_gbuf[i].frag_tab[2 * j] = virt_to_bus((void *) mem);
-                               zr->jpg_gbuf[i].frag_tab[2 * j + 1] = (PAGE_SIZE / 4) << 1;
-                               mem_map_reserve(virt_to_page(mem));
-                       }
-
-                       zr->jpg_gbuf[i].frag_tab[2 * j - 1] |= 1;
-               }
-       }
-
-       DEBUG(printk("jpg_fbuffer_alloc: %d KB allocated\n",
-                    (zr->jpg_nbufs * zr->jpg_bufsize) >> 10));
-       zr->jpg_buffers_allocated = 1;
-       return 0;
-}
-
-/* free the MJPEG grab buffers */
-static void jpg_fbuffer_free(struct zoran *zr)
-{
-       int i, j, off, alloc_contig;
-       unsigned char *mem;
-
-       /* Decide if we should alloc contiguous or fragmented memory */
-       /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */
-
-       alloc_contig = (zr->jpg_bufsize < KMALLOC_MAXSIZE);
-
-       for (i = 0; i < zr->jpg_nbufs; i++) {
-               if (!zr->jpg_gbuf[i].frag_tab)
-                       continue;
-
-               if (alloc_contig) {
-                       if (zr->jpg_gbuf[i].frag_tab[0]) {
-                               mem = (unsigned char *) bus_to_virt(zr->jpg_gbuf[i].frag_tab[0]);
-                               for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE)
-                                       mem_map_unreserve(virt_to_page(mem + off));
-                               kfree((void *) mem);
-                               zr->jpg_gbuf[i].frag_tab[0] = 0;
-                               zr->jpg_gbuf[i].frag_tab[1] = 0;
-                       }
-               } else {
-                       for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) {
-                               if (!zr->jpg_gbuf[i].frag_tab[2 * j])
-                                       break;
-                               mem_map_unreserve(virt_to_page(bus_to_virt(zr->jpg_gbuf[i].frag_tab[2 * j])));
-                               free_page((unsigned long) bus_to_virt(zr->jpg_gbuf[i].frag_tab[2 * j]));
-                               zr->jpg_gbuf[i].frag_tab[2 * j] = 0;
-                               zr->jpg_gbuf[i].frag_tab[2 * j + 1] = 0;
-                       }
-               }
-
-               free_page((unsigned long) zr->jpg_gbuf[i].frag_tab);
-               zr->jpg_gbuf[i].frag_tab = NULL;
-       }
-       zr->jpg_buffers_allocated = 0;
-}
-
-
-/* ----------------------------------------------------------------------- */
-
-/* I2C functions                                                           */
-
-#define I2C_DELAY   10
-
-
-/* software I2C functions */
-
-static void i2c_setlines(struct i2c_bus *bus, int ctrl, int data)
-{
-       struct zoran *zr = (struct zoran *) bus->data;
-       btwrite((data << 1) | ctrl, ZR36057_I2CBR);
-       btread(ZR36057_I2CBR);
-       udelay(I2C_DELAY);
-}
-
-static int i2c_getdataline(struct i2c_bus *bus)
-{
-       struct zoran *zr = (struct zoran *) bus->data;
-       return (btread(ZR36057_I2CBR) >> 1) & 1;
-}
-
-static void attach_inform(struct i2c_bus *bus, int id)
-{
-       DEBUG(struct zoran *zr = (struct zoran *) bus->data);
-       DEBUG(printk(BUZ_DEBUG "-%u: i2c attach %02x\n", zr->id, id));
-}
-
-static void detach_inform(struct i2c_bus *bus, int id)
-{
-       DEBUG(struct zoran *zr = (struct zoran *) bus->data);
-       DEBUG(printk(BUZ_DEBUG "-%u: i2c detach %02x\n", zr->id, id));
-}
-
-static struct i2c_bus zoran_i2c_bus_template = {
-       name:                   "zr36057",
-       id:                     I2C_BUSID_BT848,
-       bus_lock:               SPIN_LOCK_UNLOCKED,
-
-       attach_inform:          attach_inform,
-       detach_inform:          detach_inform,
-
-       i2c_setlines:           i2c_setlines,
-       i2c_getdataline:        i2c_getdataline,
-};
-
-
-/* ----------------------------------------------------------------------- */
-
-static void GPIO(struct zoran *zr, unsigned bit, unsigned value)
-{
-       u32 reg;
-       u32 mask;
-
-       mask = 1 << (24 + bit);
-       reg = btread(ZR36057_GPPGCR1) & ~mask;
-       if (value) {
-               reg |= mask;
-       }
-       btwrite(reg, ZR36057_GPPGCR1);
-       /* Stop any PCI posting on the GPIO bus */
-       btread(ZR36057_I2CBR);
-}
-
-
-/*
- *   Set the registers for the size we have specified. Don't bother
- *   trying to understand this without the ZR36057 manual in front of
- *   you [AC].
- *
- *   PS: The manual is free for download in .pdf format from
- *   www.zoran.com - nicely done those folks.
- */
-
-struct tvnorm {
-       u16 Wt, Wa, Ht, Ha, HStart, VStart;
-};
-
-static struct tvnorm tvnorms[] =
-{
-   /* PAL-BDGHI */
-       {864, 720, 625, 576, 31, 16},
-   /* NTSC */
-       {858, 720, 525, 480, 21, 8},
-};
-#define TVNORMS (sizeof(tvnorms) / sizeof(tvnorm))
-
-static int format2bpp(int format)
-{
-       int bpp;
-
-       /* Determine the number of bytes per pixel for the video format requested */
-
-       switch (format) {
-
-       case VIDEO_PALETTE_YUV422:
-               bpp = 2;
-               break;
-
-       case VIDEO_PALETTE_RGB555:
-               bpp = 2;
-               break;
-
-       case VIDEO_PALETTE_RGB565:
-               bpp = 2;
-               break;
-
-       case VIDEO_PALETTE_RGB24:
-               bpp = 3;
-               break;
-
-       case VIDEO_PALETTE_RGB32:
-               bpp = 4;
-               break;
-
-       default:
-               bpp = 0;
-       }
-
-       return bpp;
-}
-
-/*
- * set geometry
- */
-static void zr36057_set_vfe(struct zoran *zr, int video_width, int video_height,
-                           unsigned int video_format)
-{
-       struct tvnorm *tvn;
-       unsigned HStart, HEnd, VStart, VEnd;
-       unsigned DispMode;
-       unsigned VidWinWid, VidWinHt;
-       unsigned hcrop1, hcrop2, vcrop1, vcrop2;
-       unsigned Wa, We, Ha, He;
-       unsigned X, Y, HorDcm, VerDcm;
-       u32 reg;
-       unsigned mask_line_size;
-
-       if (zr->params.norm < 0 || zr->params.norm > 1) {
-               printk(KERN_ERR "%s: set_vfe: video_norm = %d not valid\n", zr->name,  zr->params.norm);
-               return;
-       }
-       if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT) {
-               printk(KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n", zr->name, video_width, video_height);
-               return;
-       }
-       tvn = &tvnorms[zr->params.norm];
-
-       Wa = tvn->Wa;
-       Ha = tvn->Ha;
-
-       /* if window has more than half of active height,
-          switch on interlacing - we want the full information */
-
-       zr->video_interlace = (video_height > Ha / 2);
-
-/**** zr36057 ****/
-
-       /* horizontal */
-       VidWinWid = video_width;
-       X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa;
-       We = (VidWinWid * 64) / X;
-       HorDcm = 64 - X;
-       hcrop1 = 2 * ((tvn->Wa - We) / 4);
-       hcrop2 = tvn->Wa - We - hcrop1;
-       HStart = tvn->HStart | 1;
-       HEnd = HStart + tvn->Wa - 1;
-       HStart += hcrop1;
-       HEnd -= hcrop2;
-       reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart)
-           | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd);
-       reg |= ZR36057_VFEHCR_HSPol;
-       btwrite(reg, ZR36057_VFEHCR);
-
-       /* Vertical */
-       DispMode = !zr->video_interlace;
-       VidWinHt = DispMode ? video_height : video_height / 2;
-       Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha;
-       He = (VidWinHt * 64) / Y;
-       VerDcm = 64 - Y;
-       vcrop1 = (tvn->Ha / 2 - He) / 2;
-       vcrop2 = tvn->Ha / 2 - He - vcrop1;
-       VStart = tvn->VStart;
-       VEnd = VStart + tvn->Ha / 2 - 1;
-       VStart += vcrop1;
-       VEnd -= vcrop2;
-       reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart)
-           | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd);
-       reg |= ZR36057_VFEVCR_VSPol;
-       btwrite(reg, ZR36057_VFEVCR);
-
-       /* scaler and pixel format */
-       reg = 0                 // ZR36057_VFESPFR_ExtFl /* Trying to live without ExtFl */
-            | (HorDcm << ZR36057_VFESPFR_HorDcm)
-           | (VerDcm << ZR36057_VFESPFR_VerDcm)
-           | (DispMode << ZR36057_VFESPFR_DispMode)
-           | ZR36057_VFESPFR_LittleEndian;
-       /* RJ: I don't know, why the following has to be the opposite
-          of the corresponding ZR36060 setting, but only this way
-          we get the correct colors when uncompressing to the screen  */
-       reg |= ZR36057_VFESPFR_VCLKPol;
-       /* RJ: Don't know if that is needed for NTSC also */
-       reg |= ZR36057_VFESPFR_TopField;
-       switch (video_format) {
-
-       case VIDEO_PALETTE_YUV422:
-               reg |= ZR36057_VFESPFR_YUV422;
-               break;
-
-       case VIDEO_PALETTE_RGB555:
-               reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif;
-               break;
-
-       case VIDEO_PALETTE_RGB565:
-               reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif;
-               break;
-
-       case VIDEO_PALETTE_RGB24:
-               reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24;
-               break;
-
-       case VIDEO_PALETTE_RGB32:
-               reg |= ZR36057_VFESPFR_RGB888;
-               break;
-
-       default:
-               printk(KERN_INFO "%s: Unknown color_fmt=%x\n", zr->name, video_format);
-               return;
-
-       }
-       if (HorDcm >= 48) {
-               reg |= 3 << ZR36057_VFESPFR_HFilter;    /* 5 tap filter */
-       } else if (HorDcm >= 32) {
-               reg |= 2 << ZR36057_VFESPFR_HFilter;    /* 4 tap filter */
-       } else if (HorDcm >= 16) {
-               reg |= 1 << ZR36057_VFESPFR_HFilter;    /* 3 tap filter */
-       }
-       btwrite(reg, ZR36057_VFESPFR);
-
-       /* display configuration */
-
-       reg = (16 << ZR36057_VDCR_MinPix)
-           | (VidWinHt << ZR36057_VDCR_VidWinHt)
-           | (VidWinWid << ZR36057_VDCR_VidWinWid);
-       if (triton)
-               reg &= ~ZR36057_VDCR_Triton;
-       else
-               reg |= ZR36057_VDCR_Triton;
-       btwrite(reg, ZR36057_VDCR);
-
-       /* Write overlay clipping mask data, but don't enable overlay clipping */
-       /* RJ: since this makes only sense on the screen, we use 
-          zr->window.width instead of video_width */
-
-       mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
-       reg = virt_to_bus(zr->overlay_mask);
-       btwrite(reg, ZR36057_MMTR);
-       reg = virt_to_bus(zr->overlay_mask + mask_line_size);
-       btwrite(reg, ZR36057_MMBR);
-       reg = mask_line_size - (zr->window.width + 31) / 32;
-       if (DispMode == 0)
-               reg += mask_line_size;
-       reg <<= ZR36057_OCR_MaskStride;
-       btwrite(reg, ZR36057_OCR);
-
-}
-
-/*
- * Switch overlay on or off
- */
-
-static void zr36057_overlay(struct zoran *zr, int on)
-{
-       int fmt, bpp;
-       u32 reg;
-
-       if (on) {
-               /* do the necessary settings ... */
-
-               btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);       /* switch it off first */
-
-               switch (zr->buffer.depth) {
-               case 15:
-                       fmt = VIDEO_PALETTE_RGB555;
-                       bpp = 2;
-                       break;
-               case 16:
-                       fmt = VIDEO_PALETTE_RGB565;
-                       bpp = 2;
-                       break;
-               case 24:
-                       fmt = VIDEO_PALETTE_RGB24;
-                       bpp = 3;
-                       break;
-               case 32:
-                       fmt = VIDEO_PALETTE_RGB32;
-                       bpp = 4;
-                       break;
-               default:
-                       fmt = 0;
-                       bpp = 0;
-               }
-
-               zr36057_set_vfe(zr, zr->window.width, zr->window.height, fmt);
-
-               /* Start and length of each line MUST be 4-byte aligned.
-                  This should be allready checked before the call to this routine.
-                  All error messages are internal driver checking only! */
-
-               /* video display top and bottom registers */
-
-               reg = (u32) zr->buffer.base
-                   + zr->window.x * bpp
-                   + zr->window.y * zr->buffer.bytesperline;
-               btwrite(reg, ZR36057_VDTR);
-               if (reg & 3)
-                       printk(KERN_ERR "%s: zr36057_overlay: video_address not aligned\n", zr->name);
-               if (zr->video_interlace)
-                       reg += zr->buffer.bytesperline;
-               btwrite(reg, ZR36057_VDBR);
-
-               /* video stride, status, and frame grab register */
-
-               reg = zr->buffer.bytesperline - zr->window.width * bpp;
-               if (zr->video_interlace)
-                       reg += zr->buffer.bytesperline;
-               if (reg & 3)
-                       printk(KERN_ERR "%s: zr36057_overlay: video_stride not aligned\n", zr->name);
-               reg = (reg << ZR36057_VSSFGR_DispStride);
-               reg |= ZR36057_VSSFGR_VidOvf;   /* clear overflow status */
-               btwrite(reg, ZR36057_VSSFGR);
-
-               /* Set overlay clipping */
-
-               if (zr->window.clipcount)
-                       btor(ZR36057_OCR_OvlEnable, ZR36057_OCR);
-
-               /* ... and switch it on */
-
-               btor(ZR36057_VDCR_VidEn, ZR36057_VDCR);
-       } else {
-               /* Switch it off */
-
-               btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
-       }
-}
-
-/*
- * The overlay mask has one bit for each pixel on a scan line,
- *  and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels.
- */
-static void write_overlay_mask(struct zoran *zr, struct video_clip *vp, int count)
-{
-       unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
-       u32 *mask;
-       int x, y, width, height;
-       unsigned i, j, k;
-       u32 reg;
-
-       /* fill mask with one bits */
-       memset(zr->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT);
-       reg = 0;
-
-       for (i = 0; i < count; ++i) {
-               /* pick up local copy of clip */
-               x = vp[i].x;
-               y = vp[i].y;
-               width = vp[i].width;
-               height = vp[i].height;
-
-               /* trim clips that extend beyond the window */
-               if (x < 0) {
-                       width += x;
-                       x = 0;
-               }
-               if (y < 0) {
-                       height += y;
-                       y = 0;
-               }
-               if (x + width > zr->window.width) {
-                       width = zr->window.width - x;
-               }
-               if (y + height > zr->window.height) {
-                       height = zr->window.height - y;
-               }
-               /* ignore degenerate clips */
-               if (height <= 0) {
-                       continue;
-               }
-               if (width <= 0) {
-                       continue;
-               }
-               /* apply clip for each scan line */
-               for (j = 0; j < height; ++j) {
-                       /* reset bit for each pixel */
-                       /* this can be optimized later if need be */
-                       mask = zr->overlay_mask + (y + j) * mask_line_size;
-                       for (k = 0; k < width; ++k) {
-                               mask[(x + k) / 32] &= ~((u32) 1 << (x + k) % 32);
-                       }
-               }
-       }
-}
-
-/* Enable/Disable uncompressed memory grabbing of the 36057 */
-
-static void zr36057_set_memgrab(struct zoran *zr, int mode)
-{
-       if (mode) {
-               if (btread(ZR36057_VSSFGR) & (ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab))
-                       printk(KERN_WARNING "%s: zr36057_set_memgrab_on with SnapShot or FrameGrab on ???\n", zr->name);
-
-               /* switch on VSync interrupts */
-
-               btwrite(IRQ_MASK, ZR36057_ISR);         // Clear Interrupts
-
-               btor(ZR36057_ICR_GIRQ0, ZR36057_ICR);
-
-               /* enable SnapShot */
-
-               btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
-
-               /* Set zr36057 video front end  and enable video */
-
-#ifdef XAWTV_HACK
-               zr36057_set_vfe(zr, zr->gwidth > 720 ? 720 : zr->gwidth, zr->gheight, zr->gformat);
-#else
-               zr36057_set_vfe(zr, zr->gwidth, zr->gheight, zr->gformat);
-#endif
-
-               zr->v4l_memgrab_active = 1;
-       } else {
-               zr->v4l_memgrab_active = 0;
-
-               /* switch off VSync interrupts */
-
-               btand(~ZR36057_ICR_GIRQ0, ZR36057_ICR);
-
-               /* reenable grabbing to screen if it was running */
-
-               if (zr->v4l_overlay_active) {
-                       zr36057_overlay(zr, 1);
-               } else {
-                       btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
-                       btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
-               }
-       }
-}
-
-static int wait_grab_pending(struct zoran *zr)
-{
-       unsigned long flags;
-
-       /* wait until all pending grabs are finished */
-
-       if (!zr->v4l_memgrab_active)
-               return 0;
-
-       while (zr->v4l_pend_tail != zr->v4l_pend_head) {
-               interruptible_sleep_on(&zr->v4l_capq);
-               if (signal_pending(current))
-                       return -ERESTARTSYS;
-       }
-
-       spin_lock_irqsave(&zr->lock, flags);
-       zr36057_set_memgrab(zr, 0);
-       spin_unlock_irqrestore(&zr->lock, flags);
-
-       return 0;
-}
-
-/*
- *   V4L Buffer grabbing
- */
-
-static int v4l_grab(struct zoran *zr, struct video_mmap *mp)
-{
-       unsigned long flags;
-       int res, bpp;
-
-       /*
-        * There is a long list of limitations to what is allowed to be grabbed
-        * We don't output error messages her, since some programs (e.g. xawtv)
-        * just try several settings to find out what is valid or not.
-        */
-
-       /* No grabbing outside the buffer range! */
-
-       if (mp->frame >= v4l_nbufs || mp->frame < 0)
-               return -EINVAL;
-
-       /* Check size and format of the grab wanted */
-
-       if (mp->height < BUZ_MIN_HEIGHT || mp->width < BUZ_MIN_WIDTH)
-               return -EINVAL;
-       if (mp->height > BUZ_MAX_HEIGHT || mp->width > BUZ_MAX_WIDTH)
-               return -EINVAL;
-
-       bpp = format2bpp(mp->format);
-       if (bpp == 0)
-               return -EINVAL;
-
-       /* Check against available buffer size */
-
-       if (mp->height * mp->width * bpp > v4l_bufsize)
-               return -EINVAL;
-
-       /* The video front end needs 4-byte alinged line sizes */
-
-       if ((bpp == 2 && (mp->width & 1)) || (bpp == 3 && (mp->width & 3)))
-               return -EINVAL;
-
-       /*
-        * To minimize the time spent in the IRQ routine, we avoid setting up
-        * the video front end there.
-        * If this grab has different parameters from a running streaming capture
-        * we stop the streaming capture and start it over again.
-        */
-
-       if (zr->v4l_memgrab_active &&
-           (zr->gwidth != mp->width || zr->gheight != mp->height || zr->gformat != mp->format)) {
-               res = wait_grab_pending(zr);
-               if (res)
-                       return res;
-       }
-       zr->gwidth = mp->width;
-       zr->gheight = mp->height;
-       zr->gformat = mp->format;
-       zr->gbpl = bpp * zr->gwidth;
-
-
-       spin_lock_irqsave(&zr->lock, flags);
-
-       /* make sure a grab isn't going on currently with this buffer */
-
-       switch (zr->v4l_gbuf[mp->frame].state) {
-
-       default:
-       case BUZ_STATE_PEND:
-               res = -EBUSY;   /* what are you doing? */
-               break;
-
-       case BUZ_STATE_USER:
-       case BUZ_STATE_DONE:
-               /* since there is at least one unused buffer there's room for at least one more pend[] entry */
-               zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = mp->frame;
-               zr->v4l_gbuf[mp->frame].state = BUZ_STATE_PEND;
-               res = 0;
-               break;
-
-       }
-
-       /* put the 36057 into frame grabbing mode */
-
-       if (!res && !zr->v4l_memgrab_active)
-               zr36057_set_memgrab(zr, 1);
-
-       spin_unlock_irqrestore(&zr->lock, flags);
-
-       return res;
-}
-
-/*
- * Sync on a V4L buffer
- */
-
-static int v4l_sync(struct zoran *zr, int frame)
-{
-       unsigned long flags;
-
-
-       /* check passed-in frame number */
-       if (frame >= v4l_nbufs || frame < 0) {
-               printk(KERN_ERR "%s: v4l_sync: frame %d is invalid\n", zr->name, frame);
-               return -EINVAL;
-       }
-       /* Check if is buffer was queued at all */
-
-       if (zr->v4l_gbuf[frame].state == BUZ_STATE_USER) {
-//             printk(KERN_ERR "%s: v4l_sync: Trying to sync on a buffer which was not queued?\n", zr->name);
-               return -EINVAL;
-       }
-       /* wait on this buffer to get ready */
-
-       while (zr->v4l_gbuf[frame].state == BUZ_STATE_PEND) {
-               interruptible_sleep_on(&zr->v4l_capq);
-               if (signal_pending(current))
-                       return -ERESTARTSYS;
-       }
-
-       /* buffer should now be in BUZ_STATE_DONE */
-
-       if (zr->v4l_gbuf[frame].state != BUZ_STATE_DONE)
-               printk(KERN_ERR "%s: v4l_sync - internal error\n", zr->name);
-
-       /* Check if streaming capture has finished */
-
-       spin_lock_irqsave(&zr->lock, flags);
-
-       if (zr->v4l_pend_tail == zr->v4l_pend_head)
-               zr36057_set_memgrab(zr, 0);
-
-       spin_unlock_irqrestore(&zr->lock, flags);
-
-       return 0;
-}
-/*****************************************************************************
- *                                                                           *
- *  Set up the Buz-specific MJPEG part                                       *
- *                                                                           *
- *****************************************************************************/
-
-/*
- *     Wait til post office is no longer busy 
- */
-static int post_office_wait(struct zoran *zr)
-{
-       u32 por;
-       u32 ct=0;
-
-       while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) {
-               ct++;
-               if(ct>100000)
-               {
-                       printk(KERN_ERR "%s: timeout on post office.\n", zr->name);
-                       return -1;
-               }
-               /* wait for something to happen */
-       }
-       if ((por & ZR36057_POR_POPen) != 0) {
-               printk(KERN_WARNING "%s: pop pending %08x\n", zr->name, por);
-               return -1;
-       }
-       if ((por & (ZR36057_POR_POTime | ZR36057_POR_POPen)) != 0) {
-               printk(KERN_WARNING "%s: pop timeout %08x\n", zr->name, por);
-               return -1;
-       }
-       return 0;
-}
-
-static int post_office_write(struct zoran *zr, unsigned guest, unsigned reg, unsigned value)
-{
-       u32 por;
-
-       post_office_wait(zr);
-       por = ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16) | (value & 0xFF);
-       btwrite(por, ZR36057_POR);
-       return post_office_wait(zr);
-}
-
-static int post_office_read(struct zoran *zr, unsigned guest, unsigned reg)
-{
-       u32 por;
-
-       post_office_wait(zr);
-       por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16);
-       btwrite(por, ZR36057_POR);
-       if (post_office_wait(zr) < 0) {
-               return -1;
-       }
-       return btread(ZR36057_POR) & 0xFF;
-}
-
-static int zr36060_write_8(struct zoran *zr, unsigned reg, unsigned val)
-{
-       if (post_office_wait(zr)
-           || post_office_write(zr, 0, 1, reg >> 8)
-           || post_office_write(zr, 0, 2, reg)) {
-               return -1;
-       }
-       return post_office_write(zr, 0, 3, val);
-}
-
-static int zr36060_write_16(struct zoran *zr, unsigned reg, unsigned val)
-{
-       if (zr36060_write_8(zr, reg + 0, val >> 8)) {
-               return -1;
-       }
-       return zr36060_write_8(zr, reg + 1, val >> 0);
-}
-
-static int zr36060_write_24(struct zoran *zr, unsigned reg, unsigned val)
-{
-       if (zr36060_write_8(zr, reg + 0, val >> 16)) {
-               return -1;
-       }
-       return zr36060_write_16(zr, reg + 1, val >> 0);
-}
-
-static int zr36060_write_32(struct zoran *zr, unsigned reg, unsigned val)
-{
-       if (zr36060_write_16(zr, reg + 0, val >> 16)) {
-               return -1;
-       }
-       return zr36060_write_16(zr, reg + 2, val >> 0);
-}
-
-static u32 zr36060_read_8(struct zoran *zr, unsigned reg)
-{
-       if (post_office_wait(zr)
-           || post_office_write(zr, 0, 1, reg >> 8)
-           || post_office_write(zr, 0, 2, reg)) {
-               return -1;
-       }
-       return post_office_read(zr, 0, 3) & 0xFF;
-}
-
-static int zr36060_reset(struct zoran *zr)
-{
-       return post_office_write(zr, 3, 0, 0);
-}
-
-static void zr36060_sleep(struct zoran *zr, int sleep)
-{
-       GPIO(zr, 1, !sleep);
-}
-
-
-static void zr36060_set_jpg(struct zoran *zr, enum zoran_codec_mode mode)
-{
-       struct tvnorm *tvn;
-       u32 reg;
-       int size;
-
-       reg = (1 << 0)          /* CodeMstr */
-           |(0 << 2)           /* CFIS=0 */
-           |(0 << 6)           /* Endian=0 */
-           |(0 << 7);          /* Code16=0 */
-       zr36060_write_8(zr, 0x002, reg);
-
-       switch (mode) {
-
-       case BUZ_MODE_MOTION_DECOMPRESS:
-       case BUZ_MODE_STILL_DECOMPRESS:
-               reg = 0x00;     /* Codec mode = decompression */
-               break;
-
-       case BUZ_MODE_MOTION_COMPRESS:
-       case BUZ_MODE_STILL_COMPRESS:
-       default:
-               reg = 0xa4;     /* Codec mode = compression with variable scale factor */
-               break;
-
-       }
-       zr36060_write_8(zr, 0x003, reg);
-
-       reg = 0x00;             /* reserved, mbz */
-       zr36060_write_8(zr, 0x004, reg);
-
-       reg = 0xff;             /* 510 bits/block */
-       zr36060_write_8(zr, 0x005, reg);
-
-       /* JPEG markers */
-       reg = (zr->params.jpeg_markers) & 0x38; /* DRI, DQT, DHT */
-       if (zr->params.COM_len)
-               reg |= JPEG_MARKER_COM;
-       if (zr->params.APP_len)
-               reg |= JPEG_MARKER_APP;
-       zr36060_write_8(zr, 0x006, reg);
-
-       reg = (0 << 3)          /* DATERR=0 */
-           |(0 << 2)           /* END=0 */
-           |(0 << 1)           /* EOI=0 */
-           |(0 << 0);          /* EOAV=0 */
-       zr36060_write_8(zr, 0x007, reg);
-
-       /* code volume */
-
-       /* Target field size in pixels: */
-       tvn = &tvnorms[zr->params.norm];
-       size = (tvn->Ha / 2) * (tvn->Wa) / (zr->params.HorDcm) / (zr->params.VerDcm);
-
-       /* Target compressed field size in bits: */
-       size = size * 16;       /* uncompressed size in bits */
-       size = size * zr->params.quality / 400; /* quality = 100 is a compression ratio 1:4 */
-
-       /* Lower limit (arbitrary, 1 KB) */
-       if (size < 8192)
-               size = 8192;
-
-       /* Upper limit: 7/8 of the code buffers */
-       if (size * zr->params.field_per_buff > zr->jpg_bufsize * 7)
-               size = zr->jpg_bufsize * 7 / zr->params.field_per_buff;
-
-       reg = size;
-       zr36060_write_32(zr, 0x009, reg);
-
-       /* how do we set initial SF as a function of quality parameter? */
-       reg = 0x0100;           /* SF=1.0 */
-       zr36060_write_16(zr, 0x011, reg);
-
-       reg = 0x00ffffff;       /* AF=max */
-       zr36060_write_24(zr, 0x013, reg);
-
-       reg = 0x0000;           /* test */
-       zr36060_write_16(zr, 0x024, reg);
-}
-
-static void zr36060_set_video(struct zoran *zr, enum zoran_codec_mode mode)
-{
-       struct tvnorm *tvn;
-       u32 reg;
-
-       reg = (0 << 7)          /* Video8=0 */
-           |(0 << 6)           /* Range=0 */
-           |(0 << 3)           /* FlDet=0 */
-           |(1 << 2)           /* FlVedge=1 */
-           |(0 << 1)           /* FlExt=0 */
-           |(0 << 0);          /* SyncMstr=0 */
-
-       /* According to ZR36067 documentation, FlDet should correspond
-          to the odd_even flag of the ZR36067 */
-       if (zr->params.odd_even)
-               reg |= (1 << 3);
-
-       if (mode != BUZ_MODE_STILL_DECOMPRESS) {
-               /* limit pixels to range 16..235 as per CCIR-601 */
-               reg |= (1 << 6);        /* Range=1 */
-       }
-       zr36060_write_8(zr, 0x030, reg);
-
-       reg = (0 << 7)          /* VCLKPol=0 */
-           |(0 << 6)           /* PValPol=0 */
-           |(1 << 5)           /* PoePol=1 */
-           |(0 << 4)           /* SImgPol=0 */
-           |(0 << 3)           /* BLPol=0 */
-           |(0 << 2)           /* FlPol=0 */
-           |(0 << 1)           /* HSPol=0, sync on falling edge */
-           |(1 << 0);          /* VSPol=1 */
-       zr36060_write_8(zr, 0x031, reg);
-
-       switch (zr->params.HorDcm) {
-       default:
-       case 1:
-               reg = (0 << 0);
-               break;          /* HScale = 0 */
-
-       case 2:
-               reg = (1 << 0);
-               break;          /* HScale = 1 */
-
-       case 4:
-               reg = (2 << 0);
-               break;          /* HScale = 2 */
-       }
-       if (zr->params.VerDcm == 2)
-               reg |= (1 << 2);
-       zr36060_write_8(zr, 0x032, reg);
-
-       reg = 0x80;             /* BackY */
-       zr36060_write_8(zr, 0x033, reg);
-
-       reg = 0xe0;             /* BackU */
-       zr36060_write_8(zr, 0x034, reg);
-
-       reg = 0xe0;             /* BackV */
-       zr36060_write_8(zr, 0x035, reg);
-
-       /* sync generator */
-
-       tvn = &tvnorms[zr->params.norm];
-
-       reg = tvn->Ht - 1;      /* Vtotal */
-       zr36060_write_16(zr, 0x036, reg);
-
-       reg = tvn->Wt - 1;      /* Htotal */
-       zr36060_write_16(zr, 0x038, reg);
-
-       reg = 6 - 1;            /* VsyncSize */
-       zr36060_write_8(zr, 0x03a, reg);
-
-       reg = 100 - 1;          /* HsyncSize */
-       zr36060_write_8(zr, 0x03b, reg);
-
-       reg = tvn->VStart - 1;  /* BVstart */
-       zr36060_write_8(zr, 0x03c, reg);
-
-       reg += tvn->Ha / 2;     /* BVend */
-       zr36060_write_16(zr, 0x03e, reg);
-
-       reg = tvn->HStart - 1;  /* BHstart */
-       zr36060_write_8(zr, 0x03d, reg);
-
-       reg += tvn->Wa;         /* BHend */
-       zr36060_write_16(zr, 0x040, reg);
-
-       /* active area */
-       reg = zr->params.img_y + tvn->VStart;   /* Vstart */
-       zr36060_write_16(zr, 0x042, reg);
-
-       reg += zr->params.img_height;   /* Vend */
-       zr36060_write_16(zr, 0x044, reg);
-
-       reg = zr->params.img_x + tvn->HStart;   /* Hstart */
-       zr36060_write_16(zr, 0x046, reg);
-
-       reg += zr->params.img_width;    /* Hend */
-       zr36060_write_16(zr, 0x048, reg);
-
-       /* subimage area */
-       reg = zr->params.img_y + tvn->VStart;   /* SVstart */
-       zr36060_write_16(zr, 0x04a, reg);
-
-       reg += zr->params.img_height;   /* SVend */
-       zr36060_write_16(zr, 0x04c, reg);
-
-       reg = zr->params.img_x + tvn->HStart;   /* SHstart */
-       zr36060_write_16(zr, 0x04e, reg);
-
-       reg += zr->params.img_width;    /* SHend */
-       zr36060_write_16(zr, 0x050, reg);
-}
-
-static void zr36060_set_jpg_SOF(struct zoran *zr)
-{
-       u32 reg;
-
-
-       reg = 0xffc0;           /* SOF marker */
-       zr36060_write_16(zr, 0x060, reg);
-
-       reg = 17;               /* SOF length */
-       zr36060_write_16(zr, 0x062, reg);
-
-       reg = 8;                /* precision 8 bits */
-       zr36060_write_8(zr, 0x064, reg);
-
-       reg = zr->params.img_height / zr->params.VerDcm;        /* image height */
-       zr36060_write_16(zr, 0x065, reg);
-
-       reg = zr->params.img_width / zr->params.HorDcm; /* image width */
-       zr36060_write_16(zr, 0x067, reg);
-
-       reg = 3;                /* 3 color components */
-       zr36060_write_8(zr, 0x069, reg);
-
-       reg = 0x002100;         /* Y component */
-       zr36060_write_24(zr, 0x06a, reg);
-
-       reg = 0x011101;         /* U component */
-       zr36060_write_24(zr, 0x06d, reg);
-
-       reg = 0x021101;         /* V component */
-       zr36060_write_24(zr, 0x070, reg);
-}
-
-static void zr36060_set_jpg_SOS(struct zoran *zr)
-{
-       u32 reg;
-
-
-       reg = 0xffda;           /* SOS marker */
-       zr36060_write_16(zr, 0x07a, reg);
-
-       reg = 12;               /* SOS length */
-       zr36060_write_16(zr, 0x07c, reg);
-
-       reg = 3;                /* 3 color components */
-       zr36060_write_8(zr, 0x07e, reg);
-
-       reg = 0x0000;           /* Y component */
-       zr36060_write_16(zr, 0x07f, reg);
-
-       reg = 0x0111;           /* U component */
-       zr36060_write_16(zr, 0x081, reg);
-
-       reg = 0x0211;           /* V component */
-       zr36060_write_16(zr, 0x083, reg);
-
-       reg = 0x003f00;         /* Start, end spectral scans */
-       zr36060_write_24(zr, 0x085, reg);
-}
-
-static void zr36060_set_jpg_DRI(struct zoran *zr)
-{
-       u32 reg;
-
-
-       reg = 0xffdd;           /* DRI marker */
-       zr36060_write_16(zr, 0x0c0, reg);
-
-       reg = 4;                /* DRI length */
-       zr36060_write_16(zr, 0x0c2, reg);
-
-       reg = 8;                /* length in MCUs */
-       zr36060_write_16(zr, 0x0c4, reg);
-}
-
-static void zr36060_set_jpg_DQT(struct zoran *zr)
-{
-       unsigned i;
-       unsigned adr;
-       static const u8 dqt[] =
-       {
-               0xff, 0xdb,     /* DHT marker */
-               0x00, 0x84,     /* DHT length */
-               0x00,           /* table ID 0 */
-               0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
-               0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
-               0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
-               0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
-               0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
-               0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
-               0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
-               0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
-               0x01,           /* table ID 1 */
-               0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
-               0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
-               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
-               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
-               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
-               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
-               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
-               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
-       };
-
-       /* write fixed quantitization tables */
-       adr = 0x0cc;
-       for (i = 0; i < sizeof(dqt); ++i) {
-               zr36060_write_8(zr, adr++, dqt[i]);
-       }
-}
-
-static void zr36060_set_jpg_DHT(struct zoran *zr)
-{
-       unsigned i;
-       unsigned adr;
-       static const u8 dht[] =
-       {
-               0xff, 0xc4,     /* DHT marker */
-               0x01, 0xa2,     /* DHT length */
-               0x00,           /* table class 0, ID 0 */
-               0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,         /* # codes of length 1..8 */
-               0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,         /* # codes of length 8..16 */
-               0x00,           /* values for codes of length 2 */
-               0x01, 0x02, 0x03, 0x04, 0x05,   /* values for codes of length 3 */
-               0x06,           /* values for codes of length 4 */
-               0x07,           /* values for codes of length 5 */
-               0x08,           /* values for codes of length 6 */
-               0x09,           /* values for codes of length 7 */
-               0x0a,           /* values for codes of length 8 */
-               0x0b,           /* values for codes of length 9 */
-               0x01,           /* table class 0, ID 1 */
-               0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,         /* # codes of length 1..8 */
-               0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,         /* # codes of length 9..16 */
-               0x00, 0x01, 0x02,       /* values for codes of length 2 */
-               0x03,           /* values for codes of length 3 */
-               0x04,           /* values for codes of length 4 */
-               0x05,           /* values for codes of length 5 */
-               0x06,           /* values for codes of length 6 */
-               0x07,           /* values for codes of length 7 */
-               0x08,           /* values for codes of length 8 */
-               0x09,           /* values for codes of length 9 */
-               0x0a,           /* values for codes of length 10 */
-               0x0b,           /* values for codes of length 11 */
-               0x10,
-               0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
-               0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d,
-               0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
-               0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
-               0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
-               0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
-               0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
-               0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
-               0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
-               0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
-               0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
-               0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
-               0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
-               0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
-               0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
-               0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
-               0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
-               0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
-               0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
-               0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
-               0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
-               0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
-               0xf9, 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
-               0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00,
-               0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11,
-               0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
-               0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08,
-               0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23,
-               0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a,
-               0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
-               0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35,
-               0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
-               0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55,
-               0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
-               0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
-               0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84,
-               0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
-               0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
-               0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
-               0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
-               0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
-               0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
-               0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
-               0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5,
-               0xf6, 0xf7, 0xf8, 0xf9, 0xfa
-       };
-
-       /* write fixed Huffman tables */
-       adr = 0x1d4;
-       for (i = 0; i < sizeof(dht); ++i) {
-               zr36060_write_8(zr, adr++, dht[i]);
-       }
-}
-
-static void zr36060_set_jpg_APP(struct zoran *zr)
-{
-       unsigned adr;
-       int len, i;
-       u32 reg;
-
-
-       len = zr->params.APP_len;
-       if (len < 0)
-               len = 0;
-       if (len > 60)
-               len = 60;
-
-       i = zr->params.APPn;
-       if (i < 0)
-               i = 0;
-       if (i > 15)
-               i = 15;
-
-       reg = 0xffe0 + i;       /* APPn marker */
-       zr36060_write_16(zr, 0x380, reg);
-
-       reg = len + 2;          /* APPn len */
-       zr36060_write_16(zr, 0x382, reg);
-
-       /* write APPn data */
-       adr = 0x384;
-       for (i = 0; i < 60; i++) {
-               zr36060_write_8(zr, adr++, (i < len ? zr->params.APP_data[i] : 0));
-       }
-}
-
-static void zr36060_set_jpg_COM(struct zoran *zr)
-{
-       unsigned adr;
-       int len, i;
-       u32 reg;
-
-
-       len = zr->params.COM_len;
-       if (len < 0)
-               len = 0;
-       if (len > 60)
-               len = 60;
-
-       reg = 0xfffe;           /* COM marker */
-       zr36060_write_16(zr, 0x3c0, reg);
-
-       reg = len + 2;          /* COM len */
-       zr36060_write_16(zr, 0x3c2, reg);
-
-       /* write COM data */
-       adr = 0x3c4;
-       for (i = 0; i < 60; i++) {
-               zr36060_write_8(zr, adr++, (i < len ? zr->params.COM_data[i] : 0));
-       }
-}
-
-static void zr36060_set_cap(struct zoran *zr, enum zoran_codec_mode mode)
-{
-       unsigned i;
-       u32 reg;
-
-       zr36060_reset(zr);
-       mdelay(10);
-
-       reg = (0 << 7)          /* Load=0 */
-           |(1 << 0);          /* SynRst=1 */
-       zr36060_write_8(zr, 0x000, reg);
-
-       zr36060_set_jpg(zr, mode);
-       zr36060_set_video(zr, mode);
-       zr36060_set_jpg_SOF(zr);
-       zr36060_set_jpg_SOS(zr);
-       zr36060_set_jpg_DRI(zr);
-       zr36060_set_jpg_DQT(zr);
-       zr36060_set_jpg_DHT(zr);
-       zr36060_set_jpg_APP(zr);
-       zr36060_set_jpg_COM(zr);
-
-       reg = (1 << 7)          /* Load=1 */
-           |(0 << 0);          /* SynRst=0 */
-       zr36060_write_8(zr, 0x000, reg);
-
-       /* wait for codec to unbusy */
-       for (i = 0; i < 1000; ++i) {
-               reg = zr36060_read_8(zr, 0x001);
-               if ((reg & (1 << 7)) == 0) {
-                       DEBUG(printk(KERN_DEBUG "060: loaded, loops=%u\n", i));
-                       return;
-               }
-               udelay(1000);
-       }
-       printk(KERN_INFO "060: stuck busy, statux=%02x\n", reg);
-}
-
-static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode)
-{
-       struct tvnorm *tvn;
-       u32 reg;
-       int i;
-
-       tvn = &tvnorms[zr->params.norm];
-
-       /* assert P_Reset */
-       btwrite(0, ZR36057_JPC);
-
-       /* re-initialize DMA ring stuff */
-       zr->jpg_que_head = 0;
-       zr->jpg_dma_head = 0;
-       zr->jpg_dma_tail = 0;
-       zr->jpg_que_tail = 0;
-       zr->jpg_seq_num = 0;
-       for (i = 0; i < BUZ_NUM_STAT_COM; ++i) {
-               zr->stat_com[i] = 1;    /* mark as unavailable to zr36057 */
-       }
-       for (i = 0; i < zr->jpg_nbufs; i++) {
-               zr->jpg_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */
-       }
-
-       /* MJPEG compression mode */
-       switch (mode) {
-
-       case BUZ_MODE_MOTION_COMPRESS:
-       default:
-               reg = ZR36057_JMC_MJPGCmpMode;
-               break;
-
-       case BUZ_MODE_MOTION_DECOMPRESS:
-               reg = ZR36057_JMC_MJPGExpMode;
-               reg |= ZR36057_JMC_SyncMstr;
-               /* RJ: The following is experimental - improves the output to screen */
-               if (zr->params.VFIFO_FB)
-                       reg |= ZR36057_JMC_VFIFO_FB;
-               break;
-
-       case BUZ_MODE_STILL_COMPRESS:
-               reg = ZR36057_JMC_JPGCmpMode;
-               break;
-
-       case BUZ_MODE_STILL_DECOMPRESS:
-               reg = ZR36057_JMC_JPGExpMode;
-               break;
-
-       }
-       reg |= ZR36057_JMC_JPG;
-       if (zr->params.field_per_buff == 1)
-               reg |= ZR36057_JMC_Fld_per_buff;
-       btwrite(reg, ZR36057_JMC);
-
-       /* vertical */
-       btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR);
-       reg = (6 << ZR36057_VSP_VsyncSize) | (tvn->Ht << ZR36057_VSP_FrmTot);
-       btwrite(reg, ZR36057_VSP);
-       reg = ((zr->params.img_y + tvn->VStart) << ZR36057_FVAP_NAY)
-           | (zr->params.img_height << ZR36057_FVAP_PAY);
-       btwrite(reg, ZR36057_FVAP);
-
-       /* horizontal */
-       btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
-       reg = ((tvn->Wt - 100) << ZR36057_HSP_HsyncStart) | (tvn->Wt << ZR36057_HSP_LineTot);
-       btwrite(reg, ZR36057_HSP);
-       reg = ((zr->params.img_x + tvn->HStart) << ZR36057_FHAP_NAX)
-           | (zr->params.img_width << ZR36057_FHAP_PAX);
-       btwrite(reg, ZR36057_FHAP);
-
-       /* field process parameters */
-       if (zr->params.odd_even)
-               reg = ZR36057_FPP_Odd_Even;
-       else
-               reg = 0;
-       btwrite(reg, ZR36057_FPP);
-
-       /* Set proper VCLK Polarity, else colors will be wrong during playback */
-       btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR);
-
-       /* code base address and FIFO threshold */
-       reg = virt_to_bus(zr->stat_com);
-       btwrite(reg, ZR36057_JCBA);
-       reg = 0x50;
-       btwrite(reg, ZR36057_JCFT);
-
-       /* JPEG codec guest ID */
-       reg = (1 << ZR36057_JCGI_JPEGuestID) | (0 << ZR36057_JCGI_JPEGuestReg);
-       btwrite(reg, ZR36057_JCGI);
-
-       /* Code transfer guest ID */
-       reg = (0 << ZR36057_MCTCR_CodGuestID) | (3 << ZR36057_MCTCR_CodGuestReg);
-       reg |= ZR36057_MCTCR_CFlush;
-       btwrite(reg, ZR36057_MCTCR);
-
-       /* deassert P_Reset */
-       btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC);
-}
-
-static void zr36057_enable_jpg(struct zoran *zr, enum zoran_codec_mode mode)
-{
-       static int zero = 0;
-       static int one = 1;
-
-       switch (mode) {
-
-       case BUZ_MODE_MOTION_COMPRESS:
-               zr36060_set_cap(zr, mode);
-               zr36057_set_jpg(zr, mode);
-               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &one);
-               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &zero);
-
-               /* deassert P_Reset, assert Code transfer enable */
-               btwrite(IRQ_MASK, ZR36057_ISR);
-               btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
-               break;
-
-       case BUZ_MODE_MOTION_DECOMPRESS:
-               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &zero);
-               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &one);
-               zr36060_set_cap(zr, mode);
-               zr36057_set_jpg(zr, mode);
-
-               /* deassert P_Reset, assert Code transfer enable */
-               btwrite(IRQ_MASK, ZR36057_ISR);
-               btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
-               break;
-
-       case BUZ_MODE_IDLE:
-       default:
-               /* shut down processing */
-               btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
-               btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC);
-               btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC);
-               btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC);
-               btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
-               btwrite(0, ZR36057_ISR);
-               zr36060_reset(zr);
-               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_ENABLE_OUTPUT, &one);
-               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_INPUT, &zero);
-               break;
-
-       }
-       zr->codec_mode = mode;
-}
-
-/*
- *   Queue a MJPEG buffer for capture/playback
- */
-
-static int jpg_qbuf(struct zoran *zr, int frame, enum zoran_codec_mode mode)
-{
-       unsigned long flags;
-       int res;
-
-       /* Check if buffers are allocated */
-
-       if (!zr->jpg_buffers_allocated) {
-               printk(KERN_ERR "%s: jpg_qbuf: buffers not yet allocated\n", zr->name);
-               return -ENOMEM;
-       }
-       /* Does the user want to stop streaming? */
-
-       if (frame < 0) {
-               if (zr->codec_mode == mode) {
-                       zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
-                       return 0;
-               } else {
-                       printk(KERN_ERR "%s: jpg_qbuf - stop streaming but not in streaming mode\n", zr->name);
-                       return -EINVAL;
-               }
-       }
-       /* No grabbing outside the buffer range! */
-
-       if (frame >= zr->jpg_nbufs) {
-               printk(KERN_ERR "%s: jpg_qbuf: buffer %d out of range\n", zr->name, frame);
-               return -EINVAL;
-       }
-       /* what is the codec mode right now? */
-
-       if (zr->codec_mode == BUZ_MODE_IDLE) {
-               /* Ok load up the zr36060 and go */
-               zr36057_enable_jpg(zr, mode);
-       } else if (zr->codec_mode != mode) {
-               /* wrong codec mode active - invalid */
-               printk(KERN_ERR "%s: jpg_qbuf - codec in wrong mode\n", zr->name);
-               return -EINVAL;
-       }
-       spin_lock_irqsave(&zr->lock, flags);
-
-       /* make sure a grab isn't going on currently with this buffer */
-
-       switch (zr->jpg_gbuf[frame].state) {
-
-       default:
-       case BUZ_STATE_DMA:
-       case BUZ_STATE_PEND:
-       case BUZ_STATE_DONE:
-               res = -EBUSY;   /* what are you doing? */
-               break;
-
-       case BUZ_STATE_USER:
-               /* since there is at least one unused buffer there's room for at least one more pend[] entry */
-               zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = frame;
-               zr->jpg_gbuf[frame].state = BUZ_STATE_PEND;
-               zoran_feed_stat_com(zr);
-               res = 0;
-               break;
-
-       }
-
-       spin_unlock_irqrestore(&zr->lock, flags);
-
-       /* Start the zr36060 when the first frame is queued  */
-       if (zr->jpg_que_head == 1) {
-               btor(ZR36057_JMC_Go_en, ZR36057_JMC);
-               btwrite(ZR36057_JPC_P_Reset | ZR36057_JPC_CodTrnsEn | ZR36057_JPC_Active, ZR36057_JPC);
-       }
-       return res;
-}
-
-/*
- *   Sync on a MJPEG buffer
- */
-
-static int jpg_sync(struct zoran *zr, struct zoran_sync *bs)
-{
-       unsigned long flags;
-       int frame;
-
-       if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS &&
-           zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
-               return -EINVAL;
-       }
-       while (zr->jpg_que_tail == zr->jpg_dma_tail) {
-               interruptible_sleep_on(&zr->jpg_capq);
-               if (signal_pending(current))
-                       return -ERESTARTSYS;
-       }
-
-       spin_lock_irqsave(&zr->lock, flags);
-
-       frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME];
-
-       /* buffer should now be in BUZ_STATE_DONE */
-
-       if (zr->jpg_gbuf[frame].state != BUZ_STATE_DONE)
-               printk(KERN_ERR "%s: jpg_sync - internal error\n", zr->name);
-
-       *bs = zr->jpg_gbuf[frame].bs;
-       zr->jpg_gbuf[frame].state = BUZ_STATE_USER;
-
-       spin_unlock_irqrestore(&zr->lock, flags);
-
-       return 0;
-}
-
-/* when this is called the spinlock must be held */
-static void zoran_feed_stat_com(struct zoran *zr)
-{
-       /* move frames from pending queue to DMA */
-
-       int frame, i, max_stat_com;
-
-       max_stat_com = (zr->params.TmpDcm == 1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1);
-
-       while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com
-              && zr->jpg_dma_head != zr->jpg_que_head) {
-
-               frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME];
-               if (zr->params.TmpDcm == 1) {
-                       /* fill 1 stat_com entry */
-                       i = zr->jpg_dma_head & BUZ_MASK_STAT_COM;
-                       zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus;
-               } else {
-                       /* fill 2 stat_com entries */
-                       i = (zr->jpg_dma_head & 1) * 2;
-                       zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus;
-                       zr->stat_com[i + 1] = zr->jpg_gbuf[frame].frag_tab_bus;
-               }
-               zr->jpg_gbuf[frame].state = BUZ_STATE_DMA;
-               zr->jpg_dma_head++;
-
-       }
-}
-
-/* when this is called the spinlock must be held */
-static void zoran_reap_stat_com(struct zoran *zr)
-{
-       /* move frames from DMA queue to done queue */
-
-       int i;
-       u32 stat_com;
-       unsigned int seq;
-       unsigned int dif;
-       int frame;
-       struct zoran_gbuffer *gbuf;
-
-       /* In motion decompress we don't have a hardware frame counter,
-          we just count the interrupts here */
-
-       if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
-               zr->jpg_seq_num++;
-
-       while (zr->jpg_dma_tail != zr->jpg_dma_head) {
-               if (zr->params.TmpDcm == 1)
-                       i = zr->jpg_dma_tail & BUZ_MASK_STAT_COM;
-               else
-                       i = (zr->jpg_dma_tail & 1) * 2 + 1;
-
-               stat_com = zr->stat_com[i];
-
-               if ((stat_com & 1) == 0) {
-                       return;
-               }
-               frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
-               gbuf = &zr->jpg_gbuf[frame];
-               get_fast_time(&gbuf->bs.timestamp);
-
-               if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
-                       gbuf->bs.length = (stat_com & 0x7fffff) >> 1;
-
-                       /* update sequence number with the help of the counter in stat_com */
-
-                       seq = stat_com >> 24;
-                       dif = (seq - zr->jpg_seq_num) & 0xff;
-                       zr->jpg_seq_num += dif;
-               } else {
-                       gbuf->bs.length = 0;
-               }
-               gbuf->bs.seq = zr->params.TmpDcm == 2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num;
-               gbuf->state = BUZ_STATE_DONE;
-
-               zr->jpg_dma_tail++;
-       }
-}
-
-static void zoran_irq(int irq, void *dev_id, struct pt_regs *regs)
-{
-       u32 stat, astat;
-       int count;
-       struct zoran *zr;
-       unsigned long flags;
-
-       zr = (struct zoran *) dev_id;
-       count = 0;
-
-       spin_lock_irqsave(&zr->lock, flags);
-       while (1) {
-               /* get/clear interrupt status bits */
-               stat = btread(ZR36057_ISR);
-               astat = stat & IRQ_MASK;
-               if (!astat) {
-                       break;
-               }
-               btwrite(astat, ZR36057_ISR);
-               IDEBUG(printk(BUZ_DEBUG "-%u: astat %08x stat %08x\n", zr->id, astat, stat));
-
-#if (IRQ_MASK & ZR36057_ISR_GIRQ0)
-               if (astat & ZR36057_ISR_GIRQ0) {
-
-                       /* Interrupts may still happen when zr->v4l_memgrab_active is switched off.
-                          We simply ignore them */
-
-                       if (zr->v4l_memgrab_active) {
-
-/* A lot more checks should be here ... */
-                               if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0)
-                                       printk(KERN_WARNING "%s: BuzIRQ with SnapShot off ???\n", zr->name);
-
-                               if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) {
-                                       /* There is a grab on a frame going on, check if it has finished */
-
-                                       if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) {
-                                               /* it is finished, notify the user */
-
-                                               zr->v4l_gbuf[zr->v4l_grab_frame].state = BUZ_STATE_DONE;
-                                               zr->v4l_grab_frame = NO_GRAB_ACTIVE;
-                                               zr->v4l_grab_seq++;
-                                               zr->v4l_pend_tail++;
-                                       }
-                               }
-                               if (zr->v4l_grab_frame == NO_GRAB_ACTIVE)
-                                       wake_up_interruptible(&zr->v4l_capq);
-
-                               /* Check if there is another grab queued */
-
-                               if (zr->v4l_grab_frame == NO_GRAB_ACTIVE &&
-                                   zr->v4l_pend_tail != zr->v4l_pend_head) {
-
-                                       int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME];
-                                       u32 reg;
-
-                                       zr->v4l_grab_frame = frame;
-
-                                       /* Set zr36057 video front end and enable video */
-
-                                       /* Buffer address */
-
-                                       reg = zr->v4l_gbuf[frame].fbuffer_bus;
-                                       btwrite(reg, ZR36057_VDTR);
-                                       if (zr->video_interlace)
-                                               reg += zr->gbpl;
-                                       btwrite(reg, ZR36057_VDBR);
-
-                                       /* video stride, status, and frame grab register */
-
-#ifdef XAWTV_HACK
-                                       reg = (zr->gwidth > 720) ? ((zr->gwidth & ~3) - 720) * zr->gbpl / zr->gwidth : 0;
-#else
-                                       reg = 0;
-#endif
-                                       if (zr->video_interlace)
-                                               reg += zr->gbpl;
-                                       reg = (reg << ZR36057_VSSFGR_DispStride);
-                                       reg |= ZR36057_VSSFGR_VidOvf;
-                                       reg |= ZR36057_VSSFGR_SnapShot;
-                                       reg |= ZR36057_VSSFGR_FrameGrab;
-                                       btwrite(reg, ZR36057_VSSFGR);
-
-                                       btor(ZR36057_VDCR_VidEn, ZR36057_VDCR);
-                               }
-                       }
-               }
-#endif                         /* (IRQ_MASK & ZR36057_ISR_GIRQ0) */
-
-#if (IRQ_MASK & ZR36057_ISR_GIRQ1)
-               if (astat & ZR36057_ISR_GIRQ1) {
-                       unsigned csr = zr36060_read_8(zr, 0x001);
-                       unsigned isr = zr36060_read_8(zr, 0x008);
-
-                       IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_GIRQ1 60_code=%02x 60_intr=%02x\n",
-                                     zr->name, csr, isr));
-
-                       btand(~ZR36057_ICR_GIRQ1, ZR36057_ICR);
-                       zoran_reap_stat_com(zr);
-                       zoran_feed_stat_com(zr);
-               }
-#endif                         /* (IRQ_MASK & ZR36057_ISR_GIRQ1) */
-
-#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ)
-               if (astat & ZR36057_ISR_CodRepIRQ) {
-                       IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", zr->name));
-                       btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR);
-               }
-#endif                         /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */
-
-#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ)
-               if ((astat & ZR36057_ISR_JPEGRepIRQ) &&
-                   (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
-                    zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) {
-                       zoran_reap_stat_com(zr);
-                       zoran_feed_stat_com(zr);
-                       wake_up_interruptible(&zr->jpg_capq);
-               }
-#endif                         /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */
-
-               count++;
-               if (count > 10) {
-                       printk(KERN_WARNING "%s: irq loop %d\n", zr->name, count);
-                       if (count > 20) {
-                               btwrite(0, ZR36057_ICR);
-                               printk(KERN_ERR  "%s: IRQ lockup, cleared int mask\n", zr->name);
-                               break;
-                       }
-               }
-       }
-       spin_unlock_irqrestore(&zr->lock, flags);
-}
-
-/* Check a zoran_params struct for correctness, insert default params */
-
-static int zoran_check_params(struct zoran *zr, struct zoran_params *params)
-{
-       int err = 0, err0 = 0;
-
-       /* insert constant params */
-
-       params->major_version = MAJOR_VERSION;
-       params->minor_version = MINOR_VERSION;
-
-       /* Check input and norm */
-
-       if (params->input != 0 && params->input != 1) {
-               err++;
-       }
-       if (params->norm != VIDEO_MODE_PAL && params->norm != VIDEO_MODE_NTSC) {
-               err++;
-       }
-       /* Check decimation, set default values for decimation = 1, 2, 4 */
-
-       switch (params->decimation) {
-       case 1:
-
-               params->HorDcm = 1;
-               params->VerDcm = 1;
-               params->TmpDcm = 1;
-               params->field_per_buff = 2;
-
-               params->img_x = 0;
-               params->img_y = 0;
-               params->img_width = 720;
-               params->img_height = tvnorms[params->norm].Ha / 2;
-               break;
-
-       case 2:
-
-               params->HorDcm = 2;
-               params->VerDcm = 1;
-               params->TmpDcm = 2;
-               params->field_per_buff = 1;
-
-               params->img_x = 8;
-               params->img_y = 0;
-               params->img_width = 704;
-               params->img_height = tvnorms[params->norm].Ha / 2;
-               break;
-
-       case 4:
-
-               params->HorDcm = 4;
-               params->VerDcm = 2;
-               params->TmpDcm = 2;
-               params->field_per_buff = 1;
-
-               params->img_x = 8;
-               params->img_y = 0;
-               params->img_width = 704;
-               params->img_height = tvnorms[params->norm].Ha / 2;
-               break;
-
-       case 0:
-
-               /* We have to check the data the user has set */
-
-               if (params->HorDcm != 1 && params->HorDcm != 2 && params->HorDcm != 4)
-                       err0++;
-               if (params->VerDcm != 1 && params->VerDcm != 2)
-                       err0++;
-               if (params->TmpDcm != 1 && params->TmpDcm != 2)
-                       err0++;
-               if (params->field_per_buff != 1 && params->field_per_buff != 2)
-                       err0++;
-
-               if (params->img_x < 0)
-                       err0++;
-               if (params->img_y < 0)
-                       err0++;
-               if (params->img_width < 0)
-                       err0++;
-               if (params->img_height < 0)
-                       err0++;
-               if (params->img_x + params->img_width > 720)
-                       err0++;
-               if (params->img_y + params->img_height > tvnorms[params->norm].Ha / 2)
-                       err0++;
-               if (params->img_width % (16 * params->HorDcm) != 0)
-                       err0++;
-               if (params->img_height % (8 * params->VerDcm) != 0)
-                       err0++;
-
-               if (err0) {
-                       err++;
-               }
-               break;
-
-       default:
-               err++;
-               break;
-       }
-
-       if (params->quality > 100)
-               params->quality = 100;
-       if (params->quality < 5)
-               params->quality = 5;
-
-       if (params->APPn < 0)
-               params->APPn = 0;
-       if (params->APPn > 15)
-               params->APPn = 15;
-       if (params->APP_len < 0)
-               params->APP_len = 0;
-       if (params->APP_len > 60)
-               params->APP_len = 60;
-       if (params->COM_len < 0)
-               params->COM_len = 0;
-       if (params->COM_len > 60)
-               params->COM_len = 60;
-
-       if (err)
-               return -EINVAL;
-
-       return 0;
-
-}
-static void zoran_open_init_params(struct zoran *zr)
-{
-       int i;
-
-       /* Per default, map the V4L Buffers */
-
-       zr->map_mjpeg_buffers = 0;
-
-       /* User must explicitly set a window */
-
-       zr->window_set = 0;
-
-       zr->window.x = 0;
-       zr->window.y = 0;
-       zr->window.width = 0;
-       zr->window.height = 0;
-       zr->window.chromakey = 0;
-       zr->window.flags = 0;
-       zr->window.clips = NULL;
-       zr->window.clipcount = 0;
-
-       zr->video_interlace = 0;
-
-       zr->v4l_memgrab_active = 0;
-       zr->v4l_overlay_active = 0;
-
-       zr->v4l_grab_frame = NO_GRAB_ACTIVE;
-       zr->v4l_grab_seq = 0;
-
-       zr->gwidth = 0;
-       zr->gheight = 0;
-       zr->gformat = 0;
-       zr->gbpl = 0;
-
-       /* DMA ring stuff for V4L */
-
-       zr->v4l_pend_tail = 0;
-       zr->v4l_pend_head = 0;
-       for (i = 0; i < v4l_nbufs; i++) {
-               zr->v4l_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */
-       }
-
-       /* Set necessary params and call zoran_check_params to set the defaults */
-
-       zr->params.decimation = 1;
-
-       zr->params.quality = 50;        /* default compression factor 8 */
-       zr->params.odd_even = 1;
-
-       zr->params.APPn = 0;
-       zr->params.APP_len = 0; /* No APPn marker */
-       for (i = 0; i < 60; i++)
-               zr->params.APP_data[i] = 0;
-
-       zr->params.COM_len = 0; /* No COM marker */
-       for (i = 0; i < 60; i++)
-               zr->params.COM_data[i] = 0;
-
-       zr->params.VFIFO_FB = 0;
-
-       memset(zr->params.reserved, 0, sizeof(zr->params.reserved));
-
-       zr->params.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT;
-
-       i = zoran_check_params(zr, &zr->params);
-       if (i)
-               printk(KERN_ERR "%s: zoran_open_init_params internal error\n", zr->name);
-}
-
-/*
- *   Open a buz card. Right now the flags stuff is just playing
- */
-
-static int zoran_open(struct video_device *dev, int flags)
-{
-       struct zoran *zr = (struct zoran *) dev;
-
-       DEBUG(printk(KERN_INFO ": zoran_open\n"));
-
-       switch (flags) {
-
-       case 0:
-               if (zr->user)
-                       return -EBUSY;
-               zr->user++;
-
-               if (v4l_fbuffer_alloc(zr) < 0) {
-                       zr->user--;
-                       return -ENOMEM;
-               }
-               /* default setup */
-
-               zoran_open_init_params(zr);
-
-               zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
-
-               btwrite(IRQ_MASK, ZR36057_ISR);         // Clears interrupts
-
-               btor(ZR36057_ICR_IntPinEn, ZR36057_ICR);
-
-               break;
-
-       default:
-               return -EBUSY;
-
-       }
-       return 0;
-}
-
-static void zoran_close(struct video_device *dev)
-{
-       struct zoran *zr = (struct zoran *) dev;
-
-       DEBUG(printk(KERN_INFO ": zoran_close\n"));
-       
-       /* disable interrupts */
-       btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
-
-       /* wake up sleeping beauties */
-       wake_up_interruptible(&zr->v4l_capq);
-       wake_up_interruptible(&zr->jpg_capq);
-
-       zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
-       zr36057_set_memgrab(zr, 0);
-       if (zr->v4l_overlay_active)
-               zr36057_overlay(zr, 0);
-
-       zr->user--;
-
-       v4l_fbuffer_free(zr);
-       jpg_fbuffer_free(zr);
-       zr->jpg_nbufs = 0;
-
-       DEBUG(printk(KERN_INFO ": zoran_close done\n"));
-}
-
-
-static long zoran_read(struct video_device *dev, char *buf, unsigned long count, int nonblock)
-{
-       return -EINVAL;
-}
-
-static long zoran_write(struct video_device *dev, const char *buf, unsigned long count, int nonblock)
-{
-       return -EINVAL;
-}
-
-/*
- *   ioctl routine
- */
-
-
-static int zoran_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
-{
-       struct zoran *zr = (struct zoran *) dev;
-
-       switch (cmd) {
-
-       case VIDIOCGCAP:
-               {
-                       struct video_capability b;
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCGCAP\n"));
-                       strncpy(b.name, zr->video_dev.name, sizeof(b.name));
-                       b.type = VID_TYPE_CAPTURE |
-                           VID_TYPE_OVERLAY |
-                           VID_TYPE_CLIPPING |
-                           VID_TYPE_FRAMERAM |
-                           VID_TYPE_SCALES;
-                       /* theoretically we could also flag VID_TYPE_SUBCAPTURE
-                          but this is not even implemented in the BTTV driver */
-
-                       b.channels = 2;         /* composite, svhs */
-                       b.audios = 0;
-                       b.maxwidth = BUZ_MAX_WIDTH;
-                       b.maxheight = BUZ_MAX_HEIGHT;
-                       b.minwidth = BUZ_MIN_WIDTH;
-                       b.minheight = BUZ_MIN_HEIGHT;
-                       if (copy_to_user(arg, &b, sizeof(b))) {
-                               return -EFAULT;
-                       }
-                       return 0;
-               }
-
-       case VIDIOCGCHAN:
-               {
-                       struct video_channel v;
-
-                       if (copy_from_user(&v, arg, sizeof(v))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCGCHAN for channel %d\n", v.channel));
-                       switch (v.channel) {
-                       case 0:
-                               strcpy(v.name, "Composite");
-                               break;
-                       case 1:
-                               strcpy(v.name, "SVHS");
-                               break;
-                       default:
-                               return -EINVAL;
-                       }
-                       v.tuners = 0;
-                       v.flags = 0;
-                       v.type = VIDEO_TYPE_CAMERA;
-                       v.norm = zr->params.norm;
-                       if (copy_to_user(arg, &v, sizeof(v))) {
-                               return -EFAULT;
-                       }
-                       return 0;
-               }
-
-               /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says:
-
-                * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input."
-                *                                 ^^^^^^^
-                * The famos BTTV driver has it implemented with a struct video_channel argument
-                * and we follow it for compatibility reasons
-                *
-                * BTW: this is the only way the user can set the norm!
-                */
-
-       case VIDIOCSCHAN:
-               {
-                       struct video_channel v;
-                       int input;
-                       int on, res;
-
-                       if (copy_from_user(&v, arg, sizeof(v))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCSCHAN: channel=%d, norm=%d\n", v.channel, v.norm));
-                       switch (v.channel) {
-                       case 0:
-                               input = 3;
-                               break;
-                       case 1:
-                               input = 7;
-                               break;
-                       default:
-                               return -EINVAL;
-                       }
-
-                       if (v.norm != VIDEO_MODE_PAL
-                           && v.norm != VIDEO_MODE_NTSC) {
-                               return -EINVAL;
-                       }
-                       zr->params.norm = v.norm;
-                       zr->params.input = v.channel;
-
-                       /* We switch overlay off and on since a change in the norm
-                          needs different VFE settings */
-
-                       on = zr->v4l_overlay_active && !zr->v4l_memgrab_active;
-                       if (on)
-                               zr36057_overlay(zr, 0);
-
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input);
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm);
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm);
-
-                       if (on)
-                               zr36057_overlay(zr, 1);
-
-                       /* Make sure the changes come into effect */
-                       res = wait_grab_pending(zr);
-                       if (res)
-                               return res;
-
-                       return 0;
-               }
-
-       case VIDIOCGTUNER:
-       case VIDIOCSTUNER:
-                       return -EINVAL;
-
-       case VIDIOCGPICT:
-               {
-                       struct video_picture p = zr->picture;
-
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCGPICT\n"));
-                       p.depth = zr->buffer.depth;
-                       switch (zr->buffer.depth) {
-                       case 15:
-                               p.palette = VIDEO_PALETTE_RGB555;
-                               break;
-
-                       case 16:
-                               p.palette = VIDEO_PALETTE_RGB565;
-                               break;
-
-                       case 24:
-                               p.palette = VIDEO_PALETTE_RGB24;
-                               break;
-
-                       case 32:
-                               p.palette = VIDEO_PALETTE_RGB32;
-                               break;
-                       }
-
-                       if (copy_to_user(arg, &p, sizeof(p))) {
-                               return -EFAULT;
-                       }
-                       return 0;
-               }
-
-       case VIDIOCSPICT:
-               {
-                       struct video_picture p;
-
-                       if (copy_from_user(&p, arg, sizeof(p))) {
-                               return -EFAULT;
-                       }
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &p);
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCSPICT bri=%d hue=%d col=%d con=%d dep=%d pal=%d\n",
-                                          p.brightness, p.hue, p.colour, p.contrast, p.depth, p.palette));
-                       /* The depth and palette values have no meaning to us,
-                          should we return  -EINVAL if they don't fit ? */
-                       zr->picture = p;
-                       return 0;
-               }
-
-       case VIDIOCCAPTURE:
-               {
-                       int v, res;
-
-                       if (copy_from_user(&v, arg, sizeof(v))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCCAPTURE: %d\n", v));
-                       /* If there is nothing to do, return immediatly */
-
-                       if ((v && zr->v4l_overlay_active) || (!v && !zr->v4l_overlay_active))
-                               return 0;
-
-                       if (v == 0) {
-                               zr->v4l_overlay_active = 0;
-                               if (!zr->v4l_memgrab_active)
-                                       zr36057_overlay(zr, 0);
-                               /* When a grab is running, the video simply won't be switched on any more */
-                       } else {
-                               if (!zr->buffer_set || !zr->window_set) {
-                                       return -EINVAL;
-                               }
-                               zr->v4l_overlay_active = 1;
-                               if (!zr->v4l_memgrab_active)
-                                       zr36057_overlay(zr, 1);
-                               /* When a grab is running, the video will be switched on when grab is finished */
-                       }
-                       /* Make sure the changes come into effect */
-                       res = wait_grab_pending(zr);
-                       if (res)
-                               return res;
-                       return 0;
-               }
-
-       case VIDIOCGWIN:
-               {
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCGWIN\n"));
-                       if (copy_to_user(arg, &zr->window, sizeof(zr->window))) {
-                               return -EFAULT;
-                       }
-                       return 0;
-               }
-
-       case VIDIOCSWIN:
-               {
-                       struct video_clip *vcp;
-                       struct video_window vw;
-                       int on, end, res;
-
-                       if (copy_from_user(&vw, arg, sizeof(vw))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCSWIN: x=%d y=%d w=%d h=%d clipcount=%d\n", vw.x, vw.y, vw.width, vw.height, vw.clipcount));
-                       if (!zr->buffer_set) {
-                               return -EINVAL;
-                       }
-                       /*
-                        * The video front end needs 4-byte alinged line sizes, we correct that
-                        * silently here if necessary
-                        */
-
-                       if (zr->buffer.depth == 15 || zr->buffer.depth == 16) {
-                               end = (vw.x + vw.width) & ~1;   /* round down */
-                               vw.x = (vw.x + 1) & ~1;         /* round up */
-                               vw.width = end - vw.x;
-                       }
-                       if (zr->buffer.depth == 24) {
-                               end = (vw.x + vw.width) & ~3;   /* round down */
-                               vw.x = (vw.x + 3) & ~3;         /* round up */
-                               vw.width = end - vw.x;
-                       }
-#if 0
-                       // At least xawtv seems to care about the following - just leave it away
-                       /*
-                        * Also corrected silently (as long as window fits at all):
-                        * video not fitting the screen
-                        */
-#if 0
-                       if (vw.x < 0 || vw.y < 0 || vw.x + vw.width > zr->buffer.width ||
-                           vw.y + vw.height > zr->buffer.height) {
-                               printk(BUZ_ERR ": VIDIOCSWIN: window does not fit frame buffer: %dx%d+%d*%d\n",
-                                      vw.width, vw.height, vw.x, vw.y);
-                               return -EINVAL;
-                       }
-#else
-                       if (vw.x < 0)
-                               vw.x = 0;
-                       if (vw.y < 0)
-                               vw.y = 0;
-                       if (vw.x + vw.width > zr->buffer.width)
-                               vw.width = zr->buffer.width - vw.x;
-                       if (vw.y + vw.height > zr->buffer.height)
-                               vw.height = zr->buffer.height - vw.y;
-#endif
-#endif
-
-                       /* Check for valid parameters */
-                       if (vw.width < BUZ_MIN_WIDTH || vw.height < BUZ_MIN_HEIGHT ||
-                           vw.width > BUZ_MAX_WIDTH || vw.height > BUZ_MAX_HEIGHT) {
-                               return -EINVAL;
-                       }
-#ifdef XAWTV_HACK
-                       if (vw.width > 720)
-                               vw.width = 720;
-#endif
-
-                       zr->window.x = vw.x;
-                       zr->window.y = vw.y;
-                       zr->window.width = vw.width;
-                       zr->window.height = vw.height;
-                       zr->window.chromakey = 0;
-                       zr->window.flags = 0;   // RJ: Is this intended for interlace on/off ?
-
-                       zr->window.clips = NULL;
-                       zr->window.clipcount = vw.clipcount;
-
-                       /*
-                        * If an overlay is running, we have to switch it off
-                        * and switch it on again in order to get the new settings in effect.
-                        *
-                        * We also want to avoid that the overlay mask is written
-                        * when an overlay is running.
-                        */
-
-                       on = zr->v4l_overlay_active && !zr->v4l_memgrab_active;
-                       if (on)
-                               zr36057_overlay(zr, 0);
-
-                       /*
-                        *   Write the overlay mask if clips are wanted.
-                        */
-                       if (vw.clipcount) {
-                               vcp = vmalloc(sizeof(struct video_clip) * (vw.clipcount + 4));
-                               if (vcp == NULL) {
-                                       return -ENOMEM;
-                               }
-                               if (copy_from_user(vcp, vw.clips, sizeof(struct video_clip) * vw.clipcount)) {
-                                       vfree(vcp);
-                                       return -EFAULT;
-                               }
-                               write_overlay_mask(zr, vcp, vw.clipcount);
-                               vfree(vcp);
-                       }
-                       if (on)
-                               zr36057_overlay(zr, 1);
-                       zr->window_set = 1;
-
-                       /* Make sure the changes come into effect */
-                       res = wait_grab_pending(zr);
-                       if (res)
-                               return res;
-
-                       return 0;
-               }
-
-       case VIDIOCGFBUF:
-               {
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCGFBUF\n"));
-                       if (copy_to_user(arg, &zr->buffer, sizeof(zr->buffer))) {
-                               return -EFAULT;
-                       }
-                       return 0;
-               }
-
-       case VIDIOCSFBUF:
-               {
-                       struct video_buffer v;
-
-                       if (!capable(CAP_SYS_ADMIN)
-                       || !capable(CAP_SYS_RAWIO))
-                               return -EPERM;
-
-                       if (copy_from_user(&v, arg, sizeof(v)))
-                               return -EFAULT;
-                       
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCSFBUF: base=0x%x w=%d h=%d depth=%d bpl=%d\n", (u32) v.base, v.width, v.height, v.depth, v.bytesperline));
-                       if (zr->v4l_overlay_active) {
-                               /* Has the user gotten crazy ... ? */
-                               return -EINVAL;
-                       }
-                       if (v.depth != 15
-                           && v.depth != 16
-                           && v.depth != 24
-                           && v.depth != 32) {
-                               return -EINVAL;
-                       }
-                       if (v.height <= 0 || v.width <= 0 || v.bytesperline <= 0) {
-                               return -EINVAL;
-                       }
-                       if (v.bytesperline & 3) {
-                               return -EINVAL;
-                       }
-                       if (v.base) {
-                               zr->buffer.base = (void *) ((unsigned long) v.base & ~3);
-                       }
-                       zr->buffer.height = v.height;
-                       zr->buffer.width = v.width;
-                       zr->buffer.depth = v.depth;
-                       zr->buffer.bytesperline = v.bytesperline;
-
-                       if (zr->buffer.base)
-                               zr->buffer_set = 1;
-                       zr->window_set = 0;     /* The user should set new window parameters */
-                       return 0;
-               }
-
-               /* RJ: what is VIDIOCKEY intended to do ??? */
-
-       case VIDIOCGFREQ:
-       case VIDIOCSFREQ:
-       case VIDIOCGAUDIO:
-       case VIDIOCSAUDIO:
-               return -EINVAL;
-               
-       case VIDIOCSYNC:
-               {
-                       int v;
-
-                       if (copy_from_user(&v, arg, sizeof(v))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCSYNC %d\n", v));
-                       return v4l_sync(zr, v);
-               }
-
-       case VIDIOCMCAPTURE:
-               {
-                       struct video_mmap vm;
-
-                       if (copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCMCAPTURE frame=%d geom=%dx%d fmt=%d\n",
-                              vm.frame, vm.height, vm.width, vm.format));
-                       return v4l_grab(zr, &vm);
-               }
-
-       case VIDIOCGMBUF:
-               {
-                       struct video_mbuf vm;
-                       int i;
-
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCGMBUF\n"));
-               
-                       vm.size = v4l_nbufs * v4l_bufsize;
-                       vm.frames = v4l_nbufs;
-                       for (i = 0; i < v4l_nbufs; i++) {
-                               vm.offsets[i] = i * v4l_bufsize;
-                       }
-
-                       /* The next mmap will map the V4L buffers */
-                       zr->map_mjpeg_buffers = 0;
-
-                       if (copy_to_user(arg, &vm, sizeof(vm))) {
-                               return -EFAULT;
-                       }
-                       return 0;
-               }
-
-       case VIDIOCGUNIT:
-               {
-                       struct video_unit vu;
-
-                       IOCTL_DEBUG(printk("buz ioctl VIDIOCGUNIT\n"));
-                       vu.video = zr->video_dev.minor;
-                       vu.vbi = VIDEO_NO_UNIT;
-                       vu.radio = VIDEO_NO_UNIT;
-                       vu.audio = VIDEO_NO_UNIT;
-                       vu.teletext = VIDEO_NO_UNIT;
-                       if (copy_to_user(arg, &vu, sizeof(vu)))
-                               return -EFAULT;
-                       return 0;
-               }
-
-               /*
-                * RJ: In principal we could support subcaptures for V4L grabbing.
-                *     Not even the famous BTTV driver has them, however.
-                *     If there should be a strong demand, one could consider
-                *     to implement them.
-                */
-       case VIDIOCGCAPTURE:
-       case VIDIOCSCAPTURE:
-                       return -EINVAL;
-
-       case BUZIOC_G_PARAMS:
-               {
-                       IOCTL_DEBUG(printk("buz ioctl BUZIOC_G_PARAMS\n"));
-                       if (copy_to_user(arg, &(zr->params), sizeof(zr->params))) 
-                               return -EFAULT;
-                       return 0;
-               }
-
-       case BUZIOC_S_PARAMS:
-               {
-                       struct zoran_params bp;
-                       int input, on;
-
-                       if (zr->codec_mode != BUZ_MODE_IDLE) {
-                               return -EINVAL;
-                       }
-                       if (copy_from_user(&bp, arg, sizeof(bp))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl BUZIOC_S_PARAMS\n"));
-                       
-                       /* Check the params first before overwriting our internal values */
-
-                       if (zoran_check_params(zr, &bp))
-                               return -EINVAL;
-
-                       zr->params = bp;
-
-                       /* Make changes of input and norm go into effect immediatly */
-
-                       /* We switch overlay off and on since a change in the norm
-                          needs different VFE settings */
-
-                       on = zr->v4l_overlay_active && !zr->v4l_memgrab_active;
-                       if (on)
-                               zr36057_overlay(zr, 0);
-
-                       input = zr->params.input == 0 ? 3 : 7;
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input);
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm);
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm);
-
-                       if (on)
-                               zr36057_overlay(zr, 1);
-
-                       if (copy_to_user(arg, &bp, sizeof(bp))) {
-                               return -EFAULT;
-                       }
-                       return 0;
-               }
-
-       case BUZIOC_REQBUFS:
-               {
-                       struct zoran_requestbuffers br;
-
-                       if (zr->jpg_buffers_allocated) {
-                               return -EINVAL;
-                       }
-                       if (copy_from_user(&br, arg, sizeof(br))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl BUZIOC_REQBUFS count = %lu size=%lu\n",
-                                          br.count, br.size));
-                       /* Enforce reasonable lower and upper limits */
-                       if (br.count < 4)
-                               br.count = 4;   /* Could be choosen smaller */
-                       if (br.count > BUZ_MAX_FRAME)
-                               br.count = BUZ_MAX_FRAME;
-                       br.size = PAGE_ALIGN(br.size);
-                       if (br.size < 8192)
-                               br.size = 8192;         /* Arbitrary */
-                       /* br.size is limited by 1 page for the stat_com tables to a Maximum of 2 MB */
-                       if (br.size > (512 * 1024))
-                               br.size = (512 * 1024);         /* 512 K should be enough */
-                       if (zr->need_contiguous && br.size > KMALLOC_MAXSIZE)
-                               br.size = KMALLOC_MAXSIZE;
-
-                       zr->jpg_nbufs = br.count;
-                       zr->jpg_bufsize = br.size;
-
-                       if (jpg_fbuffer_alloc(zr))
-                               return -ENOMEM;
-
-                       /* The next mmap will map the MJPEG buffers */
-                       zr->map_mjpeg_buffers = 1;
-
-                       if (copy_to_user(arg, &br, sizeof(br))) {
-                               return -EFAULT;
-                       }
-                       return 0;
-               }
-
-       case BUZIOC_QBUF_CAPT:
-               {
-                       int nb;
-
-                       if (copy_from_user((void *) &nb, (void *) arg, sizeof(int))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl BUZIOC_QBUF_CAPT %d\n", nb));
-                       return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_COMPRESS);
-               }
-
-       case BUZIOC_QBUF_PLAY:
-               {
-                       int nb;
-
-                       if (copy_from_user((void *) &nb, (void *) arg, sizeof(int))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl BUZIOC_QBUF_PLAY %d\n", nb));
-                       return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_DECOMPRESS);
-               }
-
-       case BUZIOC_SYNC:
-               {
-                       struct zoran_sync bs;
-                       int res;
-
-                       IOCTL_DEBUG(printk("buz ioctl BUZIOC_SYNC\n"));
-                       res = jpg_sync(zr, &bs);
-                       if (copy_to_user(arg, &bs, sizeof(bs))) {
-                               return -EFAULT;
-                       }
-                       return res;
-               }
-
-       case BUZIOC_G_STATUS:
-               {
-                       struct zoran_status bs;
-                       int norm, input, status;
-
-                       if (zr->codec_mode != BUZ_MODE_IDLE) {
-                               return -EINVAL;
-                       }
-                       if (copy_from_user(&bs, arg, sizeof(bs))) {
-                               return -EFAULT;
-                       }
-                       IOCTL_DEBUG(printk("buz ioctl BUZIOC_G_STATUS\n"));
-                       switch (bs.input) {
-                       case 0:
-                               input = 3;
-                               break;
-                       case 1:
-                               input = 7;
-                               break;
-                       default:
-                               return -EINVAL;
-                       }
-
-                       /* Set video norm to VIDEO_MODE_AUTO */
-
-                       norm = VIDEO_MODE_AUTO;
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input);
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &norm);
-
-                       /* sleep 1 second */
-
-                       schedule_timeout(HZ);
-                       
-                       /* Get status of video decoder */
-
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_GET_STATUS, &status);
-                       bs.signal = (status & DECODER_STATUS_GOOD) ? 1 : 0;
-                       bs.norm = (status & DECODER_STATUS_NTSC) ? VIDEO_MODE_NTSC : VIDEO_MODE_PAL;
-                       bs.color = (status & DECODER_STATUS_COLOR) ? 1 : 0;
-
-                       /* restore previous input and norm */
-                       input = zr->params.input == 0 ? 3 : 7;
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &input);
-                       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm);
-
-                       if (copy_to_user(arg, &bs, sizeof(bs))) {
-                               return -EFAULT;
-                       }
-                       return 0;
-               }
-
-       default:
-                   return -ENOIOCTLCMD;
-
-       }
-       return 0;
-}
-
-
-/*
- *   This maps the buffers to user space.
- *
- *   Depending on the state of zr->map_mjpeg_buffers
- *   the V4L or the MJPEG buffers are mapped
- *
- */
-
-static int zoran_mmap(struct video_device *dev, const char *adr, unsigned long size)
-{
-       struct zoran *zr = (struct zoran *) dev;
-       unsigned long start = (unsigned long) adr;
-       unsigned long page, pos, todo, fraglen;
-       int i, j;
-
-       if (zr->map_mjpeg_buffers) {
-               /* Map the MJPEG buffers */
-
-               if (!zr->jpg_buffers_allocated) {
-                       return -ENOMEM;
-               }
-               if (size > zr->jpg_nbufs * zr->jpg_bufsize) {
-                       return -EINVAL;
-               }
-
-               for (i = 0; i < zr->jpg_nbufs; i++) {
-                       for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) {
-                               fraglen = (zr->jpg_gbuf[i].frag_tab[2 * j + 1] & ~1) << 1;
-                               todo = size;
-                               if (todo > fraglen)
-                                       todo = fraglen;
-                               pos = (unsigned long) zr->jpg_gbuf[i].frag_tab[2 * j];
-                               page = virt_to_phys(bus_to_virt(pos));  /* should just be pos on i386 */
-                               if (remap_page_range(start, page, todo, PAGE_SHARED)) {
-                                       printk(KERN_ERR "%s: zoran_mmap(V4L): remap_page_range failed\n", zr->name);
-                                       return -EAGAIN;
-                               }
-                               size -= todo;
-                               start += todo;
-                               if (size == 0)
-                                       break;
-                               if (zr->jpg_gbuf[i].frag_tab[2 * j + 1] & 1)
-                                       break;  /* was last fragment */
-                       }
-                       if (size == 0)
-                               break;
-               }
-       } else {
-               /* Map the V4L buffers */
-
-               if (size > v4l_nbufs * v4l_bufsize) {
-                       return -EINVAL;
-               }
-
-               for (i = 0; i < v4l_nbufs; i++) {
-                       todo = size;
-                       if (todo > v4l_bufsize)
-                               todo = v4l_bufsize;
-                       page = zr->v4l_gbuf[i].fbuffer_phys;
-                       DEBUG(printk("V4L remap page range %d 0x%x %d to 0x%x\n", i, page, todo, start));
-                       if (remap_page_range(start, page, todo, PAGE_SHARED)) {
-                               printk(KERN_ERR "%s: zoran_mmap(V4L): remap_page_range failed\n", zr->name);
-                               return -EAGAIN;
-                       }
-                       size -= todo;
-                       start += todo;
-                       if (size == 0)
-                               break;
-               }
-       }
-       return 0;
-}
-
-static struct video_device zoran_template =
-{
-       owner:          THIS_MODULE,
-       name:           BUZ_NAME,
-       type:           VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM |
-                       VID_TYPE_SCALES | VID_TYPE_SUBCAPTURE,
-       hardware:       VID_HARDWARE_ZR36067,
-       open:           zoran_open,
-       close:          zoran_close,
-       read:           zoran_read,
-       write:          zoran_write,
-       ioctl:          zoran_ioctl,
-       mmap:           zoran_mmap,
-};
-
-static int zr36057_init(int i)
-{
-       struct zoran *zr = &zoran[i];
-       unsigned long mem;
-       unsigned mem_needed;
-       int j;
-       int rev;
-
-       /* reset zr36057 */
-       btwrite(0, ZR36057_SPGPPCR);
-       mdelay(10);
-
-       /* default setup of all parameters which will persist beetween opens */
-
-       zr->user = 0;
-       
-       init_waitqueue_head(&zr->v4l_capq);
-       init_waitqueue_head(&zr->jpg_capq);
-
-       zr->map_mjpeg_buffers = 0;      /* Map V4L buffers by default */
-
-       zr->jpg_nbufs = 0;
-       zr->jpg_bufsize = 0;
-       zr->jpg_buffers_allocated = 0;
-
-       zr->buffer_set = 0;     /* Flag if frame buffer has been set */
-       zr->buffer.base = (void *) vidmem;
-       zr->buffer.width = 0;
-       zr->buffer.height = 0;
-       zr->buffer.depth = 0;
-       zr->buffer.bytesperline = 0;
-
-       zr->params.norm = default_norm ? 1 : 0; /* Avoid nonsense settings from user */
-       zr->params.input = default_input ? 1 : 0;       /* Avoid nonsense settings from user */
-       zr->video_interlace = 0;
-
-       /* Should the following be reset at every open ? */
-
-       zr->picture.colour = 32768;
-       zr->picture.brightness = 32768;
-       zr->picture.hue = 32768;
-       zr->picture.contrast = 32768;
-       zr->picture.whiteness = 0;
-       zr->picture.depth = 0;
-       zr->picture.palette = 0;
-
-       for (j = 0; j < VIDEO_MAX_FRAME; j++) {
-               zr->v4l_gbuf[i].fbuffer = 0;
-               zr->v4l_gbuf[i].fbuffer_phys = 0;
-               zr->v4l_gbuf[i].fbuffer_bus = 0;
-       }
-
-       zr->stat_com = 0;
-
-       /* default setup (will be repeated at every open) */
-
-       zoran_open_init_params(zr);
-
-       /* allocate memory *before* doing anything to the hardware in case allocation fails */
-
-       /* STAT_COM table and overlay mask */
-
-       mem_needed = (BUZ_NUM_STAT_COM + ((BUZ_MAX_WIDTH + 31) / 32) * BUZ_MAX_HEIGHT) * 4;
-       mem = (unsigned long) kmalloc(mem_needed, GFP_KERNEL);
-       if (!mem) {
-               return -ENOMEM;
-       }
-       memset((void *) mem, 0, mem_needed);
-
-       zr->stat_com = (u32 *) mem;
-       for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
-               zr->stat_com[j] = 1;    /* mark as unavailable to zr36057 */
-       }
-       zr->overlay_mask = (u32 *) (mem + BUZ_NUM_STAT_COM * 4);
-
-       /* Initialize zr->jpg_gbuf */
-
-       for (j = 0; j < BUZ_MAX_FRAME; j++) {
-               zr->jpg_gbuf[j].frag_tab = 0;
-               zr->jpg_gbuf[j].frag_tab_bus = 0;
-               zr->jpg_gbuf[j].state = BUZ_STATE_USER;
-               zr->jpg_gbuf[j].bs.frame = j;
-       }
-
-       /* take zr36057 out of reset now */
-       btwrite(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR);
-       mdelay(10);
-
-       /* stop all DMA processes */
-       btwrite(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
-       btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
-       /* assert P_Reset */
-       btwrite(0, ZR36057_JPC);
-
-       switch(zr->board)
-       {
-               case BOARD_BUZ:
-       
-                       /* set up GPIO direction */
-                       btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR);
-
-                       /* Set up guest bus timing - Guests 0..3 Tdur=12, Trec=3 */
-                       btwrite((GPIO_MASK << 24) | 0x8888, ZR36057_GPPGCR1);
-                       mdelay(10);
-
-                       /* reset video decoder */
-
-                       GPIO(zr, 0, 0);
-                       mdelay(10);
-                       GPIO(zr, 0, 1);
-                       mdelay(10);
-
-                       /* reset JPEG codec */
-                       zr36060_sleep(zr, 0);
-                       mdelay(10);
-                       zr36060_reset(zr);
-                       mdelay(10);
-       
-                       /* display codec revision */
-                       if ((rev=zr36060_read_8(zr, 0x022)) == 0x33) {
-                               printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n",
-                              zr->name, zr36060_read_8(zr, 0x023));
-                       } else {
-                               printk(KERN_ERR "%s: Zoran ZR36060 not found (Rev=%d)\n", zr->name, rev);
-                               kfree((void *) zr->stat_com);
-                               return -1;
-                       }
-                       break;
-                       
-               case BOARD_LML33:
-//                     btwrite(btread(ZR36057_SPGPPCR)&~ZR36057_SPGPPCR_SoftReset , ZR36057_SPGPPCR);
-//                     udelay(100);
-//                     btwrite(btread(ZR36057_SPGPPCR)|ZR36057_SPGPPCR_SoftReset , ZR36057_SPGPPCR);
-//                     udelay(1000);
-
-                       /*
-                        *      Set up the GPIO direction
-                        */
-                       btwrite(btread(ZR36057_SPGPPCR_SoftReset)|0 , ZR36057_SPGPPCR);
-                       /* Set up guest bus timing - Guests 0..2 Tdur=12, Trec=3 */
-                       btwrite(0xFF00F888, ZR36057_GPPGCR1);
-                       mdelay(10);
-                       GPIO(zr, 5, 0);         /* Analog video bypass */
-                       udelay(3000);
-                       GPIO(zr, 0, 0);         /* Reset 819 */
-                       udelay(3000);
-                       GPIO(zr, 0, 1);         /* 819 back */
-                       udelay(3000);
-                       /* reset JPEG codec */
-                       zr36060_sleep(zr, 0);
-                       udelay(3000);
-                       zr36060_reset(zr);
-                       udelay(3000);
-
-                       /* display codec revision */
-                       if ((rev=zr36060_read_8(zr, 0x022)) == 0x33) {
-                               printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n",
-                              zr->name, zr36060_read_8(zr, 0x023));
-                       } else {
-                               printk(KERN_ERR "%s: Zoran ZR36060 not found (rev=%d)\n", zr->name, rev);
-                               kfree((void *) zr->stat_com);
-                               return -1;
-                       }
-                       break;
-       }
-       /* i2c */
-       memcpy(&zr->i2c, &zoran_i2c_bus_template, sizeof(struct i2c_bus));
-       sprintf(zr->i2c.name, "zoran%u", zr->id);
-       zr->i2c.data = zr;
-       if (i2c_register_bus(&zr->i2c) < 0) {
-               kfree((void *) zr->stat_com);
-               return -1;
-       }
-       /*
-        *   Now add the template and register the device unit.
-        */
-       memcpy(&zr->video_dev, &zoran_template, sizeof(zoran_template));
-       sprintf(zr->video_dev.name, "zoran%u", zr->id);
-       if (video_register_device(&zr->video_dev, VFL_TYPE_GRABBER) < 0) {
-               i2c_unregister_bus(&zr->i2c);
-               kfree((void *) zr->stat_com);
-               return -1;
-       }
-       /* toggle JPEG codec sleep to sync PLL */
-       zr36060_sleep(zr, 1);
-       mdelay(10);
-       zr36060_sleep(zr, 0);
-       mdelay(10);
-
-       /* Enable bus-mastering */
-       pci_set_master(zr->pci_dev);
-
-       j = zr->params.input == 0 ? 3 : 7;
-       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &j);
-       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &zr->params.norm);
-       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER, ENCODER_SET_NORM, &zr->params.norm);
-
-       /* set individual interrupt enables (without GIRQ0)
-          but don't global enable until zoran_open() */
-
-       btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ0, ZR36057_ICR);
-
-       if(request_irq(zr->pci_dev->irq, zoran_irq,
-              SA_SHIRQ | SA_INTERRUPT, zr->name, (void *) zr)<0)
-       {
-               printk(KERN_ERR "%s: Can't assign irq.\n", zr->name);
-               video_unregister_device(&zr->video_dev);
-               i2c_unregister_bus(&zr->i2c);
-               kfree((void *) zr->stat_com);
-               return -1;
-       }
-       zr->initialized = 1;
-       return 0;
-}
-
-
-
-static void release_zoran(void)
-{
-       u8 command;
-       int i;
-       struct zoran *zr;
-
-       for (i = 0; i < zoran_num; i++) {
-               zr = &zoran[i];
-
-               if (!zr->initialized)
-                       continue;
-
-               /* unregister i2c_bus */
-               i2c_unregister_bus((&zr->i2c));
-
-               /* disable PCI bus-mastering */
-               pci_read_config_byte(zr->pci_dev, PCI_COMMAND, &command);
-               command &= ~PCI_COMMAND_MASTER;
-               pci_write_config_byte(zr->pci_dev, PCI_COMMAND, command);
-
-               /* put chip into reset */
-               btwrite(0, ZR36057_SPGPPCR);
-
-               free_irq(zr->pci_dev->irq, zr);
-
-               /* unmap and free memory */
-
-               kfree((void *) zr->stat_com);
-
-               iounmap(zr->zr36057_mem);
-
-               video_unregister_device(&zr->video_dev);
-       }
-}
-
-/*
- *   Scan for a Buz card (actually for the PCI controller ZR36057),
- *   request the irq and map the io memory
- */
-
-static int find_zr36057(void)
-{
-       unsigned char latency;
-       struct zoran *zr;
-       struct pci_dev *dev = NULL;
-
-       zoran_num = 0;
-
-       while (zoran_num < BUZ_MAX
-              && (dev = pci_find_device(PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057, dev)) != NULL) {
-               zr = &zoran[zoran_num];
-               zr->pci_dev = dev;
-               zr->zr36057_mem = NULL;
-               zr->id = zoran_num;
-               sprintf(zr->name, "zoran%u", zr->id);
-
-               spin_lock_init(&zr->lock);
-
-               if (pci_enable_device(dev))
-                       continue;
-
-               zr->zr36057_adr = pci_resource_start(zr->pci_dev, 0);
-               pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, &zr->revision);
-               if (zr->revision < 2) {
-                       printk(KERN_INFO "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n",
-                              zr->name, zr->revision, zr->pci_dev->irq, zr->zr36057_adr);
-               } else {
-                       unsigned short ss_vendor_id, ss_id;
-
-                       ss_vendor_id = zr->pci_dev->subsystem_vendor;
-                       ss_id = zr->pci_dev->subsystem_device;
-                       printk(KERN_INFO "%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n",
-                              zr->name, zr->revision, zr->pci_dev->irq, zr->zr36057_adr);
-                       printk(KERN_INFO "%s: subsystem vendor=0x%04x id=0x%04x\n",
-                              zr->name, ss_vendor_id, ss_id);
-                       if(ss_vendor_id==0xFF10 && ss_id == 0xDE41)
-                       {
-                               zr->board = BOARD_LML33;
-                               printk(KERN_INFO "%s: LML33 detected.\n", zr->name);
-                       }
-               }
-
-               zr->zr36057_mem = ioremap(zr->zr36057_adr, 0x1000);
-               if (!zr->zr36057_mem) {
-                       printk(KERN_ERR "%s: ioremap failed\n", zr->name);
-                       break;
-               }
-
-               /* set PCI latency timer */
-               pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, &latency);
-               if (latency != 48) {
-                       printk(KERN_INFO "%s: Changing PCI latency from %d to 48.\n", zr->name, latency);
-                       latency = 48;
-                       pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, latency);
-               }
-               zoran_num++;
-       }
-       if (zoran_num == 0)
-               printk(KERN_INFO "zoran: no cards found.\n");
-
-       return zoran_num;
-}
-
-static void handle_chipset(void)
-{
-       if(pci_pci_problems&PCIPCI_FAIL)
-       {
-               printk(KERN_WARNING "buz: This configuration is known to have PCI to PCI DMA problems\n");
-               printk(KERN_WARNING "buz: You may not be able to use overlay mode.\n");
-       }
-                       
-
-       if(pci_pci_problems&PCIPCI_TRITON)
-       {
-               printk("buz: Enabling Triton support.\n");
-               triton = 1;
-       }
-       
-       if(pci_pci_problems&PCIPCI_NATOMA)
-       {
-               printk("buz: Enabling Natoma workaround.\n");
-               natoma = 1;
-       }
-}
-
-#ifdef MODULE
-int init_module(void)
-#else
-int init_zoran_cards(struct video_init *unused)
-#endif
-{
-       int i;
-
-
-       printk(KERN_INFO "Zoran driver 1.00 (c) 1999 Rainer Johanni, Dave Perks.\n");
-
-       /* Look for Buz cards */
-
-       if (find_zr36057() <= 0) {
-               return -EIO;
-       }
-       printk(KERN_INFO"zoran: %d zoran card(s) found\n", zoran_num);
-
-       if (zoran_num == 0)
-               return -ENXIO;
-
-       
-       /* check the parameters we have been given, adjust if necessary */
-
-       if (v4l_nbufs < 0)
-               v4l_nbufs = 0;
-       if (v4l_nbufs > VIDEO_MAX_FRAME)
-               v4l_nbufs = VIDEO_MAX_FRAME;
-       /* The user specfies the in KB, we want them in byte (and page aligned) */
-       v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
-       if (v4l_bufsize < 32768)
-               v4l_bufsize = 32768;
-       /* 2 MB is arbitrary but sufficient for the maximum possible images */
-       if (v4l_bufsize > 2048 * 1024)
-               v4l_bufsize = 2048 * 1024;
-
-       printk(KERN_INFO "zoran: using %d V4L buffers of size %d KB\n", v4l_nbufs, v4l_bufsize >> 10);
-
-       /* Use parameter for vidmem or try to find a video card */
-
-       if (vidmem) {
-               printk(KERN_INFO "zoran: Using supplied video memory base address @ 0x%lx\n", vidmem);
-       }
-
-       /* check if we have a Triton or Natome chipset */
-
-       handle_chipset();
-
-       /* take care of Natoma chipset and a revision 1 zr36057 */
-
-       for (i = 0; i < zoran_num; i++) {
-               if (natoma && zoran[i].revision <= 1) {
-                       zoran[i].need_contiguous = 1;
-                       printk(KERN_INFO "%s: ZR36057/Natome bug, max. buffer size is 128K\n", zoran[i].name);
-               } else {
-                       zoran[i].need_contiguous = 0;
-               }
-       }
-
-       /* initialize the Buzs */
-
-       /* We have to know which ones must be released if an error occurs */
-       for (i = 0; i < zoran_num; i++)
-               zoran[i].initialized = 0;
-
-       for (i = 0; i < zoran_num; i++) {
-               if (zr36057_init(i) < 0) {
-                       release_zoran();
-                       return -EIO;
-               }
-       }
-
-       return 0;
-}
-
-
-
-#ifdef MODULE
-
-void cleanup_module(void)
-{
-       release_zoran();
-}
-
-#endif
diff --git a/drivers/media/video/buz.h b/drivers/media/video/buz.h
deleted file mode 100644 (file)
index 06b794a..0000000
+++ /dev/null
@@ -1,319 +0,0 @@
-/* 
-   buz - Iomega Buz driver
-
-   Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
-
-   based on
-
-   buz.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net>
-
-   and
-
-   bttv - Bt848 frame grabber driver
-   Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
-
-   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.
- */
-
-#ifndef _BUZ_H_
-#define _BUZ_H_
-
-/* The Buz only supports a maximum width of 720, but some V4L
-   applications (e.g. xawtv are more happy with 768).
-   If XAWTV_HACK is defined, we try to fake a device with bigger width */
-
-#define XAWTV_HACK
-
-#ifdef XAWTV_HACK
-#define   BUZ_MAX_WIDTH   768  /* never display more than 768 pixels */
-#else
-#define   BUZ_MAX_WIDTH   720  /* never display more than 720 pixels */
-#endif
-#define   BUZ_MAX_HEIGHT  576  /* never display more than 576 rows */
-#define   BUZ_MIN_WIDTH    32  /* never display less than 32 pixels */
-#define   BUZ_MIN_HEIGHT   24  /* never display less than 24 rows */
-
-struct zoran_requestbuffers {
-       unsigned long count;    /* Number of buffers for MJPEG grabbing */
-       unsigned long size;     /* Size PER BUFFER in bytes */
-};
-
-struct zoran_sync {
-       unsigned long frame;    /* number of buffer that has been free'd */
-       unsigned long length;   /* number of code bytes in buffer (capture only) */
-       unsigned long seq;      /* frame sequence number */
-       struct timeval timestamp;       /* timestamp */
-};
-
-struct zoran_status {
-       int input;              /* Input channel, has to be set prior to BUZIOC_G_STATUS */
-       int signal;             /* Returned: 1 if valid video signal detected */
-       int norm;               /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
-       int color;              /* Returned: 1 if color signal detected */
-};
-
-struct zoran_params {
-
-       /* The following parameters can only be queried */
-
-       int major_version;      /* Major version number of driver */
-       int minor_version;      /* Minor version number of driver */
-
-       /* Main control parameters */
-
-       int input;              /* Input channel: 0 = Composite, 1 = S-VHS */
-       int norm;               /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
-       int decimation;         /* decimation of captured video,
-                                  enlargement of video played back.
-                                  Valid values are 1, 2, 4 or 0.
-                                  0 is a special value where the user
-                                  has full control over video scaling */
-
-       /* The following parameters only have to be set if decimation==0,
-          for other values of decimation they provide the data how the image is captured */
-
-       int HorDcm;             /* Horizontal decimation: 1, 2 or 4 */
-       int VerDcm;             /* Vertical decimation: 1 or 2 */
-       int TmpDcm;             /* Temporal decimation: 1 or 2,
-                                  if TmpDcm==2 in capture every second frame is dropped,
-                                  in playback every frame is played twice */
-       int field_per_buff;     /* Number of fields per buffer: 1 or 2 */
-       int img_x;              /* start of image in x direction */
-       int img_y;              /* start of image in y direction */
-       int img_width;          /* image width BEFORE decimation,
-                                  must be a multiple of HorDcm*16 */
-       int img_height;         /* image height BEFORE decimation,
-                                  must be a multiple of VerDcm*8 */
-
-       /* --- End of parameters for decimation==0 only --- */
-
-       /* JPEG control parameters */
-
-       int quality;            /* Measure for quality of compressed images.
-                                  Scales linearly with the size of the compressed images.
-                                  Must be beetween 0 and 100, 100 is a compression
-                                  ratio of 1:4 */
-
-       int odd_even;           /* Which field should come first ??? */
-
-       int APPn;               /* Number of APP segment to be written, must be 0..15 */
-       int APP_len;            /* Length of data in JPEG APPn segment */
-       char APP_data[60];      /* Data in the JPEG APPn segment. */
-
-       int COM_len;            /* Length of data in JPEG COM segment */
-       char COM_data[60];      /* Data in JPEG COM segment */
-
-       unsigned long jpeg_markers;     /* Which markers should go into the JPEG output.
-                                          Unless you exactly know what you do, leave them untouched.
-                                          Inluding less markers will make the resulting code
-                                          smaller, but there will be fewer aplications
-                                          which can read it.
-                                          The presence of the APP and COM marker is
-                                          influenced by APP0_len and COM_len ONLY! */
-#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */
-#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */
-#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */
-#define JPEG_MARKER_COM (1<<6) /* Comment segment */
-#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */
-
-       int VFIFO_FB;           /* Flag for enabling Video Fifo Feedback.
-                                  If this flag is turned on and JPEG decompressing
-                                  is going to the screen, the decompress process
-                                  is stopped every time the Video Fifo is full.
-                                  This enables a smooth decompress to the screen
-                                  but the video output signal will get scrambled */
-
-       /* Misc */
-
-       char reserved[312];     /* Makes 512 bytes for this structure */
-};
-
-/*
-   Private IOCTL to set up for displaying MJPEG
- */
-#define BUZIOC_G_PARAMS       _IOR ('v', BASE_VIDIOCPRIVATE+0,  struct zoran_params)
-#define BUZIOC_S_PARAMS       _IOWR('v', BASE_VIDIOCPRIVATE+1,  struct zoran_params)
-#define BUZIOC_REQBUFS        _IOWR('v', BASE_VIDIOCPRIVATE+2,  struct zoran_requestbuffers)
-#define BUZIOC_QBUF_CAPT      _IOW ('v', BASE_VIDIOCPRIVATE+3,  int)
-#define BUZIOC_QBUF_PLAY      _IOW ('v', BASE_VIDIOCPRIVATE+4,  int)
-#define BUZIOC_SYNC           _IOR ('v', BASE_VIDIOCPRIVATE+5,  struct zoran_sync)
-#define BUZIOC_G_STATUS       _IOWR('v', BASE_VIDIOCPRIVATE+6,  struct zoran_status)
-
-
-#ifdef __KERNEL__
-
-#define BUZ_NUM_STAT_COM    4
-#define BUZ_MASK_STAT_COM   3
-
-#define BUZ_MAX_FRAME     256  /* Must be a power of 2 */
-#define BUZ_MASK_FRAME    255  /* Must be BUZ_MAX_FRAME-1 */
-
-#if VIDEO_MAX_FRAME <= 32
-#define   V4L_MAX_FRAME   32
-#elif VIDEO_MAX_FRAME <= 64
-#define   V4L_MAX_FRAME   64
-#else
-#error   "Too many video frame buffers to handle"
-#endif
-#define   V4L_MASK_FRAME   (V4L_MAX_FRAME - 1)
-
-
-#include "zr36057.h"
-
-enum zoran_codec_mode {
-       BUZ_MODE_IDLE,          /* nothing going on */
-       BUZ_MODE_MOTION_COMPRESS,       /* grabbing frames */
-       BUZ_MODE_MOTION_DECOMPRESS,     /* playing frames */
-       BUZ_MODE_STILL_COMPRESS,        /* still frame conversion */
-       BUZ_MODE_STILL_DECOMPRESS       /* still frame conversion */
-};
-
-enum zoran_buffer_state {
-       BUZ_STATE_USER,         /* buffer is owned by application */
-       BUZ_STATE_PEND,         /* buffer is queued in pend[] ready to feed to I/O */
-       BUZ_STATE_DMA,          /* buffer is queued in dma[] for I/O */
-       BUZ_STATE_DONE          /* buffer is ready to return to application */
-};
-
-struct zoran_gbuffer {
-       u32 *frag_tab;          /* addresses of frag table */
-       u32 frag_tab_bus;       /* same value cached to save time in ISR */
-       enum zoran_buffer_state state;  /* non-zero if corresponding buffer is in use in grab queue */
-       struct zoran_sync bs;   /* DONE: info to return to application */
-};
-
-struct v4l_gbuffer {
-       char *fbuffer;          /* virtual  address of frame buffer */
-       unsigned long fbuffer_phys;     /* physical address of frame buffer */
-       unsigned long fbuffer_bus;      /* bus      address of frame buffer */
-       enum zoran_buffer_state state;  /* state: unused/pending/done */
-};
-
-struct zoran {
-       struct video_device video_dev;
-       struct i2c_bus i2c;
-
-       int initialized;        /* flag if zoran has been correctly initalized */
-       int user;               /* number of current users (0 or 1) */
-
-       unsigned short id;      /* number of this device */
-       char name[32];          /* name of this device */
-       struct pci_dev *pci_dev;        /* PCI device */
-       unsigned char revision;         /* revision of zr36057 */
-       int board;                      /* Board type */
-#define BOARD_BUZ              0
-#define BOARD_LML33            1       
-       unsigned int zr36057_adr;       /* bus address of IO mem returned by PCI BIOS */
-       unsigned char *zr36057_mem;     /* pointer to mapped IO memory */
-
-       int map_mjpeg_buffers;  /* Flag which bufferset will map by next mmap() */
-
-       spinlock_t lock;        /* Spinlock */
-
-       /* Video for Linux parameters */
-
-       struct video_picture picture;   /* Current picture params */
-       struct video_buffer buffer;     /* Current buffer params */
-       struct video_window window;     /* Current window params */
-       int buffer_set, window_set;     /* Flags if the above structures are set */
-       int video_interlace;    /* Image on screen is interlaced */
-
-       u32 *overlay_mask;
-
-       wait_queue_head_t v4l_capq;     /* wait here for grab to finish */
-
-       int v4l_overlay_active; /* Overlay grab is activated */
-       int v4l_memgrab_active; /* Memory grab is activated */
-
-       int v4l_grab_frame;     /* Frame number being currently grabbed */
-#define NO_GRAB_ACTIVE (-1)
-       int v4l_grab_seq;       /* Number of frames grabbed */
-       int gwidth;             /* Width of current memory capture */
-       int gheight;            /* Height of current memory capture */
-       int gformat;            /* Format of ... */
-       int gbpl;               /* byte per line of ... */
-
-       /* V4L grab queue of frames pending */
-
-       unsigned v4l_pend_head;
-       unsigned v4l_pend_tail;
-       int v4l_pend[V4L_MAX_FRAME];
-
-       struct v4l_gbuffer v4l_gbuf[VIDEO_MAX_FRAME];   /* V4L   buffers' info */
-
-       /* Buz MJPEG parameters */
-
-       unsigned long jpg_nbufs;        /* Number of buffers */
-       unsigned long jpg_bufsize;      /* Size of mjpeg buffers in bytes */
-       int jpg_buffers_allocated;      /* Flag if buffers are allocated  */
-       int need_contiguous;    /* Flag if contiguous buffers are needed */
-
-       enum zoran_codec_mode codec_mode;               /* status of codec */
-       struct zoran_params params;     /* structure with a lot of things to play with */
-
-       wait_queue_head_t  jpg_capq;    /* wait here for grab to finish */
-
-       /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */
-       /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */
-       /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */
-       unsigned long jpg_que_head;     /* Index where to put next buffer which is queued */
-       unsigned long jpg_dma_head;     /* Index of next buffer which goes into stat_com  */
-       unsigned long jpg_dma_tail;     /* Index of last buffer in stat_com               */
-       unsigned long jpg_que_tail;     /* Index of last buffer in queue                  */
-       unsigned long jpg_seq_num;      /* count of frames since grab/play started */
-
-       /* zr36057's code buffer table */
-       u32 *stat_com;          /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
-
-       /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */
-       int jpg_pend[BUZ_MAX_FRAME];
-
-       /* array indexed by frame number */
-       struct zoran_gbuffer jpg_gbuf[BUZ_MAX_FRAME];   /* MJPEG buffers' info */
-};
-
-#endif
-
-/*The following should be done in more portable way. It depends on define
-   of _ALPHA_BUZ in the Makefile. */
-
-#ifdef _ALPHA_BUZ
-#define btwrite(dat,adr)    writel((dat),(char *) (zr->zr36057_adr+(adr)))
-#define btread(adr)         readl(zr->zr36057_adr+(adr))
-#else
-#define btwrite(dat,adr)    writel((dat), (char *) (zr->zr36057_mem+(adr)))
-#define btread(adr)         readl(zr->zr36057_mem+(adr))
-#endif
-
-#define btand(dat,adr)      btwrite((dat) & btread(adr), adr)
-#define btor(dat,adr)       btwrite((dat) | btread(adr), adr)
-#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
-
-#define I2C_TSA5522        0xc2
-#define I2C_TDA9850        0xb6
-#define I2C_HAUPEE         0xa0
-#define I2C_STBEE          0xae
-#define   I2C_SAA7111        0x48
-#define   I2C_SAA7185        0x88
-
-#define TDA9850_CON1       0x04
-#define TDA9850_CON2       0x05
-#define TDA9850_CON3       0x06
-#define TDA9850_CON4       0x07
-#define TDA9850_ALI1       0x08
-#define TDA9850_ALI2       0x09
-#define TDA9850_ALI3       0x0a
-
-#endif
diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
new file mode 100644 (file)
index 0000000..61935e0
--- /dev/null
@@ -0,0 +1,1498 @@
+/* 
+ * Motion Eye video4linux driver for Sony Vaio PictureBook
+ *
+ * Copyright (C) 2001 Stelian Pop <stelian.pop@fr.alcove.com>, AlcĂ´ve
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ *
+ * Some parts borrowed from various video4linux drivers, especially
+ * bttv-driver.c and zoran.c, see original files for credits.
+ * 
+ * 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.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/wrapper.h>
+#include <linux/interrupt.h>
+
+#include "meye.h"
+#include "linux/meye.h"
+
+/* driver structure - only one possible */
+static struct meye meye;
+/* number of grab buffers */
+static unsigned int gbuffers = 2;
+/* size of a grab buffer */
+static unsigned int gbufsize = MEYE_MAX_BUFSIZE;
+/* /dev/videoX registration number */
+static int video_nr = -1;
+
+/****************************************************************************/
+/* Queue routines                                                           */
+/****************************************************************************/
+
+/* Inits the queue */
+static inline void meye_initq(struct meye_queue *queue) {
+       queue->head = queue->tail = 0;
+       queue->len = 0;
+       queue->s_lock = (spinlock_t)SPIN_LOCK_UNLOCKED;
+       init_waitqueue_head(&queue->proc_list);
+}
+
+/* Pulls an element from the queue */
+static inline int meye_pullq(struct meye_queue *queue) {
+       int result;
+       unsigned long flags;
+
+       spin_lock_irqsave(&queue->s_lock, flags);
+       if (!queue->len) {
+               spin_unlock_irqrestore(&queue->s_lock, flags);
+               return -1;
+       }
+       result = queue->buf[queue->head];
+       queue->head++;
+       queue->head &= (MEYE_QUEUE_SIZE - 1);
+       queue->len--;
+       spin_unlock_irqrestore(&queue->s_lock, flags);
+       return result;
+}
+
+/* Pushes an element into the queue */
+static inline void meye_pushq(struct meye_queue *queue, int element) {
+       unsigned long flags;
+
+       spin_lock_irqsave(&queue->s_lock, flags);
+       if (queue->len == MEYE_QUEUE_SIZE) {
+               /* remove the first element */
+               queue->head++;
+               queue->head &= (MEYE_QUEUE_SIZE - 1);
+               queue->len--;
+       }
+       queue->buf[queue->tail] = element;
+       queue->tail++;
+       queue->tail &= (MEYE_QUEUE_SIZE - 1);
+       queue->len++;
+
+       spin_unlock_irqrestore(&queue->s_lock, flags);
+}
+
+/* Tests if the queue is empty */
+static inline int meye_emptyq(struct meye_queue *queue, int *elem) {
+       int result;
+       unsigned long flags;
+
+       spin_lock_irqsave(&queue->s_lock, flags);
+       result = (queue->len == 0);
+       if (!result && elem)
+               *elem = queue->buf[queue->head];
+       spin_unlock_irqrestore(&queue->s_lock, flags);
+       return result;
+}
+
+/****************************************************************************/
+/* Memory allocation routines (stolen from bttv-driver.c)                   */
+/****************************************************************************/
+
+#define MDEBUG(x)      do {} while (0)
+/* #define MDEBUG(x)   x */
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) {
+        unsigned long ret = 0UL;
+       pmd_t *pmd;
+       pte_t *ptep, pte;
+  
+       if (!pgd_none(*pgd)) {
+                pmd = pmd_offset(pgd, adr);
+                if (!pmd_none(*pmd)) {
+                        ptep = pte_offset(pmd, adr);
+                        pte = *ptep;
+                        if(pte_present(pte)) {
+                               ret = (unsigned long)page_address(pte_page(pte));
+                               ret |= (adr & (PAGE_SIZE - 1));
+                               
+                       }
+                }
+        }
+        MDEBUG(printk("uv2kva(%lx-->%lx)\n", adr, ret));
+       return ret;
+}
+
+static inline unsigned long uvirt_to_bus(unsigned long adr) {
+        unsigned long kva, ret;
+
+        kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
+       ret = virt_to_bus((void *)kva);
+        MDEBUG(printk("uv2b(%lx-->%lx)\n", adr, ret));
+        return ret;
+}
+
+static inline unsigned long kvirt_to_bus(unsigned long adr) {
+        unsigned long va, kva, ret;
+
+        va = VMALLOC_VMADDR(adr);
+        kva = uvirt_to_kva(pgd_offset_k(va), va);
+       ret = virt_to_bus((void *)kva);
+        MDEBUG(printk("kv2b(%lx-->%lx)\n", adr, ret));
+        return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr) {
+        unsigned long va, kva, ret;
+
+        va = VMALLOC_VMADDR(adr);
+        kva = uvirt_to_kva(pgd_offset_k(va), va);
+       ret = __pa(kva);
+        MDEBUG(printk("kv2pa(%lx-->%lx)\n", adr, ret));
+        return ret;
+}
+
+static void *rvmalloc(signed long size) {
+       void *mem;
+       unsigned long adr, page;
+
+       mem = vmalloc_32(size);
+       if (mem) {
+               memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+               adr = (unsigned long)mem;
+               while (size > 0) {
+                       page = kvirt_to_pa(adr);
+                       mem_map_reserve(virt_to_page(__va(page)));
+                       adr += PAGE_SIZE;
+                       size -= PAGE_SIZE;
+               }
+       }
+       return mem;
+}
+
+static void rvfree(void * mem, signed long size) {
+        unsigned long adr, page;
+        
+       if (mem) {
+               adr = (unsigned long) mem;
+               while (size > 0) {
+                       page = kvirt_to_pa(adr);
+                       mem_map_unreserve(virt_to_page(__va(page)));
+                       adr += PAGE_SIZE;
+                       size -= PAGE_SIZE;
+               }
+               vfree(mem);
+       }
+}
+
+/* return a page table pointing to N pages of locked memory */
+static void *ptable_alloc(int npages, u32 *pt_addr) {
+       int i = 0;
+       void *vmem;
+       u32 ptable[npages+1];
+       signed long size;
+       unsigned long adr;
+
+       size = (npages + 1) * PAGE_SIZE;
+       vmem = rvmalloc(size);
+       if (!vmem)
+               return NULL;
+
+       memset(ptable, 0, sizeof(ptable));
+        adr = (unsigned long)vmem;
+       while (size > 0) {
+               ptable[i++] = virt_to_bus(__va(kvirt_to_pa(adr)));
+               adr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       memcpy(vmem + npages * PAGE_SIZE, ptable, PAGE_SIZE);
+       *pt_addr = ptable[npages];
+
+       return vmem;
+}
+
+static void ptable_free(void *vmem, int npages) {
+       rvfree(vmem, (npages + 1) * PAGE_SIZE);
+}
+
+/****************************************************************************/
+/* JPEG tables at different qualities to load into the VRJ chip             */
+/****************************************************************************/
+
+/* return a set of quantisation tables based on a quality from 1 to 10 */
+static u16 *jpeg_quantisation_tables(int *size, int quality) {
+       static u16 tables0[] = {
+               0xdbff, 0x4300, 0xff00, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 
+               0xdbff, 0x4300, 0xff01, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 
+       };
+       static u16 tables1[] = {
+               0xdbff, 0x4300, 0x5000, 0x3c37, 0x3c46, 0x5032, 0x4146, 0x5a46, 
+               0x5055, 0x785f, 0x82c8, 0x6e78, 0x786e, 0xaff5, 0x91b9, 0xffc8, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 
+               0xdbff, 0x4300, 0x5501, 0x5a5a, 0x6978, 0xeb78, 0x8282, 0xffeb, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 
+               0xffff, 0xffff, 0xffff, 
+       };
+       static u16 tables2[] = {
+               0xdbff, 0x4300, 0x2800, 0x1e1c, 0x1e23, 0x2819, 0x2123, 0x2d23, 
+               0x282b, 0x3c30, 0x4164, 0x373c, 0x3c37, 0x587b, 0x495d, 0x9164, 
+               0x9980, 0x8f96, 0x8c80, 0xa08a, 0xe6b4, 0xa0c3, 0xdaaa, 0x8aad, 
+               0xc88c, 0xcbff, 0xeeda, 0xfff5, 0xffff, 0xc19b, 0xffff, 0xfaff, 
+               0xe6ff, 0xfffd, 0xfff8, 
+               0xdbff, 0x4300, 0x2b01, 0x2d2d, 0x353c, 0x763c, 0x4141, 0xf876, 
+               0x8ca5, 0xf8a5, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 
+               0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 
+               0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 0xf8f8, 
+               0xf8f8, 0xf8f8, 0xfff8, 
+       };
+       static u16 tables3[] = {
+               0xdbff, 0x4300, 0x1b00, 0x1412, 0x1417, 0x1b11, 0x1617, 0x1e17, 
+               0x1b1c, 0x2820, 0x2b42, 0x2528, 0x2825, 0x3a51, 0x303d, 0x6042, 
+               0x6555, 0x5f64, 0x5d55, 0x6a5b, 0x9978, 0x6a81, 0x9071, 0x5b73, 
+               0x855d, 0x86b5, 0x9e90, 0xaba3, 0xabad, 0x8067, 0xc9bc, 0xa6ba, 
+               0x99c7, 0xaba8, 0xffa4, 
+               0xdbff, 0x4300, 0x1c01, 0x1e1e, 0x2328, 0x4e28, 0x2b2b, 0xa44e, 
+               0x5d6e, 0xa46e, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 
+               0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 
+               0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 
+               0xa4a4, 0xa4a4, 0xffa4, 
+       };
+       static u16 tables4[] = {
+               0xdbff, 0x4300, 0x1400, 0x0f0e, 0x0f12, 0x140d, 0x1012, 0x1712, 
+               0x1415, 0x1e18, 0x2132, 0x1c1e, 0x1e1c, 0x2c3d, 0x242e, 0x4932, 
+               0x4c40, 0x474b, 0x4640, 0x5045, 0x735a, 0x5062, 0x6d55, 0x4556, 
+               0x6446, 0x6588, 0x776d, 0x817b, 0x8182, 0x604e, 0x978d, 0x7d8c, 
+               0x7396, 0x817e, 0xff7c, 
+               0xdbff, 0x4300, 0x1501, 0x1717, 0x1a1e, 0x3b1e, 0x2121, 0x7c3b, 
+               0x4653, 0x7c53, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 
+               0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 
+               0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 0x7c7c, 
+               0x7c7c, 0x7c7c, 0xff7c, 
+       };
+       static u16 tables5[] = {
+               0xdbff, 0x4300, 0x1000, 0x0c0b, 0x0c0e, 0x100a, 0x0d0e, 0x120e, 
+               0x1011, 0x1813, 0x1a28, 0x1618, 0x1816, 0x2331, 0x1d25, 0x3a28, 
+               0x3d33, 0x393c, 0x3833, 0x4037, 0x5c48, 0x404e, 0x5744, 0x3745, 
+               0x5038, 0x516d, 0x5f57, 0x6762, 0x6768, 0x4d3e, 0x7971, 0x6470, 
+               0x5c78, 0x6765, 0xff63, 
+               0xdbff, 0x4300, 0x1101, 0x1212, 0x1518, 0x2f18, 0x1a1a, 0x632f, 
+               0x3842, 0x6342, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 
+               0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 
+               0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 0x6363, 
+               0x6363, 0x6363, 0xff63, 
+       };
+       static u16 tables6[] = {
+               0xdbff, 0x4300, 0x0d00, 0x0a09, 0x0a0b, 0x0d08, 0x0a0b, 0x0e0b, 
+               0x0d0e, 0x130f, 0x1520, 0x1213, 0x1312, 0x1c27, 0x171e, 0x2e20, 
+               0x3129, 0x2e30, 0x2d29, 0x332c, 0x4a3a, 0x333e, 0x4636, 0x2c37, 
+               0x402d, 0x4157, 0x4c46, 0x524e, 0x5253, 0x3e32, 0x615a, 0x505a, 
+               0x4a60, 0x5251, 0xff4f, 
+               0xdbff, 0x4300, 0x0e01, 0x0e0e, 0x1113, 0x2613, 0x1515, 0x4f26, 
+               0x2d35, 0x4f35, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 
+               0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 
+               0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 0x4f4f, 
+               0x4f4f, 0x4f4f, 0xff4f, 
+       };
+       static u16 tables7[] = {
+               0xdbff, 0x4300, 0x0a00, 0x0707, 0x0708, 0x0a06, 0x0808, 0x0b08, 
+               0x0a0a, 0x0e0b, 0x1018, 0x0d0e, 0x0e0d, 0x151d, 0x1116, 0x2318, 
+               0x251f, 0x2224, 0x221f, 0x2621, 0x372b, 0x262f, 0x3429, 0x2129, 
+               0x3022, 0x3141, 0x3934, 0x3e3b, 0x3e3e, 0x2e25, 0x4944, 0x3c43, 
+               0x3748, 0x3e3d, 0xff3b, 
+               0xdbff, 0x4300, 0x0a01, 0x0b0b, 0x0d0e, 0x1c0e, 0x1010, 0x3b1c, 
+               0x2228, 0x3b28, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 
+               0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 
+               0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 0x3b3b, 
+               0x3b3b, 0x3b3b, 0xff3b, 
+       };
+       static u16 tables8[] = {
+               0xdbff, 0x4300, 0x0600, 0x0504, 0x0506, 0x0604, 0x0506, 0x0706, 
+               0x0607, 0x0a08, 0x0a10, 0x090a, 0x0a09, 0x0e14, 0x0c0f, 0x1710, 
+               0x1814, 0x1718, 0x1614, 0x1a16, 0x251d, 0x1a1f, 0x231b, 0x161c, 
+               0x2016, 0x202c, 0x2623, 0x2927, 0x292a, 0x1f19, 0x302d, 0x282d, 
+               0x2530, 0x2928, 0xff28, 
+               0xdbff, 0x4300, 0x0701, 0x0707, 0x080a, 0x130a, 0x0a0a, 0x2813, 
+               0x161a, 0x281a, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 
+               0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 
+               0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 0x2828, 
+               0x2828, 0x2828, 0xff28, 
+       };
+       static u16 tables9[] = {
+               0xdbff, 0x4300, 0x0300, 0x0202, 0x0203, 0x0302, 0x0303, 0x0403, 
+               0x0303, 0x0504, 0x0508, 0x0405, 0x0504, 0x070a, 0x0607, 0x0c08, 
+               0x0c0a, 0x0b0c, 0x0b0a, 0x0d0b, 0x120e, 0x0d10, 0x110e, 0x0b0e, 
+               0x100b, 0x1016, 0x1311, 0x1514, 0x1515, 0x0f0c, 0x1817, 0x1416, 
+               0x1218, 0x1514, 0xff14, 
+               0xdbff, 0x4300, 0x0301, 0x0404, 0x0405, 0x0905, 0x0505, 0x1409, 
+               0x0b0d, 0x140d, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 
+               0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 
+               0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 0x1414, 
+               0x1414, 0x1414, 0xff14, 
+       };
+       static u16 tables10[] = {
+               0xdbff, 0x4300, 0x0100, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 
+               0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 
+               0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 
+               0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 
+               0x0101, 0x0101, 0xff01, 
+               0xdbff, 0x4300, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 
+               0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 
+               0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 
+               0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 0x0101, 
+               0x0101, 0x0101, 0xff01, 
+       };
+
+       switch (quality) {
+       case 0:
+               *size = sizeof(tables0);
+               return tables0;
+       case 1:
+               *size = sizeof(tables1);
+               return tables1;
+       case 2:
+               *size = sizeof(tables2);
+               return tables2;
+       case 3:
+               *size = sizeof(tables3);
+               return tables3;
+       case 4:
+               *size = sizeof(tables4);
+               return tables4;
+       case 5:
+               *size = sizeof(tables5);
+               return tables5;
+       case 6:
+               *size = sizeof(tables6);
+               return tables6;
+       case 7:
+               *size = sizeof(tables7);
+               return tables7;
+       case 8:
+               *size = sizeof(tables8);
+               return tables8;
+       case 9:
+               *size = sizeof(tables9);
+               return tables9;
+       case 10:
+               *size = sizeof(tables10);
+               return tables10;
+       default:
+               printk(KERN_WARNING "meye: invalid quality level %d - using 8\n", quality);
+               *size = sizeof(tables8);
+               return tables8;
+       }
+       return NULL;
+}
+
+/* return a generic set of huffman tables */
+static u16 *jpeg_huffman_tables(int *size) {
+       static u16 tables[] = {
+               0xC4FF, 0xB500, 0x0010, 0x0102, 0x0303, 0x0402, 0x0503, 0x0405, 
+               0x0004, 0x0100, 0x017D, 0x0302, 0x0400, 0x0511, 0x2112, 0x4131, 
+               0x1306, 0x6151, 0x2207, 0x1471, 0x8132, 0xA191, 0x2308, 0xB142, 
+               0x15C1, 0xD152, 0x24F0, 0x6233, 0x8272, 0x0A09, 0x1716, 0x1918, 
+               0x251A, 0x2726, 0x2928, 0x342A, 0x3635, 0x3837, 0x3A39, 0x4443, 
+               0x4645, 0x4847, 0x4A49, 0x5453, 0x5655, 0x5857, 0x5A59, 0x6463, 
+               0x6665, 0x6867, 0x6A69, 0x7473, 0x7675, 0x7877, 0x7A79, 0x8483, 
+               0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 0xA29A, 
+               0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 0xB9B8, 
+               0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 0xD7D6, 
+               0xD9D8, 0xE1DA, 0xE3E2, 0xE5E4, 0xE7E6, 0xE9E8, 0xF1EA, 0xF3F2, 
+               0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, 
+               0xC4FF, 0xB500, 0x0011, 0x0102, 0x0402, 0x0304, 0x0704, 0x0405, 
+               0x0004, 0x0201, 0x0077, 0x0201, 0x1103, 0x0504, 0x3121, 0x1206, 
+               0x5141, 0x6107, 0x1371, 0x3222, 0x0881, 0x4214, 0xA191, 0xC1B1, 
+               0x2309, 0x5233, 0x15F0, 0x7262, 0x0AD1, 0x2416, 0xE134, 0xF125, 
+               0x1817, 0x1A19, 0x2726, 0x2928, 0x352A, 0x3736, 0x3938, 0x433A, 
+               0x4544, 0x4746, 0x4948, 0x534A, 0x5554, 0x5756, 0x5958, 0x635A, 
+               0x6564, 0x6766, 0x6968, 0x736A, 0x7574, 0x7776, 0x7978, 0x827A, 
+               0x8483, 0x8685, 0x8887, 0x8A89, 0x9392, 0x9594, 0x9796, 0x9998, 
+               0xA29A, 0xA4A3, 0xA6A5, 0xA8A7, 0xAAA9, 0xB3B2, 0xB5B4, 0xB7B6, 
+               0xB9B8, 0xC2BA, 0xC4C3, 0xC6C5, 0xC8C7, 0xCAC9, 0xD3D2, 0xD5D4, 
+               0xD7D6, 0xD9D8, 0xE2DA, 0xE4E3, 0xE6E5, 0xE8E7, 0xEAE9, 0xF3F2, 
+               0xF5F4, 0xF7F6, 0xF9F8, 0xFFFA, 
+               0xC4FF, 0x1F00, 0x0000, 0x0501, 0x0101, 0x0101, 0x0101, 0x0000, 
+               0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, 
+               0xFF0B, 
+               0xC4FF, 0x1F00, 0x0001, 0x0103, 0x0101, 0x0101, 0x0101, 0x0101, 
+               0x0000, 0x0000, 0x0000, 0x0201, 0x0403, 0x0605, 0x0807, 0x0A09, 
+               0xFF0B
+       };
+
+       *size = sizeof(tables);
+       return tables;
+}
+
+/****************************************************************************/
+/* MCHIP low-level functions                                                */
+/****************************************************************************/
+
+/* waits for the specified miliseconds */
+static inline void wait_ms(unsigned int ms) {
+       if (!in_interrupt()) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(1 + ms * HZ / 1000);
+       }
+       else
+               mdelay(ms);
+}
+
+/* returns the horizontal capture size */
+static inline int mchip_hsize(void) {
+       return meye.params.subsample ? 320 : 640;
+}
+
+/* returns the vertical capture size */
+static inline int mchip_vsize(void) {
+       return meye.params.subsample ? 240 : 480;
+}
+
+/* waits for a register to be available */
+static void mchip_sync(int reg) {
+       u32 status;
+       int i;
+
+       if (reg == MCHIP_MM_FIFO_DATA) {
+               for (i = 0; i < MCHIP_REG_TIMEOUT; i++) {
+                       status = readl(meye.mchip_mmregs + MCHIP_MM_FIFO_STATUS);
+                       if (!(status & MCHIP_MM_FIFO_WAIT)) {
+                               printk(KERN_WARNING "meye: fifo not ready\n");
+                               return;
+                       }
+                       if (status & MCHIP_MM_FIFO_READY)
+                               return;
+                       udelay(1);
+               }
+       }
+       else if (reg > 0x80) {
+               u32 mask = (reg < 0x100) ? MCHIP_HIC_STATUS_MCC_RDY
+                                        : MCHIP_HIC_STATUS_VRJ_RDY;
+               for (i = 0; i < MCHIP_REG_TIMEOUT; i++) {
+                       status = readl(meye.mchip_mmregs + MCHIP_HIC_STATUS);
+                       if (status & mask)
+                               return;
+                       udelay(1);
+               }
+       }
+       else
+               return;
+       printk(KERN_WARNING "meye: mchip_sync() timeout on reg 0x%x status=0x%x\n", reg, status);
+}
+
+/* sets a value into the register */
+static inline void mchip_set(int reg, u32 v) {
+       mchip_sync(reg);
+       writel(v, meye.mchip_mmregs + reg);
+}
+
+/* get the register value */
+static inline u32 mchip_read(int reg) {
+       mchip_sync(reg);
+       return readl(meye.mchip_mmregs + reg);
+}
+
+/* wait for a register to become a particular value */
+static inline int mchip_delay(u32 reg, u32 v) {
+       int n = 10;
+       while (--n && mchip_read(reg) != v) 
+               udelay(1);
+       return n;
+}
+
+/* setup subsampling */
+static void mchip_subsample(void) {
+       mchip_set(MCHIP_MCC_R_SAMPLING, meye.params.subsample);
+       mchip_set(MCHIP_MCC_R_XRANGE, mchip_hsize());
+       mchip_set(MCHIP_MCC_R_YRANGE, mchip_vsize());
+       mchip_set(MCHIP_MCC_B_XRANGE, mchip_hsize());
+       mchip_set(MCHIP_MCC_B_YRANGE, mchip_vsize());
+       mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+}
+
+/* set the framerate into the mchip */
+static void mchip_set_framerate(void) {
+       mchip_set(MCHIP_HIC_S_RATE, meye.params.framerate);
+}
+
+/* load some huffman and quantisation tables into the VRJ chip ready
+   for JPEG compression */
+static void mchip_load_tables(void) {
+       int i;
+       int size;
+       u16 *tables;
+
+       tables = jpeg_huffman_tables(&size);
+       for (i = 0; i < size / 2; i++)
+               writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA);
+
+       tables = jpeg_quantisation_tables(&size, meye.params.quality);
+       for (i = 0; i < size / 2; i++)
+               writel(tables[i], meye.mchip_mmregs + MCHIP_VRJ_TABLE_DATA);
+}
+
+/* setup the VRJ parameters in the chip */
+static void mchip_vrj_setup(u8 mode) {
+
+       mchip_set(MCHIP_VRJ_BUS_MODE, 5);
+       mchip_set(MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL, 0x1f);
+       mchip_set(MCHIP_VRJ_PDAT_USE, 1);
+       mchip_set(MCHIP_VRJ_IRQ_FLAG, 0xa0);
+       mchip_set(MCHIP_VRJ_MODE_SPECIFY, mode);
+       mchip_set(MCHIP_VRJ_NUM_LINES, mchip_vsize());
+       mchip_set(MCHIP_VRJ_NUM_PIXELS, mchip_hsize());
+       mchip_set(MCHIP_VRJ_NUM_COMPONENTS, 0x1b);
+       mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_LO, 0xFFFF);
+       mchip_set(MCHIP_VRJ_LIMIT_COMPRESSED_HI, 0xFFFF);
+       mchip_set(MCHIP_VRJ_COMP_DATA_FORMAT, 0xC);
+       mchip_set(MCHIP_VRJ_RESTART_INTERVAL, 0);
+       mchip_set(MCHIP_VRJ_SOF1, 0x601);
+       mchip_set(MCHIP_VRJ_SOF2, 0x1502);
+       mchip_set(MCHIP_VRJ_SOF3, 0x1503);
+       mchip_set(MCHIP_VRJ_SOF4, 0x1596);
+       mchip_set(MCHIP_VRJ_SOS,  0x0ed0);
+
+       mchip_load_tables();
+}
+
+/* setup for DMA transfers - also zeros the framebuffer */
+static int mchip_dma_alloc(void) {
+       if (!meye.mchip_fbuffer) {
+               meye.mchip_fbuffer = ptable_alloc(MCHIP_NB_PAGES, 
+                                                 &meye.mchip_ptaddr);
+               if (!meye.mchip_fbuffer)
+                       return -1;
+       }
+       return 0;
+}
+
+/* frees the DMA buffer */
+static void mchip_dma_free(void) {
+       if (meye.mchip_fbuffer) {
+               ptable_free(meye.mchip_fbuffer, MCHIP_NB_PAGES);
+               meye.mchip_fbuffer = 0;
+               meye.mchip_ptaddr = 0;
+       }
+}
+
+/* sets the DMA parameters into the chip */
+static void mchip_dma_setup(void) {
+       int i;
+
+       mchip_set(MCHIP_MM_PT_ADDR, meye.mchip_ptaddr);
+       for (i = 0; i < 4; i++)
+               mchip_set(MCHIP_MM_FIR(i), 0);
+       meye.mchip_fnum = 0;
+}
+
+/* stop any existing HIC action and wait for any dma to complete then
+   reset the dma engine */
+static void mchip_hic_stop(void) {
+       int i = 0;
+
+       meye.mchip_mode = MCHIP_HIC_MODE_NOOP;
+       if (!(mchip_read(MCHIP_HIC_STATUS) & MCHIP_HIC_STATUS_BUSY)) 
+               return;
+       mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP);
+       mchip_delay(MCHIP_HIC_CMD, 0);
+       while (!mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE)) {
+               /*  resetting HIC */
+               mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_STOP);
+               mchip_delay(MCHIP_HIC_CMD, 0);
+               mchip_set(MCHIP_HIC_CTL, MCHIP_HIC_CTL_SOFT_RESET);
+               wait_ms(250);
+               if (i++ > 20) {
+                       printk(KERN_ERR "meye: resetting HIC hanged!\n");
+                       break;
+               }
+       }
+       wait_ms(100);
+}
+
+/****************************************************************************/
+/* MCHIP frame processing functions                                         */
+/****************************************************************************/
+
+/* get the next ready frame from the dma engine */
+static u32 mchip_get_frame(void) {
+       u32 v;
+       
+       v = mchip_read(MCHIP_MM_FIR(meye.mchip_fnum));
+       return v;
+}
+
+/* frees the current frame from the dma engine */
+static void mchip_free_frame(void) {
+       mchip_set(MCHIP_MM_FIR(meye.mchip_fnum), 0);
+       meye.mchip_fnum++;
+       meye.mchip_fnum %= 4;
+}
+
+
+/* read one frame from the framebuffer assuming it was captured using
+   a uncompressed transfer */
+static void  mchip_cont_read_frame(u32 v, u8 *buf, int size) {
+       int pt_id;
+       int avail;
+
+       pt_id = (v >> 17) & 0x3FF;
+       avail = MCHIP_NB_PAGES - pt_id;
+
+       if (size > avail*PAGE_SIZE) {
+               memcpy(buf, meye.mchip_fbuffer + pt_id * PAGE_SIZE, 
+                      avail * PAGE_SIZE);
+               memcpy(buf +avail * PAGE_SIZE, meye.mchip_fbuffer,
+                      size - avail * PAGE_SIZE);
+       }
+       else
+               memcpy(buf, meye.mchip_fbuffer + pt_id * PAGE_SIZE, size);
+}
+
+/* read a compressed frame from the framebuffer */
+static int mchip_comp_read_frame(u32 v, u8 *buf, int size) {
+       int pt_start, pt_end, trailer;
+       int fsize, fsize2;
+       int i;
+
+       pt_start = (v >> 19) & 0xFF;
+       pt_end = (v >> 11) & 0xFF;
+       trailer = (v >> 1) & 0x3FF;
+
+       if (pt_end < pt_start) {
+               fsize = (MCHIP_NB_PAGES_MJPEG - pt_start) * PAGE_SIZE;
+               fsize2 = pt_end * PAGE_SIZE + trailer * 4;
+               if (fsize + fsize2 > size) {
+                       printk(KERN_WARNING "meye: oversized compressed frame %d %d\n", 
+                              fsize, fsize2);
+                       return -1;
+               } else {
+                       memcpy(buf, meye.mchip_fbuffer + pt_start * PAGE_SIZE, 
+                              fsize);
+                       memcpy(buf + fsize, meye.mchip_fbuffer, fsize2); 
+                       fsize += fsize2;
+               }
+       } else {
+               fsize = (pt_end - pt_start) * PAGE_SIZE + trailer * 4;
+               if (fsize > size) {
+                       printk(KERN_WARNING "meye: oversized compressed frame %d\n", 
+                              fsize);
+                       return -1;
+               } else
+                       memcpy(buf, meye.mchip_fbuffer + pt_start * PAGE_SIZE, 
+                              fsize);
+       }
+
+
+#ifdef MEYE_JPEG_CORRECTION
+
+       /* Some mchip generated jpeg frames are incorrect. In most
+        * (all ?) of those cases, the final EOI (0xff 0xd9) marker 
+        * is not present at the end of the frame.
+        *
+        * Since adding the final marker is not enough to restore
+        * the jpeg integrity, we drop the frame.
+        */
+
+       for (i = fsize - 1; i > 0 && buf[i] == 0xff; i--) ;
+
+       if (i < 2 || buf[i - 1] != 0xff || buf[i] != 0xd9)
+               return -1;
+
+#endif
+
+       return fsize;
+}
+
+/* take a picture into SDRAM */
+static void mchip_take_picture(void) {
+       int i;
+       
+       mchip_hic_stop();
+       mchip_subsample();
+       mchip_dma_setup();
+
+       mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_CAP);
+       mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+       mchip_delay(MCHIP_HIC_CMD, 0);
+
+       for (i = 0; i < 100; ++i) {
+               if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+                       break;
+               wait_ms(1);
+       }
+}
+
+/* dma a previously taken picture into a buffer */
+static void mchip_get_picture(u8 *buf, int bufsize) {
+       u32 v;
+       int i;
+
+       mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_OUT);
+       mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+       mchip_delay(MCHIP_HIC_CMD, 0);
+       for (i = 0; i < 100; ++i) {
+               if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+                       break;
+               wait_ms(1);
+       }
+       for (i = 0; i < 4 ; ++i) {
+               v = mchip_get_frame();
+               if (v & MCHIP_MM_FIR_RDY) {
+                       mchip_cont_read_frame(v, buf, bufsize);
+                       break;
+               }
+               mchip_free_frame();
+       }
+}
+
+/* start continuous dma capture */
+static void mchip_continuous_start(void) {
+       mchip_hic_stop();
+       mchip_subsample();
+       mchip_set_framerate();
+       mchip_dma_setup();
+
+       meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT;
+
+       mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_OUT);
+       mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+       mchip_delay(MCHIP_HIC_CMD, 0);
+}
+
+/* compress one frame into a buffer */
+static int mchip_compress_frame(u8 *buf, int bufsize) {
+       u32 v;
+       int len = -1, i;
+
+       mchip_vrj_setup(0x3f);
+       udelay(50);
+
+       mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_COMP);
+       mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+       
+       mchip_delay(MCHIP_HIC_CMD, 0);
+       for (i = 0; i < 100; ++i) {
+               if (mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE))
+                       break;
+               wait_ms(1);
+       }
+
+       for (i = 0; i < 4 ; ++i) {
+               v = mchip_get_frame();
+               if (v & MCHIP_MM_FIR_RDY) {
+                       len = mchip_comp_read_frame(v, buf, bufsize);
+                       break;
+               }
+               mchip_free_frame();
+       }
+       return len;
+}
+
+#if 0
+/* uncompress one image into a buffer */
+static int mchip_uncompress_frame(u8 *img, int imgsize, u8 *buf, int bufsize) {
+       mchip_vrj_setup(0x3f);
+       udelay(50);
+
+       mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_STILL_DECOMP);
+       mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+       
+       mchip_delay(MCHIP_HIC_CMD, 0);
+
+       return mchip_comp_read_frame(buf, bufsize);
+}
+#endif
+
+/* start continuous compressed capture */
+static void mchip_cont_compression_start(void) {
+       mchip_hic_stop();
+       mchip_vrj_setup(0x3f);
+       mchip_subsample();
+       mchip_set_framerate();
+       mchip_dma_setup();
+
+       meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP;
+
+       mchip_set(MCHIP_HIC_MODE, MCHIP_HIC_MODE_CONT_COMP);
+       mchip_set(MCHIP_HIC_CMD, MCHIP_HIC_CMD_START);
+
+       mchip_delay(MCHIP_HIC_CMD, 0);
+}
+
+/****************************************************************************/
+/* Interrupt handling                                                       */
+/****************************************************************************/
+
+static void meye_irq(int irq, void *dev_id, struct pt_regs *regs) {
+       u32 v;
+       int reqnr;
+       v = mchip_read(MCHIP_MM_INTA);
+
+       while (1) {
+               v = mchip_get_frame();
+               if (!(v & MCHIP_MM_FIR_RDY))
+                       goto out;
+               switch (meye.mchip_mode) {
+
+               case MCHIP_HIC_MODE_CONT_OUT:
+                       if (!meye_emptyq(&meye.grabq, NULL)) {
+                               int nr = meye_pullq(&meye.grabq);
+                               mchip_cont_read_frame(
+                                       v, 
+                                       meye.grab_fbuffer + gbufsize * nr,
+                                       mchip_hsize() * mchip_vsize() * 2);
+                               meye.grab_buffer[nr].state = MEYE_BUF_DONE;
+                               wake_up_interruptible(&meye.grabq.proc_list);
+                       }
+                       break;
+
+               case MCHIP_HIC_MODE_CONT_COMP:
+                       if (!meye_emptyq(&meye.grabq, &reqnr)) {
+                               int size;
+                               size = mchip_comp_read_frame(
+                                       v,
+                                       meye.grab_fbuffer + gbufsize * reqnr,
+                                       gbufsize);
+                               if (size == -1)
+                                       break;
+                               reqnr = meye_pullq(&meye.grabq);
+                               meye.grab_buffer[reqnr].size = size;
+                               meye.grab_buffer[reqnr].state = MEYE_BUF_DONE;
+                               wake_up_interruptible(&meye.grabq.proc_list);
+                       }
+                       break;
+
+               default:
+                       /* do not free frame, since it can be a snap */
+                       goto out;
+               } /* switch */
+
+               mchip_free_frame();
+       }
+out:
+}
+
+/****************************************************************************/
+/* video4linux integration                                                  */
+/****************************************************************************/
+
+static int meye_open(struct video_device *dev, int flags) {
+       int i;
+
+       down(&meye.lock);
+       if (meye.open_count) {
+               up(&meye.lock);
+               return -EBUSY;
+       }
+       meye.open_count++;
+       if (mchip_dma_alloc()) {
+               printk(KERN_ERR "meye: mchip framebuffer allocation failed\n");
+               up(&meye.lock);
+               return -ENOBUFS;
+       }
+       mchip_hic_stop();
+       meye_initq(&meye.grabq);
+       for (i = 0; i < MEYE_MAX_BUFNBRS; i++)
+               meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+       up(&meye.lock);
+       return 0;
+}
+
+static void meye_close(struct video_device *dev) {
+       down(&meye.lock);
+       meye.open_count--;
+       mchip_hic_stop();
+       up(&meye.lock);
+}
+
+static int meye_ioctl(struct video_device *dev, unsigned int cmd, void *arg) {
+
+       switch (cmd) {
+
+       case VIDIOCGCAP: {
+               struct video_capability b;
+               strcpy(b.name,meye.video_dev.name);
+               b.type = VID_TYPE_CAPTURE;
+               b.channels = 1;
+               b.audios = 0;
+               b.maxwidth = 640;
+               b.maxheight = 480;
+               b.minwidth = 320;
+               b.minheight = 240;
+               if(copy_to_user(arg,&b,sizeof(b)))
+                       return -EFAULT;
+               break;
+       }
+
+       case VIDIOCGCHAN: {
+               struct video_channel v;
+               if(copy_from_user(&v, arg,sizeof(v)))
+                       return -EFAULT;
+               v.flags = 0;
+               v.tuners = 0;
+               v.type = VIDEO_TYPE_CAMERA;
+               if (v.channel != 0)
+                       return -EINVAL;
+               strcpy(v.name,"Camera");
+               if(copy_to_user(arg,&v,sizeof(v)))
+                       return -EFAULT;
+               break;
+       }
+
+       case VIDIOCSCHAN: {
+               struct video_channel v;
+               if(copy_from_user(&v, arg,sizeof(v)))
+                       return -EFAULT;
+               if (v.channel != 0)
+                       return -EINVAL;
+               break;
+       }
+
+       case VIDIOCGPICT: {
+               struct video_picture p = meye.picture;
+               if(copy_to_user(arg, &p, sizeof(p)))
+                       return -EFAULT;
+               break;
+       }
+
+       case VIDIOCSPICT: {
+               struct video_picture p;
+               if(copy_from_user(&p, arg,sizeof(p)))
+                       return -EFAULT;
+               if (p.depth != 2)
+                       return -EINVAL;
+               if (p.palette != VIDEO_PALETTE_YUV422)
+                       return -EINVAL;
+               down(&meye.lock);
+               sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 
+                                     p.brightness >> 10);
+               sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 
+                                     p.hue >> 10);
+               sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 
+                                     p.colour >> 10);
+               sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 
+                                     p.contrast >> 10);
+               memcpy(&meye.picture, &p, sizeof(p));
+               up(&meye.lock);
+               break;
+       }
+
+       case VIDIOCSYNC: {
+               int i;
+               DECLARE_WAITQUEUE(wait, current);
+
+               if(copy_from_user((void *)&i,arg,sizeof(int)))
+                       return -EFAULT;
+               if (i < 0 || i >= gbuffers)
+                       return -EINVAL;
+
+               switch (meye.grab_buffer[i].state) {
+
+               case MEYE_BUF_UNUSED:
+                       return -EINVAL;
+               case MEYE_BUF_USING:
+                       add_wait_queue(&meye.grabq.proc_list, &wait);
+                       current->state = TASK_INTERRUPTIBLE;
+                       while (meye.grab_buffer[i].state == MEYE_BUF_USING) {
+                               schedule();
+                               if(signal_pending(current)) {
+                                       remove_wait_queue(&meye.grabq.proc_list, &wait);
+                                       current->state = TASK_RUNNING;
+                                       return -EINTR;
+                               }
+                       }
+                       remove_wait_queue(&meye.grabq.proc_list, &wait);
+                       current->state = TASK_RUNNING;
+                       /* fall through */
+               case MEYE_BUF_DONE:
+                       meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+               }
+               break;
+       }
+
+       case VIDIOCMCAPTURE: {
+               struct video_mmap vm;
+               int restart = 0;
+
+               if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm)))
+                       return -EFAULT;
+               if (vm.frame >= gbuffers || vm.frame < 0)
+                       return -EINVAL;
+               if (vm.format != VIDEO_PALETTE_YUV422)
+                       return -EINVAL;
+               if (vm.height * vm.width * 2 > gbufsize)
+                       return -EINVAL;
+               if (!meye.grab_fbuffer)
+                       return -EINVAL;
+               if (meye.grab_buffer[vm.frame].state != MEYE_BUF_UNUSED)
+                       return -EBUSY;
+
+               down(&meye.lock);
+               if (vm.width == 640 && vm.height == 480) {
+                       if (meye.params.subsample) {
+                               meye.params.subsample = 0;
+                               restart = 1;
+                       }
+               }
+               else if (vm.width == 320 && vm.height == 240) {
+                       if (!meye.params.subsample) {
+                               meye.params.subsample = 1;
+                               restart = 1;
+                       }
+               }
+               else {
+                       up(&meye.lock);
+                       return -EINVAL;
+               }
+
+               if (restart || meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT)
+                       mchip_continuous_start();
+               meye.grab_buffer[vm.frame].state = MEYE_BUF_USING;
+               meye_pushq(&meye.grabq, vm.frame);
+               up(&meye.lock);
+               break;
+       }
+
+       case VIDIOCGMBUF: {
+               struct video_mbuf vm;
+               int i;
+
+               memset(&vm, 0 , sizeof(vm));
+               vm.size = gbufsize * gbuffers;
+               vm.frames = gbuffers;
+               for (i = 0; i < gbuffers; i++)
+                       vm.offsets[i] = i * gbufsize;
+               if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+                       return -EFAULT;
+               break;
+       }
+
+       case MEYEIOC_G_PARAMS: {
+               if (copy_to_user(arg, &meye.params, sizeof(meye.params)))
+                       return -EFAULT;
+               break;
+       }
+
+       case MEYEIOC_S_PARAMS: {
+               struct meye_params jp;
+               if (copy_from_user(&jp, arg, sizeof(jp)))
+                       return -EFAULT;
+               if (jp.subsample > 1)
+                       return -EINVAL;
+               if (jp.quality > 10)
+                       return -EINVAL;
+               if (jp.sharpness > 63 || jp.agc > 63 || jp.picture > 63)
+                       return -EINVAL;
+               if (jp.framerate > 31)
+                       return -EINVAL;
+               down(&meye.lock);
+               if (meye.params.subsample != jp.subsample ||
+                   meye.params.quality != jp.quality)
+                       mchip_hic_stop();       /* need restart */
+               memcpy(&meye.params, &jp, sizeof(jp));
+               sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS,
+                                     meye.params.sharpness);
+               sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC,
+                                     meye.params.agc);
+               sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE,
+                                     meye.params.picture);
+               up(&meye.lock);
+               break;
+       }
+
+       case MEYEIOC_QBUF_CAPT: {
+               int nb;
+
+               if (copy_from_user((void *) &nb, (void *) arg, sizeof(int)))
+                       return -EFAULT;
+
+               if (!meye.grab_fbuffer) 
+                       return -EINVAL;
+               if (nb >= gbuffers)
+                       return -EINVAL;
+               if (nb < 0) {
+                       /* stop capture */
+                       mchip_hic_stop();
+                       return 0;
+               }
+               if (meye.grab_buffer[nb].state != MEYE_BUF_UNUSED)
+                       return -EBUSY;
+               down(&meye.lock);
+               if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP)
+                       mchip_cont_compression_start();
+               meye.grab_buffer[nb].state = MEYE_BUF_USING;
+               meye_pushq(&meye.grabq, nb);
+               up(&meye.lock);
+               break;
+       }
+
+       case MEYEIOC_SYNC: {
+               int i;
+               DECLARE_WAITQUEUE(wait, current);
+
+               if(copy_from_user((void *)&i,arg,sizeof(int)))
+                       return -EFAULT;
+               if (i < 0 || i >= gbuffers)
+                       return -EINVAL;
+
+               switch (meye.grab_buffer[i].state) {
+
+               case MEYE_BUF_UNUSED:
+                       return -EINVAL;
+               case MEYE_BUF_USING:
+                       add_wait_queue(&meye.grabq.proc_list, &wait);
+                       current->state = TASK_INTERRUPTIBLE;
+                       while (meye.grab_buffer[i].state == MEYE_BUF_USING) {
+                               schedule();
+                               if(signal_pending(current)) {
+                                       remove_wait_queue(&meye.grabq.proc_list, &wait);
+                                       current->state = TASK_RUNNING;
+                                       return -EINTR;
+                               }
+                       }
+                       remove_wait_queue(&meye.grabq.proc_list, &wait);
+                       current->state = TASK_RUNNING;
+                       /* fall through */
+               case MEYE_BUF_DONE:
+                       meye.grab_buffer[i].state = MEYE_BUF_UNUSED;
+               }
+               i = meye.grab_buffer[i].size;
+               if (copy_to_user(arg, (void *)&i, sizeof(int)))
+                       return -EFAULT;
+               break;
+       }
+
+       case MEYEIOC_STILLCAPT: {
+
+               if (!meye.grab_fbuffer) 
+                       return -EINVAL;
+               if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED)
+                       return -EBUSY;
+               down(&meye.lock);
+               meye.grab_buffer[0].state = MEYE_BUF_USING;
+               mchip_take_picture();
+               mchip_get_picture(
+                       meye.grab_fbuffer,
+                       mchip_hsize() * mchip_vsize() * 2);
+               meye.grab_buffer[0].state = MEYE_BUF_DONE;
+               up(&meye.lock);
+               break;
+       }
+
+       case MEYEIOC_STILLJCAPT: {
+               int len = -1;
+
+               if (!meye.grab_fbuffer) 
+                       return -EINVAL;
+               if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED)
+                       return -EBUSY;
+               down(&meye.lock);
+               meye.grab_buffer[0].state = MEYE_BUF_USING;
+               while (len == -1) {
+                       mchip_take_picture();
+                       len = mchip_compress_frame(meye.grab_fbuffer, gbufsize);
+               }
+               meye.grab_buffer[0].state = MEYE_BUF_DONE;
+               up(&meye.lock);
+               if (copy_to_user(arg, (void *)&len, sizeof(int)))
+                       return -EFAULT;
+               break;
+       }
+
+       default:
+               return -ENOIOCTLCMD;
+               
+       } /* switch */
+
+       return 0;
+}
+
+static int meye_mmap(struct video_device *dev, const char *adr, 
+                    unsigned long size) {
+       unsigned long start=(unsigned long) adr;
+       unsigned long page,pos;
+
+       down(&meye.lock);
+       if (size > gbuffers * gbufsize) {
+               up(&meye.lock);
+               return -EINVAL;
+       }
+       if (!meye.grab_fbuffer) {
+               /* lazy allocation */
+               meye.grab_fbuffer = rvmalloc(gbuffers*gbufsize);
+               if (!meye.grab_fbuffer) {
+                       printk(KERN_ERR "meye: v4l framebuffer allocation failed\n");
+                       up(&meye.lock);
+                       return -ENOMEM;
+               }
+       }
+       pos = (unsigned long)meye.grab_fbuffer;
+
+       while (size > 0) {
+               page = kvirt_to_pa(pos);
+               if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+                       up(&meye.lock);
+                       return -EAGAIN;
+               }
+               start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+       up(&meye.lock);
+       return 0;
+}
+
+static struct video_device meye_template = {
+       owner:          THIS_MODULE,
+       name:           "meye",
+       type:           VID_TYPE_CAPTURE,
+       hardware:       VID_HARDWARE_MEYE,
+       open:           meye_open,
+       close:          meye_close,
+       ioctl:          meye_ioctl,
+       mmap:           meye_mmap,
+};
+
+static int __devinit meye_probe(struct pci_dev *pcidev, 
+                               const struct pci_device_id *ent) {
+       int ret;
+       unsigned long mchip_adr;
+       u8 revision;
+
+       if (meye.mchip_dev != NULL) {
+               printk(KERN_ERR "meye: only one device allowed!\n");
+               ret = -EBUSY;
+               goto out1;
+       }
+
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 1);
+
+       meye.mchip_dev = pcidev;
+       meye.mchip_irq = pcidev->irq;
+       memcpy(&meye.video_dev, &meye_template, sizeof(meye_template));
+
+       if (mchip_dma_alloc()) {
+               printk(KERN_ERR "meye: mchip framebuffer allocation failed\n");
+               ret = -ENOMEM;
+               goto out2;
+       }
+
+       if ((ret = pci_enable_device(meye.mchip_dev))) {
+               printk(KERN_ERR "meye: pci_enable_device failed\n");
+               goto out3;
+       }
+
+       mchip_adr = pci_resource_start(meye.mchip_dev,0);
+       if (!mchip_adr) {
+               printk(KERN_ERR "meye: mchip has no device base address\n");
+               ret = -EIO;
+               goto out4;
+       }
+       if (!request_mem_region(pci_resource_start(meye.mchip_dev, 0),
+                               pci_resource_len(meye.mchip_dev, 0),
+                               "meye")) {
+               ret = -EIO;
+               printk(KERN_ERR "meye: request_mem_region failed\n");
+               goto out4;
+       }
+
+       pci_read_config_byte(meye.mchip_dev, PCI_REVISION_ID, &revision);
+
+       pci_set_master(meye.mchip_dev);
+
+       pci_write_config_byte(meye.mchip_dev, PCI_CACHE_LINE_SIZE, 8);
+       pci_write_config_byte(meye.mchip_dev, PCI_LATENCY_TIMER, 64);
+
+       if ((ret = request_irq(meye.mchip_irq, meye_irq, 
+                              SA_INTERRUPT | SA_SHIRQ, "meye", meye_irq))) {
+               printk(KERN_ERR "meye: request_irq failed (ret=%d)\n", ret);
+               goto out5;
+       }
+
+       meye.mchip_mmregs = ioremap(mchip_adr, MCHIP_MM_REGS);
+       if (!meye.mchip_mmregs) {
+               printk(KERN_ERR "meye: ioremap failed\n");
+               ret = -EIO;
+               goto out6;
+       }
+       
+       /* Ask the camera to perform a soft reset. */
+       pci_write_config_word(meye.mchip_dev, MCHIP_PCI_SOFTRESET_SET, 1);
+
+       mchip_delay(MCHIP_HIC_CMD, 0);
+       mchip_delay(MCHIP_HIC_STATUS, MCHIP_HIC_STATUS_IDLE);
+
+       wait_ms(1);
+       mchip_set(MCHIP_VRJ_SOFT_RESET, 1);
+
+       wait_ms(1);
+       mchip_set(MCHIP_MM_PCI_MODE, 5);
+
+       wait_ms(1);
+       mchip_set(MCHIP_MM_INTA, MCHIP_MM_INTA_HIC_1_MASK);
+
+       if (video_register_device(&meye.video_dev, VFL_TYPE_GRABBER, video_nr) < 0) {
+
+               printk(KERN_ERR "meye: video_register_device failed\n");
+               ret = -EIO;
+               goto out7;
+       }
+       
+       printk(KERN_INFO "meye: Motion Eye Camera Driver v%d.%d.\n",
+              MEYE_DRIVER_MAJORVERSION,
+              MEYE_DRIVER_MINORVERSION);
+       printk(KERN_INFO "meye: mchip KL5A72002 rev. %d, base %lx, irq %d\n", 
+               revision, mchip_adr, meye.mchip_irq);
+
+       /* init all fields */
+       init_MUTEX(&meye.lock);
+
+       meye.picture.depth = 2;
+       meye.picture.palette = VIDEO_PALETTE_YUV422;
+       meye.picture.brightness = 32 << 10;
+       meye.picture.hue = 32 << 10;
+       meye.picture.colour = 32 << 10;
+       meye.picture.contrast = 32 << 10;
+       meye.picture.whiteness = 0;
+       meye.params.subsample = 0;
+       meye.params.quality = 7;
+       meye.params.sharpness = 32;
+       meye.params.agc = 48;
+       meye.params.picture = 0;
+       meye.params.framerate = 0;
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 32);
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 32);
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 32);
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 32);
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, 32);
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, 0);
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48);
+
+       return 0;
+out7:
+       iounmap(meye.mchip_mmregs);
+out6:
+       free_irq(meye.mchip_irq, meye_irq);
+out5:
+       release_mem_region(pci_resource_start(meye.mchip_dev, 0),
+                          pci_resource_len(meye.mchip_dev, 0));
+out4:
+       pci_disable_device(meye.mchip_dev);
+out3:
+       mchip_dma_free();
+out2:
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
+out1:
+       return ret;
+}
+
+static void __devexit meye_remove(struct pci_dev *pcidev) {
+
+       video_unregister_device(&meye.video_dev);
+
+       mchip_hic_stop();
+
+       /* disable interrupts */
+       mchip_set(MCHIP_MM_INTA, 0x0);
+
+       free_irq(meye.mchip_irq, meye_irq);
+
+
+       iounmap(meye.mchip_mmregs);
+
+       release_mem_region(pci_resource_start(meye.mchip_dev, 0),
+                          pci_resource_len(meye.mchip_dev, 0));
+
+       pci_disable_device(meye.mchip_dev);
+
+       mchip_dma_free();
+
+       if (meye.grab_fbuffer)
+               rvfree(meye.grab_fbuffer, gbuffers*gbufsize);
+
+       sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0);
+
+       printk(KERN_INFO "meye: removed\n");
+}
+
+static struct pci_device_id meye_pci_tbl[] __devinitdata = {
+       { PCI_VENDOR_ID_KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002, 
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { }
+};
+
+MODULE_DEVICE_TABLE(pci, meye_pci_tbl);
+
+static struct pci_driver meye_driver = {
+       name:           "meye",
+       id_table:       meye_pci_tbl,
+       probe:          meye_probe,
+       remove:         meye_remove,
+};
+
+static int __init meye_init_module(void) {
+       if (gbuffers < 2)
+               gbuffers = 2;
+       if (gbuffers > MEYE_MAX_BUFNBRS)
+               gbuffers = MEYE_MAX_BUFNBRS;
+       if (gbufsize < 0 || gbufsize > MEYE_MAX_BUFSIZE)
+               gbufsize = MEYE_MAX_BUFSIZE;
+       printk(KERN_INFO "meye: using %d buffers with %dk (%dk total) for capture\n",
+              gbuffers, gbufsize/1024, gbuffers*gbufsize/1024);
+       return pci_module_init(&meye_driver);
+}
+
+static void __exit meye_cleanup_module(void) {
+       pci_unregister_driver(&meye_driver);
+}
+
+MODULE_AUTHOR("Stelian Pop <stelian.pop@fr.alcove.com>");
+MODULE_DESCRIPTION("video4linux driver for the MotionEye camera");
+
+MODULE_PARM(gbuffers,"i");
+MODULE_PARM_DESC(gbuffers,"number of capture buffers, default is 2 (32 max)");
+MODULE_PARM(gbufsize,"i");
+MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 614400");
+MODULE_PARM(video_nr,"i");
+MODULE_PARM_DESC(video_nr,"video device to register (0=/dev/video0, etc)");
+
+/* Module entry points */
+module_init(meye_init_module);
+module_exit(meye_cleanup_module);
diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h
new file mode 100644 (file)
index 0000000..10c28be
--- /dev/null
@@ -0,0 +1,313 @@
+/* 
+ * Motion Eye video4linux driver for Sony Vaio PictureBook
+ *
+ * Copyright (C) 2001 Stelian Pop <stelian.pop@fr.alcove.com>, AlcĂ´ve
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ * 
+ * Some parts borrowed from various video4linux drivers, especially
+ * bttv-driver.c and zoran.c, see original files for credits.
+ * 
+ * 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.
+ */
+
+#ifndef _MEYE_PRIV_H_
+#define _MEYE_PRIV_H_
+
+#define MEYE_DRIVER_MAJORVERSION       1
+#define MEYE_DRIVER_MINORVERSION       0
+
+/****************************************************************************/
+/* Motion JPEG chip registers                                               */
+/****************************************************************************/
+
+/* Motion JPEG chip PCI configuration registers */
+#define MCHIP_PCI_POWER_CSR            0x54
+#define MCHIP_PCI_MCORE_STATUS         0x60    /* see HIC_STATUS   */
+#define MCHIP_PCI_HOSTUSEREQ_SET       0x64
+#define MCHIP_PCI_HOSTUSEREQ_CLR       0x68
+#define MCHIP_PCI_LOWPOWER_SET         0x6c
+#define MCHIP_PCI_LOWPOWER_CLR         0x70
+#define MCHIP_PCI_SOFTRESET_SET                0x74
+
+/* Motion JPEG chip memory mapped registers */
+#define MCHIP_MM_REGS                  0x200           /* 512 bytes        */
+#define MCHIP_REG_TIMEOUT              1000            /* reg access, ~us  */
+#define MCHIP_MCC_VRJ_TIMEOUT          1000            /* MCC & VRJ access */
+
+#define MCHIP_MM_PCI_MODE              0x00            /* PCI access mode */
+#define MCHIP_MM_PCI_MODE_RETRY                0x00000001      /* retry mode */
+#define MCHIP_MM_PCI_MODE_MASTER       0x00000002      /* master access */
+#define MCHIP_MM_PCI_MODE_READ_LINE    0x00000004      /* read line */
+
+#define MCHIP_MM_INTA                  0x04            /* Int status/mask */
+#define MCHIP_MM_INTA_MCC              0x00000001      /* MCC interrupt */
+#define MCHIP_MM_INTA_VRJ              0x00000002      /* VRJ interrupt */
+#define MCHIP_MM_INTA_HIC_1            0x00000004      /* one frame done */
+#define MCHIP_MM_INTA_HIC_1_MASK       0x00000400      /* 1: enable */
+#define MCHIP_MM_INTA_HIC_END          0x00000008      /* all frames done */
+#define MCHIP_MM_INTA_HIC_END_MASK     0x00000800
+#define MCHIP_MM_INTA_JPEG             0x00000010      /* decompress. error */
+#define MCHIP_MM_INTA_JPEG_MASK                0x00001000
+#define MCHIP_MM_INTA_CAPTURE          0x00000020      /* capture end */
+#define MCHIP_MM_INTA_PCI_ERR          0x00000040      /* PCI error */
+#define MCHIP_MM_INTA_PCI_ERR_MASK     0x00004000
+
+#define MCHIP_MM_PT_ADDR               0x08            /* page table address */
+                                                       /* n*4kB */
+#define MCHIP_NB_PAGES                 1024            /* pages for display */
+#define MCHIP_NB_PAGES_MJPEG           256             /* pages for mjpeg */
+
+#define MCHIP_MM_FIR(n)                        (0x0c+(n)*4)    /* Frame info 0-3 */
+#define MCHIP_MM_FIR_RDY               0x00000001      /* frame ready */
+#define MCHIP_MM_FIR_FAILFR_MASK       0xf8000000      /* # of failed frames */
+#define MCHIP_MM_FIR_FAILFR_SHIFT      27
+
+       /* continuous comp/decomp mode */
+#define MCHIP_MM_FIR_C_ENDL_MASK       0x000007fe      /* end DW [10] */
+#define MCHIP_MM_FIR_C_ENDL_SHIFT      1
+#define MCHIP_MM_FIR_C_ENDP_MASK       0x0007f800      /* end page [8] */
+#define MCHIP_MM_FIR_C_ENDP_SHIFT      11
+#define MCHIP_MM_FIR_C_STARTP_MASK     0x07f80000      /* start page [8] */
+#define MCHIP_MM_FIR_C_STARTP_SHIFT    19
+
+       /* continuous picture output mode */
+#define MCHIP_MM_FIR_O_STARTP_MASK     0x7ffe0000      /* start page [10] */
+#define MCHIP_MM_FIR_O_STARTP_SHIFT    17
+
+#define MCHIP_MM_FIFO_DATA             0x1c            /* PCI TGT FIFO data */
+#define MCHIP_MM_FIFO_STATUS           0x20            /* PCI TGT FIFO stat */
+#define MCHIP_MM_FIFO_MASK             0x00000003
+#define MCHIP_MM_FIFO_WAIT_OR_READY    0x00000002      /* Bits common to WAIT & READY*/
+#define MCHIP_MM_FIFO_IDLE             0x0             /* HIC idle */
+#define MCHIP_MM_FIFO_IDLE1            0x1             /* idem ??? */
+#define        MCHIP_MM_FIFO_WAIT              0x2             /* wait request */
+#define MCHIP_MM_FIFO_READY            0x3             /* data ready */
+
+#define MCHIP_HIC_HOST_USEREQ          0x40            /* host uses MCORE */
+
+#define MCHIP_HIC_TP_BUSY              0x44            /* taking picture */
+
+#define MCHIP_HIC_PIC_SAVED            0x48            /* pic in SDRAM */
+
+#define MCHIP_HIC_LOWPOWER             0x4c            /* clock stopped */
+
+#define MCHIP_HIC_CTL                  0x50            /* HIC control */
+#define MCHIP_HIC_CTL_SOFT_RESET       0x00000001      /* MCORE reset */
+#define MCHIP_HIC_CTL_MCORE_RDY                0x00000002      /* MCORE ready */
+
+#define MCHIP_HIC_CMD                  0x54            /* HIC command */
+#define MCHIP_HIC_CMD_BITS             0x00000003      /* cmd width=[1:0]*/
+#define MCHIP_HIC_CMD_NOOP             0x0
+#define MCHIP_HIC_CMD_START            0x1
+#define MCHIP_HIC_CMD_STOP             0x2
+
+#define MCHIP_HIC_MODE                 0x58
+#define MCHIP_HIC_MODE_NOOP            0x0
+#define MCHIP_HIC_MODE_STILL_CAP       0x1             /* still pic capt */
+#define MCHIP_HIC_MODE_DISPLAY         0x2             /* display */
+#define MCHIP_HIC_MODE_STILL_COMP      0x3             /* still pic comp. */
+#define MCHIP_HIC_MODE_STILL_DECOMP    0x4             /* still pic decomp. */
+#define MCHIP_HIC_MODE_CONT_COMP       0x5             /* cont capt+comp */
+#define MCHIP_HIC_MODE_CONT_DECOMP     0x6             /* cont decomp+disp */
+#define MCHIP_HIC_MODE_STILL_OUT       0x7             /* still pic output */
+#define MCHIP_HIC_MODE_CONT_OUT                0x8             /* cont output */
+
+#define MCHIP_HIC_STATUS               0x5c
+#define MCHIP_HIC_STATUS_MCC_RDY       0x00000001      /* MCC reg acc ok */
+#define MCHIP_HIC_STATUS_VRJ_RDY       0x00000002      /* VRJ reg acc ok */
+#define MCHIP_HIC_STATUS_IDLE           0x00000003
+#define MCHIP_HIC_STATUS_CAPDIS                0x00000004      /* cap/disp in prog */
+#define MCHIP_HIC_STATUS_COMPDEC       0x00000008      /* (de)comp in prog */
+#define MCHIP_HIC_STATUS_BUSY          0x00000010      /* HIC busy */
+
+#define MCHIP_HIC_S_RATE               0x60            /* MJPEG # frames */
+
+#define MCHIP_HIC_PCI_VFMT             0x64            /* video format */
+#define MCHIP_HIC_PCI_VFMT_YVYU                0x00000001      /* 0: V Y' U Y */
+                                                       /* 1: Y' V Y U */
+
+#define MCHIP_MCC_CMD                  0x80            /* MCC commands */
+#define MCHIP_MCC_CMD_INITIAL          0x0             /* idle ? */
+#define MCHIP_MCC_CMD_IIC_START_SET    0x1
+#define MCHIP_MCC_CMD_IIC_END_SET      0x2
+#define MCHIP_MCC_CMD_FM_WRITE         0x3             /* frame memory */
+#define MCHIP_MCC_CMD_FM_READ          0x4
+#define MCHIP_MCC_CMD_FM_STOP          0x5
+#define MCHIP_MCC_CMD_CAPTURE          0x6
+#define MCHIP_MCC_CMD_DISPLAY          0x7
+#define MCHIP_MCC_CMD_END_DISP         0x8
+#define MCHIP_MCC_CMD_STILL_COMP       0x9
+#define MCHIP_MCC_CMD_STILL_DECOMP     0xa
+#define MCHIP_MCC_CMD_STILL_OUTPUT     0xb
+#define MCHIP_MCC_CMD_CONT_OUTPUT      0xc
+#define MCHIP_MCC_CMD_CONT_COMP                0xd
+#define MCHIP_MCC_CMD_CONT_DECOMP      0xe
+#define MCHIP_MCC_CMD_RESET            0xf             /* MCC reset */
+
+#define MCHIP_MCC_IIC_WR               0x84
+
+#define MCHIP_MCC_MCC_WR               0x88
+
+#define MCHIP_MCC_MCC_RD               0x8c
+
+#define MCHIP_MCC_STATUS               0x90
+#define MCHIP_MCC_STATUS_CAPT          0x00000001      /* capturing */
+#define MCHIP_MCC_STATUS_DISP          0x00000002      /* displaying */
+#define MCHIP_MCC_STATUS_COMP          0x00000004      /* compressing */
+#define MCHIP_MCC_STATUS_DECOMP                0x00000008      /* decompressing */
+#define MCHIP_MCC_STATUS_MCC_WR                0x00000010      /* register ready */
+#define MCHIP_MCC_STATUS_MCC_RD                0x00000020      /* register ready */
+#define MCHIP_MCC_STATUS_IIC_WR                0x00000040      /* register ready */
+#define MCHIP_MCC_STATUS_OUTPUT                0x00000080      /* output in prog */
+
+#define MCHIP_MCC_SIG_POLARITY         0x94
+#define MCHIP_MCC_SIG_POL_VS_H         0x00000001      /* VS active-high */
+#define MCHIP_MCC_SIG_POL_HS_H         0x00000002      /* HS active-high */
+#define MCHIP_MCC_SIG_POL_DOE_H                0x00000004      /* DOE active-high */
+
+#define MCHIP_MCC_IRQ                  0x98
+#define MCHIP_MCC_IRQ_CAPDIS_STRT      0x00000001      /* cap/disp started */
+#define MCHIP_MCC_IRQ_CAPDIS_STRT_MASK 0x00000010
+#define MCHIP_MCC_IRQ_CAPDIS_END       0x00000002      /* cap/disp ended */
+#define MCHIP_MCC_IRQ_CAPDIS_END_MASK  0x00000020
+#define MCHIP_MCC_IRQ_COMPDEC_STRT     0x00000004      /* (de)comp started */
+#define MCHIP_MCC_IRQ_COMPDEC_STRT_MASK        0x00000040
+#define MCHIP_MCC_IRQ_COMPDEC_END      0x00000008      /* (de)comp ended */
+#define MCHIP_MCC_IRQ_COMPDEC_END_MASK 0x00000080
+
+#define MCHIP_MCC_HSTART               0x9c            /* video in */
+#define MCHIP_MCC_VSTART               0xa0
+#define MCHIP_MCC_HCOUNT               0xa4
+#define MCHIP_MCC_VCOUNT               0xa8
+#define MCHIP_MCC_R_XBASE              0xac            /* capt/disp */
+#define MCHIP_MCC_R_YBASE              0xb0
+#define MCHIP_MCC_R_XRANGE             0xb4
+#define MCHIP_MCC_R_YRANGE             0xb8
+#define MCHIP_MCC_B_XBASE              0xbc            /* comp/decomp */
+#define MCHIP_MCC_B_YBASE              0xc0
+#define MCHIP_MCC_B_XRANGE             0xc4
+#define MCHIP_MCC_B_YRANGE             0xc8
+
+#define MCHIP_MCC_R_SAMPLING           0xcc            /* 1: 1:4 */
+
+#define MCHIP_VRJ_CMD                  0x100           /* VRJ commands */
+
+/* VRJ registers (see table 12.2.4) */
+#define MCHIP_VRJ_COMPRESSED_DATA      0x1b0
+#define MCHIP_VRJ_PIXEL_DATA           0x1b8
+
+#define MCHIP_VRJ_BUS_MODE             0x100
+#define MCHIP_VRJ_SIGNAL_ACTIVE_LEVEL  0x108
+#define MCHIP_VRJ_PDAT_USE             0x110
+#define MCHIP_VRJ_MODE_SPECIFY         0x118
+#define MCHIP_VRJ_LIMIT_COMPRESSED_LO  0x120
+#define MCHIP_VRJ_LIMIT_COMPRESSED_HI  0x124
+#define MCHIP_VRJ_COMP_DATA_FORMAT     0x128
+#define MCHIP_VRJ_TABLE_DATA           0x140
+#define MCHIP_VRJ_RESTART_INTERVAL     0x148
+#define MCHIP_VRJ_NUM_LINES            0x150
+#define MCHIP_VRJ_NUM_PIXELS           0x158
+#define MCHIP_VRJ_NUM_COMPONENTS       0x160
+#define MCHIP_VRJ_SOF1                 0x168
+#define MCHIP_VRJ_SOF2                 0x170
+#define MCHIP_VRJ_SOF3                 0x178
+#define MCHIP_VRJ_SOF4                 0x180
+#define MCHIP_VRJ_SOS                  0x188
+#define MCHIP_VRJ_SOFT_RESET           0x190
+
+#define MCHIP_VRJ_STATUS               0x1c0
+#define MCHIP_VRJ_STATUS_BUSY          0x00001
+#define MCHIP_VRJ_STATUS_COMP_ACCESS   0x00002
+#define MCHIP_VRJ_STATUS_PIXEL_ACCESS  0x00004
+#define MCHIP_VRJ_STATUS_ERROR         0x00008
+
+#define MCHIP_VRJ_IRQ_FLAG             0x1c8
+#define MCHIP_VRJ_ERROR_REPORT         0x1d8
+
+#define MCHIP_VRJ_START_COMMAND                0x1a0
+
+/****************************************************************************/
+/* Driver definitions.                                                      */
+/****************************************************************************/
+
+/* Sony Programmable I/O Controller for accessing the camera commands */
+#include <linux/sonypi.h>
+
+/* private API definitions */
+#include <linux/meye.h>
+
+/* Enable jpg software correction */
+#define MEYE_JPEG_CORRECTION   1
+
+/* Maximum size of a buffer */
+#define MEYE_MAX_BUFSIZE       614400  /* 640 * 480 * 2 */
+
+/* Maximum number of buffers */
+#define MEYE_MAX_BUFNBRS       32
+
+/* State of a buffer */
+#define MEYE_BUF_UNUSED        0       /* not used */
+#define MEYE_BUF_USING 1       /* currently grabbing / playing */
+#define MEYE_BUF_DONE  2       /* done */
+
+/* grab buffer */
+struct meye_grab_buffer {
+       int state;                      /* state of buffer */
+       unsigned long size;             /* size of jpg frame */
+};
+
+/* queues containing the buffer indices */
+#define MEYE_QUEUE_SIZE        MEYE_MAX_BUFNBRS
+struct meye_queue {
+       unsigned int head;              /* queue head */
+       unsigned int tail;              /* queue tail */
+       unsigned int len;               /* queue length */
+       spinlock_t s_lock;              /* spinlock protecting the queue */
+       wait_queue_head_t proc_list;    /* wait queue */
+       int buf[MEYE_QUEUE_SIZE];       /* queue contents */
+};
+
+/* Motion Eye device structure */
+struct meye {
+
+       /* mchip related */
+       struct pci_dev *mchip_dev;      /* pci device */
+       u8 mchip_irq;                   /* irq */
+       u8 mchip_mode;                  /* actual mchip mode: HIC_MODE... */
+       u8 mchip_fnum;                  /* current mchip frame number */
+
+       unsigned char *mchip_mmregs;    /* mchip: memory mapped registers */
+       unsigned char *mchip_fbuffer;   /* mchip: framebuffer */
+       u32 mchip_ptaddr;               /* mchip: pointer to framebuffer */
+
+       unsigned char *grab_fbuffer;    /* capture framebuffer */
+                                       /* list of buffers */
+       struct meye_grab_buffer grab_buffer[MEYE_MAX_BUFNBRS];
+
+       /* other */
+       unsigned int open_count;        /* open() count */
+       struct semaphore lock;          /* semaphore for open/mmap... */
+
+       struct meye_queue grabq;        /* queue for buffers to be grabbed */
+
+       struct video_device video_dev;  /* video device parameters */
+       struct video_picture picture;   /* video picture parameters */
+       struct meye_params params;      /* additional parameters */
+};
+
+#endif
index b41bb43e79c9f3708a99a90a199611bf53848bb3..22be539c74260fe6333e43c5cf5f6fe2a0579455 100644 (file)
@@ -102,6 +102,7 @@ struct saa5249_device
        int disp_mode;
        int virtual_mode;
        struct i2c_client *client;
+       struct semaphore lock;
 };
 
 
@@ -175,6 +176,7 @@ static int saa5249_attach(struct i2c_adapter *adap, int addr, unsigned short fla
        }
        memset(t, 0, sizeof(*t));
        strcpy(client->name, IF_NAME);
+       init_MUTEX(&t->lock);
        
        /*
         *      Now create a video4linux device
@@ -188,7 +190,7 @@ static int saa5249_attach(struct i2c_adapter *adap, int addr, unsigned short fla
                return -ENOMEM;
        }
        memcpy(vd, &saa_template, sizeof(*vd));
-       
+               
        for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) 
        {
                memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
@@ -199,7 +201,8 @@ static int saa5249_attach(struct i2c_adapter *adap, int addr, unsigned short fla
                t->vdau[pgbuf].stopped = TRUE;
                t->is_searching[pgbuf] = FALSE;
        }
-       vd->priv=t;              
+       vd->priv=t;     
+        
        
        /*
         *      Register it
@@ -342,9 +345,8 @@ static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf)
  *     Standard character-device-driver functions
  */
 
-static int saa5249_ioctl(struct video_device *vd, unsigned int cmd, void *arg) 
+static int do_saa5249_ioctl(struct saa5249_device *t, unsigned int cmd, void *arg) 
 {
-       struct saa5249_device *t=vd->priv;
        static int virtual_mode = FALSE;
 
        switch(cmd) 
@@ -602,6 +604,21 @@ static int saa5249_ioctl(struct video_device *vd, unsigned int cmd, void *arg)
        return -EINVAL;
 }
 
+/*
+ *     Handle the locking
+ */
+static int saa5249_ioctl(struct video_device *vd, unsigned int cmd, void *arg) 
+{
+       struct saa5249_device *t=vd->priv;
+       int err;
+       
+       down(&t->lock);
+       err = do_saa5249_ioctl(t, cmd, arg);
+       up(&t->lock);
+
+       return err;
+}
 
 static int saa5249_open(struct video_device *vd, int nb) 
 {
@@ -632,7 +649,6 @@ static int saa5249_open(struct video_device *vd, int nb)
                t->is_searching[pgbuf] = FALSE;
        }
        t->virtual_mode=FALSE;
-       MOD_INC_USE_COUNT;
        return 0;
 }
 
@@ -643,7 +659,6 @@ static void saa5249_release(struct video_device *vd)
        struct saa5249_device *t=vd->priv;
        i2c_senddata(t, 1, 0x20, -1);           /* Turn off CCT */
        i2c_senddata(t, 5, 3, 3, -1);           /* Turn off TV-display */
-       MOD_DEC_USE_COUNT;
        return;
 }
 
@@ -669,6 +684,7 @@ module_exit(cleanup_saa_5249);
 
 static struct video_device saa_template =
 {
+       owner:          THIS_MODULE,
        name:           IF_NAME,
        type:           VID_TYPE_TELETEXT,      /*| VID_TYPE_TUNER ?? */
        hardware:       VID_HARDWARE_SAA5249,
diff --git a/drivers/media/video/zoran.h b/drivers/media/video/zoran.h
new file mode 100644 (file)
index 0000000..33ce8e5
--- /dev/null
@@ -0,0 +1,372 @@
+/* 
+    zoran - Iomega Buz driver
+
+    Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+
+   based on
+
+    zoran.0.0.3 Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+
+   and
+
+    bttv - Bt848 frame grabber driver
+    Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+    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.
+*/
+
+#ifndef _BUZ_H_
+#define _BUZ_H_
+
+#include <linux/config.h>
+
+#if LINUX_VERSION_CODE < 0x20212
+typedef struct wait_queue *wait_queue_head_t;
+#endif
+
+/* The Buz only supports a maximum width of 720, but some V4L
+   applications (e.g. xawtv are more happy with 768).
+   If XAWTV_HACK is defined, we try to fake a device with bigger width */
+
+//#define XAWTV_HACK
+
+//#ifdef XAWTV_HACK
+//#define   BUZ_MAX_WIDTH   768   /* never display more than 768 pixels */
+#define   BUZ_MAX_WIDTH   (zr->timing->Wa)
+//#else
+//#define   BUZ_MAX_WIDTH   720   /* never display more than 720 pixels */
+//#endif
+//#define   BUZ_MAX_HEIGHT  576   /* never display more than 576 rows */
+#define   BUZ_MAX_HEIGHT  (zr->timing->Ha)
+#define   BUZ_MIN_WIDTH    32  /* never display less than 32 pixels */
+#define   BUZ_MIN_HEIGHT   24  /* never display less than 24 rows */
+
+struct zoran_requestbuffers {
+       unsigned long count;    /* Number of buffers for MJPEG grabbing */
+       unsigned long size;     /* Size PER BUFFER in bytes */
+};
+
+struct zoran_sync {
+       unsigned long frame;    /* number of buffer that has been free'd */
+       unsigned long length;   /* number of code bytes in buffer (capture only) */
+       unsigned long seq;      /* frame sequence number */
+       struct timeval timestamp;       /* timestamp */
+};
+
+struct zoran_status {
+       int input;              /* Input channel, has to be set prior to BUZIOC_G_STATUS */
+       int signal;             /* Returned: 1 if valid video signal detected */
+       int norm;               /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
+       int color;              /* Returned: 1 if color signal detected */
+};
+
+struct zoran_params {
+
+       /* The following parameters can only be queried */
+
+       int major_version;      /* Major version number of driver */
+       int minor_version;      /* Minor version number of driver */
+
+       /* Main control parameters */
+
+       int input;              /* Input channel: 0 = Composite, 1 = S-VHS */
+       int norm;               /* Norm: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */
+       int decimation;         /* decimation of captured video,
+                                  enlargement of video played back.
+                                  Valid values are 1, 2, 4 or 0.
+                                  0 is a special value where the user
+                                  has full control over video scaling */
+
+       /* The following parameters only have to be set if decimation==0,
+          for other values of decimation they provide the data how the image is captured */
+
+       int HorDcm;             /* Horizontal decimation: 1, 2 or 4 */
+       int VerDcm;             /* Vertical decimation: 1 or 2 */
+       int TmpDcm;             /* Temporal decimation: 1 or 2,
+                                  if TmpDcm==2 in capture every second frame is dropped,
+                                  in playback every frame is played twice */
+       int field_per_buff;     /* Number of fields per buffer: 1 or 2 */
+       int img_x;              /* start of image in x direction */
+       int img_y;              /* start of image in y direction */
+       int img_width;          /* image width BEFORE decimation,
+                                  must be a multiple of HorDcm*16 */
+       int img_height;         /* image height BEFORE decimation,
+                                  must be a multiple of VerDcm*8 */
+
+       /* --- End of parameters for decimation==0 only --- */
+
+       /* JPEG control parameters */
+
+       int quality;            /* Measure for quality of compressed images.
+                                  Scales linearly with the size of the compressed images.
+                                  Must be beetween 0 and 100, 100 is a compression
+                                  ratio of 1:4 */
+
+       int odd_even;           /* Which field should come first ??? */
+
+       int APPn;               /* Number of APP segment to be written, must be 0..15 */
+       int APP_len;            /* Length of data in JPEG APPn segment */
+       char APP_data[60];      /* Data in the JPEG APPn segment. */
+
+       int COM_len;            /* Length of data in JPEG COM segment */
+       char COM_data[60];      /* Data in JPEG COM segment */
+
+       unsigned long jpeg_markers;     /* Which markers should go into the JPEG output.
+                                          Unless you exactly know what you do, leave them untouched.
+                                          Inluding less markers will make the resulting code
+                                          smaller, but there will be fewer aplications
+                                          which can read it.
+                                          The presence of the APP and COM marker is
+                                          influenced by APP0_len and COM_len ONLY! */
+#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */
+#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */
+#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */
+#define JPEG_MARKER_COM (1<<6) /* Comment segment */
+#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */
+
+       int VFIFO_FB;           /* Flag for enabling Video Fifo Feedback.
+                                  If this flag is turned on and JPEG decompressing
+                                  is going to the screen, the decompress process
+                                  is stopped every time the Video Fifo is full.
+                                  This enables a smooth decompress to the screen
+                                  but the video output signal will get scrambled */
+
+       /* Misc */
+
+       char reserved[312];     /* Makes 512 bytes for this structure */
+};
+
+/*
+Private IOCTL to set up for displaying MJPEG
+*/
+#define BUZIOC_G_PARAMS       _IOR ('v', BASE_VIDIOCPRIVATE+0,  struct zoran_params)
+#define BUZIOC_S_PARAMS       _IOWR('v', BASE_VIDIOCPRIVATE+1,  struct zoran_params)
+#define BUZIOC_REQBUFS        _IOWR('v', BASE_VIDIOCPRIVATE+2,  struct zoran_requestbuffers)
+#define BUZIOC_QBUF_CAPT      _IOW ('v', BASE_VIDIOCPRIVATE+3,  int)
+#define BUZIOC_QBUF_PLAY      _IOW ('v', BASE_VIDIOCPRIVATE+4,  int)
+#define BUZIOC_SYNC           _IOR ('v', BASE_VIDIOCPRIVATE+5,  struct zoran_sync)
+#define BUZIOC_G_STATUS       _IOWR('v', BASE_VIDIOCPRIVATE+6,  struct zoran_status)
+
+
+#ifdef __KERNEL__
+
+#define BUZ_NUM_STAT_COM    4
+#define BUZ_MASK_STAT_COM   3
+
+#define BUZ_MAX_FRAME     256  /* Must be a power of 2 */
+#define BUZ_MASK_FRAME    255  /* Must be BUZ_MAX_FRAME-1 */
+
+#if VIDEO_MAX_FRAME <= 32
+#   define   V4L_MAX_FRAME   32
+#elif VIDEO_MAX_FRAME <= 64
+#   define   V4L_MAX_FRAME   64
+#else
+#   error   "Too many video frame buffers to handle"
+#endif
+#define   V4L_MASK_FRAME   (V4L_MAX_FRAME - 1)
+
+
+#include "zr36057.h"
+
+enum card_type {
+        UNKNOWN = 0,
+        DC10,
+        DC10plus,
+        LML33,
+        BUZ
+};
+
+enum zoran_codec_mode {
+       BUZ_MODE_IDLE,          /* nothing going on */
+       BUZ_MODE_MOTION_COMPRESS,       /* grabbing frames */
+       BUZ_MODE_MOTION_DECOMPRESS,     /* playing frames */
+       BUZ_MODE_STILL_COMPRESS,        /* still frame conversion */
+       BUZ_MODE_STILL_DECOMPRESS       /* still frame conversion */
+};
+
+enum zoran_buffer_state {
+       BUZ_STATE_USER,         /* buffer is owned by application */
+       BUZ_STATE_PEND,         /* buffer is queued in pend[] ready to feed to I/O */
+       BUZ_STATE_DMA,          /* buffer is queued in dma[] for I/O */
+       BUZ_STATE_DONE          /* buffer is ready to return to application */
+};
+
+struct zoran_gbuffer {
+       u32 *frag_tab;          /* addresses of frag table */
+       u32 frag_tab_bus;       /* same value cached to save time in ISR */
+       enum zoran_buffer_state state;  /* non-zero if corresponding buffer is in use in grab queue */
+       struct zoran_sync bs;   /* DONE: info to return to application */
+};
+
+struct v4l_gbuffer {
+       char *fbuffer;                  /* virtual  address of frame buffer */
+       unsigned long fbuffer_phys;     /* physical address of frame buffer */
+       unsigned long fbuffer_bus;      /* bus      address of frame buffer */
+       enum zoran_buffer_state state;  /* state: unused/pending/done */
+};
+
+struct tvnorm {
+       u16 Wt, Wa, HStart, HSyncStart, Ht, Ha, VStart;
+};
+
+struct zoran {
+       struct video_device video_dev;
+       struct i2c_bus i2c;
+
+       int initialized;                /* flag if zoran has been correctly initalized */
+       int user;                       /* number of current users (0 or 1) */
+        enum card_type card;
+        struct tvnorm *timing;
+
+       unsigned short id;              /* number of this device */
+       char name[32];                  /* name of this device */
+       struct pci_dev *pci_dev;        /* PCI device */
+       unsigned char revision;         /* revision of zr36057 */
+       unsigned int zr36057_adr;       /* bus address of IO mem returned by PCI BIOS */
+       unsigned char *zr36057_mem;     /* pointer to mapped IO memory */
+
+       int map_mjpeg_buffers;          /* Flag which bufferset will map by next mmap() */
+
+       spinlock_t lock;                /* Spinlock irq and hardware */
+       struct semaphore sem;           /* Guard parallel ioctls and mmap */
+
+       /* Video for Linux parameters */
+
+       struct video_picture picture;   /* Current picture params */
+       struct video_buffer buffer;     /* Current buffer params */
+       struct video_window window;     /* Current window params */
+       int buffer_set, window_set;     /* Flags if the above structures are set */
+       int video_interlace;            /* Image on screen is interlaced */
+
+       u32 *overlay_mask;
+        wait_queue_head_t v4l_capq;
+
+       int v4l_overlay_active;         /* Overlay grab is activated */
+       int v4l_memgrab_active;         /* Memory grab is activated */
+
+       int v4l_grab_frame;             /* Frame number being currently grabbed */
+#define NO_GRAB_ACTIVE (-1)
+       int v4l_grab_seq;               /* Number of frames grabbed */
+       int gwidth;                     /* Width of current memory capture */
+       int gheight;                    /* Height of current memory capture */
+       int gformat;                    /* Format of ... */
+       int gbpl;                       /* byte per line of ... */
+
+       /* V4L grab queue of frames pending */
+
+       unsigned v4l_pend_head;
+       unsigned v4l_pend_tail;
+       int v4l_pend[V4L_MAX_FRAME];
+
+       struct v4l_gbuffer v4l_gbuf[VIDEO_MAX_FRAME];   /* V4L   buffers' info */
+
+       /* Buz MJPEG parameters */
+
+       unsigned long jpg_nbufs;        /* Number of buffers */
+       unsigned long jpg_bufsize;      /* Size of mjpeg buffers in bytes */
+       int jpg_buffers_allocated;      /* Flag if buffers are allocated  */
+       int need_contiguous;    /* Flag if contiguous buffers are needed */
+
+       enum zoran_codec_mode codec_mode;       /* status of codec */
+       struct zoran_params params;     /* structure with a lot of things to play with */
+
+       wait_queue_head_t jpg_capq;     /* wait here for grab to finish */
+
+       /* grab queue counts/indices, mask with BUZ_MASK_STAT_COM before using as index */
+       /* (dma_head - dma_tail) is number active in DMA, must be <= BUZ_NUM_STAT_COM */
+       /* (value & BUZ_MASK_STAT_COM) corresponds to index in stat_com table */
+       unsigned long jpg_que_head;     /* Index where to put next buffer which is queued */
+       unsigned long jpg_dma_head;     /* Index of next buffer which goes into stat_com  */
+       unsigned long jpg_dma_tail;     /* Index of last buffer in stat_com               */
+       unsigned long jpg_que_tail;     /* Index of last buffer in queue                  */
+       unsigned long jpg_seq_num;      /* count of frames since grab/play started        */
+       unsigned long jpg_err_seq;      /* last seq_num before error                      */
+       unsigned long jpg_err_shift;
+       unsigned long jpg_queued_num;   /* count of frames queued since grab/play started */
+
+       /* zr36057's code buffer table */
+       u32 *stat_com;                  /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
+
+       /* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */
+       int jpg_pend[BUZ_MAX_FRAME];
+
+       /* array indexed by frame number */
+       struct zoran_gbuffer jpg_gbuf[BUZ_MAX_FRAME];   /* MJPEG buffers' info */
+
+       /* Additional stuff for testing */
+       struct proc_dir_entry *zoran_proc;
+
+       int testing;
+       int jpeg_error;
+       int intr_counter_GIRQ1;
+       int intr_counter_GIRQ0;
+       int intr_counter_CodRepIRQ;
+       int intr_counter_JPEGRepIRQ;
+       int field_counter;
+       int IRQ1_in;
+       int IRQ1_out;
+       int JPEG_in;
+       int JPEG_out;
+       int JPEG_0;
+       int JPEG_1;
+       int END_event_missed;
+       int JPEG_missed;
+       int JPEG_error;
+       int num_errors;
+       int JPEG_max_missed;
+       int JPEG_min_missed;
+
+       u32 last_isr;
+       unsigned long frame_num;
+
+       wait_queue_head_t test_q;
+};
+
+#endif
+
+/*The following should be done in more portable way. It depends on define
+  of _ALPHA_BUZ in the Makefile.*/
+
+#ifdef _ALPHA_BUZ
+#define btwrite(dat,adr)    writel((dat),(char *) (zr->zr36057_adr+(adr)))
+#define btread(adr)         readl(zr->zr36057_adr+(adr))
+#else
+#define btwrite(dat,adr)    writel((dat), (char *) (zr->zr36057_mem+(adr)))
+#define btread(adr)         readl(zr->zr36057_mem+(adr))
+#endif
+
+#define btand(dat,adr)      btwrite((dat) & btread(adr), adr)
+#define btor(dat,adr)       btwrite((dat) | btread(adr), adr)
+#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
+
+#define I2C_TSA5522        0xc2
+#define I2C_TDA9850        0xb6
+#define I2C_HAUPEE         0xa0
+#define I2C_STBEE          0xae
+#define   I2C_SAA7111        0x48
+#define   I2C_SAA7110        0x9c
+#define   I2C_SAA7185        0x88
+//#define   I2C_ADV7175        0xd4
+#define   I2C_ADV7175        0x54
+
+#define TDA9850_CON1       0x04
+#define TDA9850_CON2       0x05
+#define TDA9850_CON3       0x06
+#define TDA9850_CON4       0x07
+#define TDA9850_ALI1       0x08
+#define TDA9850_ALI2       0x09
+#define TDA9850_ALI3       0x0a
+
+#endif
diff --git a/drivers/media/video/zoran_procfs.c b/drivers/media/video/zoran_procfs.c
new file mode 100644 (file)
index 0000000..9287aa2
--- /dev/null
@@ -0,0 +1,170 @@
+#include <linux/config.h>
+#include <linux/ctype.h>
+
+struct procfs_params_zr36067 {
+       char *name;
+       short reg;
+       u32 mask;
+       short bit;
+};
+
+static struct procfs_params_zr36067 zr67[] = {
+       {"HSPol", 0x000, 1, 30},
+       {"HStart", 0x000, 0x3ff, 10},
+       {"HEnd", 0x000, 0x3ff, 0},
+
+       {"VSPol", 0x004, 1, 30},
+       {"VStart", 0x004, 0x3ff, 10},
+       {"VEnd", 0x004, 0x3ff, 0},
+
+       {"ExtFl", 0x008, 1, 26},
+       {"TopField", 0x008, 1, 25},
+       {"VCLKPol", 0x008, 1, 24},
+       {"DupFld", 0x008, 1, 20},
+       {"LittleEndian", 0x008, 1, 0},
+
+       {"HsyncStart", 0x10c, 0xffff, 16},
+       {"LineTot", 0x10c, 0xffff, 0},
+
+       {"NAX", 0x110, 0xffff, 16},
+       {"PAX", 0x110, 0xffff, 0},
+
+       {"NAY", 0x114, 0xffff, 16},
+       {"PAY", 0x114, 0xffff, 0},
+/*    {"",,,}, */
+
+       {NULL, 0, 0, 0},
+};
+
+static void setparam(struct zoran *zr, char *name, char *sval)
+{
+       int i, reg0, reg, val;
+       i = 0;
+       while (zr67[i].name != NULL) {
+               if (!strncmp(name, zr67[i].name, strlen(zr67[i].name))) {
+                       reg = reg0 = btread(zr67[i].reg);
+                       reg &= ~(zr67[i].mask << zr67[i].bit);
+                       if (!isdigit(sval[0]))
+                               break;
+                       val = simple_strtoul(sval, NULL, 0);
+                       if ((val & ~zr67[i].mask))
+                               break;
+                       reg |= (val & zr67[i].mask) << zr67[i].bit;
+                       printk(KERN_INFO "%s: setparam: setting ZR36067 register 0x%03x: 0x%08x=>0x%08x %s=%d\n",
+                              zr->name, zr67[i].reg, reg0, reg, zr67[i].name, val);
+                       btwrite(reg, zr67[i].reg);
+                       break;
+               }
+               i++;
+       }
+}
+
+/* This macro was stolen from /usr/src/drivers/char/nvram.c and modified */
+#define        PRINT_PROC(args...)                                     \
+       do {                                                    \
+               if (begin + len > offset + size) {              \
+                       *eof = 0;                               \
+                        break;                                 \
+               }                                               \
+                len += sprintf( buffer+len, ##args );          \
+               if (begin + len < offset) {                     \
+                       begin += len;                           \
+                       len = 0;                                \
+               }                                               \
+       } while(0)
+
+static int zoran_read_proc(char *buffer, char **start, off_t offset, int size, int *eof, void *data)
+{
+#ifdef CONFIG_PROC_FS
+       int len = 0;
+       off_t begin = 0;
+
+       int i;
+       struct zoran *zr;
+
+       zr = (struct zoran *) data;
+       DEBUG2(printk(KERN_INFO "%s: read_proc: buffer=%x, offset=%d, size=%d, data=%x\n", zr->name, (int) buffer, (int) offset, size, (int) data));
+       *eof = 1;
+       PRINT_PROC("ZR36067 registers:");
+       for (i = 0; i < 0x130; i += 4) {
+               if (!(i % 16)) {
+                       PRINT_PROC("\n%03X", i);
+               }
+               PRINT_PROC(" %08X ", btread(i));
+       }
+       PRINT_PROC("\n");
+       if (offset >= len + begin) {
+               return 0;
+       }
+       *start = buffer + begin - offset;
+       return ((size < begin + len - offset) ? size : begin + len - offset);
+#endif
+       return 0;
+}
+
+static int zoran_write_proc(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+#ifdef CONFIG_PROC_FS
+       char *string, *sp;
+       char *line, *ldelim, *varname, *svar, *tdelim;
+       struct zoran *zr;
+
+       zr = (struct zoran *) data;
+
+       string = sp = vmalloc(count + 1);
+       if (!string) {
+               printk(KERN_ERR "%s: write_proc: can not allocate memory\n", zr->name);
+               return -ENOMEM;
+       }
+       memcpy(string, buffer, count);
+       string[count] = 0;
+       DEBUG2(printk(KERN_INFO "%s: write_proc: name=%s count=%lu data=%x\n", zr->name, file->f_dentry->d_name.name, count, (int) data));
+       ldelim = " \t\n";
+       tdelim = "=";
+       line = strpbrk(sp, ldelim);
+       while (line) {
+               *line = 0;
+               svar = strpbrk(sp, tdelim);
+               if (svar) {
+                       *svar = 0;
+                       varname = sp;
+                       svar++;
+                       setparam(zr, varname, svar);
+               }
+               sp = line + 1;
+               line = strpbrk(sp, ldelim);
+       }
+       vfree(string);
+#endif
+       return count;
+}
+
+static int zoran_proc_init(int i)
+{
+#ifdef CONFIG_PROC_FS
+       char name[8];
+       sprintf(name, "zoran%d", i);
+       if ((zoran[i].zoran_proc = create_proc_entry(name, 0, 0))) {
+               zoran[i].zoran_proc->read_proc = zoran_read_proc;
+               zoran[i].zoran_proc->write_proc = zoran_write_proc;
+               zoran[i].zoran_proc->data = &zoran[i];
+               printk(KERN_INFO "%s: procfs entry /proc/%s allocated. data=%x\n", zoran[i].name, name, (int) zoran[i].zoran_proc->data);
+       } else {
+               printk(KERN_ERR "%s: Unable to initialise /proc/%s\n", zoran[i].name, name);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+static void zoran_proc_cleanup(int i)
+{
+#ifdef CONFIG_PROC_FS
+       char name[8];
+       sprintf(name, "zoran%d", i);
+       if (zoran[i].zoran_proc) {
+               remove_proc_entry(name, 0);
+       }
+       zoran[i].zoran_proc = NULL;
+#endif
+}
index b672357bc666b8a06ae02afc5907ff034e8481f0..9c6b50f175e3567813118e3bb04e617d784d56ba 100644 (file)
@@ -1,22 +1,22 @@
 /* 
-   zr36057.h - zr36057 register offsets
+    zr36057.h - zr36057 register offsets
 
-   Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+    Copyright (C) 1998 Dave Perks <dperks@ibm.net>
 
-   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 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.
+    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.
- */
+    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.
+*/
 
 #ifndef _ZR36057_H_
 #define _ZR36057_H_
 
 /* Zoran ZR36057 registers */
 
-#define ZR36057_VFEHCR          0x000  /* Video Front End, Horizontal Configuration Register */
+#define ZR36057_VFEHCR          0x000   /* Video Front End, Horizontal Configuration Register */
 #define ZR36057_VFEHCR_HSPol            (1<<30)
 #define ZR36057_VFEHCR_HStart           10
 #define ZR36057_VFEHCR_HEnd            0
 #define ZR36057_VFEHCR_Hmask           0x3ff
 
-#define ZR36057_VFEVCR          0x004  /* Video Front End, Vertical Configuration Register */
+#define ZR36057_VFEVCR          0x004   /* Video Front End, Vertical Configuration Register */
 #define ZR36057_VFEVCR_VSPol            (1<<30)
 #define ZR36057_VFEVCR_VStart           10
 #define ZR36057_VFEVCR_VEnd            0
 #define ZR36057_VFEVCR_Vmask           0x3ff
 
-#define ZR36057_VFESPFR         0x008  /* Video Front End, Scaler and Pixel Format Register */
+#define ZR36057_VFESPFR         0x008   /* Video Front End, Scaler and Pixel Format Register */
 #define ZR36057_VFESPFR_ExtFl           (1<<26)
 #define ZR36057_VFESPFR_TopField        (1<<25)
 #define ZR36057_VFESPFR_VCLKPol         (1<<24)
 #define ZR36057_VFESPFR_Pack24          (1<<1)
 #define ZR36057_VFESPFR_LittleEndian    (1<<0)
 
-#define ZR36057_VDTR            0x00c  /* Video Display "Top" Register */
+#define ZR36057_VDTR            0x00c   /* Video Display "Top" Register */
 
-#define ZR36057_VDBR            0x010  /* Video Display "Bottom" Register */
+#define ZR36057_VDBR            0x010   /* Video Display "Bottom" Register */
 
-#define ZR36057_VSSFGR          0x014  /* Video Stride, Status, and Frame Grab Register */
+#define ZR36057_VSSFGR          0x014   /* Video Stride, Status, and Frame Grab Register */
 #define ZR36057_VSSFGR_DispStride       16
 #define ZR36057_VSSFGR_VidOvf           (1<<8)
 #define ZR36057_VSSFGR_SnapShot         (1<<1)
 #define ZR36057_VSSFGR_FrameGrab        (1<<0)
 
-#define ZR36057_VDCR            0x018  /* Video Display Configuration Register */
+#define ZR36057_VDCR            0x018   /* Video Display Configuration Register */
 #define ZR36057_VDCR_VidEn              (1<<31)
 #define ZR36057_VDCR_MinPix             24
 #define ZR36057_VDCR_Triton             (1<<24)
 #define ZR36057_VDCR_VidWinHt           12
 #define ZR36057_VDCR_VidWinWid          0
 
-#define ZR36057_MMTR            0x01c  /* Masking Map "Top" Register */
+#define ZR36057_MMTR            0x01c   /* Masking Map "Top" Register */
 
-#define ZR36057_MMBR            0x020  /* Masking Map "Bottom" Register */
+#define ZR36057_MMBR            0x020   /* Masking Map "Bottom" Register */
 
-#define ZR36057_OCR             0x024  /* Overlay Control Register */
+#define ZR36057_OCR             0x024   /* Overlay Control Register */
 #define ZR36057_OCR_OvlEnable           (1 << 15)
 #define ZR36057_OCR_MaskStride          0
 
-#define ZR36057_SPGPPCR         0x028  /* System, PCI, and General Purpose Pins Control Register */
+#define ZR36057_SPGPPCR         0x028   /* System, PCI, and General Purpose Pins Control Register */
 #define ZR36057_SPGPPCR_SoftReset      (1<<24)
 
-#define ZR36057_GPPGCR1         0x02c  /* General Purpose Pins and GuestBus Control Register (1) */
+#define ZR36057_GPPGCR1         0x02c   /* General Purpose Pins and GuestBus Control Register (1) */
 
-#define ZR36057_MCSAR           0x030  /* MPEG Code Source Address Register */
+#define ZR36057_MCSAR           0x030   /* MPEG Code Source Address Register */
 
-#define ZR36057_MCTCR           0x034  /* MPEG Code Transfer Control Register */
+#define ZR36057_MCTCR           0x034   /* MPEG Code Transfer Control Register */
 #define ZR36057_MCTCR_CodTime           (1 << 30)
 #define ZR36057_MCTCR_CEmpty            (1 << 29)
 #define ZR36057_MCTCR_CFlush            (1 << 28)
 #define ZR36057_MCTCR_CodGuestID       20
 #define ZR36057_MCTCR_CodGuestReg      16
 
-#define ZR36057_MCMPR           0x038  /* MPEG Code Memory Pointer Register */
+#define ZR36057_MCMPR           0x038   /* MPEG Code Memory Pointer Register */
 
-#define ZR36057_ISR             0x03c  /* Interrupt Status Register */
+#define ZR36057_ISR             0x03c   /* Interrupt Status Register */
 #define ZR36057_ISR_GIRQ1               (1<<30)
 #define ZR36057_ISR_GIRQ0               (1<<29)
 #define ZR36057_ISR_CodRepIRQ           (1<<28)
 #define ZR36057_ISR_JPEGRepIRQ          (1<<27)
 
-#define ZR36057_ICR             0x040  /* Interrupt Control Register */
+#define ZR36057_ICR             0x040   /* Interrupt Control Register */
 #define ZR36057_ICR_GIRQ1               (1<<30)
 #define ZR36057_ICR_GIRQ0               (1<<29)
 #define ZR36057_ICR_CodRepIRQ           (1<<28)
 #define ZR36057_ICR_JPEGRepIRQ          (1<<27)
 #define ZR36057_ICR_IntPinEn            (1<<24)
 
-#define ZR36057_I2CBR           0x044  /* I2C Bus Register */
+#define ZR36057_I2CBR           0x044   /* I2C Bus Register */
 #define ZR36057_I2CBR_SDA              (1<<1)
 #define ZR36057_I2CBR_SCL              (1<<0)
 
-#define ZR36057_JMC             0x100  /* JPEG Mode and Control */
+#define ZR36057_JMC             0x100   /* JPEG Mode and Control */
 #define ZR36057_JMC_JPG                 (1 << 31)
 #define ZR36057_JMC_JPGExpMode          (0 << 29)
 #define ZR36057_JMC_JPGCmpMode          (1 << 29)
 #define ZR36057_JMC_CFIFO_FB            (1 << 1)
 #define ZR36057_JMC_Stll_LitEndian      (1 << 0)
 
-#define ZR36057_JPC             0x104  /* JPEG Process Control */
+#define ZR36057_JPC             0x104   /* JPEG Process Control */
 #define ZR36057_JPC_P_Reset             (1 << 7)
 #define ZR36057_JPC_CodTrnsEn           (1 << 5)
 #define ZR36057_JPC_Active              (1 << 0)
 
-#define ZR36057_VSP             0x108  /* Vertical Sync Parameters */
+#define ZR36057_VSP             0x108   /* Vertical Sync Parameters */
 #define ZR36057_VSP_VsyncSize           16
 #define ZR36057_VSP_FrmTot              0
 
-#define ZR36057_HSP             0x10c  /* Horizontal Sync Parameters */
+#define ZR36057_HSP             0x10c   /* Horizontal Sync Parameters */
 #define ZR36057_HSP_HsyncStart          16
 #define ZR36057_HSP_LineTot             0
 
-#define ZR36057_FHAP            0x110  /* Field Horizontal Active Portion */
+#define ZR36057_FHAP            0x110   /* Field Horizontal Active Portion */
 #define ZR36057_FHAP_NAX                16
 #define ZR36057_FHAP_PAX                0
 
-#define ZR36057_FVAP            0x114  /* Field Vertical Active Portion */
+#define ZR36057_FVAP            0x114   /* Field Vertical Active Portion */
 #define ZR36057_FVAP_NAY                16
 #define ZR36057_FVAP_PAY                0
 
-#define ZR36057_FPP             0x118  /* Field Process Parameters */
+#define ZR36057_FPP             0x118   /* Field Process Parameters */
 #define ZR36057_FPP_Odd_Even            (1 << 0)
 
-#define ZR36057_JCBA            0x11c  /* JPEG Code Base Address */
+#define ZR36057_JCBA            0x11c   /* JPEG Code Base Address */
 
-#define ZR36057_JCFT            0x120  /* JPEG Code FIFO Threshold */
+#define ZR36057_JCFT            0x120   /* JPEG Code FIFO Threshold */
 
-#define ZR36057_JCGI            0x124  /* JPEG Codec Guest ID */
+#define ZR36057_JCGI            0x124   /* JPEG Codec Guest ID */
 #define ZR36057_JCGI_JPEGuestID         4
 #define ZR36057_JCGI_JPEGuestReg        0
 
-#define ZR36057_GCR2            0x12c  /* GuestBus Control Register (2) */
+#define ZR36057_GCR2            0x12c   /* GuestBus Control Register (2) */
 
-#define ZR36057_POR             0x200  /* Post Office Register */
+#define ZR36057_POR             0x200   /* Post Office Register */
 #define ZR36057_POR_POPen               (1<<25)
 #define ZR36057_POR_POTime              (1<<24)
 #define ZR36057_POR_PODir               (1<<23)
 
-#define ZR36057_STR             0x300  /* "Still" Transfer Register */
+#define ZR36057_STR             0x300   /* "Still" Transfer Register */
 
 #endif
index d94f56b0b9ef707ea6eda6f8dbc6e89f2f939beb..4938c36dec8700b6b04357994ce8e56b336eb86b 100644 (file)
@@ -1,22 +1,22 @@
 /* 
-   zr36060.h - zr36060 register offsets
+    zr36060.h - zr36060 register offsets
 
-   Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+    Copyright (C) 1998 Dave Perks <dperks@ibm.net>
 
-   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 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.
+    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.
- */
+    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.
+*/
 
 #ifndef _ZR36060_H_
 #define _ZR36060_H_
diff --git a/drivers/media/video/zr36067.c b/drivers/media/video/zr36067.c
new file mode 100644 (file)
index 0000000..ab74223
--- /dev/null
@@ -0,0 +1,4900 @@
+#define DEBUGLEVEL 0
+#define MAX_KMALLOC_MEM (128*1024)
+
+/*
+   Miro/Pinnacle Systems Inc. DC10/DC10plus and
+   Linux Media Labs LML33 video capture boards driver
+   now with IOMega BUZ support!
+   
+   Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+
+   Changes for BUZ by Wolfgang Scherr <scherr@net4you.net>
+   
+   Based on
+   
+    Miro DC10 driver
+    Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+   
+    Iomega Buz driver version 1.0
+    Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+
+    buz.0.0.3
+    Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+
+    bttv - Bt848 frame grabber driver
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+                           & Marcus Metzler (mocm@thp.uni-koeln.de)
+
+    
+    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.
+*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/wrapper.h>
+
+#include <linux/spinlock.h>
+#include <linux/vmalloc.h>
+#include <linux/i2c-old.h>
+#define     MAP_NR(x)       virt_to_page(x)
+#define     ZORAN_HARDWARE  VID_HARDWARE_ZR36067
+
+#include <linux/videodev.h>
+
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+
+#include "zoran.h"
+#include <linux/video_decoder.h>
+#include <linux/video_encoder.h>
+
+// RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined
+#if !defined(CONFIG_BIGPHYS_AREA)
+//#undef CONFIG_BIGPHYS_AREA
+#define BUZ_USE_HIMEM
+#endif
+
+#if defined(CONFIG_BIGPHYS_AREA)
+#   include <linux/bigphysarea.h>
+#endif
+
+#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | ZR36057_ISR_GIRQ1 | /* ZR36057_ISR_CodRepIRQ | */  ZR36057_ISR_JPEGRepIRQ )     // SW
+//#define GPIO_MASK 0xcd
+//#define GPIO_MASK 0x8d
+
+/*
+DC10:
+GPIO0 = /RESET ZR 36060
+GPIO1 = VIDEO BUS DIRECTION (0: CAPTURE, 1: DISPLAY)
+GPIO2 = VIDEO BUS ENABLE    (0: ON, 1: OFF)
+GPIO3 = /SLEEP ZR 36060
+GPIO4 = ADC7175 (video out) FREQUENCY (0: LCC/SAA7110, 1: 27 MHz (quarz))
+GPIO5 = ZORAN FREQUENCY  (0: LCC and LCC2 from SAA7110,
+                          1: 27 and 13.5 MHz (quarz))
+GPIO6 = /FRAME ZR 36060
+GPIO7 = /RESET ADV7175 (video out)
+(I think they lost the SAA7110 reset.....)
+
+GIRQ0 signals that ZR36060's DATERR# line is asserted.
+GIRQ1 signals a vertical sync of the video signal (VS SAA7110)
+
+  SAA7110A:
+   mode 0 - Composite
+   mode 1 - 
+   mode 2 - 
+   mode 3 - 
+   mode 4 - 
+   mode 5 - internal Composite (from PCTV)
+   mode 6 - 
+   mode 7 - S-Video
+
+BUZ:
+GPIO0 = 1, take board out of reset
+GPIO1 = 1, take JPEG codec out of sleep mode
+GPIO3 = 1, deassert FRAME# to 36060
+
+GIRQ0 signals a vertical sync of the video signal
+GIRQ1 signals that ZR36060's DATERR# line is asserted.
+
+SAA7111A
+
+   In their infinite wisdom, the Iomega engineers decided to
+   use the same input line for composite and S-Video Color,
+   although there are two entries not connected at all!
+   Through this ingenious strike, it is not possible to
+   keep two running video sources connected at the same time
+   to Composite and S-VHS input!
+
+   mode 0 - N/C
+   mode 1 - S-Video Y
+   mode 2 - noise or something I don't know
+   mode 3 - Composite and S-Video C
+   mode 4 - N/C
+   mode 5 - S-Video (gain C independently selectable of gain Y)
+   mode 6 - N/C
+   mode 7 - S-Video (gain C adapted to gain Y)
+*/
+
+#define MAJOR_VERSION 0                /* driver major version */
+#define MINOR_VERSION 7                /* driver minor version */
+
+#define ZORAN_NAME    "zr36067"        /* name of the device */
+
+#define BUZ_ERR       KERN_ERR     ZORAN_NAME
+#define BUZ_DEBUG     KERN_INFO    ZORAN_NAME
+#define BUZ_INFO      KERN_INFO    ZORAN_NAME
+#define BUZ_WARNING   KERN_WARNING ZORAN_NAME
+
+#if(DEBUGLEVEL>0)
+#define DEBUG1(x...)    x
+#else
+#define DEBUG1(x...)
+#endif
+
+#if(DEBUGLEVEL>1)
+#define DEBUG2(x...)    x
+#else
+#define DEBUG2(x...)
+#endif
+
+#if(DEBUGLEVEL>2)
+#define DEBUG3(x...)    x
+#else
+#define DEBUG3(x...)
+#endif
+
+#if(DEBUGLEVEL>3)
+#define DEBUG4(x...)    x
+#else
+#define DEBUG4(x...)
+#endif
+
+/* The parameters for this driver */
+
+/*
+   The video mem address of the video card.
+   The driver has a little database for some videocards
+   to determine it from there. If your video card is not in there
+   you have either to give it to the driver as a parameter
+   or set in in a VIDIOCSFBUF ioctl
+ */
+
+static unsigned long vidmem = 0;       /* Video memory base address */
+
+/* Special purposes only: */
+
+static int triton = 0;         /* 0=no, 1=yes */
+static int natoma = 0;         /* 0=no, 1=yes */
+
+/*
+   Number and size of grab buffers for Video 4 Linux
+   The vast majority of applications should not need more than 2,
+   the very popular BTTV driver actually does ONLY have 2.
+   Time sensitive applications might need more, the maximum
+   is VIDEO_MAX_FRAME (defined in <linux/videodev.h>).
+
+   The size is set so that the maximum possible request
+   can be satisfied. Decrease  it, if bigphys_area alloc'd
+   memory is low. If you don't have the bigphys_area patch,
+   set it to 128 KB. Will you allow only to grab small
+   images with V4L, but that's better than nothing.
+
+   v4l_bufsize has to be given in KB !
+
+*/
+
+static int v4l_nbufs = 2;
+static int v4l_bufsize = 128;  /* Everybody should be able to work with this setting */
+
+/*
+   Default input and video norm at startup of the driver.
+*/
+
+static int default_input = 0;  /* 0=Composite, 1=S-VHS */
+static int default_norm = 0;   /* 0=PAL, 1=NTSC 2=SECAM */
+static int lock_norm = 0;      /* 1=Don't change TV standard (norm) */
+
+static int pass_through = 0;   /* 1=Pass through TV signal when device is not used */
+                               /* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */
+
+static int lml33dpath = 0;     /* 1 will use digital path in capture mode instead of analog.
+                                  It can be used for picture adjustments using tool like xawtv
+                                  while watching image on TV monitor connected to the output.
+                                  However, due to absence of 75 Ohm load on Bt819 input, there
+                                  will be some image imperfections */
+static int video_nr = -1;
+
+MODULE_PARM(vidmem, "i");
+MODULE_PARM(triton, "i");
+MODULE_PARM(natoma, "i");
+MODULE_PARM(v4l_nbufs, "i");
+MODULE_PARM(v4l_bufsize, "i");
+MODULE_PARM(default_input, "i");
+MODULE_PARM(default_norm, "i");
+MODULE_PARM(lock_norm, "i");
+MODULE_PARM(pass_through, "i");
+MODULE_PARM(lml33dpath, "i");
+MODULE_PARM(video_nr, "i");
+
+/* Anybody who uses more than four? */
+#define BUZ_MAX 4
+
+static int zoran_num;          /* number of Buzs in use */
+static struct zoran zoran[BUZ_MAX];
+
+/* forward references */
+
+static void v4l_fbuffer_free(struct zoran *zr);
+static void jpg_fbuffer_free(struct zoran *zr);
+static void zoran_feed_stat_com(struct zoran *zr);
+
+/*
+ *   Allocate the V4L grab buffers
+ *
+ *   These have to be pysically contiguous.
+ *   If v4l_bufsize <= MAX_KMALLOC_MEM we use kmalloc
+ *   else we try to allocate them with bigphysarea_alloc_pages
+ *   if the bigphysarea patch is present in the kernel,
+ *   else we try to use high memory (if the user has bootet
+ *   Linux with the necessary memory left over).
+ */
+
+static int v4l_fbuffer_alloc(struct zoran *zr)
+{
+       int i, off;
+       unsigned char *mem;
+
+       for (i = 0; i < v4l_nbufs; i++) {
+               if (zr->v4l_gbuf[i].fbuffer)
+                       printk(KERN_WARNING
+                              "%s: v4l_fbuffer_alloc: buffer %d allready allocated ???\n",
+                              zr->name, i);
+
+               //udelay(20);
+               if (v4l_bufsize <= MAX_KMALLOC_MEM) {
+                       /* Use kmalloc */
+
+                       mem =
+                           (unsigned char *) kmalloc(v4l_bufsize,
+                                                     GFP_KERNEL);
+                       if (mem == 0) {
+                               printk(KERN_ERR
+                                      "%s: kmalloc for V4L bufs failed\n",
+                                      zr->name);
+                               v4l_fbuffer_free(zr);
+                               return -ENOBUFS;
+                       }
+                       zr->v4l_gbuf[i].fbuffer = mem;
+                       zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem);
+                       zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem);
+                       for (off = 0; off < v4l_bufsize; off += PAGE_SIZE)
+                               mem_map_reserve(MAP_NR(mem + off));
+                       DEBUG1(printk
+                              (KERN_INFO
+                               "%s: V4L frame %d mem 0x%lx (bus: 0x%lx)\n",
+                               zr->name, i, (unsigned long) mem,
+                               virt_to_bus(mem)));
+               } else {
+#if defined(CONFIG_BIGPHYS_AREA)
+                       /* Use bigphysarea_alloc_pages */
+
+                       int n = (v4l_bufsize + PAGE_SIZE - 1) / PAGE_SIZE;
+                       mem =
+                           (unsigned char *) bigphysarea_alloc_pages(n, 0,
+                                                                     GFP_KERNEL);
+                       if (mem == 0) {
+                               printk(KERN_ERR
+                                      "%s: bigphysarea_alloc_pages for V4L bufs failed\n",
+                                      zr->name);
+                               v4l_fbuffer_free(zr);
+                               return -ENOBUFS;
+                       }
+                       zr->v4l_gbuf[i].fbuffer = mem;
+                       zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem);
+                       zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem);
+                       DEBUG1(printk
+                              (KERN_INFO
+                               "%s: Bigphysarea frame %d mem 0x%x (bus: 0x%x)\n",
+                               zr->name, i, (unsigned) mem,
+                               (unsigned) virt_to_bus(mem)));
+
+                       /* Zero out the allocated memory */
+                       memset(zr->v4l_gbuf[i].fbuffer, 0, v4l_bufsize);
+#else
+                       /* No bigphysarea present, usage of high memory disabled,
+                          but user wants buffers of more than MAX_KMALLOC_MEM */
+                       printk(KERN_ERR
+                              "%s: No bigphysarea_patch present, usage of high memory disabled,\n",
+                              zr->name);
+                       printk(KERN_ERR
+                              "%s: sorry, could not allocate V4L buffers of size %d KB.\n",
+                              zr->name, v4l_bufsize >> 10);
+                       return -ENOBUFS;
+#endif
+               }
+       }
+
+       return 0;
+}
+
+/* free the V4L grab buffers */
+
+static void v4l_fbuffer_free(struct zoran *zr)
+{
+       int i, off;
+       unsigned char *mem;
+
+       for (i = 0; i < v4l_nbufs; i++) {
+               if (!zr->v4l_gbuf[i].fbuffer)
+                       continue;
+
+               if (v4l_bufsize <= MAX_KMALLOC_MEM) {
+                       mem = zr->v4l_gbuf[i].fbuffer;
+                       for (off = 0; off < v4l_bufsize; off += PAGE_SIZE)
+                               mem_map_unreserve(MAP_NR(mem + off));
+                       kfree((void *) zr->v4l_gbuf[i].fbuffer);
+               }
+#if defined(CONFIG_BIGPHYS_AREA)
+               else
+                       bigphysarea_free_pages((void *) zr->v4l_gbuf[i].
+                                              fbuffer);
+#endif
+               zr->v4l_gbuf[i].fbuffer = NULL;
+       }
+}
+
+/*
+ *   Allocate the MJPEG grab buffers.
+ *
+ *   If the requested buffer size is smaller than MAX_KMALLOC_MEM,
+ *   kmalloc is used to request a physically contiguous area,
+ *   else we allocate the memory in framgents with get_free_page.
+ *
+ *   If a Natoma chipset is present and this is a revision 1 zr36057,
+ *   each MJPEG buffer needs to be physically contiguous.
+ *   (RJ: This statement is from Dave Perks' original driver,
+ *   I could never check it because I have a zr36067)
+ *   The driver cares about this because it reduces the buffer
+ *   size to MAX_KMALLOC_MEM in that case (which forces contiguous allocation).
+ *
+ *   RJ: The contents grab buffers needs never be accessed in the driver.
+ *       Therefore there is no need to allocate them with vmalloc in order
+ *       to get a contiguous virtual memory space.
+ *       I don't understand why many other drivers first allocate them with
+ *       vmalloc (which uses internally also get_free_page, but delivers you
+ *       virtual addresses) and then again have to make a lot of efforts
+ *       to get the physical address.
+ *
+ */
+
+static int jpg_fbuffer_alloc(struct zoran *zr)
+{
+       int i, j, off;          //alloc_contig;
+       unsigned long mem;
+
+       /* Decide if we should alloc contiguous or fragmented memory */
+       /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */
+
+       //alloc_contig = (zr->jpg_bufsize <= MAX_KMALLOC_MEM);
+
+       for (i = 0; i < zr->jpg_nbufs; i++) {
+               if (zr->jpg_gbuf[i].frag_tab)
+                       printk(KERN_WARNING
+                              "%s: jpg_fbuffer_alloc: buffer %d allready allocated ???\n",
+                              zr->name, i);
+
+               /* Allocate fragment table for this buffer */
+
+               mem = get_free_page(GFP_KERNEL);
+               if (mem == 0) {
+                       printk(KERN_ERR
+                              "%s: jpg_fbuffer_alloc: get_free_page (frag_tab) failed for buffer %d\n",
+                              zr->name, i);
+                       jpg_fbuffer_free(zr);
+                       return -ENOBUFS;
+               }
+               memset((void *) mem, 0, PAGE_SIZE);
+               zr->jpg_gbuf[i].frag_tab = (u32 *) mem;
+               zr->jpg_gbuf[i].frag_tab_bus = virt_to_bus((void *) mem);
+
+               //if (alloc_contig) {
+               if (zr->need_contiguous) {
+                       mem = (unsigned long) kmalloc(zr->jpg_bufsize, GFP_KERNEL);
+                       if (mem == 0) {
+                               printk(KERN_ERR "%s: jpg_fbuffer_alloc: kmalloc failed for buffer %d\n",
+                                      zr->name, i);
+                               jpg_fbuffer_free(zr);
+                               return -ENOBUFS;
+                       }
+                       zr->jpg_gbuf[i].frag_tab[0] = virt_to_bus((void *) mem);
+                       zr->jpg_gbuf[i].frag_tab[1] =
+                           ((zr->jpg_bufsize / 4) << 1) | 1;
+                       for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE)
+                               mem_map_reserve(MAP_NR(mem + off));
+               } else {
+                       /* jpg_bufsize is allreay page aligned */
+                       for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) 
+                       {
+                               mem = get_free_page(GFP_KERNEL);
+                               if (mem == 0) {
+                                       printk(KERN_ERR
+                                              "%s: jpg_fbuffer_alloc: get_free_page failed for buffer %d\n",
+                                              zr->name, i);
+                                       jpg_fbuffer_free(zr);
+                                       return -ENOBUFS;
+                               }
+
+                               zr->jpg_gbuf[i].frag_tab[2 * j] =
+                                   virt_to_bus((void *) mem);
+                               zr->jpg_gbuf[i].frag_tab[2 * j + 1] =
+                                   (PAGE_SIZE / 4) << 1;
+                               mem_map_reserve(MAP_NR(mem));
+                       }
+
+                       zr->jpg_gbuf[i].frag_tab[2 * j - 1] |= 1;
+               }
+       }
+
+       DEBUG1(printk
+              ("%s: jpg_fbuffer_alloc: %ld KB allocated\n", zr->name,
+               (zr->jpg_nbufs * zr->jpg_bufsize) >> 10));
+       zr->jpg_buffers_allocated = 1;
+       return 0;
+}
+
+/* free the MJPEG grab buffers */
+static void jpg_fbuffer_free(struct zoran *zr)
+{
+       int i, j, off;          // alloc_contig;
+       unsigned char *mem;
+
+       /* Decide if we should alloc contiguous or fragmented memory */
+       /* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */
+
+       //alloc_contig = (zr->jpg_bufsize <= MAX_KMALLOC_MEM);
+
+       for (i = 0; i < zr->jpg_nbufs; i++) {
+               if (!zr->jpg_gbuf[i].frag_tab)
+                       continue;
+
+               //if (alloc_contig) {
+               if (zr->need_contiguous) {
+                       if (zr->jpg_gbuf[i].frag_tab[0]) {
+                               mem =
+                                   (unsigned char *) bus_to_virt(zr->
+                                                                 jpg_gbuf
+                                                                 [i].
+                                                                 frag_tab
+                                                                 [0]);
+                               for (off = 0; off < zr->jpg_bufsize;
+                                    off += PAGE_SIZE)
+                                       mem_map_unreserve(MAP_NR
+                                                         (mem + off));
+                               kfree((void *) mem);
+                               zr->jpg_gbuf[i].frag_tab[0] = 0;
+                               zr->jpg_gbuf[i].frag_tab[1] = 0;
+                       }
+               } else {
+                       for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) {
+                               if (!zr->jpg_gbuf[i].frag_tab[2 * j])
+                                       break;
+                               mem_map_unreserve(MAP_NR
+                                                 (bus_to_virt
+                                                  (zr->jpg_gbuf[i].
+                                                   frag_tab[2 * j])));
+                               free_page((unsigned long)
+                                         bus_to_virt(zr->jpg_gbuf[i].
+                                                     frag_tab[2 * j]));
+                               zr->jpg_gbuf[i].frag_tab[2 * j] = 0;
+                               zr->jpg_gbuf[i].frag_tab[2 * j + 1] = 0;
+                       }
+               }
+
+               free_page((unsigned long) zr->jpg_gbuf[i].frag_tab);
+               zr->jpg_gbuf[i].frag_tab = NULL;
+       }
+       zr->jpg_buffers_allocated = 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+/* I2C functions                                                           */
+
+#define I2C_DELAY   10
+
+
+/* software I2C functions */
+
+static void i2c_setlines(struct i2c_bus *bus, int ctrl, int data)
+{
+       struct zoran *zr = (struct zoran *) bus->data;
+       btwrite((data << 1) | ctrl, ZR36057_I2CBR);
+       udelay(I2C_DELAY);
+}
+
+static int i2c_getdataline(struct i2c_bus *bus)
+{
+       struct zoran *zr = (struct zoran *) bus->data;
+       return (btread(ZR36057_I2CBR) >> 1) & 1;
+}
+
+static void attach_inform(struct i2c_bus *bus, int id)
+{
+       int i;
+       struct zoran *zr = (struct zoran *) bus->data;
+
+       DEBUG1(printk(KERN_DEBUG "%s: i2c attach %02x\n", zr->name, id));
+       for (i = 0; i < bus->devcount; i++) {
+               if (strcmp(bus->devices[i]->name, "saa7110") == 0) {
+                       if (zr->revision < 2) {
+                               zr->card = DC10;
+                               sprintf(zr->name, "DC10[%u]", zr->id);
+                       } else {
+                               zr->card = DC10plus;
+                               sprintf(zr->name, "DC10plus[%u]", zr->id);
+                       }
+                       break;
+               }
+               if (strcmp(bus->devices[i]->name, "bt819") == 0) {
+                       zr->card = LML33;
+                       sprintf(zr->name, "LML33[%u]", zr->id);
+                       break;
+               }
+               if (strcmp(bus->devices[i]->name, "saa7111") == 0) {
+                       zr->card = BUZ;
+                       sprintf(zr->name, "Buz[%u]", zr->id);
+                       break;
+               }
+       }
+}
+
+static void detach_inform(struct i2c_bus *bus, int id)
+{
+       DEBUG1(struct zoran *zr = (struct zoran *) bus->data);
+       DEBUG1(printk(KERN_DEBUG "%s: i2c detach %02x\n", zr->name, id));
+}
+
+static struct i2c_bus zoran_i2c_bus_template = {
+       "zr36057",
+       I2C_BUSID_BT848,
+       NULL,
+
+       SPIN_LOCK_UNLOCKED,
+
+       attach_inform,
+       detach_inform,
+
+       i2c_setlines,
+       i2c_getdataline,
+       NULL,
+       NULL,
+};
+
+/*
+ *   Set the registers for the size we have specified. Don't bother
+ *   trying to understand this without the ZR36057 manual in front of
+ *   you [AC].
+ *
+ *   PS: The manual is free for download in .pdf format from
+ *   www.zoran.com - nicely done those folks.
+ */
+
+static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 };
+static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 };
+
+static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 };
+
+static struct tvnorm *dc10norms[] = {
+       &f50sqpixel,            /* PAL-BDGHI */
+       &f60sqpixel,            /* NTSC */
+       &f50sqpixel,            /* SECAM */
+};
+
+static struct tvnorm *lml33norms[] = {
+       &f50ccir601,            /* PAL-BDGHI */
+       &f60ccir601,            /* NTSC      */
+       NULL,                   /* SECAM (not supported in LML33) */
+};
+
+static struct tvnorm *buznorms[] = {
+       &f50ccir601,            /* PAL-BDGHI */
+       &f60ccir601,            /* NTSC      */
+       NULL,                   /* SECAM     */
+};
+
+static struct tvnorm *unsupported[] = {
+       NULL,                   /* PAL-BDGHI */
+       NULL,                   /* NTSC      */
+       NULL,                   /* SECAM     */
+};
+
+static struct tvnorm **cardnorms[] = {
+       unsupported,            /* UNKNOWN  */
+       dc10norms,              /* DC10     */
+       dc10norms,              /* DC10plus */
+       lml33norms,             /* LML33    */
+       buznorms,               /* Buz      */
+};
+
+static u32 cardvsync[] = {
+       0,                      /* UNKNOWN  */
+       ZR36057_ISR_GIRQ1,      /* DC10     */
+       ZR36057_ISR_GIRQ1,      /* DC10plus */
+       ZR36057_ISR_GIRQ0,      /* LML33    */
+       ZR36057_ISR_GIRQ0,      /* Buz      */
+};
+
+static u32 cardjpegint[] = {
+       0,                      /* UNKNOWN  */
+       ZR36057_ISR_GIRQ0,      /* DC10     */
+       ZR36057_ISR_GIRQ0,      /* DC10plus */
+       ZR36057_ISR_GIRQ1,      /* LML33    */
+       ZR36057_ISR_GIRQ1,      /* Buz      */
+};
+
+static int format2bpp(int format)
+{
+       int bpp;
+
+       /* Determine the number of bytes per pixel for the video format requested */
+
+       switch (format) {
+
+       case VIDEO_PALETTE_YUV422:
+               bpp = 2;
+               break;
+
+       case VIDEO_PALETTE_RGB555:
+               bpp = 2;
+               break;
+
+       case VIDEO_PALETTE_RGB565:
+               bpp = 2;
+               break;
+
+       case VIDEO_PALETTE_RGB24:
+               bpp = 3;
+               break;
+
+       case VIDEO_PALETTE_RGB32:
+               bpp = 4;
+               break;
+
+       default:
+               bpp = 0;
+       }
+
+       return bpp;
+}
+
+static void zr36057_adjust_vfe(struct zoran *zr,
+                              enum zoran_codec_mode mode)
+{
+       u32 reg;
+       switch (mode) {
+       case BUZ_MODE_MOTION_DECOMPRESS:
+               btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+               reg = btread(ZR36057_VFEHCR);
+               if (reg & (1 << 10)) {
+                       reg += ((1 << 10) | 1);
+               }
+               btwrite(reg, ZR36057_VFEHCR);
+               break;
+       case BUZ_MODE_MOTION_COMPRESS:
+       case BUZ_MODE_IDLE:
+       default:
+               if (zr->params.norm == VIDEO_MODE_NTSC)
+                       btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+               else
+                       btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+               reg = btread(ZR36057_VFEHCR);
+               if (!(reg & (1 << 10))) {
+                       reg -= ((1 << 10) | 1);
+               }
+               btwrite(reg, ZR36057_VFEHCR);
+               break;
+       }
+}
+
+/*
+ * set geometry
+ */
+static void zr36057_set_vfe(struct zoran *zr, int video_width,
+                           int video_height, unsigned int video_format)
+{
+       struct tvnorm *tvn;
+       unsigned HStart, HEnd, VStart, VEnd;
+       unsigned DispMode;
+       unsigned VidWinWid, VidWinHt;
+       unsigned hcrop1, hcrop2, vcrop1, vcrop2;
+       unsigned Wa, We, Ha, He;
+       unsigned X, Y, HorDcm, VerDcm;
+       u32 reg;
+       unsigned mask_line_size;
+
+       tvn = zr->timing;
+
+       Wa = tvn->Wa;
+       Ha = tvn->Ha;
+
+       DEBUG1(printk (BUZ_INFO ": width = %d, height = %d\n", video_width, video_height));
+
+       if (zr->params.norm != VIDEO_MODE_PAL
+           && zr->params.norm != VIDEO_MODE_NTSC
+           && zr->params.norm != VIDEO_MODE_SECAM) {
+               printk(KERN_ERR "%s: set_vfe: video_norm = %d not valid\n",
+                      zr->name, zr->params.norm);
+               return;
+       }
+       if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT
+           || video_width > Wa || video_height > Ha) {
+               printk(KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n",
+                      zr->name, video_width, video_height);
+               return;
+       }
+
+       /* if window has more than half of active height,
+          switch on interlacing - we want the full information */
+
+       zr->video_interlace = (video_height > Ha / 2);
+
+       /**** zr36057 ****/
+
+       /* horizontal */
+       VidWinWid = video_width;
+       X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa;
+       We = (VidWinWid * 64) / X;
+       HorDcm = 64 - X;
+       hcrop1 = 2 * ((tvn->Wa - We) / 4);
+       hcrop2 = tvn->Wa - We - hcrop1;
+       HStart = tvn->HStart | 1;
+       if (zr->card == LML33)
+               HStart += 62;
+       if (zr->card == BUZ) {  //HStart += 67;
+               HStart += 44;
+       }
+       HEnd = HStart + tvn->Wa - 1;
+       HStart += hcrop1;
+       HEnd -= hcrop2;
+       reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart)
+           | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd);
+       if (zr->card != BUZ)
+               reg |= ZR36057_VFEHCR_HSPol;
+       btwrite(reg, ZR36057_VFEHCR);
+
+       /* Vertical */
+       DispMode = !zr->video_interlace;
+       VidWinHt = DispMode ? video_height : video_height / 2;
+       Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha;
+       He = (VidWinHt * 64) / Y;
+       VerDcm = 64 - Y;
+       vcrop1 = (tvn->Ha / 2 - He) / 2;
+       vcrop2 = tvn->Ha / 2 - He - vcrop1;
+       VStart = tvn->VStart;
+       VEnd = VStart + tvn->Ha / 2 - 1;
+       VStart += vcrop1;
+       VEnd -= vcrop2;
+       reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart)
+           | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd);
+       reg |= ZR36057_VFEVCR_VSPol;
+       btwrite(reg, ZR36057_VFEVCR);
+
+       /* scaler and pixel format */
+       reg = 0;
+       reg |= (HorDcm << ZR36057_VFESPFR_HorDcm);
+       reg |= (VerDcm << ZR36057_VFESPFR_VerDcm);
+       reg |= (DispMode << ZR36057_VFESPFR_DispMode);
+       reg |= ZR36057_VFESPFR_LittleEndian;
+       /* RJ: I don't know, why the following has to be the opposite
+          of the corresponding ZR36060 setting, but only this way
+          we get the correct colors when uncompressing to the screen  */
+       //reg |= ZR36057_VFESPFR_VCLKPol; /**/
+       /* RJ: Don't know if that is needed for NTSC also */
+       if (zr->params.norm != VIDEO_MODE_NTSC)
+               reg |= ZR36057_VFESPFR_ExtFl;   // NEEDED!!!!!!! Wolfgang
+       reg |= ZR36057_VFESPFR_TopField;
+       switch (video_format) {
+
+       case VIDEO_PALETTE_YUV422:
+               reg |= ZR36057_VFESPFR_YUV422;
+               break;
+
+       case VIDEO_PALETTE_RGB555:
+               reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif;
+               break;
+
+       case VIDEO_PALETTE_RGB565:
+               reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif;
+               break;
+
+       case VIDEO_PALETTE_RGB24:
+               reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24;
+               break;
+
+       case VIDEO_PALETTE_RGB32:
+               reg |= ZR36057_VFESPFR_RGB888;
+               break;
+
+       default:
+               printk(KERN_INFO "%s: Unknown color_fmt=%x\n", zr->name,
+                      video_format);
+               return;
+
+       }
+       if (HorDcm >= 48) {
+               reg |= 3 << ZR36057_VFESPFR_HFilter;    /* 5 tap filter */
+       } else if (HorDcm >= 32) {
+               reg |= 2 << ZR36057_VFESPFR_HFilter;    /* 4 tap filter */
+       } else if (HorDcm >= 16) {
+               reg |= 1 << ZR36057_VFESPFR_HFilter;    /* 3 tap filter */
+       }
+       btwrite(reg, ZR36057_VFESPFR);
+
+       /* display configuration */
+
+       reg = (16 << ZR36057_VDCR_MinPix)
+           | (VidWinHt << ZR36057_VDCR_VidWinHt)
+           | (VidWinWid << ZR36057_VDCR_VidWinWid);
+       if (triton || zr->revision <= 1)
+               reg &= ~ZR36057_VDCR_Triton;
+       else
+               reg |= ZR36057_VDCR_Triton;
+       btwrite(reg, ZR36057_VDCR);
+
+       /* Write overlay clipping mask data, but don't enable overlay clipping */
+       /* RJ: since this makes only sense on the screen, we use 
+          zr->window.width instead of video_width */
+
+       mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+       reg = virt_to_bus(zr->overlay_mask);
+       btwrite(reg, ZR36057_MMTR);
+       reg = virt_to_bus(zr->overlay_mask + mask_line_size);
+       btwrite(reg, ZR36057_MMBR);
+       reg = mask_line_size - (zr->window.width + 31) / 32;
+       if (DispMode == 0)
+               reg += mask_line_size;
+       reg <<= ZR36057_OCR_MaskStride;
+       btwrite(reg, ZR36057_OCR);
+
+       zr36057_adjust_vfe(zr, zr->codec_mode);
+
+}
+
+/*
+ * Switch overlay on or off
+ */
+
+static void zr36057_overlay(struct zoran *zr, int on)
+{
+       int fmt, bpp;
+       u32 reg;
+
+       if (on) {
+               /* do the necessary settings ... */
+
+               btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);       /* switch it off first */
+
+               switch (zr->buffer.depth) {
+               case 15:
+                       fmt = VIDEO_PALETTE_RGB555;
+                       bpp = 2;
+                       break;
+               case 16:
+                       fmt = VIDEO_PALETTE_RGB565;
+                       bpp = 2;
+                       break;
+               case 24:
+                       fmt = VIDEO_PALETTE_RGB24;
+                       bpp = 3;
+                       break;
+               case 32:
+                       fmt = VIDEO_PALETTE_RGB32;
+                       bpp = 4;
+                       break;
+               default:
+                       fmt = 0;
+                       bpp = 0;
+               }
+
+               zr36057_set_vfe(zr, zr->window.width, zr->window.height,
+                               fmt);
+
+               /* Start and length of each line MUST be 4-byte aligned.
+                  This should be allready checked before the call to this routine.
+                  All error messages are internal driver checking only! */
+
+               /* video display top and bottom registers */
+
+               reg =
+                   (u32) zr->buffer.base + zr->window.x * bpp +
+                   zr->window.y * zr->buffer.bytesperline;
+               btwrite(reg, ZR36057_VDTR);
+               if (reg & 3)
+                       printk(KERN_ERR
+                              "%s: zr36057_overlay: video_address not aligned\n",
+                              zr->name);
+               if (zr->video_interlace)
+                       reg += zr->buffer.bytesperline;
+               btwrite(reg, ZR36057_VDBR);
+
+               /* video stride, status, and frame grab register */
+
+               reg = zr->buffer.bytesperline - zr->window.width * bpp;
+               if (zr->video_interlace)
+                       reg += zr->buffer.bytesperline;
+               if (reg & 3)
+                       printk(KERN_ERR
+                              "%s: zr36057_overlay: video_stride not aligned\n",
+                              zr->name);
+               reg = (reg << ZR36057_VSSFGR_DispStride);
+               reg |= ZR36057_VSSFGR_VidOvf;   /* clear overflow status */
+               btwrite(reg, ZR36057_VSSFGR);
+
+               /* Set overlay clipping */
+
+               if (zr->window.clipcount)
+                       btor(ZR36057_OCR_OvlEnable, ZR36057_OCR);
+
+               /* ... and switch it on */
+
+               btor(ZR36057_VDCR_VidEn, ZR36057_VDCR);
+       } else {
+               /* Switch it off */
+
+               btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+       }
+}
+
+/*
+ * The overlay mask has one bit for each pixel on a scan line,
+ *  and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels.
+ */
+static void write_overlay_mask(struct zoran *zr, struct video_clip *vp,
+                              int count)
+{
+       unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+       u32 *mask;
+       int x, y, width, height;
+       unsigned i, j, k;
+       u32 reg;
+
+       /* fill mask with one bits */
+       memset(zr->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT);
+       reg = 0;
+
+       for (i = 0; i < count; ++i) {
+               /* pick up local copy of clip */
+               x = vp[i].x;
+               y = vp[i].y;
+               width = vp[i].width;
+               height = vp[i].height;
+
+               /* trim clips that extend beyond the window */
+               if (x < 0) {
+                       width += x;
+                       x = 0;
+               }
+               if (y < 0) {
+                       height += y;
+                       y = 0;
+               }
+               if (x + width > zr->window.width) {
+                       width = zr->window.width - x;
+               }
+               if (y + height > zr->window.height) {
+                       height = zr->window.height - y;
+               }
+
+               /* ignore degenerate clips */
+               if (height <= 0) {
+                       continue;
+               }
+               if (width <= 0) {
+                       continue;
+               }
+
+               /* apply clip for each scan line */
+               for (j = 0; j < height; ++j) {
+                       /* reset bit for each pixel */
+                       /* this can be optimized later if need be */
+                       mask = zr->overlay_mask + (y + j) * mask_line_size;
+                       for (k = 0; k < width; ++k) {
+                               mask[(x + k) / 32] &=
+                                   ~((u32) 1 << (x + k) % 32);
+                       }
+               }
+       }
+}
+
+/* Enable/Disable uncompressed memory grabbing of the 36057 */
+
+static void zr36057_set_memgrab(struct zoran *zr, int mode)
+{
+       if (mode) {
+               if (btread(ZR36057_VSSFGR) &
+                   (ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab))
+                       printk(KERN_WARNING
+                              "%s: zr36057_set_memgrab_on with SnapShot or FrameGrab on ???\n",
+                              zr->name);
+
+               /* switch on VSync interrupts */
+
+               btwrite(IRQ_MASK, ZR36057_ISR); // Clear Interrupts
+               btor(cardvsync[zr->card], ZR36057_ICR); // SW
+
+               /* enable SnapShot */
+
+               btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+
+               /* Set zr36057 video front end  and enable video */
+
+               zr36057_set_vfe(zr, zr->gwidth, zr->gheight, zr->gformat);
+
+               zr->v4l_memgrab_active = 1;
+       } else {
+               zr->v4l_memgrab_active = 0;
+
+               /* switch off VSync interrupts */
+
+               //btand(~ZR36057_ICR_GIRQ1, ZR36057_ICR); // SW
+
+               /* reenable grabbing to screen if it was running */
+
+               if (zr->v4l_overlay_active) {
+                       zr36057_overlay(zr, 1);
+               } else {
+                       btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+                       btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+               }
+       }
+}
+
+static int wait_grab_pending(struct zoran *zr)
+{
+       unsigned long flags;
+
+       /* wait until all pending grabs are finished */
+
+       if (!zr->v4l_memgrab_active)
+               return 0;
+
+       while (zr->v4l_pend_tail != zr->v4l_pend_head) {
+               interruptible_sleep_on(&zr->v4l_capq);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+       }
+
+       spin_lock_irqsave(&zr->lock, flags);
+       zr36057_set_memgrab(zr, 0);
+       spin_unlock_irqrestore(&zr->lock, flags);
+
+       return 0;
+}
+
+/*
+ *   V4L Buffer grabbing
+ */
+
+static int v4l_grab(struct zoran *zr, struct video_mmap *mp)
+{
+       unsigned long flags;
+       int res, bpp;
+
+       /*
+        * There is a long list of limitations to what is allowed to be grabbed
+        * We don't output error messages here, since some programs (e.g. xawtv)
+        * just try several settings to find out what is valid or not.
+        */
+
+       /* No grabbing outside the buffer range! */
+
+       if (mp->frame >= v4l_nbufs || mp->frame < 0) {
+               DEBUG2(printk
+                      (KERN_ERR "%s: Can not grab frame %d\n", zr->name,
+                       mp->frame));
+               return -EINVAL;
+       }
+
+       /* Check size and format of the grab wanted */
+
+       if (mp->height < BUZ_MIN_HEIGHT || mp->width < BUZ_MIN_WIDTH
+           || mp->height > BUZ_MAX_HEIGHT || mp->width > BUZ_MAX_WIDTH) {
+               DEBUG2(printk
+                      (KERN_ERR "%s: Wrong frame size.\n", zr->name));
+               return -EINVAL;
+       }
+
+       bpp = format2bpp(mp->format);
+       if (bpp == 0) {
+               DEBUG2(printk
+                      (KERN_ERR "%s: Wrong bytes-per-pixel format\n",
+                       zr->name));
+               return -EINVAL;
+       }
+
+       /* Check against available buffer size */
+
+       if (mp->height * mp->width * bpp > v4l_bufsize) {
+               DEBUG2(printk
+                      (KERN_ERR "%s: Video buffer size is too small\n",
+                       zr->name));
+               return -EINVAL;
+       }
+
+       /* The video front end needs 4-byte alinged line sizes */
+
+       if ((bpp == 2 && (mp->width & 1)) || (bpp == 3 && (mp->width & 3))) {
+               DEBUG2(printk
+                      (KERN_ERR "%s: Wrong frame alingment\n", zr->name));
+               return -EINVAL;
+       }
+
+       /*
+        * To minimize the time spent in the IRQ routine, we avoid setting up
+        * the video front end there.
+        * If this grab has different parameters from a running streaming capture
+        * we stop the streaming capture and start it over again.
+        */
+
+       if (zr->v4l_memgrab_active
+           && (zr->gwidth != mp->width || zr->gheight != mp->height
+               || zr->gformat != mp->format)) {
+               res = wait_grab_pending(zr);
+               if (res)
+                       return res;
+       }
+       zr->gwidth = mp->width;
+       zr->gheight = mp->height;
+       zr->gformat = mp->format;
+       zr->gbpl = bpp * zr->gwidth;
+
+
+       spin_lock_irqsave(&zr->lock, flags);
+
+       /* make sure a grab isn't going on currently with this buffer */
+
+       switch (zr->v4l_gbuf[mp->frame].state) {
+
+       default:
+       case BUZ_STATE_PEND:
+               res = -EBUSY;   /* what are you doing? */
+               break;
+
+       case BUZ_STATE_USER:
+       case BUZ_STATE_DONE:
+               /* since there is at least one unused buffer there's room for at least one more pend[] entry */
+               zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] =
+                   mp->frame;
+               zr->v4l_gbuf[mp->frame].state = BUZ_STATE_PEND;
+               res = 0;
+               break;
+
+       }
+
+       /* put the 36057 into frame grabbing mode */
+
+       if (!res && !zr->v4l_memgrab_active)
+               zr36057_set_memgrab(zr, 1);
+
+       spin_unlock_irqrestore(&zr->lock, flags);
+       //DEBUG2(printk(KERN_INFO "%s: Frame grab 3...\n", zr->name));
+
+       return res;
+}
+
+/*
+ * Sync on a V4L buffer
+ */
+
+static int v4l_sync(struct zoran *zr, int frame)
+{
+       unsigned long flags;
+
+       /* check passed-in frame number */
+
+       if (frame >= v4l_nbufs || frame < 0) {
+               DEBUG1(printk(KERN_ERR "%s: v4l_sync: frame %d is invalid\n",
+                      zr->name, frame));
+               return -EINVAL;
+       }
+
+       /* Check if is buffer was queued at all */
+
+       if (zr->v4l_gbuf[frame].state == BUZ_STATE_USER) {
+               DEBUG1(printk(KERN_ERR
+                      "%s: v4l_sync: Attempt to sync on a buffer which was not queued?\n",
+                      zr->name));
+               return -EPROTO;
+       }
+
+       /* wait on this buffer to get ready */
+
+       while (zr->v4l_gbuf[frame].state == BUZ_STATE_PEND) {
+               interruptible_sleep_on(&zr->v4l_capq);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+       }
+
+       /* buffer should now be in BUZ_STATE_DONE */
+
+       if (zr->v4l_gbuf[frame].state != BUZ_STATE_DONE)
+               printk(KERN_ERR "%s: v4l_sync - internal error\n",
+                      zr->name);
+
+       /* Check if streaming capture has finished */
+
+       spin_lock_irqsave(&zr->lock, flags);
+
+       if (zr->v4l_pend_tail == zr->v4l_pend_head)
+               zr36057_set_memgrab(zr, 0);
+
+       spin_unlock_irqrestore(&zr->lock, flags);
+
+       return 0;
+}
+
+/*****************************************************************************
+ *                                                                           *
+ *  Set up the Buz-specific MJPEG part                                       *
+ *                                                                           *
+ *****************************************************************************/
+
+/*
+Wait til post office is no longer busy */
+static int post_office_wait(struct zoran *zr)
+{
+       u32 por;
+
+//      while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) {
+       while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) {
+               /* wait for something to happen */
+       }
+       if ((por & ZR36057_POR_POTime) && zr->card != LML33
+           && zr->card != BUZ) {
+               /* In LML33/BUZ \GWS line is not connected, so it has always timeout set */
+               printk(KERN_WARNING "%s: pop timeout %08x\n", zr->name, por);
+               return -1;
+       }
+       return 0;
+}
+
+static int post_office_write(struct zoran *zr, unsigned guest,
+                            unsigned reg, unsigned value)
+{
+       u32 por;
+
+       por =
+           ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) |
+           ((reg & 7) << 16) | (value & 0xFF);
+       btwrite(por, ZR36057_POR);
+       return post_office_wait(zr);
+}
+
+static int post_office_read(struct zoran *zr, unsigned guest, unsigned reg)
+{
+       u32 por;
+
+       por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16);
+       btwrite(por, ZR36057_POR);
+       if (post_office_wait(zr) < 0) {
+               return -1;
+       }
+       return btread(ZR36057_POR) & 0xFF;
+}
+
+static int zr36060_write_8(struct zoran *zr, unsigned reg, unsigned val)
+{
+       if (post_office_wait(zr)
+           || post_office_write(zr, 0, 1, reg >> 8)
+           || post_office_write(zr, 0, 2, reg)) {
+               return -1;
+       }
+       return post_office_write(zr, 0, 3, val);
+}
+
+static int zr36060_write_16(struct zoran *zr, unsigned reg, unsigned val)
+{
+       if (zr36060_write_8(zr, reg + 0, val >> 8)) {
+               return -1;
+       }
+       return zr36060_write_8(zr, reg + 1, val >> 0);
+}
+
+static int zr36060_write_24(struct zoran *zr, unsigned reg, unsigned val)
+{
+       if (zr36060_write_8(zr, reg + 0, val >> 16)) {
+               return -1;
+       }
+       return zr36060_write_16(zr, reg + 1, val >> 0);
+}
+
+static int zr36060_write_32(struct zoran *zr, unsigned reg, unsigned val)
+{
+       if (zr36060_write_16(zr, reg + 0, val >> 16)) {
+               return -1;
+       }
+       return zr36060_write_16(zr, reg + 2, val >> 0);
+}
+
+static u32 zr36060_read_8(struct zoran *zr, unsigned reg)
+{
+       if (post_office_wait(zr)
+           || post_office_write(zr, 0, 1, reg >> 8)
+           || post_office_write(zr, 0, 2, reg)) {
+               return -1;
+       }
+       return post_office_read(zr, 0, 3) & 0xFF;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void GPIO(struct zoran *zr, unsigned bit, unsigned value)
+{
+       u32 reg;
+       u32 mask;
+
+       mask = 1 << (24 + bit);
+       reg = btread(ZR36057_GPPGCR1) & ~mask;
+       if (value) {
+               reg |= mask;
+       }
+       btwrite(reg, ZR36057_GPPGCR1);
+       udelay(1);
+}
+
+
+static void zr36060_sleep(struct zoran *zr, int sleep)
+{
+       switch (zr->card) {
+       case DC10:
+       case DC10plus:
+               GPIO(zr, 3, !sleep);
+               break;
+       case BUZ:
+       case LML33:
+               GPIO(zr, 1, !sleep);
+               break;
+       default:
+               break;
+       }
+       if (!sleep)
+               udelay(500);
+       else
+               udelay(2);
+}
+
+static int zr36060_reset(struct zoran *zr)
+{
+       switch (zr->card) {
+       case DC10:
+       case DC10plus:
+               zr36060_sleep(zr, 0);
+               GPIO(zr, 0, 0);
+               udelay(2);
+               GPIO(zr, 0, 1);
+               udelay(2);
+               break;
+       case LML33:
+       case BUZ:
+               zr36060_sleep(zr, 0);
+               post_office_write(zr, 3, 0, 0);
+               udelay(2);
+       default:
+       }
+       return 0;
+}
+
+static void set_frame(struct zoran *zr, int val)
+{
+       switch (zr->card) {
+       case DC10:
+       case DC10plus:
+               GPIO(zr, 6, val);
+               break;
+       case LML33:
+       case BUZ:
+               GPIO(zr, 3, val);
+               break;
+       default:
+               break;
+       }
+}
+
+static void set_videobus_dir(struct zoran *zr, int val)
+{
+       switch (zr->card) {
+       case DC10:
+       case DC10plus:
+               GPIO(zr, 1, val);
+               break;
+       case LML33:
+               if (lml33dpath == 0)
+                       GPIO(zr, 5, val);
+               else
+                       GPIO(zr, 5, 1);
+               break;
+       case BUZ:
+       default:
+               break;
+       }
+}
+
+static void set_videobus_enable(struct zoran *zr, int val)
+{
+       switch (zr->card) {
+       case LML33:
+               GPIO(zr, 7, val);
+               break;
+       case DC10:
+       case DC10plus:
+       case BUZ:
+       default:
+               break;
+       }
+}
+
+static void zr36060_set_jpg(struct zoran *zr, enum zoran_codec_mode mode)
+{
+       struct tvnorm *tvn;
+       u32 reg;
+       int size, blocks;
+
+       reg = (1 << 0)          /* CodeMstr */
+           |(0 << 2)           /* CFIS=0 */
+           |(0 << 6)           /* Endian=0 */
+           |(0 << 7);          /* Code16=0 */
+       zr36060_write_8(zr, 0x002, reg);
+
+       switch (mode) {
+
+       case BUZ_MODE_MOTION_DECOMPRESS:
+       case BUZ_MODE_STILL_DECOMPRESS:
+               reg = 0x00;     /* Codec mode = decompression */
+               break;
+
+       case BUZ_MODE_MOTION_COMPRESS:
+       case BUZ_MODE_STILL_COMPRESS:
+       default:
+               reg = 0xa4;     /* Codec mode = compression with variable scale factor */
+               break;
+
+       }
+       zr36060_write_8(zr, 0x003, reg);
+
+       reg = 0x00;             /* reserved, mbz */
+       zr36060_write_8(zr, 0x004, reg);
+
+       /* code volume */
+
+       /* Target field size in pixels: */
+       tvn = zr->timing;
+       size =
+           (tvn->Ha / 2) * (tvn->Wa) / (zr->params.HorDcm) /
+           (zr->params.VerDcm);
+       blocks = size / 64;
+
+       /* Target compressed field size in bits: */
+       size = size * 16;       /* uncompressed size in bits */
+       size = size * zr->params.quality / 400; /* quality = 100 is a compression ratio 1:4 */
+
+       /* Lower limit (arbitrary, 1 KB) */
+       if (size < 8192)
+               size = 8192;
+
+       /* Upper limit: 6/8 of the code buffers */
+       if (size * zr->params.field_per_buff > zr->jpg_bufsize * 6)
+               size = zr->jpg_bufsize * 6 / zr->params.field_per_buff;
+
+       reg = size * 4 / blocks;
+       if (reg > 0xf0)
+               reg = 0xf0;     /* 480 bits/block, does 0xff represents unlimited? */
+       zr36060_write_8(zr, 0x005, reg);
+
+       /* JPEG markers */
+       reg = (zr->params.jpeg_markers) & 0x38; /* DRI, DQT, DHT */
+       if (zr->params.COM_len)
+               reg |= JPEG_MARKER_COM;
+       if (zr->params.APP_len)
+               reg |= JPEG_MARKER_APP;
+       zr36060_write_8(zr, 0x006, reg);
+
+       if (zr->card != LML33 && zr->card != BUZ) {
+               reg = (0 << 3)  /* EOAV=0 */
+                   |(0 << 2)   /* EOI=0 */
+                   |(0 << 1)   /* END=0 */
+                   |(1 << 0);  /* DATERR=1 */
+       } else {
+               reg = (0 << 3)  /* EOAV=0 */
+                   |(0 << 2)   /* EOI=0 */
+                   |(0 << 1)   /* END=0 */
+                   |(0 << 0);  /* DATERR=0 */
+       }
+       zr36060_write_8(zr, 0x007, reg);
+
+       reg = size;
+       zr36060_write_32(zr, 0x009, reg);
+
+       reg = (size * 10) / 11;
+       zr36060_write_32(zr, 0x00d, reg);       // Not needed for compr. with variable scale factor, just in case ...
+
+       /* how do we set initial SF as a function of quality parameter? */
+       reg = 0x0100;           /* SF=1.0 */
+       zr36060_write_16(zr, 0x011, reg);
+
+       reg = 0x00ffffff;       /* AF=max */
+       zr36060_write_24(zr, 0x013, reg);
+
+       reg = 0x0000;           /* test */
+       zr36060_write_16(zr, 0x024, reg);
+
+       //post_office_read(zr,1,0);
+}
+
+static void zr36060_set_video(struct zoran *zr, enum zoran_codec_mode mode)
+{
+       struct tvnorm *tvn;
+       u32 reg;
+       unsigned HStart;
+
+       tvn = zr->timing;
+
+       reg = (0 << 7)          /* Video8     */
+           |(0 << 6)           /* Range      */
+           |(0 << 3)           /* FlDet      */
+           |(1 << 2)           /* FlVedge    */
+           |(0 << 1)           /* FlExt      */
+           |(0 << 0);          /* SyncMstr   */
+
+       if (mode != BUZ_MODE_STILL_DECOMPRESS) {
+               /* limit pixels to range 16..235 as per CCIR-601 */
+               reg |= (1 << 6);        /* Range=1 */
+       }
+       zr36060_write_8(zr, 0x030, reg);
+
+       switch (zr->card) {
+       case DC10:
+       case DC10plus:
+               reg = (0 << 7)  /* VCLKPol   */
+                   |(0 << 6)   /* PValPol   */
+                   |(0 << 5)   /* PoePol    */
+                   |(0 << 4)   /* SImgPol   */
+                   |(1 << 3)   /* BLPol     */
+                   |(1 << 2)   /* FlPol     */
+                   |(1 << 1)   /* HSPol     */
+                   |(1 << 0);  /* VSPol     */
+               break;
+       case LML33:
+               reg = (0 << 7)  /* VCLKPol=0 */
+                   |(0 << 6)   /* PValPol=0 */
+                   |(1 << 5)   /* PoePol=1 */
+                   |(0 << 4)   /* SImgPol=0 */
+                   |(0 << 3)   /* BLPol=0 */
+                   |(0 << 2)   /* FlPol=0 */
+                   |(0 << 1)   /* HSPol=0, sync on falling edge */
+                   |(1 << 0);  /* VSPol=1 */
+               break;
+       case BUZ:
+       default:
+               reg = (0 << 7)  /* VCLKPol=0 */
+                   |(0 << 6)   /* PValPol=0 */
+                   |(1 << 5)   /* PoePol=1 */
+                   |(0 << 4)   /* SImgPol=0 */
+                   |(0 << 3)   /* BLPol=0 */
+                   |(0 << 2)   /* FlPol=0 */
+                   |(1 << 1)   /* HSPol=0, sync on falling edge */
+                   |(1 << 0);  /* VSPol=1 */
+               break;
+       }
+       zr36060_write_8(zr, 0x031, reg);
+
+       switch (zr->params.HorDcm) {
+       default:
+       case 1:
+               reg = (0 << 0);
+               break;          /* HScale = 0 */
+
+       case 2:
+               reg = (1 << 0);
+               break;          /* HScale = 1 */
+
+       case 4:
+               reg = (2 << 0);
+               break;          /* HScale = 2 */
+       }
+       if (zr->params.VerDcm == 2)
+               reg |= (1 << 2);
+       zr36060_write_8(zr, 0x032, reg);
+
+       reg = 0x00;             /* BackY */
+       zr36060_write_8(zr, 0x033, reg);
+
+       reg = 0x80;             /* BackU */
+       zr36060_write_8(zr, 0x034, reg);
+
+       reg = 0x80;             /* BackV */
+       zr36060_write_8(zr, 0x035, reg);
+
+       /* sync generator */
+
+       reg = tvn->Ht - 1;      /* Vtotal */
+       zr36060_write_16(zr, 0x036, reg);
+
+       reg = tvn->Wt - 1;      /* Htotal */
+       zr36060_write_16(zr, 0x038, reg);
+
+       reg = 6 - 1;            /* VsyncSize */
+       zr36060_write_8(zr, 0x03a, reg);
+
+       //reg   = 30 - 1;               /* HsyncSize */
+       reg = (zr->params.norm == 1 ? 57 : 68);
+       zr36060_write_8(zr, 0x03b, reg);
+
+       reg = tvn->VStart - 1;  /* BVstart */
+       zr36060_write_8(zr, 0x03c, reg);
+
+       reg += tvn->Ha / 2;     /* BVend */
+       zr36060_write_16(zr, 0x03e, reg);
+
+       reg = tvn->HStart + 64 - 1;     /* BHstart */
+       zr36060_write_8(zr, 0x03d, reg);
+
+       reg += tvn->Wa;         /* BHend */
+       zr36060_write_16(zr, 0x040, reg);
+
+       /* active area */
+       reg = zr->params.img_y + tvn->VStart;   /* Vstart */
+       zr36060_write_16(zr, 0x042, reg);
+
+       reg += zr->params.img_height;   /* Vend */
+       zr36060_write_16(zr, 0x044, reg);
+
+       HStart = tvn->HStart;
+       if (zr->card == BUZ) {
+               HStart += 44;
+       } else {
+               HStart += 64;
+       }
+       reg = zr->params.img_x + HStart;        /* Hstart */
+       zr36060_write_16(zr, 0x046, reg);
+
+       reg += zr->params.img_width;    /* Hend */
+       zr36060_write_16(zr, 0x048, reg);
+
+       /* subimage area */
+       reg = tvn->VStart - 4;  /* SVstart */
+       zr36060_write_16(zr, 0x04a, reg);
+
+       reg += tvn->Ha / 2 + 8; /* SVend */
+       zr36060_write_16(zr, 0x04c, reg);
+
+       reg = tvn->HStart + 64 - 4;     /* SHstart */
+       zr36060_write_16(zr, 0x04e, reg);
+
+       reg += tvn->Wa + 8;     /* SHend */
+       zr36060_write_16(zr, 0x050, reg);
+}
+
+static void zr36060_set_jpg_SOF(struct zoran *zr)
+{
+       u32 reg;
+
+
+       reg = 0xffc0;           /* SOF marker */
+       zr36060_write_16(zr, 0x060, reg);
+
+       reg = 17;               /* SOF length */
+       zr36060_write_16(zr, 0x062, reg);
+
+       reg = 8;                /* precision 8 bits */
+       zr36060_write_8(zr, 0x064, reg);
+
+       reg = zr->params.img_height / zr->params.VerDcm;        /* image height */
+       zr36060_write_16(zr, 0x065, reg);
+
+       reg = zr->params.img_width / zr->params.HorDcm; /* image width */
+       zr36060_write_16(zr, 0x067, reg);
+
+       reg = 3;                /* 3 color components */
+       zr36060_write_8(zr, 0x069, reg);
+
+       reg = 0x002100;         /* Y component */
+       zr36060_write_24(zr, 0x06a, reg);
+
+       reg = 0x011101;         /* U component */
+       zr36060_write_24(zr, 0x06d, reg);
+
+       reg = 0x021101;         /* V component */
+       zr36060_write_24(zr, 0x070, reg);
+}
+
+static void zr36060_set_jpg_SOS(struct zoran *zr)
+{
+       u32 reg;
+
+
+       reg = 0xffda;           /* SOS marker */
+       zr36060_write_16(zr, 0x07a, reg);
+
+       reg = 12;               /* SOS length */
+       zr36060_write_16(zr, 0x07c, reg);
+
+       reg = 3;                /* 3 color components */
+       zr36060_write_8(zr, 0x07e, reg);
+
+       reg = 0x0000;           /* Y component */
+       zr36060_write_16(zr, 0x07f, reg);
+
+       reg = 0x0111;           /* U component */
+       zr36060_write_16(zr, 0x081, reg);
+
+       reg = 0x0211;           /* V component */
+       zr36060_write_16(zr, 0x083, reg);
+
+       reg = 0x003f00;         /* Start, end spectral scans */
+       zr36060_write_24(zr, 0x085, reg);
+}
+
+static void zr36060_set_jpg_DRI(struct zoran *zr)
+{
+       u32 reg;
+
+
+       reg = 0xffdd;           /* DRI marker */
+       zr36060_write_16(zr, 0x0c0, reg);
+
+       reg = 4;                /* DRI length */
+       zr36060_write_16(zr, 0x0c2, reg);
+
+       reg = 8;                /* length in MCUs */
+       zr36060_write_16(zr, 0x0c4, reg);
+}
+
+static void zr36060_set_jpg_DQT(struct zoran *zr)
+{
+       unsigned i;
+       unsigned adr;
+       static const u8 dqt[] = {
+               0xff, 0xdb,     /* DHT marker */
+               0x00, 0x84,     /* DHT length */
+               0x00,           /* table ID 0 */
+               0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+               0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+               0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+               0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+               0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+               0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+               0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+               0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+               0x01,           /* table ID 1 */
+               0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+               0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+               0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+       };
+
+       /* write fixed quantitization tables */
+       adr = 0x0cc;
+       for (i = 0; i < sizeof(dqt); ++i) {
+               zr36060_write_8(zr, adr++, dqt[i]);
+       }
+}
+
+static void zr36060_set_jpg_DHT(struct zoran *zr)
+{
+       unsigned i;
+       unsigned adr;
+       static const u8 dht[] = {
+               0xff, 0xc4,     /* DHT marker */
+               0x01, 0xa2,     /* DHT length */
+               0x00,           /* table class 0, ID 0 */
+               0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */
+               0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 8..16 */
+               0x00,           /* values for codes of length 2 */
+               0x01, 0x02, 0x03, 0x04, 0x05,   /* values for codes of length 3 */
+               0x06,           /* values for codes of length 4 */
+               0x07,           /* values for codes of length 5 */
+               0x08,           /* values for codes of length 6 */
+               0x09,           /* values for codes of length 7 */
+               0x0a,           /* values for codes of length 8 */
+               0x0b,           /* values for codes of length 9 */
+               0x01,           /* table class 0, ID 1 */
+               0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, /* # codes of length 1..8 */
+               0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* # codes of length 9..16 */
+               0x00, 0x01, 0x02,       /* values for codes of length 2 */
+               0x03,           /* values for codes of length 3 */
+               0x04,           /* values for codes of length 4 */
+               0x05,           /* values for codes of length 5 */
+               0x06,           /* values for codes of length 6 */
+               0x07,           /* values for codes of length 7 */
+               0x08,           /* values for codes of length 8 */
+               0x09,           /* values for codes of length 9 */
+               0x0a,           /* values for codes of length 10 */
+               0x0b,           /* values for codes of length 11 */
+               0x10,
+               0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
+               0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d,
+               0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+               0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+               0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+               0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+               0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+               0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+               0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+               0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+               0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+               0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+               0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+               0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+               0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+               0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+               0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+               0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+               0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+               0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+               0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+               0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+               0xf9, 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+               0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00,
+               0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11,
+               0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
+               0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08,
+               0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23,
+               0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a,
+               0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
+               0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35,
+               0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
+               0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55,
+               0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+               0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
+               0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84,
+               0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+               0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
+               0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
+               0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+               0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+               0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+               0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+               0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5,
+               0xf6, 0xf7, 0xf8, 0xf9, 0xfa
+       };
+
+       /* write fixed Huffman tables */
+       adr = 0x1d4;
+       for (i = 0; i < sizeof(dht); ++i) {
+               zr36060_write_8(zr, adr++, dht[i]);
+       }
+}
+
+static void zr36060_set_jpg_APP(struct zoran *zr)
+{
+       unsigned adr;
+       int len, i;
+       u32 reg;
+
+
+       len = zr->params.APP_len;
+       if (len < 0)
+               len = 0;
+       if (len > 60)
+               len = 60;
+
+       i = zr->params.APPn;
+       if (i < 0)
+               i = 0;
+       if (i > 15)
+               i = 15;
+
+       reg = 0xffe0 + i;       /* APPn marker */
+       zr36060_write_16(zr, 0x380, reg);
+
+       reg = len + 2;          /* APPn len */
+       zr36060_write_16(zr, 0x382, reg);
+
+       /* write APPn data */
+       adr = 0x384;
+       for (i = 0; i < 60; i++) {
+               zr36060_write_8(zr, adr++,
+                               (i < len ? zr->params.APP_data[i] : 0));
+       }
+}
+
+static void zr36060_set_jpg_COM(struct zoran *zr)
+{
+       unsigned adr;
+       int len, i;
+       u32 reg;
+
+
+       len = zr->params.COM_len;
+       if (len < 0)
+               len = 0;
+       if (len > 60)
+               len = 60;
+
+       reg = 0xfffe;           /* COM marker */
+       zr36060_write_16(zr, 0x3c0, reg);
+
+       reg = len + 2;          /* COM len */
+       zr36060_write_16(zr, 0x3c2, reg);
+
+       /* write COM data */
+       adr = 0x3c4;
+       for (i = 0; i < 60; i++) {
+               zr36060_write_8(zr, adr++,
+                               (i < len ? zr->params.COM_data[i] : 0));
+       }
+}
+
+static void zr36060_set_cap(struct zoran *zr, enum zoran_codec_mode mode)
+{
+       unsigned i;
+       u32 reg;
+
+       zr36060_reset(zr);
+       mdelay(10);
+
+       reg = (0 << 7)          /* Load=0 */
+           |(1 << 0);          /* SynRst=1 */
+       zr36060_write_8(zr, 0x000, reg);
+
+       zr36060_set_jpg(zr, mode);
+       zr36060_set_video(zr, mode);
+       zr36060_set_jpg_SOF(zr);
+       zr36060_set_jpg_SOS(zr);
+       zr36060_set_jpg_DRI(zr);
+       zr36060_set_jpg_DQT(zr);
+       zr36060_set_jpg_DHT(zr);
+       zr36060_set_jpg_APP(zr);
+       zr36060_set_jpg_COM(zr);
+
+       reg = (1 << 7)          /* Load=1 */
+           |(1 << 0);          /* SynRst=0 */
+       zr36060_write_8(zr, 0x000, reg);
+
+       /* wait for codec to unbusy */
+       for (i = 0; i < 100000; ++i) {
+               reg = zr36060_read_8(zr, 0x001);
+               if ((reg & (1 << 7)) == 0) {
+                       return;
+               }
+               //udelay(100);
+       }
+       printk(KERN_ERR "%sZR36060: stuck busy, statux=%02x\n", zr->name,
+              reg);
+}
+
+static void init_jpeg_queue(struct zoran *zr)
+{
+       int i;
+       /* re-initialize DMA ring stuff */
+       zr->jpg_que_head = 0;
+       zr->jpg_dma_head = 0;
+       zr->jpg_dma_tail = 0;
+       zr->jpg_que_tail = 0;
+       zr->jpg_seq_num = 0;
+       zr->JPEG_error = 0;
+       zr->num_errors = 0;
+       zr->jpg_err_seq = 0;
+       zr->jpg_err_shift = 0;
+       zr->jpg_queued_num = 0;
+       for (i = 0; i < zr->jpg_nbufs; i++) {
+               zr->jpg_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */
+       }
+       for (i = 0; i < BUZ_NUM_STAT_COM; i++) {
+               zr->stat_com[i] = 1;    /* mark as unavailable to zr36057 */
+       }
+}
+
+static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode)
+{
+       struct tvnorm *tvn;
+       u32 reg;
+
+       tvn = zr->timing;
+
+       /* assert P_Reset */
+       btwrite(0, ZR36057_JPC);
+
+       /* MJPEG compression mode */
+       switch (mode) {
+
+       case BUZ_MODE_MOTION_COMPRESS:
+       default:
+               reg = ZR36057_JMC_MJPGCmpMode;
+               break;
+
+       case BUZ_MODE_MOTION_DECOMPRESS:
+               reg = ZR36057_JMC_MJPGExpMode;
+               reg |= ZR36057_JMC_SyncMstr;
+               /* RJ: The following is experimental - improves the output to screen */
+               //if(zr->params.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM
+               break;
+
+       case BUZ_MODE_STILL_COMPRESS:
+               reg = ZR36057_JMC_JPGCmpMode;
+               break;
+
+       case BUZ_MODE_STILL_DECOMPRESS:
+               reg = ZR36057_JMC_JPGExpMode;
+               break;
+
+       }
+       reg |= ZR36057_JMC_JPG;
+       if (zr->params.field_per_buff == 1)
+               reg |= ZR36057_JMC_Fld_per_buff;
+       btwrite(reg, ZR36057_JMC);
+
+       /* vertical */
+       btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR);
+       reg =
+           (6 << ZR36057_VSP_VsyncSize) | (tvn->Ht << ZR36057_VSP_FrmTot);
+       btwrite(reg, ZR36057_VSP);
+       reg = ((zr->params.img_y + tvn->VStart) << ZR36057_FVAP_NAY)
+           | (zr->params.img_height << ZR36057_FVAP_PAY);
+       btwrite(reg, ZR36057_FVAP);
+
+       /* horizontal */
+       btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
+       reg =
+           ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) | (tvn->
+                                                            Wt <<
+                                                            ZR36057_HSP_LineTot);
+       btwrite(reg, ZR36057_HSP);
+       reg = ((zr->params.img_x + tvn->HStart + 4) << ZR36057_FHAP_NAX)
+           | (zr->params.img_width << ZR36057_FHAP_PAX);
+       btwrite(reg, ZR36057_FHAP);
+
+       /* field process parameters */
+       if (zr->params.odd_even)
+               reg = ZR36057_FPP_Odd_Even;
+       else
+               reg = 0;
+       if (mode == BUZ_MODE_MOTION_DECOMPRESS && zr->card != LML33
+           && zr->card != BUZ)
+               reg ^= ZR36057_FPP_Odd_Even;
+
+       btwrite(reg, ZR36057_FPP);
+
+       /* Set proper VCLK Polarity, else colors will be wrong during playback */
+       //btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR);
+
+       /* code base address */
+       reg = virt_to_bus(zr->stat_com);
+       btwrite(reg, ZR36057_JCBA);
+
+       /* FIFO threshold (FIFO is 160. double words) */
+       /* NOTE: decimal values here */
+       switch (mode) {
+
+       case BUZ_MODE_STILL_COMPRESS:
+       case BUZ_MODE_MOTION_COMPRESS:
+               reg = 140;
+               break;
+
+       case BUZ_MODE_STILL_DECOMPRESS:
+       case BUZ_MODE_MOTION_DECOMPRESS:
+               reg = 20;
+               break;
+
+       default:
+               reg = 80;
+               break;
+
+       }
+       btwrite(reg, ZR36057_JCFT);
+       zr36057_adjust_vfe(zr, mode);
+
+}
+
+#if (DEBUGLEVEL > 2)
+static void dump_guests(struct zoran *zr)
+{
+       int i, guest[8];
+
+       for (i = 1; i < 8; i++) {       // Don't read zr36060 here
+               guest[i] = post_office_read(zr, i, 0);
+       }
+
+       printk(KERN_INFO "%s: Guests:", zr->name);
+
+       for (i = 1; i < 8; i++) {
+               printk(" 0x%02x", guest[i]);
+       }
+       printk("\n");
+}
+
+static unsigned long get_time(void)
+{
+       struct timeval tv;
+       do_gettimeofday(&tv);
+       return (1000000 * tv.tv_sec + tv.tv_usec);
+}
+
+static void detect_guest_activity(struct zoran *zr)
+{
+       int timeout, i, j, res, guest[8], guest0[8], change[8][3];
+       unsigned long t0, t1;
+
+       dump_guests(zr);
+       printk(KERN_INFO "%s: Detecting guests activity, please wait...\n",
+              zr->name);
+       for (i = 1; i < 8; i++) {       // Don't read zr36060 here
+               guest0[i] = guest[i] = post_office_read(zr, i, 0);
+       }
+
+       timeout = 0;
+       j = 0;
+       t0 = get_time();
+       while (timeout < 10000) {
+               udelay(10);
+               timeout++;
+               for (i = 1; (i < 8) && (j < 8); i++) {
+                       res = post_office_read(zr, i, 0);
+                       if (res != guest[i]) {
+                               t1 = get_time();
+                               change[j][0] = (t1 - t0);
+                               t0 = t1;
+                               change[j][1] = i;
+                               change[j][2] = res;
+                               j++;
+                               guest[i] = res;
+                       }
+               }
+               if (j >= 8)
+                       break;
+       }
+       printk(KERN_INFO "%s: Guests:", zr->name);
+
+       for (i = 1; i < 8; i++) {
+               printk(" 0x%02x", guest0[i]);
+       }
+       printk("\n");
+       if (j == 0) {
+               printk(KERN_INFO "%s: No activity detected.\n", zr->name);
+               return;
+       }
+       for (i = 0; i < j; i++) {
+               printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", zr->name,
+                      change[i][0], change[i][1], change[i][2]);
+       }
+}
+#endif
+
+static void print_interrupts(struct zoran *zr)
+{
+       int res, noerr;
+       noerr = 0;
+       printk(KERN_INFO "%s: interrupts received:", zr->name);
+       if ((res = zr->field_counter) < -1 || res > 1) {
+               printk(" FD:%d", res);
+       }
+       if ((res = zr->intr_counter_GIRQ1) != 0) {
+               printk(" GIRQ1:%d", res);
+               noerr++;
+       }
+       if ((res = zr->intr_counter_GIRQ0) != 0) {
+               printk(" GIRQ0:%d", res);
+               noerr++;
+       }
+       if ((res = zr->intr_counter_CodRepIRQ) != 0) {
+               printk(" CodRepIRQ:%d", res);
+               noerr++;
+       }
+       if ((res = zr->intr_counter_JPEGRepIRQ) != 0) {
+               printk(" JPEGRepIRQ:%d", res);
+               noerr++;
+       }
+       if (zr->JPEG_max_missed) {
+               printk(" JPEG delays: max=%d min=%d", zr->JPEG_max_missed,
+                      zr->JPEG_min_missed);
+       }
+       if (zr->END_event_missed) {
+               printk(" ENDs missed: %d", zr->END_event_missed);
+       }
+       //if (zr->jpg_queued_num) {
+       printk(" queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail,
+              zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head);
+       //}
+       if (!noerr) {
+               printk(": no interrupts detected.");
+       }
+       printk("\n");
+}
+
+static void clear_interrupt_counters(struct zoran *zr)
+{
+       zr->intr_counter_GIRQ1 = 0;
+       zr->intr_counter_GIRQ0 = 0;
+       zr->intr_counter_CodRepIRQ = 0;
+       zr->intr_counter_JPEGRepIRQ = 0;
+       zr->field_counter = 0;
+       zr->IRQ1_in = 0;
+       zr->IRQ1_out = 0;
+       zr->JPEG_in = 0;
+       zr->JPEG_out = 0;
+       zr->JPEG_0 = 0;
+       zr->JPEG_1 = 0;
+       zr->END_event_missed = 0;
+       zr->JPEG_missed = 0;
+       zr->JPEG_max_missed = 0;
+       zr->JPEG_min_missed = 0x7fffffff;
+}
+
+static u32 count_reset_interrupt(struct zoran *zr)
+{
+       u32 isr;
+       if ((isr = btread(ZR36057_ISR) & 0x78000000)) {
+               if (isr & ZR36057_ISR_GIRQ1) {
+                       btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR);
+                       zr->intr_counter_GIRQ1++;
+               }
+               if (isr & ZR36057_ISR_GIRQ0) {
+                       btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR);
+                       zr->intr_counter_GIRQ0++;
+               }
+               if (isr & ZR36057_ISR_CodRepIRQ) {
+                       btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR);
+                       zr->intr_counter_CodRepIRQ++;
+               }
+               if (isr & ZR36057_ISR_JPEGRepIRQ) {
+                       btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR);
+                       zr->intr_counter_JPEGRepIRQ++;
+               }
+       }
+       return isr;
+}
+
+static void jpeg_start(struct zoran *zr)
+{
+       int reg;
+       zr->frame_num = 0;
+
+       btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC);      // /P_Reset
+       btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR);    // \CFlush
+       btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC);       // /CodTrnsEn
+       btwrite(IRQ_MASK, ZR36057_ISR); // Clear IRQs
+       btwrite(IRQ_MASK | ZR36057_ICR_IntPinEn, ZR36057_ICR);  // Enable IRQs
+
+       set_frame(zr, 0);       // \FRAME
+
+       /* JPEG codec guest ID */
+       reg =
+           (1 << ZR36057_JCGI_JPEGuestID) | (0 <<
+                                             ZR36057_JCGI_JPEGuestReg);
+       btwrite(reg, ZR36057_JCGI);
+
+       btor(ZR36057_JPC_Active, ZR36057_JPC);  // /Active
+       btor(ZR36057_JMC_Go_en, ZR36057_JMC);   // /Go_en
+       udelay(30);
+       set_frame(zr, 1);       // /FRAME
+}
+
+static void zr36057_enable_jpg(struct zoran *zr,
+                              enum zoran_codec_mode mode)
+{
+       static int zero = 0;
+       static int one = 1;
+
+       zr->codec_mode = mode;
+       switch (mode) {
+
+       case BUZ_MODE_MOTION_COMPRESS:
+               set_videobus_enable(zr, 0);
+               set_videobus_dir(zr, 0);        // GPIO(zr, 1, 0);
+               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+                                  DECODER_ENABLE_OUTPUT, &one);
+               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+                                  ENCODER_SET_INPUT, &zero);
+               set_videobus_enable(zr, 1);
+               zr36060_sleep(zr, 0);
+               zr36060_set_cap(zr, mode);      // Load ZR36060
+               init_jpeg_queue(zr);
+               zr36057_set_jpg(zr, mode);      // \P_Reset, ... Video param, FIFO
+
+               clear_interrupt_counters(zr);
+               DEBUG1(printk
+                      (KERN_INFO "%s: enable_jpg MOTION_COMPRESS\n",
+                       zr->name));
+               break;
+
+       case BUZ_MODE_MOTION_DECOMPRESS:
+               set_videobus_enable(zr, 0);
+               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+                                  DECODER_ENABLE_OUTPUT, &zero);
+               set_videobus_dir(zr, 1);        // GPIO(zr, 1, 1);
+               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+                                  ENCODER_SET_INPUT, &one);
+               set_videobus_enable(zr, 1);
+               zr36060_sleep(zr, 0);
+               zr36060_set_cap(zr, mode);      // Load ZR36060
+               init_jpeg_queue(zr);
+               zr36057_set_jpg(zr, mode);      // \P_Reset, ... Video param, FIFO
+
+               clear_interrupt_counters(zr);
+               DEBUG1(printk
+                      (KERN_INFO "%s: enable_jpg MOTION_DECOMPRESS\n",
+                       zr->name));
+               break;
+
+       case BUZ_MODE_IDLE:
+       default:
+               /* shut down processing */
+               btand(~(cardjpegint[zr->card] | ZR36057_ICR_JPEGRepIRQ),
+                     ZR36057_ICR);
+               btwrite(cardjpegint[zr->card] | ZR36057_ICR_JPEGRepIRQ,
+                       ZR36057_ISR);
+               btand(~ZR36057_JMC_Go_en, ZR36057_JMC); // \Go_en
+
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(HZ/20);
+
+               set_videobus_dir(zr, 0);        // GPIO(zr, 1, 0);
+               set_frame(zr, 1);       //GPIO(zr, 6, 1);     // /FRAME
+               btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);      // /CFlush
+               btwrite(0, ZR36057_JPC);        // \P_Reset,\CodTrnsEn,\Active
+               btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC);
+               btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC);
+               zr36060_reset(zr);
+               zr36060_sleep(zr, 1);
+               zr36057_adjust_vfe(zr, mode);
+               set_videobus_enable(zr, 0);
+               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+                                  DECODER_ENABLE_OUTPUT, &one);
+               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+                                  ENCODER_SET_INPUT, &zero);
+               set_videobus_enable(zr, 1);
+               DEBUG1(printk
+                      (KERN_INFO "%s: enable_jpg IDLE\n", zr->name));
+               break;
+
+       }
+}
+
+/*
+ *   Queue a MJPEG buffer for capture/playback
+ */
+
+static int jpg_qbuf(struct zoran *zr, int frame,
+                   enum zoran_codec_mode mode)
+{
+       unsigned long flags;
+       int res;
+
+       /* Check if buffers are allocated */
+
+       if (!zr->jpg_buffers_allocated) {
+               printk(KERN_ERR
+                      "%s: jpg_qbuf: buffers not yet allocated\n",
+                      zr->name);
+               return -ENOMEM;
+       }
+
+       /* Does the user want to stop streaming? */
+
+       if (frame < 0) {
+               if (zr->codec_mode == mode) {
+                       zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+                       return 0;
+               } else {
+                       printk(KERN_ERR
+                              "%s: jpg_qbuf - stop streaming but not in streaming mode\n",
+                              zr->name);
+                       return -EINVAL;
+               }
+       }
+
+       /* No grabbing outside the buffer range! */
+
+       if (frame >= zr->jpg_nbufs) {
+               printk(KERN_ERR "%s: jpg_qbuf: buffer %d out of range\n",
+                      zr->name, frame);
+               return -EINVAL;
+       }
+
+       /* what is the codec mode right now? */
+
+       if (zr->codec_mode == BUZ_MODE_IDLE) {
+               /* Ok load up the zr36060 */
+               zr36057_enable_jpg(zr, mode);
+       } else if (zr->codec_mode != mode) {
+               /* wrong codec mode active - invalid */
+               printk(KERN_ERR "%s: jpg_qbuf - codec in wrong mode\n",
+                      zr->name);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&zr->lock, flags);
+
+       /* make sure a grab isn't going on currently with this buffer */
+
+       switch (zr->jpg_gbuf[frame].state) {
+
+       case BUZ_STATE_DONE:
+               DEBUG1(printk
+                      (KERN_WARNING
+                       "%s: Warning: queing frame in BUZ_STATE_DONE state\n",
+                       zr->name));
+       case BUZ_STATE_USER:
+               /* since there is at least one unused buffer there's room for at least one more pend[] entry */
+               zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = frame;
+               zr->jpg_gbuf[frame].state = BUZ_STATE_PEND;
+               zoran_feed_stat_com(zr);
+               res = 0;
+               break;
+
+       default:
+       case BUZ_STATE_DMA:
+       case BUZ_STATE_PEND:
+               res = -EBUSY;   /* what are you doing? */
+               break;
+
+       }
+
+       spin_unlock_irqrestore(&zr->lock, flags);
+
+       /* Start the zr36060 when the first frame is queued  */
+       if (zr->jpg_que_head == 1)
+               jpeg_start(zr);
+
+       return res;
+}
+
+/*
+ *   Sync on a MJPEG buffer
+ */
+
+static int jpg_sync(struct zoran *zr, struct zoran_sync *bs)
+{
+       unsigned long flags;
+       int frame, timeout;
+
+       if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS
+           && zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
+               DEBUG1(printk(KERN_ERR
+                      "%s: BUZIOCSYNC: - codec not in streaming mode\n",
+                      zr->name));
+               return -EINVAL;
+       }
+       while (zr->jpg_que_tail == zr->jpg_dma_tail) {
+               if (zr->jpg_dma_tail == zr->jpg_dma_head)
+                       break;
+               timeout =
+                   interruptible_sleep_on_timeout(&zr->jpg_capq, 10 * HZ);
+               if (!timeout) {
+                       btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+                       udelay(1);
+                       printk(KERN_ERR
+                              "%s: timeout: codec isr=0x%02x, csr=0x%02x\n",
+                              zr->name, zr36060_read_8(zr, 0x008),
+                              zr36060_read_8(zr, 0x001));
+                       return -ETIME;
+               } else if (signal_pending(current))
+                       return -ERESTARTSYS;
+       }
+
+       spin_lock_irqsave(&zr->lock, flags);
+
+       if (zr->jpg_dma_tail != zr->jpg_dma_head)
+               frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME];
+       else
+               frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+       /* buffer should now be in BUZ_STATE_DONE */
+
+#if(DEBUGLEVEL > 0)
+       if (zr->jpg_gbuf[frame].state != BUZ_STATE_DONE)
+               printk(KERN_ERR "%s: jpg_sync - internal error\n",
+                      zr->name);
+#endif
+
+       *bs = zr->jpg_gbuf[frame].bs;
+       zr->jpg_gbuf[frame].state = BUZ_STATE_USER;
+
+       spin_unlock_irqrestore(&zr->lock, flags);
+
+       return 0;
+}
+
+/* when this is called the spinlock must be held */
+static void zoran_feed_stat_com(struct zoran *zr)
+{
+       /* move frames from pending queue to DMA */
+
+       int frame, i, max_stat_com;
+
+       max_stat_com =
+           (zr->params.TmpDcm ==
+            1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1);
+
+       while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com
+              && zr->jpg_dma_head < zr->jpg_que_head) {
+
+               frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME];
+               if (zr->params.TmpDcm == 1) {
+                       /* fill 1 stat_com entry */
+                       i = (zr->jpg_dma_head -
+                            zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+                       if (!(zr->stat_com[i] & 1))
+                               break;
+                       zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus;
+               } else {
+                       /* fill 2 stat_com entries */
+                       i = ((zr->jpg_dma_head -
+                             zr->jpg_err_shift) & 1) * 2;
+                       if (!(zr->stat_com[i] & 1))
+                               break;
+                       zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus;
+                       zr->stat_com[i + 1] =
+                           zr->jpg_gbuf[frame].frag_tab_bus;
+               }
+               zr->jpg_gbuf[frame].state = BUZ_STATE_DMA;
+               zr->jpg_dma_head++;
+
+       }
+       if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
+               zr->jpg_queued_num++;
+}
+
+/* when this is called the spinlock must be held */
+static void zoran_reap_stat_com(struct zoran *zr)
+{
+       /* move frames from DMA queue to done queue */
+
+       int i;
+       u32 stat_com;
+       unsigned int seq;
+       unsigned int dif;
+       int frame;
+       struct zoran_gbuffer *gbuf;
+
+       /* In motion decompress we don't have a hardware frame counter,
+          we just count the interrupts here */
+
+       if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+               zr->jpg_seq_num++;
+       }
+       while (zr->jpg_dma_tail < zr->jpg_dma_head) {
+               if (zr->params.TmpDcm == 1)
+                       i = (zr->jpg_dma_tail -
+                            zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+               else
+                       i = ((zr->jpg_dma_tail -
+                             zr->jpg_err_shift) & 1) * 2 + 1;
+
+               stat_com = zr->stat_com[i];
+
+               if ((stat_com & 1) == 0) {
+                       return;
+               }
+               frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
+               gbuf = &zr->jpg_gbuf[frame];
+               get_fast_time(&gbuf->bs.timestamp);
+
+               if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+                       gbuf->bs.length = (stat_com & 0x7fffff) >> 1;
+
+                       /* update sequence number with the help of the counter in stat_com */
+
+                       seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff;
+                       dif = (seq - zr->jpg_seq_num) & 0xff;
+                       zr->jpg_seq_num += dif;
+               } else {
+                       gbuf->bs.length = 0;
+               }
+               gbuf->bs.seq =
+                   zr->params.TmpDcm ==
+                   2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num;
+               gbuf->state = BUZ_STATE_DONE;
+
+               zr->jpg_dma_tail++;
+       }
+}
+
+static void error_handler(struct zoran *zr, u32 astat, u32 stat)
+{
+       /* This is JPEG error handling part */
+       if ((zr->codec_mode != BUZ_MODE_MOTION_COMPRESS)
+           && (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS)) {
+               //printk(KERN_ERR "%s: Internal error: error handling request in mode %d\n", zr->name, zr->codec_mode);
+               return;
+       }
+       if ((stat & 1) == 0
+           && zr->codec_mode == BUZ_MODE_MOTION_COMPRESS
+           && zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_nbufs) {
+               /* No free buffers... */
+               zoran_reap_stat_com(zr);
+               zoran_feed_stat_com(zr);
+               wake_up_interruptible(&zr->jpg_capq);
+               zr->JPEG_missed = 0;
+               return;
+       }
+       if (zr->JPEG_error != 1) {
+               /*
+                * First entry: error just happened during normal operation
+                * 
+                * In BUZ_MODE_MOTION_COMPRESS:
+                * 
+                * Possible glitch in TV signal. In this case we should
+                * stop the codec and wait for good quality signal before
+                * restarting it to avoid further problems
+                * 
+                * In BUZ_MODE_MOTION_DECOMPRESS:
+                * 
+                * Bad JPEG frame: we have to mark it as processed (codec crashed
+                * and was not able to do it itself), and to remove it from queue.
+                */
+               btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+               udelay(1);
+               stat =
+                   stat | (post_office_read(zr, 7, 0) & 3) << 8 |
+                   zr36060_read_8(zr, 0x008);
+               btwrite(0, ZR36057_JPC);
+               btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
+               zr36060_reset(zr);
+               zr36060_sleep(zr, 1);
+               zr->JPEG_error = 1;
+               zr->num_errors++;
+               /* Report error */
+#if(DEBUGLEVEL > 1)
+               if (zr->num_errors <= 8) {
+                       long frame;
+                       frame =
+                           zr->jpg_pend[zr->
+                                        jpg_dma_tail & BUZ_MASK_FRAME];
+                       printk(KERN_ERR
+                              "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ",
+                              zr->name, stat, zr->last_isr,
+                              zr->jpg_que_tail, zr->jpg_dma_tail,
+                              zr->jpg_dma_head, zr->jpg_que_head,
+                              zr->jpg_seq_num, frame);
+                       printk("stat_com frames:");
+                       {
+                               int i, j;
+                               for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+                                       for (i = 0; i < zr->jpg_nbufs; i++) {
+                                               if (zr->stat_com[j] ==
+                                                   zr->jpg_gbuf[i].
+                                                   frag_tab_bus) {
+                                                       printk("% d->%d",
+                                                              j, i);
+                                               }
+                                       }
+                               }
+                               printk("\n");
+                       }
+               }
+#endif
+               /* Find an entry in stat_com and rotate contents */
+               {
+                       int i;
+
+                       if (zr->params.TmpDcm == 1)
+                               i = (zr->jpg_dma_tail -
+                                    zr->
+                                    jpg_err_shift) & BUZ_MASK_STAT_COM;
+                       else
+                               i = ((zr->jpg_dma_tail -
+                                     zr->jpg_err_shift) & 1) * 2;
+                       if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+                               /* Mimic zr36067 operation */
+                               zr->stat_com[i] |= 1;
+                               if (zr->params.TmpDcm != 1)
+                                       zr->stat_com[i + 1] |= 1;
+                               /* Refill */
+                               zoran_reap_stat_com(zr);
+                               zoran_feed_stat_com(zr);
+                               wake_up_interruptible(&zr->jpg_capq);
+                               /* Find an entry in stat_com again after refill */
+                               if (zr->params.TmpDcm == 1)
+                                       i = (zr->jpg_dma_tail -
+                                            zr->
+                                            jpg_err_shift) &
+                                           BUZ_MASK_STAT_COM;
+                               else
+                                       i = ((zr->jpg_dma_tail -
+                                             zr->jpg_err_shift) & 1) * 2;
+                       }
+                       if (i) {
+                               /* Rotate stat_comm entries to make current entry first */
+                               int j;
+                               u32 bus_addr[BUZ_NUM_STAT_COM];
+
+                               memcpy(bus_addr, zr->stat_com,
+                                      sizeof(bus_addr));
+                               for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+                                       zr->stat_com[j] =
+                                           bus_addr[(i +
+                                                     j) &
+                                                    BUZ_MASK_STAT_COM];
+                               }
+                               zr->jpg_err_shift += i;
+                               zr->jpg_err_shift &= BUZ_MASK_STAT_COM;
+                       }
+                       if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
+                               zr->jpg_err_seq = zr->jpg_seq_num;      /* + 1; */
+               }
+       }
+       /* Now the stat_comm buffer is ready for restart */
+       {
+               int status;
+
+               status = 0;
+               if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_GET_STATUS, &status);
+               if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS
+                   || (status & DECODER_STATUS_GOOD)) {
+           /********** RESTART code *************/
+                       zr36060_reset(zr);
+                       zr36060_set_cap(zr, zr->codec_mode);
+                       zr36057_set_jpg(zr, zr->codec_mode);
+                       jpeg_start(zr);
+#if(DEBUGLEVEL > 1)
+                       if (zr->num_errors <= 8)
+                               printk(KERN_INFO "%s: Restart\n",
+                                      zr->name);
+#endif
+                       zr->JPEG_missed = 0;
+                       zr->JPEG_error = 2;
+           /********** End RESTART code ***********/
+               }
+       }
+}
+
+static void zoran_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       u32 stat, astat;
+       int count;
+       struct zoran *zr;
+       unsigned long flags;
+
+       zr = (struct zoran *) dev_id;
+       count = 0;
+
+       if (zr->testing) {
+               /* Testing interrupts */
+               spin_lock_irqsave(&zr->lock, flags);
+               while ((stat = count_reset_interrupt(zr))) {
+                       if (count++ > 100) {
+                               btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+                               printk(KERN_ERR
+                                      "%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n",
+                                      zr->name, stat);
+                               wake_up_interruptible(&zr->test_q);
+                       }
+               }
+               zr->last_isr = stat;
+               spin_unlock_irqrestore(&zr->lock, flags);
+               return;
+       }
+
+       spin_lock_irqsave(&zr->lock, flags);
+       while (1) {
+               /* get/clear interrupt status bits */
+               stat = count_reset_interrupt(zr);
+               astat = stat & IRQ_MASK;
+               if (!astat) {
+                       break;
+               }
+               if (astat & cardvsync[zr->card]) {      // SW
+
+                       if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS
+                           || zr->codec_mode ==
+                           BUZ_MODE_MOTION_COMPRESS) {
+                               /* count missed interrupts */
+                               zr->JPEG_missed++;
+                       }
+                       //post_office_read(zr,1,0);
+                       /* Interrupts may still happen when zr->v4l_memgrab_active is switched off.
+                          We simply ignore them */
+
+                       if (zr->v4l_memgrab_active) {
+
+                               /* A lot more checks should be here ... */
+                               if ((btread(ZR36057_VSSFGR) &
+                                    ZR36057_VSSFGR_SnapShot) == 0)
+                                       printk(KERN_WARNING
+                                              "%s: BuzIRQ with SnapShot off ???\n",
+                                              zr->name);
+
+                               if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) {
+                                       /* There is a grab on a frame going on, check if it has finished */
+
+                                       if ((btread(ZR36057_VSSFGR) &
+                                            ZR36057_VSSFGR_FrameGrab) ==
+                                           0) {
+                                               /* it is finished, notify the user */
+
+                                               zr->v4l_gbuf[zr->
+                                                            v4l_grab_frame].
+                                                   state = BUZ_STATE_DONE;
+                                               zr->v4l_grab_frame =
+                                                   NO_GRAB_ACTIVE;
+                                               zr->v4l_grab_seq++;
+                                               zr->v4l_pend_tail++;
+                                       }
+                               }
+
+                               if (zr->v4l_grab_frame == NO_GRAB_ACTIVE)
+                                       wake_up_interruptible(&zr->
+                                                             v4l_capq);
+
+                               /* Check if there is another grab queued */
+
+                               if (zr->v4l_grab_frame == NO_GRAB_ACTIVE
+                                   && zr->v4l_pend_tail !=
+                                   zr->v4l_pend_head) {
+
+                                       int frame =
+                                           zr->v4l_pend[zr->
+                                                        v4l_pend_tail &
+                                                        V4L_MASK_FRAME];
+                                       u32 reg;
+
+                                       zr->v4l_grab_frame = frame;
+
+                                       /* Set zr36057 video front end and enable video */
+
+                                       /* Buffer address */
+
+                                       reg =
+                                           zr->v4l_gbuf[frame].
+                                           fbuffer_bus;
+                                       btwrite(reg, ZR36057_VDTR);
+                                       if (zr->video_interlace)
+                                               reg += zr->gbpl;
+                                       btwrite(reg, ZR36057_VDBR);
+
+                                       /* video stride, status, and frame grab register */
+
+                                       reg = 0;
+                                       if (zr->video_interlace)
+                                               reg += zr->gbpl;
+                                       reg =
+                                           (reg <<
+                                            ZR36057_VSSFGR_DispStride);
+                                       reg |= ZR36057_VSSFGR_VidOvf;
+                                       reg |= ZR36057_VSSFGR_SnapShot;
+                                       reg |= ZR36057_VSSFGR_FrameGrab;
+                                       btwrite(reg, ZR36057_VSSFGR);
+
+                                       btor(ZR36057_VDCR_VidEn,
+                                            ZR36057_VDCR);
+                               }
+                       }
+               }
+#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ)
+               if (astat & ZR36057_ISR_CodRepIRQ) {
+                       zr->intr_counter_CodRepIRQ++;
+                       IDEBUG(printk
+                              (KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n",
+                               zr->name));
+                       btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR);
+               }
+#endif                         /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */
+
+#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ)
+               if (astat & ZR36057_ISR_JPEGRepIRQ) {
+
+                       if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS
+                           || zr->codec_mode ==
+                           BUZ_MODE_MOTION_COMPRESS) {
+#if(DEBUGLEVEL > 1)
+                               if (!zr->frame_num || zr->JPEG_error) {
+                                       printk(KERN_INFO
+                                              "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n",
+                                              zr->name, stat,
+                                              zr->params.odd_even,
+                                              zr->params.field_per_buff,
+                                              zr->JPEG_missed);
+                                       {
+                                               char sc[] = "0000";
+                                               char sv[5];
+                                               int i;
+                                               strcpy(sv, sc);
+                                               for (i = 0; i < 4; i++) {
+                                                       if (zr->
+                                                           stat_com[i] &
+                                                           1)
+                                                               sv[i] =
+                                                                   '1';
+                                               }
+                                               sv[4] = 0;
+                                               printk(KERN_INFO
+                                                      "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
+                                                      zr->name, sv,
+                                                      zr->jpg_que_tail,
+                                                      zr->jpg_dma_tail,
+                                                      zr->jpg_dma_head,
+                                                      zr->jpg_que_head);
+                                       }
+                               } else {
+                                       if (zr->JPEG_missed > zr->JPEG_max_missed)      // Get statistics
+                                               zr->JPEG_max_missed =
+                                                   zr->JPEG_missed;
+                                       if (zr->JPEG_missed <
+                                           zr->JPEG_min_missed)
+                                               zr->JPEG_min_missed =
+                                                   zr->JPEG_missed;
+                               }
+#endif
+#if(DEBUGLEVEL > 2)
+                               if (zr->frame_num < 6) {
+                                       int i;
+                                       printk("%s: seq=%ld stat_com:",
+                                              zr->name, zr->jpg_seq_num);
+                                       for (i = 0; i < 4; i++) {
+                                               printk(" %08x",
+                                                      zr->stat_com[i]);
+                                       }
+                                       printk("\n");
+                               }
+#endif
+                               zr->frame_num++;
+                               zr->JPEG_missed = 0;
+                               zr->JPEG_error = 0;
+                               zoran_reap_stat_com(zr);
+                               zoran_feed_stat_com(zr);
+                               wake_up_interruptible(&zr->jpg_capq);
+                       }       //else {
+                       //      printk(KERN_ERR "%s: JPEG interrupt while not in motion (de)compress mode!\n", zr->name);
+                       //}
+               }
+#endif                         /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */
+
+               if ((astat & cardjpegint[zr->card])     /* DATERR interrupt received                 */
+                   ||zr->JPEG_missed > 25      /* Too many fields missed without processing */
+                   || zr->JPEG_error == 1      /* We are already in error processing        */
+                   || ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
+                       && (zr->
+                           frame_num & (zr->JPEG_missed >
+                                        zr->params.field_per_buff)))
+                   /* fields missed during decompression */
+                   ) {
+                       error_handler(zr, astat, stat);
+               }
+
+               count++;
+               if (count > 10) {
+                       printk(KERN_WARNING "%s: irq loop %d\n", zr->name,
+                              count);
+                       if (count > 20) {
+                               btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+                               printk(KERN_ERR
+                                      "%s: IRQ lockup, cleared int mask\n",
+                                      zr->name);
+                               break;
+                       }
+               }
+               zr->last_isr = stat;
+       }
+       spin_unlock_irqrestore(&zr->lock, flags);
+}
+
+/* Check a zoran_params struct for correctness, insert default params */
+
+static int zoran_check_params(struct zoran *zr,
+                             struct zoran_params *params)
+{
+       int err = 0, err0 = 0;
+
+       /* insert constant params */
+
+       params->major_version = MAJOR_VERSION;
+       params->minor_version = MINOR_VERSION;
+
+       /* Check input and norm: must be set by calling VIDIOCSCHAN only! */
+
+       params->norm = zr->params.norm;
+       params->input = zr->params.input;
+
+       /* Check decimation, set default values for decimation = 1, 2, 4 */
+
+       switch (params->decimation) {
+       case 1:
+
+               params->HorDcm = 1;
+               params->VerDcm = 1;
+               params->TmpDcm = 1;
+               params->field_per_buff = 2;
+
+               params->img_x = 0;
+               params->img_y = 0;
+               params->img_width = zr->timing->Wa;
+               params->img_height = zr->timing->Ha / 2;
+               break;
+
+       case 2:
+
+               params->HorDcm = 2;
+               params->VerDcm = 1;
+               params->TmpDcm = 2;
+               params->field_per_buff = 1;
+
+               params->img_x = 8;
+               params->img_y = 0;
+               params->img_width = zr->timing->Wa;
+               params->img_height = zr->timing->Ha / 2;
+               break;
+
+       case 4:
+
+               params->HorDcm = 4;
+               params->VerDcm = 2;
+               params->TmpDcm = 2;
+               params->field_per_buff = 1;
+
+               params->img_x = 8;
+               params->img_y = 0;
+               params->img_width = zr->timing->Wa;
+               params->img_height = zr->timing->Ha / 2;
+               break;
+
+       case 0:
+
+               /* We have to check the data the user has set */
+
+               if (params->HorDcm != 1 && params->HorDcm != 2
+                   && params->HorDcm != 4)
+                       err0++;
+               if (params->VerDcm != 1 && params->VerDcm != 2)
+                       err0++;
+               if (params->TmpDcm != 1 && params->TmpDcm != 2)
+                       err0++;
+               if (params->field_per_buff != 1
+                   && params->field_per_buff != 2)
+                       err0++;
+
+               if (params->img_x < 0)
+                       err0++;
+               if (params->img_y < 0)
+                       err0++;
+               if (params->img_width < 0)
+                       err0++;
+               if (params->img_height < 0)
+                       err0++;
+               if (params->img_x + params->img_width > zr->timing->Wa)
+                       err0++;
+               if (params->img_y + params->img_height >
+                   zr->timing->Ha / 2)
+                       err0++;
+               if (params->HorDcm) {
+                       if (params->img_width % (16 * params->HorDcm) != 0)
+                               err0++;
+                       if (params->img_height % (8 * params->VerDcm) != 0)
+                               err0++;
+               }
+
+               if (err0) {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: SET PARAMS: error in params for decimation = 0\n",
+                              zr->name));
+                       err++;
+               }
+               break;
+
+       default:
+               DEBUG1(printk(KERN_ERR
+                      "%s: SET PARAMS: decimation = %d, must be 0, 1, 2 or 4\n",
+                      zr->name, params->decimation));
+               err++;
+               break;
+       }
+
+       if (params->quality > 100)
+               params->quality = 100;
+       if (params->quality < 5)
+               params->quality = 5;
+
+       if (params->APPn < 0)
+               params->APPn = 0;
+       if (params->APPn > 15)
+               params->APPn = 15;
+       if (params->APP_len < 0)
+               params->APP_len = 0;
+       if (params->APP_len > 60)
+               params->APP_len = 60;
+       if (params->COM_len < 0)
+               params->COM_len = 0;
+       if (params->COM_len > 60)
+               params->COM_len = 60;
+
+       if (err)
+               return -EINVAL;
+
+       return 0;
+
+}
+static void zoran_open_init_params(struct zoran *zr)
+{
+       int i;
+
+       /* Per default, map the V4L Buffers */
+
+       zr->map_mjpeg_buffers = 0;
+
+       /* User must explicitly set a window */
+
+       zr->window_set = 0;
+
+       zr->window.x = 0;
+       zr->window.y = 0;
+       zr->window.width = 0;
+       zr->window.height = 0;
+       zr->window.chromakey = 0;
+       zr->window.flags = 0;
+       zr->window.clips = NULL;
+       zr->window.clipcount = 0;
+
+       zr->video_interlace = 0;
+
+       zr->v4l_memgrab_active = 0;
+       zr->v4l_overlay_active = 0;
+
+       zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+       zr->v4l_grab_seq = 0;
+
+       zr->gwidth = 0;
+       zr->gheight = 0;
+       zr->gformat = 0;
+       zr->gbpl = 0;
+
+       /* DMA ring stuff for V4L */
+
+       zr->v4l_pend_tail = 0;
+       zr->v4l_pend_head = 0;
+       for (i = 0; i < v4l_nbufs; i++) {
+               zr->v4l_gbuf[i].state = BUZ_STATE_USER; /* nothing going on */
+       }
+
+       /* Set necessary params and call zoran_check_params to set the defaults */
+
+       zr->params.decimation = 1;
+
+       zr->params.quality = 50;        /* default compression factor 8 */
+       if (zr->card != BUZ)
+               zr->params.odd_even = 1;
+       else
+               zr->params.odd_even = 0;
+
+       zr->params.APPn = 0;
+       zr->params.APP_len = 0; /* No APPn marker */
+       for (i = 0; i < 60; i++)
+               zr->params.APP_data[i] = 0;
+
+       zr->params.COM_len = 0; /* No COM marker */
+       for (i = 0; i < 60; i++)
+               zr->params.COM_data[i] = 0;
+
+       zr->params.VFIFO_FB = 0;
+
+       memset(zr->params.reserved, 0, sizeof(zr->params.reserved));
+
+       zr->params.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT;
+
+       i = zoran_check_params(zr, &zr->params);
+       if (i)
+               printk(KERN_ERR
+                      "%s: zoran_open_init_params internal error\n",
+                      zr->name);
+
+       clear_interrupt_counters(zr);
+       zr->testing = 0;
+}
+
+/*
+ *   Open a zoran card. Right now the flags stuff is just playing
+ */
+
+static int zoran_open(struct video_device *dev, int flags)
+{
+       struct zoran *zr = (struct zoran *) dev;
+       //int one = 1;
+
+       DEBUG1(printk
+              (KERN_INFO "%s: zoran_open, %s pid=[%d]\n", zr->name,
+               current->comm, current->pid));
+
+       switch (flags) {
+
+       case 0:
+               if (zr->user > 1) {
+                       DEBUG1(printk(KERN_WARNING
+                              "%s: zoran_open: Buz is allready in use\n",
+                              zr->name));
+                       return -EBUSY;
+               }
+               zr->user++;
+
+               if (zr->user == 1 && v4l_fbuffer_alloc(zr) < 0) {
+                       zr->user--;
+                       printk(KERN_ERR
+                              "%s: zoran_open: v4l_fbuffer_alloc failed\n",
+                              zr->name);
+                       return -ENOMEM;
+               }
+
+               /* default setup */
+
+               if (zr->user == 1) {    /* First device open */
+                       zoran_open_init_params(zr);
+
+                       zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+
+                       btwrite(IRQ_MASK, ZR36057_ISR); // Clears interrupts
+                       btor(ZR36057_ICR_IntPinEn, ZR36057_ICR);
+                       dev->busy = 0;  /* Allow second open */
+               }
+
+               break;
+
+       default:
+               DEBUG1(printk(KERN_WARNING
+                      "%s: zoran_open: flags = 0x%x not yet supported\n",
+                      zr->name, flags));
+               return -EBUSY;
+               break;
+
+       }
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static void zoran_close(struct video_device *dev)
+{
+       struct zoran *zr = (struct zoran *) dev;
+       int zero = 0, two = 2;
+
+       DEBUG1(printk
+              (KERN_INFO "%s: zoran_close, %s pid=[%d]\n", zr->name,
+               current->comm, current->pid));
+       /* Clean up JPEG process */
+
+       wake_up_interruptible(&zr->jpg_capq);
+       zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+       jpg_fbuffer_free(zr);
+       zr->jpg_nbufs = 0;
+
+       if (zr->user == 1) {    /* Last process */
+               /* disable interrupts */
+               btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+
+#if(DEBUGLEVEL > 1)
+               print_interrupts(zr);
+#endif
+               /* Overlay off */
+               wake_up_interruptible(&zr->v4l_capq);
+               zr36057_set_memgrab(zr, 0);
+               if (zr->v4l_overlay_active)
+                       zr36057_overlay(zr, 0);
+               v4l_fbuffer_free(zr);
+
+               if (!pass_through) {    /* Switch to color bar */
+                       set_videobus_enable(zr, 0);
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_ENABLE_OUTPUT, &zero);
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEOENCODER,
+                                          ENCODER_SET_INPUT, &two);
+                       set_videobus_enable(zr, 1);
+               }
+       }
+
+       zr->user--;
+
+       MOD_DEC_USE_COUNT;
+       DEBUG2(printk(KERN_INFO "%s: zoran_close done\n", zr->name));
+}
+
+
+static long zoran_read(struct video_device *dev, char *buf,
+                      unsigned long count, int nonblock)
+{
+       return -EINVAL;
+}
+
+static long zoran_write(struct video_device *dev, const char *buf,
+                       unsigned long count, int nonblock)
+{
+       return -EINVAL;
+}
+
+/*
+ *   ioctl routine
+ */
+
+static int do_zoran_ioctl(struct zoran *zr, unsigned int cmd,
+                      void *arg)
+{
+       switch (cmd) {
+
+       case VIDIOCGCAP:
+               {
+                       struct video_capability b;
+                       DEBUG2(printk("%s: ioctl VIDIOCGCAP\n", zr->name));
+
+                       strncpy(b.name, zr->video_dev.name,
+                               sizeof(b.name));
+                       b.type =
+                           VID_TYPE_CAPTURE | VID_TYPE_OVERLAY |
+                           VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM |
+                           VID_TYPE_SCALES;
+                       /* theoretically we could also flag VID_TYPE_SUBCAPTURE
+                          but this is not even implemented in the BTTV driver */
+
+                       if (zr->card == DC10 || zr->card == DC10plus) {
+                               b.channels = 3; /* composite, svhs, internal */
+                       } else {
+                               b.channels = 2; /* composite, svhs */
+                       }
+                       b.audios = 0;
+                       b.maxwidth = BUZ_MAX_WIDTH;
+                       b.maxheight = BUZ_MAX_HEIGHT;
+                       b.minwidth = BUZ_MIN_WIDTH;
+                       b.minheight = BUZ_MIN_HEIGHT;
+                       if (copy_to_user(arg, &b, sizeof(b))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+       case VIDIOCGCHAN:
+               {
+                       struct video_channel v;
+
+                       if (copy_from_user(&v, arg, sizeof(v))) {
+                               return -EFAULT;
+                       }
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCGCHAN for channel %d\n",
+                               zr->name, v.channel));
+                       switch (v.channel) {
+                       case 0:
+                               strcpy(v.name, "Composite");
+                               break;
+                       case 1:
+                               strcpy(v.name, "SVHS");
+                               break;
+                       case 2:
+                               if (zr->card == DC10
+                                   || zr->card == DC10plus) {
+                                       strcpy(v.name, "Internal/comp");
+                                       break;
+                               }
+                       default:
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: VIDIOCGCHAN on not existing channel %d\n",
+                                      zr->name, v.channel));
+                               return -EINVAL;
+                       }
+                       v.tuners = 0;
+                       v.flags = 0;
+                       v.type = VIDEO_TYPE_CAMERA;
+                       v.norm = zr->params.norm;
+                       if (copy_to_user(arg, &v, sizeof(v))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+               /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says:
+
+                * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input."
+                *                                 ^^^^^^^
+                * The famos BTTV driver has it implemented with a struct video_channel argument
+                * and we follow it for compatibility reasons
+                *
+                * BTW: this is the only way the user can set the norm!
+                */
+
+       case VIDIOCSCHAN:
+               {
+                       struct video_channel v;
+                       int input;
+                       int on, res;
+                       int encoder_norm;
+
+                       if (copy_from_user(&v, arg, sizeof(v))) {
+                               return -EFAULT;
+                       }
+
+                       if (zr->codec_mode != BUZ_MODE_IDLE) {
+                               if (v.norm != zr->params.norm
+                                   || v.channel != zr->params.input) {
+                                       DEBUG1(printk(KERN_ERR
+                                              "%s: VIDIOCSCHAN called while the card in capture/playback mode\n",
+                                              zr->name));
+                                       return -EINVAL;
+                               } else {
+                                       DEBUG1(printk(BUZ_WARNING
+                                              "%s: Warning: VIDIOCSCHAN called while the card in capture/playback mode\n",
+                                              zr->name));
+                               }
+                       }
+
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCSCHAN: channel=%d, norm=%d\n",
+                               zr->name, v.channel, v.norm));
+                       switch (v.channel) {
+                       case 0:
+                               if (zr->card == BUZ)
+                                       input = 3;
+                               else
+                                       input = 0;
+                               break;
+                       case 1:
+                               input = 7;
+                               break;
+                       case 2:
+                               if (zr->card == DC10
+                                   || zr->card == DC10plus) {
+                                       input = 5;
+                                       break;
+                               }
+                       default:
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: VIDIOCSCHAN on not existing channel %d\n",
+                                      zr->name, v.channel));
+                               return -EINVAL;
+                               break;
+                       }
+
+                       if (lock_norm && v.norm != zr->params.norm) {
+                               if (lock_norm > 1) {
+                                       DEBUG1(printk(KERN_WARNING
+                                              "%s: VIDIOCSCHAN: TV standard is locked, can not switch norm.\n",
+                                              zr->name));
+                                       return -EINVAL;
+                               } else {
+                                       DEBUG1(printk(KERN_WARNING
+                                              "%s: VIDIOCSCHAN: TV standard is locked, norm was not changed.\n",
+                                              zr->name));
+                                       v.norm = zr->params.norm;
+                               }
+                       }
+                       
+                       if(v.norm >= 2)
+                               return -EINVAL;
+
+                       if (!cardnorms[zr->card][v.norm]) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: VIDIOCSCHAN with not supported norm %d\n",
+                                      zr->name, v.norm));
+                               return -EOPNOTSUPP;
+                               break;
+                       }
+                       encoder_norm = v.norm;
+
+                       zr->params.norm = v.norm;
+                       zr->params.input = v.channel;
+                       zr->timing = cardnorms[zr->card][zr->params.norm];
+
+                       /* We switch overlay off and on since a change in the norm
+                          needs different VFE settings */
+
+                       on = zr->v4l_overlay_active
+                           && !zr->v4l_memgrab_active;
+                       if (on)
+                               zr36057_overlay(zr, 0);
+
+                       set_videobus_enable(zr, 0);
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_SET_INPUT, &input);
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_SET_NORM,
+                                          &zr->params.norm);
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEOENCODER,
+                                          ENCODER_SET_NORM,
+                                          &encoder_norm);
+                       set_videobus_enable(zr, 1);
+
+                       if (on)
+                               zr36057_overlay(zr, 1);
+
+                       /* Make sure the changes come into effect */
+                       res = wait_grab_pending(zr);
+                       if (res)
+                               return res;
+
+                       return 0;
+               }
+               break;
+
+       case VIDIOCGTUNER:
+               {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: ioctl VIDIOCGTUNER not supported\n",
+                              zr->name));
+                       return -EINVAL;
+               }
+               break;
+
+       case VIDIOCSTUNER:
+               {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: ioctl VIDIOCSTUNER not supported\n",
+                              zr->name));
+                       return -EINVAL;
+               }
+               break;
+
+       case VIDIOCGPICT:
+               {
+                       struct video_picture p = zr->picture;
+
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCGPICT\n", zr->name));
+                       p.depth = zr->buffer.depth;
+                       switch (zr->buffer.depth) {
+                       case 15:
+                               p.palette = VIDEO_PALETTE_RGB555;
+                               break;
+
+                       case 16:
+                               p.palette = VIDEO_PALETTE_RGB565;
+                               break;
+
+                       case 24:
+                               p.palette = VIDEO_PALETTE_RGB24;
+                               break;
+
+                       case 32:
+                               p.palette = VIDEO_PALETTE_RGB32;
+                               break;
+                       }
+
+                       if (copy_to_user(arg, &p, sizeof(p))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+       case VIDIOCSPICT:
+               {
+                       struct video_picture p;
+
+                       if (copy_from_user(&p, arg, sizeof(p))) {
+                               return -EFAULT;
+                       }
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_SET_PICTURE, &p);
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCSPICT bri=%d hue=%d col=%d con=%d dep=%d pal=%d\n",
+                               zr->name, p.brightness, p.hue, p.colour,
+                               p.contrast, p.depth, p.palette));
+                       /* The depth and palette values have no meaning to us,
+                          should we return  -EINVAL if they don't fit ? */
+                       zr->picture = p;
+                       return 0;
+               }
+               break;
+
+       case VIDIOCCAPTURE:
+               {
+                       int v, res;
+
+                       if (copy_from_user(&v, arg, sizeof(v))) {
+                               return -EFAULT;
+                       }
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCCAPTURE: %d\n", zr->name,
+                               v));
+
+                       /* If there is nothing to do, return immediatly */
+
+                       if ((v && zr->v4l_overlay_active)
+                           || (!v && !zr->v4l_overlay_active))
+                               return 0;
+
+                       if (v == 0) {
+                               zr->v4l_overlay_active = 0;
+                               if (!zr->v4l_memgrab_active)
+                                       zr36057_overlay(zr, 0);
+                               /* When a grab is running, the video simply won't be switched on any more */
+                       } else {
+                               if (!zr->buffer_set || !zr->window_set) {
+                                       DEBUG1(printk(KERN_ERR
+                                              "%s: VIDIOCCAPTURE: buffer or window not set\n",
+                                              zr->name));
+                                       return -EINVAL;
+                               }
+                               zr->v4l_overlay_active = 1;
+                               if (!zr->v4l_memgrab_active)
+                                       zr36057_overlay(zr, 1);
+                               /* When a grab is running, the video will be switched on when grab is finished */
+                       }
+                       /* Make sure the changes come into effect */
+                       res = wait_grab_pending(zr);
+                       if (res)
+                               return res;
+                       return 0;
+               }
+               break;
+
+       case VIDIOCGWIN:
+               {
+                       DEBUG2(printk("%s: ioctl VIDIOCGWIN\n", zr->name));
+                       if (copy_to_user
+                           (arg, &zr->window, sizeof(zr->window))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+       case VIDIOCSWIN:
+               {
+                       struct video_clip *vcp;
+                       struct video_window vw;
+                       struct tvnorm *tvn;
+                       int on, end, res, Wa, Ha;
+
+                       tvn = zr->timing;
+
+                       Wa = tvn->Wa;
+                       Ha = tvn->Ha;
+
+                       if (copy_from_user(&vw, arg, sizeof(vw))) {
+                               return -EFAULT;
+                       }
+
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCSWIN: x=%d y=%d w=%d h=%d clipcount=%d\n",
+                               zr->name, vw.x, vw.y, vw.width, vw.height,
+                               vw.clipcount));
+
+                       if (!zr->buffer_set) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: VIDIOCSWIN: frame buffer has to be set first\n",
+                                      zr->name));
+                               return -EINVAL;
+                       }
+
+                       /*
+                        * The video front end needs 4-byte alinged line sizes, we correct that
+                        * silently here if necessary
+                        */
+
+                       if (zr->buffer.depth == 15
+                           || zr->buffer.depth == 16) {
+                               end = (vw.x + vw.width) & ~1;   /* round down */
+                               vw.x = (vw.x + 1) & ~1; /* round up */
+                               vw.width = end - vw.x;
+                       }
+
+                       if (zr->buffer.depth == 24) {
+                               end = (vw.x + vw.width) & ~3;   /* round down */
+                               vw.x = (vw.x + 3) & ~3; /* round up */
+                               vw.width = end - vw.x;
+                       }
+
+                       if (vw.width > Wa)
+                               vw.width = Wa;
+                       if (vw.height > Ha)
+                               vw.height = Ha;
+
+                       /* Check for vaild parameters */
+                       if (vw.width < BUZ_MIN_WIDTH
+                           || vw.height < BUZ_MIN_HEIGHT
+                           || vw.width > BUZ_MAX_WIDTH
+                           || vw.height > BUZ_MAX_HEIGHT) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: VIDIOCSWIN: width = %d or height = %d invalid\n",
+                                      zr->name, vw.width, vw.height));
+                               return -EINVAL;
+                       }
+
+                       zr->window.x = vw.x;
+                       zr->window.y = vw.y;
+                       zr->window.width = vw.width;
+                       zr->window.height = vw.height;
+                       zr->window.chromakey = 0;
+                       zr->window.flags = 0;   // RJ: Is this intended for interlace on/off ?
+                       zr->window.clips = NULL;
+                       zr->window.clipcount = vw.clipcount;
+
+                       /*
+                        * If an overlay is running, we have to switch it off
+                        * and switch it on again in order to get the new settings in effect.
+                        *
+                        * We also want to avoid that the overlay mask is written
+                        * when an overlay is running.
+                        */
+
+                       on = zr->v4l_overlay_active
+                           && !zr->v4l_memgrab_active;
+                       if (on)
+                               zr36057_overlay(zr, 0);
+
+                       /*
+                        *   Write the overlay mask if clips are wanted.
+                        */
+                       if (vw.clipcount) {
+                               vcp =
+                                   vmalloc(sizeof(struct video_clip) *
+                                           (vw.clipcount + 4));
+                               if (vcp == NULL) {
+                                       printk(KERN_ERR
+                                              "%s: zoran_ioctl: Alloc of clip mask failed\n",
+                                              zr->name);
+                                       return -ENOMEM;
+                               }
+                               if (copy_from_user
+                                   (vcp, vw.clips,
+                                    sizeof(struct video_clip) *
+                                    vw.clipcount)) {
+                                       vfree(vcp);
+                                       return -EFAULT;
+                               }
+                               write_overlay_mask(zr, vcp, vw.clipcount);
+                               vfree(vcp);
+                       }
+
+                       if (on)
+                               zr36057_overlay(zr, 1);
+                       zr->window_set = 1;
+
+                       /* Make sure the changes come into effect */
+                       res = wait_grab_pending(zr);
+                       if (res)
+                               return res;
+
+                       return 0;
+               }
+               break;
+
+       case VIDIOCGFBUF:
+               {
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCGFBUF\n", zr->name));
+                       if (copy_to_user
+                           (arg, &zr->buffer, sizeof(zr->buffer))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+       case VIDIOCSFBUF:
+               {
+                       struct video_buffer v;
+
+                       /* RJ: Isn't this too restrictive? As long as the user doesn't set
+                          the base address it shouldn't be too dangerous */
+
+                       if (!capable(CAP_SYS_ADMIN)) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: Only the superuser may issue VIDIOCSFBUF ioctl\n",
+                                      zr->name));
+                               return -EPERM;
+                       }
+                       if (copy_from_user(&v, arg, sizeof(v))) {
+                               return -EFAULT;
+                       }
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCSFBUF: base=0x%x w=%d h=%d depth=%d bpl=%d\n",
+                               zr->name, (u32) v.base, v.width, v.height,
+                               v.depth, v.bytesperline));
+                       if (zr->v4l_overlay_active) {
+                               /* Has the user gotten crazy ... ? */
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: VIDIOCSFBUF not allowed when overlay active\n",
+                                      zr->name));
+                               return -EINVAL;
+                       }
+                       if (v.depth != 15 && v.depth != 16 && v.depth != 24
+                           && v.depth != 32) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: VIDIOCSFBUF: depth=%d not supported\n",
+                                      zr->name, v.depth));
+                               return -EINVAL;
+                       }
+                       if (v.height <= 0 || v.width <= 0
+                           || v.bytesperline <= 0) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: VIDIOCSFBUF: invalid height/width/bpl value\n",
+                                      zr->name));
+                               return -EINVAL;
+                       }
+                       if (v.bytesperline & 3) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: VIDIOCSFBUF: bytesperline must be 4-byte aligned\n",
+                                      zr->name));
+                               return -EINVAL;
+                       }
+                       if (v.base) {
+                               zr->buffer.base =
+                                   (void *) ((unsigned long) v.base & ~3);
+                       }
+                       zr->buffer.height = v.height;
+                       zr->buffer.width = v.width;
+                       zr->buffer.depth = v.depth;
+                       zr->buffer.bytesperline = v.bytesperline;
+
+                       if (zr->buffer.base)
+                               zr->buffer_set = 1;
+                       zr->window_set = 0;     /* The user should set new window parameters */
+                       return 0;
+               }
+               break;
+
+               /* RJ: what is VIDIOCKEY intended to do ??? */
+
+       case VIDIOCKEY:
+               {
+                       /* Will be handled higher up .. */
+                       DEBUG2(printk("%s: ioctl VIDIOCKEY\n", zr->name));
+                       return 0;
+               }
+               break;
+
+       case VIDIOCGFREQ:
+               {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: ioctl VIDIOCGFREQ not supported\n",
+                              zr->name));
+                       return -EINVAL;
+               }
+               break;
+
+       case VIDIOCSFREQ:
+               {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: ioctl VIDIOCSFREQ not supported\n",
+                              zr->name));
+                       return -EINVAL;
+               }
+               break;
+
+       case VIDIOCGAUDIO:
+               {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: ioctl VIDIOCGAUDIO not supported\n",
+                              zr->name));
+                       return -EINVAL;
+               }
+               break;
+
+       case VIDIOCSAUDIO:
+               {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: ioctl VIDIOCSAUDIO not supported\n",
+                              zr->name));
+                       return -EINVAL;
+               }
+               break;
+
+       case VIDIOCSYNC:
+               {
+                       int v;
+
+                       if (copy_from_user(&v, arg, sizeof(v))) {
+                               return -EFAULT;
+                       }
+                       DEBUG3(printk
+                              ("%s: ioctl VIDIOCSYNC %d\n", zr->name, v));
+                       return v4l_sync(zr, v);
+               }
+               break;
+
+       case VIDIOCMCAPTURE:
+               {
+                       struct video_mmap vm;
+
+                       if (copy_from_user
+                           ((void *) &vm, (void *) arg, sizeof(vm))) {
+                               return -EFAULT;
+                       }
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCMCAPTURE frame=%d geom=%dx%d fmt=%d\n",
+                               zr->name, vm.frame, vm.width, vm.height,
+                               vm.format));
+                       return v4l_grab(zr, &vm);
+               }
+               break;
+
+       case VIDIOCGMBUF:
+               {
+                       struct video_mbuf vm;
+                       int i;
+
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCGMBUF\n", zr->name));
+                       vm.size = v4l_nbufs * v4l_bufsize;
+                       vm.frames = v4l_nbufs;
+                       for (i = 0; i < v4l_nbufs; i++) {
+                               vm.offsets[i] = i * v4l_bufsize;
+                       }
+
+                       /* The next mmap will map the V4L buffers */
+                       zr->map_mjpeg_buffers = 0;
+
+                       if (copy_to_user(arg, &vm, sizeof(vm))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+       case VIDIOCGUNIT:
+               {
+                       struct video_unit vu;
+
+                       DEBUG2(printk
+                              ("%s: ioctl VIDIOCGUNIT\n", zr->name));
+                       vu.video = zr->video_dev.minor;
+                       vu.vbi = VIDEO_NO_UNIT;
+                       vu.radio = VIDEO_NO_UNIT;
+                       vu.audio = VIDEO_NO_UNIT;
+                       vu.teletext = VIDEO_NO_UNIT;
+                       if (copy_to_user(arg, &vu, sizeof(vu))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+               /*
+                * RJ: In principal we could support subcaptures for V4L grabbing.
+                *     Not even the famous BTTV driver has them, however.
+                *     If there should be a strong demand, one could consider
+                *     to implement them.
+                */
+       case VIDIOCGCAPTURE:
+               {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: ioctl VIDIOCGCAPTURE not supported\n",
+                              zr->name));
+                       return -EINVAL;
+               }
+               break;
+
+       case VIDIOCSCAPTURE:
+               {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: ioctl VIDIOCSCAPTURE not supported\n",
+                              zr->name));
+                       return -EINVAL;
+               }
+               break;
+
+       case BUZIOC_G_PARAMS:
+               {
+                       DEBUG2(printk
+                              ("%s: ioctl BUZIOC_G_PARAMS\n", zr->name));
+
+                       if (copy_to_user
+                           (arg, &(zr->params), sizeof(zr->params))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+       case BUZIOC_S_PARAMS:
+               {
+                       struct zoran_params bp;
+                       /* int input, on; */
+
+                       if (zr->codec_mode != BUZ_MODE_IDLE) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: BUZIOC_S_PARAMS called but Buz in capture/playback mode\n",
+                                      zr->name));
+                               return -EINVAL;
+                       }
+
+                       if (copy_from_user(&bp, arg, sizeof(bp))) {
+                               return -EFAULT;
+                       }
+                       DEBUG2(printk
+                              ("%s: ioctl BUZIOC_S_PARAMS\n", zr->name));
+
+                       /* Check the params first before overwriting our internal values */
+
+                       if (zoran_check_params(zr, &bp))
+                               return -EINVAL;
+
+                       zr->params = bp;
+
+                       /* Make changes of input and norm go into effect immediatly */
+
+                       /* We switch overlay off and on since a change in the norm
+                          needs different VFE settings */
+
+                       if (copy_to_user(arg, &bp, sizeof(bp))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+       case BUZIOC_REQBUFS:
+               {
+                       struct zoran_requestbuffers br;
+
+                       if (zr->jpg_buffers_allocated) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: BUZIOC_REQBUFS: buffers allready allocated\n",
+                                      zr->name));
+                               return -EINVAL;
+                       }
+                       if (copy_from_user(&br, arg, sizeof(br))) {
+                               return -EFAULT;
+                       }
+                       DEBUG2(printk
+                              ("%s: ioctl BUZIOC_REQBUFS count = %lu size=%lu\n",
+                               zr->name, br.count, br.size));
+
+                       /* Enforce reasonable lower and upper limits */
+                       if (br.count < 4)
+                               br.count = 4;   /* Could be choosen smaller */
+                       if (br.count > BUZ_MAX_FRAME)
+                               br.count = BUZ_MAX_FRAME;
+                       br.size = PAGE_ALIGN(br.size);
+                       if (br.size < 8192)
+                               br.size = 8192; /* Arbitrary */
+                       /* br.size is limited by 1 page for the stat_com tables to a Maximum of 2 MB */
+                       if (br.size > (512 * 1024))
+                               br.size = (512 * 1024); /* 512 K should be enough */
+                       if (zr->need_contiguous
+                           && br.size > MAX_KMALLOC_MEM)
+                               br.size = MAX_KMALLOC_MEM;
+
+                       zr->jpg_nbufs = br.count;
+                       zr->jpg_bufsize = br.size;
+
+                       if (jpg_fbuffer_alloc(zr))
+                               return -ENOMEM;
+
+                       /* The next mmap will map the MJPEG buffers */
+                       zr->map_mjpeg_buffers = 1;
+
+                       if (copy_to_user(arg, &br, sizeof(br))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+       case BUZIOC_QBUF_CAPT:
+               {
+                       int nb;
+
+                       if (copy_from_user
+                           ((void *) &nb, (void *) arg, sizeof(int))) {
+                               return -EFAULT;
+                       }
+                       DEBUG4(printk
+                              ("%s: ioctl BUZIOC_QBUF_CAPT %d\n",
+                               zr->name, nb));
+                       return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_COMPRESS);
+               }
+               break;
+
+       case BUZIOC_QBUF_PLAY:
+               {
+                       int nb;
+
+                       if (copy_from_user
+                           ((void *) &nb, (void *) arg, sizeof(int))) {
+                               return -EFAULT;
+                       }
+                       DEBUG4(printk
+                              ("%s: ioctl BUZIOC_QBUF_PLAY %d\n",
+                               zr->name, nb));
+                       return jpg_qbuf(zr, nb,
+                                       BUZ_MODE_MOTION_DECOMPRESS);
+               }
+               break;
+
+       case BUZIOC_SYNC:
+               {
+                       struct zoran_sync bs;
+                       int res;
+
+                       DEBUG4(printk
+                              ("%s: ioctl BUZIOC_SYNC\n", zr->name));
+                       res = jpg_sync(zr, &bs);
+                       if (copy_to_user(arg, &bs, sizeof(bs))) {
+                               return -EFAULT;
+                       }
+                       return res;
+               }
+               break;
+
+       case BUZIOC_G_STATUS:
+               {
+                       struct zoran_status bs;
+                       int norm, input, status;
+                       unsigned long timeout;
+
+                       if (zr->codec_mode != BUZ_MODE_IDLE) {
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: BUZIOC_G_STATUS called but Buz in capture/playback mode\n",
+                                      zr->name));
+                               return -EINVAL;
+                       }
+
+                       if (copy_from_user(&bs, arg, sizeof(bs))) {
+                               return -EFAULT;
+                       }
+                       DEBUG2(printk
+                              ("%s: ioctl BUZIOC_G_STATUS\n", zr->name));
+
+                       switch (bs.input) {
+                       case 0:
+                               if (zr->card == BUZ)
+                                       input = 3;
+                               else
+                                       input = 0;
+                               break;
+                       case 1:
+                               input = 7;
+                               break;
+                       default:
+                               DEBUG1(printk(KERN_ERR
+                                      "%s: BUZIOC_G_STATUS on not existing input %d\n",
+                                      zr->name, bs.input));
+                               return -EINVAL;
+                       }
+
+                       /* Set video norm to VIDEO_MODE_AUTO */
+
+                       norm = VIDEO_MODE_AUTO;
+                       set_videobus_enable(zr, 0);
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_SET_INPUT, &input);
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_SET_NORM, &norm);
+                       set_videobus_enable(zr, 1);
+
+                       /* sleep 1 second */
+
+                       timeout = jiffies + 1 * HZ;
+                       while (jiffies < timeout)
+                               schedule();
+
+                       /* Get status of video decoder */
+
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_GET_STATUS, &status);
+                       bs.signal = (status & DECODER_STATUS_GOOD) ? 1 : 0;
+
+                       if (status & DECODER_STATUS_NTSC)
+                               bs.norm = VIDEO_MODE_NTSC;
+                       else if (status & DECODER_STATUS_SECAM)
+                               bs.norm = VIDEO_MODE_SECAM;
+                       else
+                               bs.norm = VIDEO_MODE_PAL;
+
+                       bs.color = (status & DECODER_STATUS_COLOR) ? 1 : 0;
+
+                       /* restore previous input and norm */
+                       if (zr->card == BUZ)
+                               input = zr->params.input == 0 ? 3 : 7;
+                       else
+                               input = zr->params.input == 0 ? 0 : 7;
+                       set_videobus_enable(zr, 0);
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_SET_INPUT, &input);
+                       i2c_control_device(&zr->i2c,
+                                          I2C_DRIVERID_VIDEODECODER,
+                                          DECODER_SET_NORM,
+                                          &zr->params.norm);
+                       set_videobus_enable(zr, 1);
+
+                       if (copy_to_user(arg, &bs, sizeof(bs))) {
+                               return -EFAULT;
+                       }
+                       return 0;
+               }
+               break;
+
+       default:
+               DEBUG1(printk
+                      ("%s: UNKNOWN ioctl cmd: 0x%x\n", zr->name, cmd));
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+static int zoran_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+       struct zoran *zr = (struct zoran *) dev;
+       int err;
+       
+       down(&zr->sem);
+       err = do_zoran_ioctl(zr, cmd, arg);
+       up(&zr->sem);
+       
+       return err;
+}
+
+/*
+ *   This maps the buffers to user space.
+ *
+ *   Depending on the state of zr->map_mjpeg_buffers
+ *   the V4L or the MJPEG buffers are mapped
+ *
+ */
+
+static int do_zoran_mmap(struct zoran *zr, const char *adr,
+                     unsigned long size)
+{
+       unsigned long start = (unsigned long) adr;
+       unsigned long page, pos, todo, fraglen;
+       int i, j;
+
+       DEBUG2(printk
+              (KERN_INFO "%s: mmap at 0x%08lx, size %lu\n", zr->name,
+               start, size));
+       if (zr->map_mjpeg_buffers) {
+               /* Map the MJPEG buffers */
+
+               if (!zr->jpg_buffers_allocated) {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: zoran_mmap(MJPEG): buffers not yet allocated\n",
+                              zr->name));
+                       return -ENOMEM;
+               }
+
+               if (size > zr->jpg_nbufs * zr->jpg_bufsize) {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: zoran_mmap(MJPEG): Max size is %lu - you wanted %lu\n",
+                              zr->name, zr->jpg_nbufs * zr->jpg_bufsize,
+                              size));
+                       return -EINVAL;
+               }
+
+               if (size != zr->jpg_nbufs * zr->jpg_bufsize)
+                       DEBUG1(printk(KERN_WARNING
+                              "%s: zoran_mmap(MJPEG): Expected %lu - you wanted %lu\n",
+                              zr->name, zr->jpg_nbufs * zr->jpg_bufsize,
+                              size));
+
+               for (i = 0; i < zr->jpg_nbufs; i++) {
+                       for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) {
+                               fraglen =
+                                   (zr->jpg_gbuf[i].
+                                    frag_tab[2 * j + 1] & ~1) << 1;
+                               todo = size;
+                               if (todo > fraglen)
+                                       todo = fraglen;
+                               pos =
+                                   (unsigned long) zr->jpg_gbuf[i].
+                                   frag_tab[2 * j];
+                               page = virt_to_phys(bus_to_virt(pos));  /* should just be pos on i386 */
+                               if (remap_page_range
+                                   (start, page, todo, PAGE_SHARED)) {
+                                       printk(KERN_ERR
+                                              "%s: zoran_mmap(V4L): remap_page_range failed\n",
+                                              zr->name);
+                                       return -EAGAIN;
+                               }
+                               size -= todo;
+                               start += todo;
+                               if (size == 0)
+                                       break;
+                               if (zr->jpg_gbuf[i].
+                                   frag_tab[2 * j + 1] & 1)
+                                       break;  /* was last fragment */
+                       }
+                       if (size == 0)
+                               break;
+               }
+       } else {
+               /* Map the V4L buffers */
+
+               if (size > v4l_nbufs * v4l_bufsize) {
+                       DEBUG1(printk(KERN_ERR
+                              "%s: zoran_mmap(V4L): Max size is %d - you wanted %ld\n",
+                              zr->name, v4l_nbufs * v4l_bufsize, size));
+                       return -EINVAL;
+               }
+
+               if (size != v4l_nbufs * v4l_bufsize)
+                       DEBUG1(printk(KERN_WARNING
+                              "%s: zoran_mmap(V4L): Expected %d - you wanted %ld\n",
+                              zr->name, v4l_nbufs * v4l_bufsize, size));
+
+               for (i = 0; i < v4l_nbufs; i++) {
+                       todo = size;
+                       if (todo > v4l_bufsize)
+                               todo = v4l_bufsize;
+                       page = zr->v4l_gbuf[i].fbuffer_phys;
+                       DEBUG2(printk
+                              ("V4L remap page range %d 0x%lx %ld to 0x%lx\n",
+                               i, page, todo, start));
+                       if (remap_page_range
+                           (start, page, todo, PAGE_SHARED)) {
+                               printk(KERN_ERR
+                                      "%s: zoran_mmap(V4L): remap_page_range failed\n",
+                                      zr->name);
+                               return -EAGAIN;
+                       }
+                       size -= todo;
+                       start += todo;
+                       if (size == 0)
+                               break;
+               }
+       }
+       return 0;
+}
+
+static int zoran_mmap(struct video_device *dev, const char *adr,
+                     unsigned long size)
+{
+       int err;
+       struct zoran *zr = (struct zoran *) dev;
+       
+       down(&zr->sem);
+       err = do_zoran_mmap(zr, adr, size);
+       up(&zr->sem);
+               
+       return err;
+}
+
+static int zoran_init_done(struct video_device *dev)
+{
+       return 0;
+}
+
+static struct video_device zoran_template = {
+       THIS_MODULE,
+       ZORAN_NAME,
+       VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CLIPPING |
+           VID_TYPE_FRAMERAM | VID_TYPE_SCALES | VID_TYPE_SUBCAPTURE,
+       ZORAN_HARDWARE,
+       zoran_open,
+       zoran_close,
+       zoran_read,
+       zoran_write,
+       NULL,
+       zoran_ioctl,
+       zoran_mmap,
+       zoran_init_done,
+       NULL,
+       0,
+       0
+};
+
+/*
+ * initialize video front end
+ */
+static void zr36057_init_vfe(struct zoran *zr)
+{
+       u32 reg;
+       reg = btread(ZR36057_VFESPFR);
+       reg |= ZR36057_VFESPFR_LittleEndian;
+       reg &= ~ZR36057_VFESPFR_VCLKPol;
+       reg |= ZR36057_VFESPFR_ExtFl;
+       reg |= ZR36057_VFESPFR_TopField;
+       btwrite(reg, ZR36057_VFESPFR);
+       reg = btread(ZR36057_VDCR);
+       if (triton || zr->revision <= 1)
+               reg &= ~ZR36057_VDCR_Triton;
+       else
+               reg |= ZR36057_VDCR_Triton;
+       btwrite(reg, ZR36057_VDCR);
+}
+
+static void test_interrupts(struct zoran *zr)
+{
+       int timeout, icr;
+
+       clear_interrupt_counters(zr);
+       zr->testing = 1;
+       icr = btread(ZR36057_ICR);
+       btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR);
+       timeout = interruptible_sleep_on_timeout(&zr->test_q, 1 * HZ);
+       btwrite(0, ZR36057_ICR);
+       btwrite(0x78000000, ZR36057_ISR);
+       zr->testing = 0;
+       printk(KERN_INFO "%s: Testing interrupts...\n", zr->name);
+       if (timeout) {
+               printk(": time spent: %d\n", 1 * HZ - timeout);
+       }
+       print_interrupts(zr);
+       btwrite(icr, ZR36057_ICR);
+}
+
+static int zr36057_init(int i)
+{
+       struct zoran *zr = &zoran[i];
+       unsigned long mem;
+       unsigned mem_needed;
+       int j;
+       int two = 2;
+       int zero = 0;
+
+       printk(KERN_INFO "%s: Initializing card[%d], zr=%x\n", zr->name, i, (int) zr);
+
+       /* default setup of all parameters which will persist beetween opens */
+
+       zr->user = 0;
+
+       init_waitqueue_head(&zr->v4l_capq);
+       init_waitqueue_head(&zr->jpg_capq);
+       init_waitqueue_head(&zr->test_q);
+
+       zr->map_mjpeg_buffers = 0;      /* Map V4L buffers by default */
+
+       zr->jpg_nbufs = 0;
+       zr->jpg_bufsize = 0;
+       zr->jpg_buffers_allocated = 0;
+
+       zr->buffer_set = 0;     /* Flag if frame buffer has been set */
+       zr->buffer.base = (void *) vidmem;
+       zr->buffer.width = 0;
+       zr->buffer.height = 0;
+       zr->buffer.depth = 0;
+       zr->buffer.bytesperline = 0;
+
+       zr->params.norm = default_norm = (default_norm < 3 ? default_norm : VIDEO_MODE_PAL);    /* Avoid nonsense settings from user */
+       if (!(zr->timing = cardnorms[zr->card][zr->params.norm])) {
+               printk(KERN_WARNING
+                      "%s: default TV statdard not supported by hardware. PAL will be used.\n",
+                      zr->name);
+               zr->params.norm = VIDEO_MODE_PAL;
+               zr->timing = cardnorms[zr->card][zr->params.norm];
+       }
+       zr->params.input = default_input = (default_input ? 1 : 0);     /* Avoid nonsense settings from user */
+       zr->video_interlace = 0;
+
+       /* Should the following be reset at every open ? */
+
+       zr->picture.colour = 32768;
+       zr->picture.brightness = 32768;
+       zr->picture.hue = 32768;
+       zr->picture.contrast = 32768;
+       zr->picture.whiteness = 0;
+       zr->picture.depth = 0;
+       zr->picture.palette = 0;
+
+       for (j = 0; j < VIDEO_MAX_FRAME; j++) {
+               zr->v4l_gbuf[i].fbuffer = 0;
+               zr->v4l_gbuf[i].fbuffer_phys = 0;
+               zr->v4l_gbuf[i].fbuffer_bus = 0;
+       }
+
+       zr->stat_com = 0;
+
+       /* default setup (will be repeated at every open) */
+
+       zoran_open_init_params(zr);
+
+       /* allocate memory *before* doing anything to the hardware in case allocation fails */
+
+       /* STAT_COM table and overlay mask */
+
+       mem_needed = (BUZ_NUM_STAT_COM + ((BUZ_MAX_WIDTH + 31) / 32) * BUZ_MAX_HEIGHT) * 4;
+       mem = (unsigned long) kmalloc(mem_needed, GFP_KERNEL);
+       if (!mem) {
+               printk(KERN_ERR "%s: zr36057_init: kmalloc (STAT_COM + ovl.mask) failed\n", zr->name);
+               return -ENOMEM;
+       }
+       memset((void *) mem, 0, mem_needed);
+
+       zr->stat_com = (u32 *) mem;
+       for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+               zr->stat_com[j] = 1;    /* mark as unavailable to zr36057 */
+       }
+       zr->overlay_mask = (u32 *) (mem + BUZ_NUM_STAT_COM * 4);
+
+       /* Initialize zr->jpg_gbuf */
+
+       for (j = 0; j < BUZ_MAX_FRAME; j++) {
+               zr->jpg_gbuf[j].frag_tab = 0;
+               zr->jpg_gbuf[j].frag_tab_bus = 0;
+               zr->jpg_gbuf[j].state = BUZ_STATE_USER;
+               zr->jpg_gbuf[j].bs.frame = j;
+       }
+
+       /*
+        *   Now add the template and register the device unit.
+        */
+       memcpy(&zr->video_dev, &zoran_template, sizeof(zoran_template));
+       strcpy(zr->video_dev.name, zr->name);
+       if (video_register_device(&zr->video_dev, VFL_TYPE_GRABBER, video_nr) < 0) {
+               i2c_unregister_bus(&zr->i2c);
+               kfree((void *) zr->stat_com);
+               return -1;
+       }
+
+       /* Enable bus-mastering */
+       pci_set_master(zr->pci_dev);
+
+       if (zr->card == BUZ)
+               j = zr->params.input == 0 ? 3 : 7;
+       else
+               j = zr->params.input == 0 ? 0 : 7;
+       set_videobus_enable(zr, 0);
+       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+                          DECODER_SET_INPUT, &j);
+       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+                          DECODER_SET_NORM, &zr->params.norm);
+       i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+                          ENCODER_SET_NORM, &zr->params.norm);
+       set_videobus_enable(zr, 1);
+
+       /* toggle JPEG codec sleep to sync PLL */
+       zr36060_sleep(zr, 1);
+       zr36060_sleep(zr, 0);
+
+       /* set individual interrupt enables (without GIRQ1)
+          but don't global enable until zoran_open() */
+
+       //btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR);  // SW
+       // It looks like using only JPEGRepIRQEn is not always reliable,
+       // may be when JPEG codec crashes it won't generate IRQ? So,
+       btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM
+
+       zr36057_init_vfe(zr);
+
+       zr->zoran_proc = NULL;
+       zr->initialized = 1;
+
+       zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+#if (DEBUGLEVEL > 2)
+       detect_guest_activity(zr);
+#endif
+       test_interrupts(zr);
+       btwrite(IRQ_MASK, ZR36057_ICR); // Enable Vsync interrupts too. SM
+       if (!pass_through) {
+               set_videobus_enable(zr, 0);
+               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+                                  DECODER_ENABLE_OUTPUT, &zero);
+               i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+                                  ENCODER_SET_INPUT, &two);
+               set_videobus_enable(zr, 1);
+       }
+       return 0;
+}
+
+#include "zoran_procfs.c"
+
+static void release_dc10(void)
+{
+       u8 command;
+       int i;
+       struct zoran *zr;
+
+       for (i = 0; i < zoran_num; i++) {
+               zr = &zoran[i];
+
+               if (!zr->initialized)
+                       continue;
+
+               /* unregister i2c_bus */
+               i2c_unregister_bus((&zr->i2c));
+
+               /* disable PCI bus-mastering */
+               pci_read_config_byte(zr->pci_dev, PCI_COMMAND, &command);
+               command &= ~PCI_COMMAND_MASTER;
+               pci_write_config_byte(zr->pci_dev, PCI_COMMAND, command);
+
+               /* put chip into reset */
+               btwrite(0, ZR36057_SPGPPCR);
+
+               free_irq(zr->pci_dev->irq, zr);
+
+               /* unmap and free memory */
+
+               kfree((void *) zr->stat_com);
+
+               zoran_proc_cleanup(i);
+               iounmap(zr->zr36057_mem);
+
+               video_unregister_device(&zr->video_dev);
+       }
+}
+
+/*
+ *   Scan for a Buz card (actually for the PCI contoler ZR36057),
+ *   request the irq and map the io memory
+ */
+
+static int find_zr36057(void)
+{
+       unsigned char latency, need_latency;
+       struct zoran *zr;
+       struct pci_dev *dev = NULL;
+       int result;
+
+       zoran_num = 0;
+
+       while (zoran_num < BUZ_MAX
+              && (dev =
+                  pci_find_device(PCI_VENDOR_ID_ZORAN,
+                                  PCI_DEVICE_ID_ZORAN_36057,
+                                  dev)) != NULL) {
+               zr = &zoran[zoran_num];
+               zr->pci_dev = dev;
+               zr->zr36057_mem = NULL;
+               zr->id = zoran_num;
+               sprintf(zr->name, "MJPEG[%u]", zr->id);
+
+               spin_lock_init(&zr->lock);
+               init_MUTEX(&zr->sem);
+
+               if (pci_enable_device(dev))
+                       continue;
+
+               zr->zr36057_adr = pci_resource_start(zr->pci_dev, 0);
+               pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION,
+                                    &zr->revision);
+               if (zr->revision < 2) {
+                       printk(KERN_INFO
+                              "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n",
+                              zr->name, zr->revision, zr->pci_dev->irq,
+                              zr->zr36057_adr);
+               } else {
+                       unsigned short ss_vendor_id, ss_id;
+                       ss_vendor_id = zr->pci_dev->subsystem_vendor;
+                       ss_id = zr->pci_dev->subsystem_device;
+                       printk(KERN_INFO
+                              "%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n",
+                              zr->name, zr->revision, zr->pci_dev->irq,
+                              zr->zr36057_adr);
+                       printk(KERN_INFO
+                              "%s: subsystem vendor=0x%04x id=0x%04x\n",
+                              zr->name, ss_vendor_id, ss_id);
+               }
+
+               zr->zr36057_mem = ioremap_nocache(zr->zr36057_adr, 0x1000);
+               if (!zr->zr36057_mem) {
+                       printk(KERN_ERR "%s: ioremap failed\n", zr->name);
+                       /* XXX handle error */
+               }
+
+               result = request_irq(zr->pci_dev->irq, zoran_irq, SA_SHIRQ | SA_INTERRUPT, zr->name, (void *) zr);
+               if (result < 0) {
+                       if (result == -EINVAL) {
+                               printk(KERN_ERR
+                                      "%s: Bad irq number or handler\n",
+                                      zr->name);
+                       } else if (result == -EBUSY) {
+                               printk(KERN_ERR
+                                      "%s: IRQ %d busy, change your PnP config in BIOS\n",
+                                      zr->name, zr->pci_dev->irq);
+                       } else {
+                               printk(KERN_ERR
+                                      "%s: Can't assign irq, error code %d\n",
+                                      zr->name, result);
+                       }
+                       iounmap(zr->zr36057_mem);
+                       continue;
+               }
+
+               /* set PCI latency timer */
+               pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, &latency);
+               need_latency = zr->revision > 1 ? 32 : 48;
+               if (latency != need_latency) {
+                       printk(KERN_INFO "%s: Changing PCI latency from %d to %d.\n",  zr->name, latency, need_latency);
+                       pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,  need_latency);
+               }
+
+               btwrite(0, ZR36057_SPGPPCR);
+               mdelay(1);
+               btwrite(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR);
+               mdelay(1);
+
+               /* assert P_Reset */
+               btwrite(0, ZR36057_JPC);
+
+               /* set up GPIO direction - all output */
+               btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR);
+               btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1);
+
+               /* i2c */
+               memcpy(&zr->i2c, &zoran_i2c_bus_template,
+                      sizeof(struct i2c_bus));
+               strcpy(zr->i2c.name, zr->name);
+               zr->i2c.data = zr;
+               printk(KERN_INFO "%s: Initializing i2c bus...\n", zr->name);
+               if (i2c_register_bus(&zr->i2c) < 0) {
+                       /* put chip into reset */
+                       btwrite(0, ZR36057_SPGPPCR);
+                       free_irq(zr->pci_dev->irq, zr);
+                       iounmap(zr->zr36057_mem);
+                       printk(KERN_ERR "%s: Can't initialize i2c bus\n", zr->name);
+                       continue;
+               }
+
+               if (zr->card != DC10 && zr->card != DC10plus
+                   && zr->card != LML33 && zr->card != BUZ) {
+                       /* unregister i2c_bus */
+                       i2c_unregister_bus((&zr->i2c));
+                       /* put chip into reset */
+                       btwrite(0, ZR36057_SPGPPCR);
+                       free_irq(zr->pci_dev->irq, zr);
+                       iounmap(zr->zr36057_mem);
+                       printk(KERN_ERR "%s: Card not supported\n",
+                              zr->name);
+                       continue;
+               }
+               printk(KERN_INFO "%s card detected\n", zr->name);
+               if (zr->card == LML33) {
+                       GPIO(zr, 2, 1); // Set Composite input/output
+               }
+
+               /* reset JPEG codec */
+               zr36060_sleep(zr, 1);
+               zr36060_reset(zr);
+
+               /* video bus enabled */
+
+               /* display codec revision */
+               if (zr36060_read_8(zr, 0x022) == 0x33) {
+                       printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n",
+                              zr->name, zr36060_read_8(zr, 0x023));
+               } else {
+                       /* unregister i2c_bus */
+                       i2c_unregister_bus((&zr->i2c));
+                       /* put chip into reset */
+                       btwrite(0, ZR36057_SPGPPCR);
+                       free_irq(zr->pci_dev->irq, zr);
+                       iounmap(zr->zr36057_mem);
+                       printk(KERN_ERR "%s: Zoran ZR36060 not found\n",
+                              zr->name);
+                       continue;
+               }
+
+               zoran_num++;
+       }
+       if (zoran_num == 0) {
+               printk(KERN_INFO "No known MJPEG cards found.\n");
+       }
+       return zoran_num;
+}
+
+static void handle_chipset(void)
+{
+       if(pci_pci_problems & PCIPCI_FAIL)
+               printk(KERN_WARNING "Chipset may not support reliable PCI-PCI DMA.\n");
+
+       if(pci_pci_problems & PCIPCI_TRITON)
+       {
+               printk(KERN_WARNING "Enabling Triton support.\n");
+               triton = 1;
+       }
+       
+       if(pci_pci_problems & PCIPCI_NATOMA)
+       {
+               printk(KERN_WARNING "Enabling Natoma workaround.\n");
+               natoma = 1;
+       }
+}
+
+static int init_dc10_cards(void)
+{
+       int i;
+
+       memset(zoran, 0, sizeof(zoran));
+       printk(KERN_INFO
+              "Zoran ZR36060 + ZR36057/67 MJPEG board driver version %d.%d\n",
+              MAJOR_VERSION, MINOR_VERSION);
+
+       /* Look for cards */
+
+       if (find_zr36057() < 0) {
+               return -EIO;
+       }
+       if (zoran_num == 0)
+               return 0;       //-ENXIO;
+
+       printk(KERN_INFO "MJPEG: %d card(s) found\n", zoran_num);
+
+       /* check the parameters we have been given, adjust if necessary */
+
+       if (v4l_nbufs < 0)
+               v4l_nbufs = 0;
+       if (v4l_nbufs > VIDEO_MAX_FRAME)
+               v4l_nbufs = VIDEO_MAX_FRAME;
+       /* The user specfies the in KB, we want them in byte (and page aligned) */
+       v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
+       if (v4l_bufsize < 32768)
+               v4l_bufsize = 32768;
+       /* 2 MB is arbitrary but sufficient for the maximum possible images */
+       if (v4l_bufsize > 2048 * 1024)
+               v4l_bufsize = 2048 * 1024;
+
+       printk(KERN_INFO "MJPEG: using %d V4L buffers of size %d KB\n",
+              v4l_nbufs, v4l_bufsize >> 10);
+
+       /* Use parameter for vidmem or try to find a video card */
+
+       if (vidmem) {
+               printk(KERN_INFO
+                      "MJPEG: Using supplied video memory base address @ 0x%lx\n",
+                      vidmem);
+       }
+       /* check if we have a Triton or Natome chipset */
+
+       handle_chipset();
+
+       /* take care of Natoma chipset and a revision 1 zr36057 */
+
+       for (i = 0; i < zoran_num; i++) {
+               if (natoma && zoran[i].revision <= 1) {
+                       zoran[i].need_contiguous = 1;
+                       printk(KERN_INFO
+                              "%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
+                              zoran[i].name);
+               } else {
+                       zoran[i].need_contiguous = 0;
+               }
+       }
+
+       /* initialize the Buzs */
+
+       /* We have to know which ones must be released if an error occurs */
+       for (i = 0; i < zoran_num; i++)
+               zoran[i].initialized = 0;
+
+       for (i = 0; i < zoran_num; i++) {
+               if (zr36057_init(i) < 0) {
+                       release_dc10();
+                       return -EIO;
+               }
+               zoran_proc_init(i);
+       }
+
+       return 0;
+}
+
+static void unload_dc10_cards(void)
+{
+       release_dc10();
+}
+
+
+module_init(init_dc10_cards);
+module_exit(unload_dc10_cards);
index 5dc5ec39d227fb99992d0b58f955bedd17e42c86..bc8ef6891a33672a9089381ea729e8a86f1714c1 100644 (file)
@@ -2731,7 +2731,7 @@ static int ace_change_mtu(struct net_device *dev, int new_mtu)
        struct ace_private *ap = dev->priv;
        struct ace_regs *regs = ap->regs;
 
-       if ((new_mtu < 68) || (new_mtu > ACE_JUMBO_MTU))
+       if (new_mtu > ACE_JUMBO_MTU)
                return -EINVAL;
 
        writel(new_mtu + ETH_HLEN + 4, &regs->IfMtu);
index 6f62491b8b0b101ddd45d9de66fa93314704bc73..5d942a499ac3b09a9e2239f37ba9028a536b7f94 100644 (file)
@@ -1144,7 +1144,7 @@ static int arlan_change_mtu(struct net_device *dev, int new_mtu)
        struct arlan_conf_stru *conf = ((struct arlan_private *) dev->priv)->Conf;
 
        ARLAN_DEBUG_ENTRY("arlan_change_mtu");
-       if ((new_mtu < 68) || (new_mtu > 2032))
+       if (new_mtu > 2032)
                return -EINVAL;
        dev->mtu = new_mtu;
        if (new_mtu < 256)
diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c
new file mode 100644 (file)
index 0000000..3ebf32a
--- /dev/null
@@ -0,0 +1,1266 @@
+/*
+ *
+ * Alchemy Semi Au1000 ethernet driver
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *             ppopov@mvista.com or source@mvista.com
+ *
+ * ########################################################################
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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.,
+ *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * ########################################################################
+ *
+ * 
+ */
+
+#ifndef __mips__
+#error This driver only works with MIPS architectures!
+#endif
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <asm/au1000.h>
+#include "au1000_eth.h"
+
+#ifdef AU1000_ETH_DEBUG
+static int au1000_debug = 10;
+#else
+static int au1000_debug = 3;
+#endif
+
+// prototypes
+static void *dma_alloc(size_t, dma_addr_t *);
+static void dma_free(void *, size_t);
+static void hard_stop(struct net_device *);
+static int __init au1000_probe1(struct net_device *, long, int, int);
+static int au1000_init(struct net_device *);
+static int au1000_open(struct net_device *);
+static int au1000_close(struct net_device *);
+static int au1000_tx(struct sk_buff *, struct net_device *);
+static int au1000_rx(struct net_device *);
+static void au1000_interrupt(int, void *, struct pt_regs *);
+static void au1000_tx_timeout(struct net_device *);
+static int au1000_set_config(struct net_device *dev, struct ifmap *map);
+static void set_rx_mode(struct net_device *);
+static struct net_device_stats *au1000_get_stats(struct net_device *);
+static inline void update_tx_stats(struct net_device *, u32, u32);
+static inline void update_rx_stats(struct net_device *, u32);
+static void au1000_timer(unsigned long);
+static void cleanup_buffers(struct net_device *);
+static int au1000_ioctl(struct net_device *, struct ifreq *, int);
+static int mdio_read(struct net_device *, int, int);
+static void mdio_write(struct net_device *, int, int, u16);
+static inline void sync(void);
+
+extern  void ack_rise_edge_irq(unsigned int);
+
+static int next_dev;
+
+/*
+ * Theory of operation
+ *
+ * The Au1000 MACs use a simple rx and tx descriptor ring scheme. 
+ * There are four receive and four transmit descriptors.  These 
+ * descriptors are not in memory; rather, they are just a set of 
+ * hardware registers.
+ *
+ * Since the Au1000 has a coherent data cache, the receive and
+ * transmit buffers are allocated from the KSEG0 segment. The 
+ * hardware registers, however, are still mapped at KSEG1 to
+ * make sure there's no out-of-order writes, and that all writes
+ * complete immediately.
+ */
+
+
+/*
+ * Base address and interupt of the Au1000 ethernet macs
+ */
+static struct {
+       unsigned int port;
+       int irq;
+} au1000_iflist[NUM_INTERFACES] = {
+       {AU1000_ETH0_BASE, AU1000_ETH0_IRQ}, 
+       {AU1000_ETH1_BASE, AU1000_ETH1_IRQ}
+};
+
+
+static char version[] __devinitdata =
+    "au1000eth.c:0.1 ppopov@mvista.com\n";
+
+// FIX! Need real Ethernet addresses
+static unsigned char au1000_mac_addr[2][6] __devinitdata = { 
+       {0x00, 0x50, 0xc2, 0x0c, 0x30, 0x00},
+       {0x00, 0x50, 0xc2, 0x0c, 0x40, 0x00}
+};
+
+#define nibswap(x) ((((x) >> 4) & 0x0f) | (((x) << 4) & 0xf0))
+#define RUN_AT(x) (jiffies + (x))
+
+// For reading/writing 32-bit words from/to DMA memory
+#define cpu_to_dma32 cpu_to_be32
+#define dma32_to_cpu be32_to_cpu
+
+/* CPU pipeline flush */
+static inline void sync(void)
+{
+       asm volatile ("sync");
+}
+
+/* FIXME 
+ * All of the PHY code really should be detached from the MAC 
+ * code.
+ */
+
+static char *phy_link[] = 
+       {"unknown", 
+       "10Base2", "10BaseT", 
+       "AUI",
+       "100BaseT", "100BaseTX", "100BaseFX"};
+
+int bcm_5201_init(struct net_device *dev, int phy_addr)
+{
+       s16 data;
+       
+       /* Stop auto-negotiation */
+       //printk("bcm_5201_init\n");
+       data = mdio_read(dev, phy_addr, MII_CONTROL);
+       mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO);
+
+       /* Set advertisement to 10/100 and Half/Full duplex
+        * (full capabilities) */
+       data = mdio_read(dev, phy_addr, MII_ANADV);
+       data |= MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_T_FDX | MII_NWAY_T;
+       mdio_write(dev, phy_addr, MII_ANADV, data);
+       
+       /* Restart auto-negotiation */
+       data = mdio_read(dev, phy_addr, MII_CONTROL);
+       data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO;
+       mdio_write(dev, phy_addr, MII_CONTROL, data);
+       //dump_mii(dev, phy_addr);
+       return 0;
+}
+
+int bcm_5201_reset(struct net_device *dev, int phy_addr)
+{
+       s16 mii_control, timeout;
+       
+       //printk("bcm_5201_reset\n");
+       mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+       mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
+       mdelay(1);
+       for (timeout = 100; timeout > 0; --timeout) {
+               mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+               if ((mii_control & MII_CNTL_RESET) == 0)
+                       break;
+               mdelay(1);
+       }
+       if (mii_control & MII_CNTL_RESET) {
+               printk(KERN_ERR "%s PHY reset timeout !\n", dev->name);
+               return -1;
+       }
+       return 0;
+}
+
+int 
+bcm_5201_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+       u16 mii_data;
+       struct au1000_private *aup;
+
+       if (!dev) {
+               printk(KERN_ERR "bcm_5201_status error: NULL dev\n");
+               return -1;
+       }
+       aup = (struct au1000_private *) dev->priv;
+
+       mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+       if (mii_data & MII_STAT_LINK) {
+               *link = 1;
+               mii_data = mdio_read(dev, aup->phy_addr, MII_AUX_CNTRL);
+               if (mii_data & MII_AUX_100) {
+                       if (mii_data & MII_AUX_FDX) {
+                               *speed = IF_PORT_100BASEFX;
+                               dev->if_port = IF_PORT_100BASEFX;
+                       }
+                       else {
+                               *speed = IF_PORT_100BASETX;
+                               dev->if_port = IF_PORT_100BASETX;
+                       }
+               }
+               else  {
+                       *speed = IF_PORT_10BASET;
+                       dev->if_port = IF_PORT_10BASET;
+               }
+
+       }
+       else {
+               *link = 0;
+               *speed = 0;
+       }
+       return 0;
+}
+
+
+int am79c901_init(struct net_device *dev, int phy_addr)
+{
+       printk("am79c901_init\n");
+       return 0;
+}
+
+int am79c901_reset(struct net_device *dev, int phy_addr)
+{
+       printk("am79c901_reset\n");
+       return 0;
+}
+
+int 
+am79c901_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+       return 0;
+}
+
+struct phy_ops bcm_5201_ops = {
+       bcm_5201_init,
+       bcm_5201_reset,
+       bcm_5201_status,
+};
+
+struct phy_ops am79c901_ops = {
+       am79c901_init,
+       am79c901_reset,
+       am79c901_status,
+};
+
+static struct mii_chip_info {
+       const char * name;
+       u16 phy_id0;
+       u16 phy_id1;
+       struct phy_ops *phy_ops;        
+} mii_chip_table[] = {
+       {"Broadcom BCM5201 10/100 BaseT PHY",  0x0040, 0x6212, &bcm_5201_ops },
+       {"AMD 79C901 HomePNA PHY",  0x0000, 0x35c8, &am79c901_ops },
+       {0,},
+};
+
+static int mdio_read(struct net_device *dev, int phy_id, int reg)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       u32 timedout = 20;
+       u32 mii_control;
+
+       while (aup->mac->mii_control & MAC_MII_BUSY) {
+               mdelay(1);
+               if (--timedout == 0) {
+                       printk(KERN_ERR "%s: read_MII busy timeout!!\n", dev->name);
+                       return -1;
+               }
+       }
+
+       mii_control = MAC_SET_MII_SELECT_REG(reg) | 
+               MAC_SET_MII_SELECT_PHY(phy_id) | MAC_MII_READ;
+
+       aup->mac->mii_control = mii_control;
+
+       timedout = 20;
+       while (aup->mac->mii_control & MAC_MII_BUSY) {
+               mdelay(1);
+               if (--timedout == 0) {
+                       printk(KERN_ERR "%s: mdio_read busy timeout!!\n", dev->name);
+                       return -1;
+               }
+       }
+       return (int)aup->mac->mii_data;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 value)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       u32 timedout = 20;
+       u32 mii_control;
+
+       while (aup->mac->mii_control & MAC_MII_BUSY) {
+               mdelay(1);
+               if (--timedout == 0) {
+                       printk(KERN_ERR "%s: mdio_write busy timeout!!\n", dev->name);
+                       return;
+               }
+       }
+
+       mii_control = MAC_SET_MII_SELECT_REG(reg) | 
+               MAC_SET_MII_SELECT_PHY(phy_id) | MAC_MII_WRITE;
+
+       aup->mac->mii_data = value;
+       aup->mac->mii_control = mii_control;
+}
+
+
+static void dump_mii(struct net_device *dev, int phy_id)
+{
+       int i, val;
+
+       for (i = 0; i < 7; i++) {
+               if ((val = mdio_read(dev, phy_id, i)) >= 0)
+                       printk("%s: MII Reg %d=%x\n", dev->name, i, val);
+       }
+       for (i = 16; i < 25; i++) {
+               if ((val = mdio_read(dev, phy_id, i)) >= 0)
+                       printk("%s: MII Reg %d=%x\n", dev->name, i, val);
+       }
+}
+
+static int __init mii_probe (struct net_device * dev)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       int phy_addr;
+
+       aup->mii = NULL;
+
+       /* search for total of 32 possible mii phy addresses */
+       for (phy_addr = 0; phy_addr < 32; phy_addr++) {
+               u16 mii_status;
+               u16 phy_id0, phy_id1;
+               int i;
+
+               mii_status = mdio_read(dev, phy_addr, MII_STATUS);
+               if (mii_status == 0xffff || mii_status == 0x0000)
+                       /* the mii is not accessable, try next one */
+                       continue;
+
+               phy_id0 = mdio_read(dev, phy_addr, MII_PHY_ID0);
+               phy_id1 = mdio_read(dev, phy_addr, MII_PHY_ID1);
+
+               /* search our mii table for the current mii */ 
+               for (i = 0; mii_chip_table[i].phy_id1; i++)
+                       if (phy_id0 == mii_chip_table[i].phy_id0 &&
+                           phy_id1 == mii_chip_table[i].phy_id1) {
+                               struct mii_phy * mii_phy;
+
+                               printk(KERN_INFO "%s: %s found at phy address %d\n",
+                                      dev->name, mii_chip_table[i].name, phy_addr);
+                               if ((mii_phy = kmalloc(sizeof(struct mii_phy), GFP_KERNEL)) != NULL) {
+                                       mii_phy->chip_info = mii_chip_table+i;
+                                       mii_phy->phy_addr = phy_addr;
+                                       //mii_phy->status = mdio_read(dev, phy_addr, MII_STATUS);
+                                       mii_phy->next = aup->mii;
+                                       aup->phy_ops = mii_chip_table[i].phy_ops;
+                                       aup->mii = mii_phy;
+                               }
+                               /* the current mii is on our mii_info_table,
+                                  try next address */
+                               break;
+                       }
+       }
+
+       if (aup->mii == NULL) {
+               printk(KERN_ERR "%s: No MII transceivers found!\n", dev->name);
+               return -1;
+       }
+
+       /* use last PHY */
+       aup->phy_addr = aup->mii->phy_addr;
+       printk(KERN_INFO "%s: Using %s as default\n", dev->name, aup->mii->chip_info->name);
+
+       return 0;
+}
+
+
+/*
+ * Buffer allocation/deallocation routines. The buffer descriptor returned
+ * has the virtual and dma address of a buffer suitable for 
+ * both, receive and transmit operations.
+ */
+static db_dest_t *GetFreeDB(struct au1000_private *aup)
+{
+       db_dest_t *pDB;
+       pDB = aup->pDBfree;
+
+       if (pDB) {
+               aup->pDBfree = pDB->pnext;
+       }
+       //printk("GetFreeDB: %x\n", pDB);
+       return pDB;
+}
+
+void ReleaseDB(struct au1000_private *aup, db_dest_t *pDB)
+{
+       db_dest_t *pDBfree = aup->pDBfree;
+       if (pDBfree)
+               pDBfree->pnext = pDB;
+       aup->pDBfree = pDB;
+}
+
+
+/*
+  DMA memory allocation, derived from pci_alloc_consistent.
+  However, the Au1000 data cache is coherent (when programmed
+  so), therefore we return KSEG0 address, not KSEG1.
+*/
+static void *dma_alloc(size_t size, dma_addr_t * dma_handle)
+{
+       void *ret;
+       int gfp = GFP_ATOMIC | GFP_DMA;
+
+       ret = (void *) __get_free_pages(gfp, get_order(size));
+
+       if (ret != NULL) {
+               memset(ret, 0, size);
+               *dma_handle = virt_to_bus(ret);
+               ret = KSEG0ADDR(ret);
+       }
+       return ret;
+}
+
+
+static void dma_free(void *vaddr, size_t size)
+{
+       vaddr = KSEG0ADDR(vaddr);
+       free_pages((unsigned long) vaddr, get_order(size));
+}
+
+
+static void hard_stop(struct net_device *dev)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+
+       if (au1000_debug > 4)
+               printk(KERN_INFO "%s: hard stop\n", dev->name);
+
+       aup->mac->control &= ~(MAC_RX_ENABLE | MAC_TX_ENABLE);
+       sync();
+       mdelay(10);
+}
+
+
+static void reset_mac(struct net_device *dev)
+{
+       u32 flags;
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+
+       if (au1000_debug > 4)
+               printk(KERN_INFO "%s: reset mac, aup %x\n", dev->name, (unsigned)aup);
+
+       spin_lock_irqsave(&aup->lock, flags);
+       del_timer(&aup->timer);
+       hard_stop(dev);
+       *aup->enable |= MAC_DMA_RESET;
+       sync();
+       mdelay(10);
+       aup->tx_full = 0;
+       spin_unlock_irqrestore(&aup->lock, flags);
+}
+
+static void cleanup_buffers(struct net_device *dev)
+{
+       int i;
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+
+       for (i=0; i<NUM_RX_DMA; i++) {
+               if (aup->rx_db_inuse[i]) {
+                       ReleaseDB(aup, aup->rx_db_inuse[i]);
+                       aup->rx_db_inuse[i] = 0;
+               }
+       }
+
+       for (i=0; i<NUM_TX_DMA; i++) {
+               if (aup->tx_db_inuse[i]) {
+                       ReleaseDB(aup, aup->tx_db_inuse[i]);
+                       aup->tx_db_inuse[i] = 0;
+               }
+       }
+}
+
+
+/* 
+ * Setup the receive and transmit "rings".  These pointers are the addresses
+ * of the rx and tx MAC DMA registers so they are fixed by the hardware --
+ * these are not descriptors sitting in memory.
+ */
+static void 
+setup_hw_rings(struct au1000_private *aup, u32 rx_base, u32 tx_base)
+{
+       int i;
+
+       for (i=0; i<NUM_RX_DMA; i++) {
+               aup->rx_dma_ring[i] = (volatile rx_dma_t *) ioremap_nocache((unsigned long)
+                                       (rx_base + sizeof(rx_dma_t)*i), sizeof(rx_dma_t));
+       }
+       for (i=0; i<NUM_TX_DMA; i++) {
+               aup->tx_dma_ring[i] = (volatile tx_dma_t *)ioremap_nocache((unsigned long)
+                               (tx_base + sizeof(tx_dma_t)*i), sizeof(tx_dma_t));
+       }
+}
+
+/*
+ * Probe for a AU1000 ethernet controller.
+ */
+int __init au1000_probe(struct net_device *dev)
+{
+       int base_addr = au1000_iflist[next_dev].port;
+       int irq = au1000_iflist[next_dev].irq;
+
+#ifndef CONFIG_MIPS_AU1000_ENET
+       return -ENODEV;
+#endif
+
+       if (au1000_debug > 4)
+               printk(KERN_INFO "%s: au1000_probe base_addr %x\n", 
+                               dev->name, base_addr);
+
+       if (next_dev >= NUM_INTERFACES) {
+               return -ENODEV;
+       }
+       if (au1000_probe1(dev, base_addr, irq, next_dev) == 0) {
+               next_dev++;
+               return 0;
+       }
+       next_dev++;
+       return -ENODEV;
+}
+
+
+
+static int __init
+au1000_probe1(struct net_device *dev, long ioaddr, int irq, int port_num)
+{
+       static unsigned version_printed = 0;
+       struct au1000_private *aup = NULL;
+       int i, retval = 0;
+       db_dest_t *pDB, *pDBfree;
+       u16 link, speed;
+
+       if ((ioaddr != AU1000_ETH0_BASE) && (ioaddr != AU1000_ETH1_BASE))  {
+               return -ENODEV;
+       }
+
+       if (!request_region(ioaddr, MAC_IOSIZE, "Au1000 ENET")) {
+                return -ENODEV;
+       }
+
+       if (version_printed++ == 0) printk(version);
+
+       if (!dev) {
+               dev = init_etherdev(0, sizeof(struct au1000_private));
+       }
+       if (!dev) {
+                printk (KERN_ERR "au1000 eth: init_etherdev failed\n");  
+                return -ENODEV;
+       }
+
+       printk("%s: Au1000 ethernet found at 0x%lx, irq %d\n",
+              dev->name, ioaddr, irq);
+
+
+       /* Initialize our private structure */
+       if (dev->priv == NULL) {
+               aup = (struct au1000_private *) kmalloc(sizeof(*aup), GFP_KERNEL);
+               if (aup == NULL) {
+                       retval = -ENOMEM;
+                       goto free_region;
+               }
+               dev->priv = aup;
+       }
+
+       aup = dev->priv;
+       memset(aup, 0, sizeof(*aup));
+
+
+       /* Allocate the data buffers */
+       aup->vaddr = (u32)dma_alloc(MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS), &aup->dma_addr);
+       if (!aup->vaddr) {
+               retval = -ENOMEM;
+               goto free_region;
+       }
+
+       /* aup->mac is the base address of the MAC's registers */
+       aup->mac = (volatile mac_reg_t *)ioremap_nocache((unsigned long)ioaddr, sizeof(*aup->mac));
+       /* Setup some variables for quick register address access */
+       if (ioaddr == AU1000_ETH0_BASE) {
+               aup->enable = (volatile u32 *)
+                       ioremap_nocache((unsigned long)MAC0_ENABLE, sizeof(*aup->enable)); 
+               memcpy(dev->dev_addr, au1000_mac_addr[0], sizeof(dev->dev_addr));
+               setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
+       }
+       else if (ioaddr == AU1000_ETH1_BASE) {
+               aup->enable = (volatile u32 *)
+                       ioremap_nocache((unsigned long)MAC1_ENABLE, sizeof(*aup->enable)); 
+               memcpy(dev->dev_addr, au1000_mac_addr[1], sizeof(dev->dev_addr));
+               setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
+       }
+       else { /* should never happen */
+                printk (KERN_ERR "au1000 eth: bad ioaddr %x\n", (unsigned)ioaddr);  
+                retval = -ENODEV;
+                goto free_region;
+       }
+
+       aup->phy_addr = PHY_ADDRESS;
+       /* bring the device out of reset, otherwise probing the mii
+        * will hang */
+       *aup->enable = MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2 |
+               MAC_EN_CLOCK_ENABLE | MAC_EN_TOSS;
+       sync();
+       mdelay(2);
+       if (mii_probe(dev) != 0) {
+                goto free_region;
+       }
+       aup->phy_ops->phy_status(dev, aup->phy_addr, &link, &speed);
+       if (!link) {
+               printk(KERN_INFO "%s: link down resetting...\n", dev->name);
+               aup->phy_ops->phy_reset(dev, aup->phy_addr);
+               aup->phy_ops->phy_init(dev, aup->phy_addr);
+       }
+       else {
+               printk(KERN_INFO "%s: link up (%s)\n", dev->name, phy_link[speed]);
+       }
+
+       pDBfree = NULL;
+       /* setup the data buffer descriptors and attach a buffer to each one */
+       pDB = aup->db;
+       for (i=0; i<(NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
+               pDB->pnext = pDBfree;
+               pDBfree = pDB;
+               pDB->vaddr = (u32 *)((unsigned)aup->vaddr + MAX_BUF_SIZE*i);
+               pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr);
+               pDB++;
+       }
+       aup->pDBfree = pDBfree;
+
+       for (i=0; i<NUM_RX_DMA; i++) {
+               pDB = GetFreeDB(aup);
+               if (!pDB) goto free_region;
+               aup->rx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
+               aup->rx_db_inuse[i] = pDB;
+       }
+       for (i=0; i<NUM_TX_DMA; i++) {
+               pDB = GetFreeDB(aup);
+               if (!pDB) goto free_region;
+               aup->tx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
+               aup->tx_dma_ring[i]->len = 0;
+               aup->tx_db_inuse[i] = pDB;
+       }
+
+       spin_lock_init(&aup->lock);
+       dev->base_addr = ioaddr;
+       dev->irq = irq;
+       dev->open = au1000_open;
+       dev->hard_start_xmit = au1000_tx;
+       dev->stop = au1000_close;
+       dev->get_stats = au1000_get_stats;
+       dev->set_multicast_list = &set_rx_mode;
+       dev->do_ioctl = &au1000_ioctl;
+       dev->set_config = &au1000_set_config;
+       dev->tx_timeout = au1000_tx_timeout;
+       dev->watchdog_timeo = ETH_TX_TIMEOUT;
+
+
+       /* Fill in the fields of the device structure with ethernet values. */
+       ether_setup(dev);
+
+       /* 
+        * The boot code uses the ethernet controller, so reset it to start fresh.
+        * au1000_init() expects that the device is in reset state.
+        */
+       reset_mac(dev);
+
+       return 0;
+
+free_region:
+       release_region(ioaddr, MAC_IOSIZE);
+       unregister_netdev(dev);
+       if (aup->vaddr)
+               dma_free((void *)aup->vaddr, MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS));
+       if (dev->priv != NULL)
+               kfree(dev->priv);
+       kfree(dev);
+       printk(KERN_ERR "%s: au1000_probe1 failed.  Returns %d\n",
+              dev->name, retval);
+       return retval;
+}
+
+
+/* 
+ * Initialize the interface.
+ *
+ * When the device powers up, the clocks are disabled and the
+ * mac is in reset state.  When the interface is closed, we
+ * do the same -- reset the device and disable the clocks to
+ * conserve power. Thus, whenever au1000_init() is called,
+ * the device should already be in reset state.
+ */
+static int au1000_init(struct net_device *dev)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       u32 flags;
+       int i;
+       u32 value, control;
+
+       if (au1000_debug > 4) printk("%s: au1000_init", dev->name);
+
+       spin_lock_irqsave(&aup->lock, flags);
+
+       /* bring the device out of reset */
+       value = MAC_EN_RESET0 | MAC_EN_RESET1 | MAC_EN_RESET2 |
+               MAC_EN_CLOCK_ENABLE | MAC_EN_TOSS;
+       *aup->enable = value;
+       sync();
+       mdelay(200);
+
+       aup->mac->control = 0;
+       aup->tx_head = (aup->tx_dma_ring[0]->buff_stat & 0xC) >> 2;
+       aup->tx_tail = aup->tx_head;
+       aup->rx_head = (aup->rx_dma_ring[0]->buff_stat & 0xC) >> 2;
+
+       aup->mac->mac_addr_high = dev->dev_addr[5]<<8 | dev->dev_addr[4];
+       aup->mac->mac_addr_low = dev->dev_addr[3]<<24 | dev->dev_addr[2]<<16 |
+               dev->dev_addr[1]<<8 | dev->dev_addr[0];
+
+       for (i=0; i<NUM_RX_DMA; i++) {
+               aup->rx_dma_ring[i]->buff_stat |= RX_DMA_ENABLE;
+       }
+
+       sync();
+       control = MAC_DISABLE_RX_OWN | MAC_RX_ENABLE | MAC_TX_ENABLE;
+#ifndef CONFIG_CPU_LITTLE_ENDIAN
+       control |= MAC_BIG_ENDIAN;
+#endif
+       aup->mac->control = control;
+
+       spin_unlock_irqrestore(&aup->lock, flags);
+       return 0;
+}
+
+static void au1000_timer(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       u16 mii_data, link, speed;
+
+       if (!dev) {
+               /* fatal error, don't restart the timer */
+               printk(KERN_ERR "au1000_timer error: NULL dev\n");
+               return;
+       }
+       if (!(dev->flags & IFF_UP)) {
+               goto set_timer;
+       }
+
+       if (aup->phy_ops->phy_status(dev, aup->phy_addr, &link, &speed) == 0) {
+               if (link) {
+                       if (!(dev->flags & IFF_RUNNING)) {
+                               netif_carrier_on(dev);
+                               dev->flags |= IFF_RUNNING;
+                               printk(KERN_DEBUG "%s: link up\n", dev->name);
+                       }
+               }
+               else {
+                       if (dev->flags & IFF_RUNNING) {
+                               netif_carrier_off(dev);
+                               dev->flags &= ~IFF_RUNNING;
+                               dev->if_port = 0;
+                               printk(KERN_DEBUG "%s: link down\n", dev->name);
+                       }
+               }
+       }
+
+set_timer:
+       aup->timer.expires = RUN_AT((1*HZ)); 
+       aup->timer.data = (unsigned long)dev;
+       aup->timer.function = &au1000_timer; /* timer handler */
+       add_timer(&aup->timer);
+
+}
+
+static int au1000_open(struct net_device *dev)
+{
+       int retval;
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+
+       MOD_INC_USE_COUNT;
+
+       if (au1000_debug > 4)
+               printk("%s: open: dev=%p\n", dev->name, dev);
+
+       if ((retval = au1000_init(dev))) {
+               printk(KERN_ERR "%s: error in au1000_init\n", dev->name);
+               free_irq(dev->irq, dev);
+               MOD_DEC_USE_COUNT;
+               return retval;
+       }
+       netif_start_queue(dev);
+
+       if ((retval = request_irq(dev->irq, &au1000_interrupt, 0, dev->name, dev))) {
+               printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, dev->irq);
+               MOD_DEC_USE_COUNT;
+               return retval;
+       }
+
+       aup->timer.expires = RUN_AT((3*HZ)); 
+       aup->timer.data = (unsigned long)dev;
+       aup->timer.function = &au1000_timer; /* timer handler */
+       add_timer(&aup->timer);
+
+       if (au1000_debug > 4)
+               printk("%s: open: Initialization done.\n", dev->name);
+
+       return 0;
+}
+
+static int au1000_close(struct net_device *dev)
+{
+       u32 flags;
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+
+       if (au1000_debug > 4)
+               printk("%s: close: dev=%p\n", dev->name, dev);
+
+       spin_lock_irqsave(&aup->lock, flags);
+       
+       /* stop the device */
+       if (netif_device_present(dev)) {
+               netif_stop_queue(dev);
+       }
+
+       /* disable the interrupt */
+       free_irq(dev->irq, dev);
+       spin_unlock_irqrestore(&aup->lock, flags);
+
+       reset_mac(dev);
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+
+static inline void update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       struct net_device_stats *ps = &aup->stats;
+
+       ps->tx_packets++;
+       ps->tx_bytes += pkt_len;
+
+       if (status & TX_FRAME_ABORTED) {
+               ps->tx_errors++;
+               ps->tx_aborted_errors++;
+               if (status & (TX_NO_CARRIER | TX_LOSS_CARRIER))
+                       ps->tx_carrier_errors++;
+       }
+}
+
+
+/*
+ * Called from the interrupt service routine to acknowledge
+ * the TX DONE bits.  This is a must if the irq is setup as
+ * edge triggered.
+ */
+static void au1000_tx_ack(struct net_device *dev)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       volatile tx_dma_t *ptxd;
+
+       ptxd = aup->tx_dma_ring[aup->tx_tail];
+
+       while (ptxd->buff_stat & TX_T_DONE) {
+               update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff);
+               ptxd->buff_stat &= ~TX_T_DONE;
+               ptxd->len = 0;
+               sync();
+
+               aup->tx_tail = (aup->tx_tail + 1) & (NUM_TX_DMA - 1);
+               ptxd = aup->tx_dma_ring[aup->tx_tail];
+
+               if (aup->tx_full) {
+                       aup->tx_full = 0;
+                       netif_wake_queue(dev);
+               }
+       }
+}
+
+
+/*
+ * Au1000 transmit routine.
+ */
+static int au1000_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       //unsigned long flags;
+       volatile tx_dma_t *ptxd;
+       u32 buff_stat;
+       db_dest_t *pDB;
+       int i;
+
+       if (au1000_debug > 4)
+               printk("%s: tx: aup %x len=%d, data=%p, head %d\n",
+                      dev->name, (unsigned)aup, skb->len, skb->data, aup->tx_head);
+
+       /* Prevent interrupts from changing the Tx ring */
+       //spin_lock_irqsave(&aup->lock, flags);
+       
+       ptxd = aup->tx_dma_ring[aup->tx_head];
+       buff_stat = ptxd->buff_stat;
+       if (buff_stat & TX_DMA_ENABLE) {
+               /* We've wrapped around and the transmitter is still busy */
+               netif_stop_queue(dev);
+               aup->tx_full = 1;
+               //spin_unlock_irqrestore(&aup->lock, flags);
+               return 1;
+       }
+       else if (buff_stat & TX_T_DONE) {
+               update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff);
+               ptxd->len = 0;
+       }
+
+       if (aup->tx_full) {
+               aup->tx_full = 0;
+               netif_wake_queue(dev);
+       }
+
+       pDB = aup->tx_db_inuse[aup->tx_head];
+       memcpy((void *)pDB->vaddr, skb->data, skb->len);
+       if (skb->len < MAC_MIN_PKT_SIZE) {
+               for (i=skb->len; i<MAC_MIN_PKT_SIZE; i++) { 
+                       ((char *)pDB->vaddr)[i] = 0;
+               }
+               ptxd->len = MAC_MIN_PKT_SIZE;
+       }
+       else
+               ptxd->len = skb->len;
+
+       ptxd->buff_stat = pDB->dma_addr | TX_DMA_ENABLE;
+       sync();
+       dev_kfree_skb(skb);
+       aup->tx_head = (aup->tx_head + 1) & (NUM_TX_DMA - 1);
+       dev->trans_start = jiffies;
+       //spin_unlock_irqrestore(&aup->lock, flags);
+       return 0;
+}
+
+
+static inline void update_rx_stats(struct net_device *dev, u32 status)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       struct net_device_stats *ps = &aup->stats;
+
+       ps->rx_packets++;
+       if (status & RX_MCAST_FRAME)
+               ps->multicast++;
+
+       if (status & RX_ERROR) {
+               ps->rx_errors++;
+               if (status & RX_MISSED_FRAME)
+                       ps->rx_missed_errors++;
+               if (status & (RX_OVERLEN | RX_OVERLEN | RX_LEN_ERROR))
+                       ps->rx_length_errors++;
+               if (status & RX_CRC_ERROR)
+                       ps->rx_crc_errors++;
+               if (status & RX_COLL)
+                       ps->collisions++;
+       }
+       else 
+               ps->rx_bytes += status & RX_FRAME_LEN_MASK;
+
+}
+
+/*
+ * Au1000 receive routine.
+ */
+static int au1000_rx(struct net_device *dev)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       struct sk_buff *skb;
+       volatile rx_dma_t *prxd;
+       u32 buff_stat, status;
+       db_dest_t *pDB;
+
+       if (au1000_debug > 4)
+               printk("%s: au1000_rx head %d\n", dev->name, aup->rx_head);
+
+       prxd = aup->rx_dma_ring[aup->rx_head];
+       buff_stat = prxd->buff_stat;
+       while (buff_stat & RX_T_DONE)  {
+               status = prxd->status;
+               pDB = aup->rx_db_inuse[aup->rx_head];
+               update_rx_stats(dev, status);
+               if (!(status & RX_ERROR))  {
+
+                       /* good frame */
+                       skb = dev_alloc_skb((status & RX_FRAME_LEN_MASK) + 2);
+                       if (skb == NULL) {
+                               printk(KERN_ERR
+                                      "%s: Memory squeeze, dropping packet.\n",
+                                      dev->name);
+                               aup->stats.rx_dropped++;
+                               continue;
+                       }
+                       skb->dev = dev;
+                       skb_reserve(skb, 2);    /* 16 byte IP header align */
+                       eth_copy_and_sum(skb, (unsigned char *)pDB->vaddr, 
+                                       status & RX_FRAME_LEN_MASK, 0);
+                       skb_put(skb, status & RX_FRAME_LEN_MASK); /* Make room */
+                       skb->protocol = eth_type_trans(skb, dev);
+                       netif_rx(skb);  /* pass the packet to upper layers */
+               }
+               else {
+                       if (au1000_debug > 4) {
+                               if (status & RX_MISSED_FRAME) 
+                                       printk("rx miss\n");
+                               if (status & RX_WDOG_TIMER) 
+                                       printk("rx wdog\n");
+                               if (status & RX_RUNT) 
+                                       printk("rx runt\n");
+                               if (status & RX_OVERLEN) 
+                                       printk("rx overlen\n");
+                               if (status & RX_COLL)
+                                       printk("rx coll\n");
+                               if (status & RX_MII_ERROR)
+                                       printk("rx mii error\n");
+                               if (status & RX_CRC_ERROR)
+                                       printk("rx crc error\n");
+                               if (status & RX_LEN_ERROR)
+                                       printk("rx len error\n");
+                               if (status & RX_U_CNTRL_FRAME)
+                                       printk("rx u control frame\n");
+                               if (status & RX_MISSED_FRAME)
+                                       printk("rx miss\n");
+                       }
+               }
+               prxd->buff_stat = (u32)(pDB->dma_addr | RX_DMA_ENABLE);
+               aup->rx_head = (aup->rx_head + 1) & (NUM_RX_DMA - 1);
+               sync();
+
+               /* next descriptor */
+               prxd = aup->rx_dma_ring[aup->rx_head];
+               buff_stat = prxd->buff_stat;
+               dev->last_rx = jiffies;
+       }
+       return 0;
+}
+
+
+/*
+ * Au1000 interrupt service routine.
+ */
+void au1000_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct net_device *dev = (struct net_device *) dev_id;
+
+       if (dev == NULL) {
+               printk(KERN_ERR "%s: isr: null dev ptr\n", dev->name);
+               return;
+       }
+       au1000_rx(dev);
+       au1000_tx_ack(dev);
+}
+
+
+/*
+ * The Tx ring has been full longer than the watchdog timeout
+ * value. The transmitter must be hung?
+ */
+static void au1000_tx_timeout(struct net_device *dev)
+{
+       printk(KERN_ERR "%s: au1000_tx_timeout: dev=%p\n", dev->name, dev);
+       reset_mac(dev);
+       au1000_init(dev);
+}
+
+
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
+{
+    int crc = -1;
+
+    while(--length >= 0) {
+               unsigned char current_octet = *data++;
+               int bit;
+               for (bit = 0; bit < 8; bit++, current_octet >>= 1)
+                       crc = (crc << 1) ^
+                               ((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
+    }
+    return crc;
+}
+
+static void set_rx_mode(struct net_device *dev)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+
+       /* fixme */
+       if (au1000_debug > 4) 
+               printk("%s: set_multicast: flags=%x\n", dev->name, dev->flags);
+
+       if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous. */
+               aup->mac->control |= MAC_PROMISCUOUS;
+               printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
+       } else if ((dev->flags & IFF_ALLMULTI)  ||
+                          dev->mc_count > MULTICAST_FILTER_LIMIT) {
+               aup->mac->control |= MAC_PASS_ALL_MULTI;
+               aup->mac->control &= ~MAC_PROMISCUOUS;
+               printk(KERN_INFO "%s: Pass all multicast\n", dev->name);
+       } else {
+               int i;
+               struct dev_mc_list *mclist;
+               u32 mc_filter[2];       /* Multicast hash filter */
+
+               mc_filter[1] = mc_filter[0] = 0;
+               for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+                        i++, mclist = mclist->next) {
+                       set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, mc_filter);
+               }
+               aup->mac->multi_hash_high = mc_filter[1];
+               aup->mac->multi_hash_low = mc_filter[0];
+               aup->mac->control |= MAC_HASH_MODE;
+       }
+}
+
+
+static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       //struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       u16 *data = (u16 *)&rq->ifr_data;
+
+       /* fixme */
+       switch(cmd) { 
+               case SIOCDEVPRIVATE:            /* Get the address of the PHY in use. */
+               data[0] = PHY_ADDRESS;
+               case SIOCDEVPRIVATE+1:          /* Read the specified MII register. */
+               //data[3] = mdio_read(ioaddr, data[0], data[1]); 
+               return 0;
+               case SIOCDEVPRIVATE+2:          /* Write the specified MII register */
+               //mdio_write(ioaddr, data[0], data[1], data[2]);
+               return 0;
+               default:
+               return -EOPNOTSUPP;
+       }
+}
+
+
+static int au1000_set_config(struct net_device *dev, struct ifmap *map)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       u16 control;
+
+       if (au1000_debug > 4)  {
+               printk("%s: set_config called: dev->if_port %d map->port %x\n", 
+                               dev->name, dev->if_port, map->port);
+       }
+
+       switch(map->port){
+               case IF_PORT_UNKNOWN: /* use auto here */   
+                       printk("auto\\n");
+                       dev->if_port = map->port;
+                       /* Link Down: the timer will bring it up */
+                       netif_carrier_off(dev);
+       
+                       /* read current control */
+                       control = mdio_read(dev, aup->phy_addr, MII_CONTROL);
+                       control &= ~(MII_CNTL_FDX | MII_CNTL_F100);
+
+                       /* enable auto negotiation and reset the negotiation */
+                       mdio_write(dev, aup->phy_addr,
+                                  MII_CONTROL, control | MII_CNTL_AUTO | MII_CNTL_RST_AUTO);
+
+                       break;
+    
+               case IF_PORT_10BASET: /* 10BaseT */         
+                       printk("10baseT\n");
+                       dev->if_port = map->port;
+       
+                       /* Link Down: the timer will bring it up */
+                       netif_carrier_off(dev);
+
+                       /* set Speed to 10Mbps, Half Duplex */
+                       control = mdio_read(dev, aup->phy_addr, MII_CONTROL);
+                       printk("read control %x\n", control);
+                       control &= ~(MII_CNTL_F100 | MII_CNTL_AUTO | MII_CNTL_FDX);
+       
+                       /* disable auto negotiation and force 10M/HD mode*/
+                       mdio_write(dev, aup->phy_addr, MII_CONTROL, control);
+                       break;
+    
+               case IF_PORT_100BASET: /* 100BaseT */
+               case IF_PORT_100BASETX: /* 100BaseTx */ 
+                       printk("100 base T/TX\n");
+                       dev->if_port = map->port;
+       
+                       /* Link Down: the timer will bring it up */
+                       netif_carrier_off(dev);
+       
+                       /* set Speed to 100Mbps, Half Duplex */
+                       /* disable auto negotiation and enable 100MBit Mode */
+                       control = mdio_read(dev, aup->phy_addr, MII_CONTROL);
+                       printk("read control %x\n", control);
+                       control &= ~(MII_CNTL_AUTO | MII_CNTL_FDX);
+                       control |= MII_CNTL_F100;
+                       mdio_write(dev, aup->phy_addr, MII_CONTROL, control);
+                       break;
+    
+               case IF_PORT_100BASEFX: /* 100BaseFx */
+                       printk("100 Base FX\n");
+                       dev->if_port = map->port;
+       
+                       /* Link Down: the timer will bring it up */
+                       netif_carrier_off(dev);
+       
+                       /* set Speed to 100Mbps, Full Duplex */
+                       /* disable auto negotiation and enable 100MBit Mode */
+                       control = mdio_read(dev, aup->phy_addr, MII_CONTROL);
+                       control &= ~MII_CNTL_AUTO;
+                       control |=  MII_CNTL_F100 | MII_CNTL_FDX;
+                       mdio_write(dev, aup->phy_addr, MII_CONTROL, control);
+                       break;
+               case IF_PORT_10BASE2: /* 10Base2 */
+               case IF_PORT_AUI: /* AUI */
+               /* These Modes are not supported (are they?)*/
+                       printk(KERN_INFO "Not supported");
+                       return -EOPNOTSUPP;
+                       break;
+    
+               default:
+                       printk("Invalid");
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static struct net_device_stats *au1000_get_stats(struct net_device *dev)
+{
+       struct au1000_private *aup = (struct au1000_private *) dev->priv;
+
+       if (au1000_debug > 4)
+               printk("%s: au1000_get_stats: dev=%p\n", dev->name, dev);
+
+       if (netif_device_present(dev)) {
+               return &aup->stats;
+       }
+       return 0;
+}
diff --git a/drivers/net/au1000_eth.h b/drivers/net/au1000_eth.h
new file mode 100644 (file)
index 0000000..e36821e
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ *
+ * Alchemy Semi Au1000 ethernet driver include file
+ *
+ * Author: Pete Popov <ppopov@mvista.com>
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ *
+ * ########################################################################
+ *
+ *  This program is free software; you can distribute it and/or modify it
+ *  under the terms of the GNU General Public License (Version 2) as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope 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.,
+ *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * ########################################################################
+ *
+ * 
+ */
+
+
+#define NUM_INTERFACES 2
+#define MAC_IOSIZE 0x10000
+#define NUM_RX_DMA 4       /* Au1000 has 4 rx hardware descriptors */
+#define NUM_TX_DMA 4       /* Au1000 has 4 tx hardware descriptors */
+
+#define NUM_RX_BUFFS 4
+#define NUM_TX_BUFFS 4
+#define MAX_BUF_SIZE 2048
+
+#define ETH_TX_TIMEOUT HZ/4
+#define MAC_MIN_PKT_SIZE 64
+
+#ifdef CONFIG_MIPS_PB1000
+#define PHY_ADDRESS              0
+#define PHY_CONTROL_DEFAULT 0x3000
+#define PHY_CONTROL_REG_ADDR     0
+#endif
+
+#define MULTICAST_FILTER_LIMIT 64
+
+/* FIXME 
+ * The PHY defines should be in a separate file.
+ */
+
+/* MII register offsets */
+#define        MII_CONTROL 0x0000
+#define MII_STATUS  0x0001
+#define MII_PHY_ID0 0x0002
+#define        MII_PHY_ID1 0x0003
+#define MII_ANADV   0x0004
+#define MII_ANLPAR  0x0005
+#define MII_AEXP    0x0006
+#define MII_ANEXT   0x0007
+#define MII_AUX_CNTRL 0x18
+
+/* mii registers specific to AMD 79C901 */
+#define        MII_STATUS_SUMMARY = 0x0018
+
+/* MII Control register bit definitions. */
+#define        MII_CNTL_FDX      0x0100
+#define MII_CNTL_RST_AUTO 0x0200
+#define        MII_CNTL_ISOLATE  0x0400
+#define MII_CNTL_PWRDWN   0x0800
+#define        MII_CNTL_AUTO     0x1000
+#define MII_CNTL_F100     0x2000
+#define        MII_CNTL_LPBK     0x4000
+#define MII_CNTL_RESET    0x8000
+
+/* MII Status register bit  */
+#define        MII_STAT_EXT        0x0001 
+#define MII_STAT_JAB        0x0002
+#define        MII_STAT_LINK       0x0004
+#define MII_STAT_CAN_AUTO   0x0008
+#define        MII_STAT_FAULT      0x0010 
+#define MII_STAT_AUTO_DONE  0x0020
+#define        MII_STAT_CAN_T      0x0800
+#define MII_STAT_CAN_T_FDX  0x1000
+#define        MII_STAT_CAN_TX     0x2000 
+#define MII_STAT_CAN_TX_FDX 0x4000
+#define        MII_STAT_CAN_T4     0x8000
+
+
+#define                MII_ID1_OUI_LO          0xFC00  /* low bits of OUI mask */
+#define                MII_ID1_MODEL           0x03F0  /* model number */
+#define                MII_ID1_REV             0x000F  /* model number */
+
+/* MII NWAY Register Bits ...
+   valid for the ANAR (Auto-Negotiation Advertisement) and
+   ANLPAR (Auto-Negotiation Link Partner) registers */
+#define        MII_NWAY_NODE_SEL 0x001f
+#define MII_NWAY_CSMA_CD  0x0001
+#define        MII_NWAY_T        0x0020
+#define MII_NWAY_T_FDX    0x0040
+#define        MII_NWAY_TX       0x0080
+#define MII_NWAY_TX_FDX   0x0100
+#define        MII_NWAY_T4       0x0200 
+#define MII_NWAY_PAUSE    0x0400 
+#define        MII_NWAY_RF       0x2000 /* Remote Fault */
+#define MII_NWAY_ACK      0x4000 /* Remote Acknowledge */
+#define        MII_NWAY_NP       0x8000 /* Next Page (Enable) */
+
+/* mii stsout register bits */
+#define        MII_STSOUT_LINK_FAIL 0x4000
+#define        MII_STSOUT_SPD       0x0080
+#define MII_STSOUT_DPLX      0x0040
+
+/* mii stsics register bits */
+#define        MII_STSICS_SPD       0x8000
+#define MII_STSICS_DPLX      0x4000
+#define        MII_STSICS_LINKSTS   0x0001
+
+/* mii stssum register bits */
+#define        MII_STSSUM_LINK  0x0008
+#define MII_STSSUM_DPLX  0x0004
+#define        MII_STSSUM_AUTO  0x0002
+#define MII_STSSUM_SPD   0x0001
+
+/* Auxilliary Control/Status Register */
+#define MII_AUX_FDX      0x0001
+#define MII_AUX_100      0x0002
+#define MII_AUX_F100     0x0004
+#define MII_AUX_ANEG     0x0008
+
+typedef struct mii_phy {
+       struct mii_phy * next;
+       struct mii_chip_info * chip_info;
+       int phy_addr;
+       u16 status;
+} mii_phy_t;
+
+struct phy_ops {
+       int (*phy_init) (struct net_device *, int);
+       int (*phy_reset) (struct net_device *, int);
+       int (*phy_status) (struct net_device *, int, u16 *, u16 *);
+};
+
+/* 
+ * Data Buffer Descriptor. Data buffers must be aligned on 32 byte 
+ * boundary for both, receive and transmit.
+ */
+typedef struct db_dest {
+       struct db_dest *pnext;
+       volatile u32 *vaddr;
+       dma_addr_t dma_addr;
+} db_dest_t;
+
+/*
+ * The transmit and receive descriptors are memory 
+ * mapped registers.
+ */
+typedef struct tx_dma {
+       u32 status;
+       u32 buff_stat;
+       u32 len;
+       u32 pad;
+} tx_dma_t;
+
+typedef struct rx_dma {
+       u32 status;
+       u32 buff_stat;
+       u32 pad[2];
+} rx_dma_t;
+
+
+/*
+ * MAC control registers, memory mapped.
+ */
+typedef struct mac_reg {
+       u32 control;
+       u32 mac_addr_high;
+       u32 mac_addr_low;
+       u32 multi_hash_high;
+       u32 multi_hash_low;
+       u32 mii_control;
+       u32 mii_data;
+       u32 flow_control;
+       u32 vlan1_tag;
+       u32 vlan2_tag;
+} mac_reg_t;
+
+
+struct au1000_private {
+       
+       db_dest_t *pDBfree;
+       db_dest_t db[NUM_RX_BUFFS+NUM_TX_BUFFS];
+       volatile rx_dma_t *rx_dma_ring[NUM_RX_DMA];
+       volatile tx_dma_t *tx_dma_ring[NUM_TX_DMA];
+       db_dest_t *rx_db_inuse[NUM_RX_DMA];
+       db_dest_t *tx_db_inuse[NUM_TX_DMA];
+       u32 rx_head;
+       u32 tx_head;
+       u32 tx_tail;
+       u32 tx_full;
+
+       mii_phy_t *mii;
+       struct phy_ops *phy_ops;
+       
+       /* These variables are just for quick access to certain regs addresses. */
+       volatile mac_reg_t *mac;  /* mac registers                      */   
+       volatile u32 *enable;     /* address of MAC Enable Register     */
+
+       u32 vaddr;                /* virtual address of rx/tx buffers   */
+       dma_addr_t dma_addr;      /* dma address of rx/tx buffers       */
+
+       u8 *hash_table;
+       u32 hash_mode;
+       u32 intr_work_done; /* number of Rx and Tx pkts processed in the isr */
+       u32 phy_addr;          /* PHY address */
+       u32 options;           /* User-settable misc. driver options. */
+       u32 drv_flags;
+       struct net_device_stats stats;
+       struct timer_list timer;
+       spinlock_t lock;       /* Serialise access to device */
+};
index 541ee4497ae32b9111e5948d58ad6a5726719f27..89a2871d38a7a423d4e9d22186a685805eb6d3e8 100644 (file)
 #define DMFE_TXTH_1K   0xC000          /* TX TH 1K  byte */
 
 #define DMFE_TIMER_WUT  (jiffies + HZ * 1)/* timer wakeup time : 1 second */
-#define DMFE_TX_TIMEOUT (HZ * 1.5)     /* tx packet time-out time 1.5 s" */
-#define DMFE_TX_KICK   (HZ * 0.5)      /* tx packet Kick-out time 0.5 s" */
+#define DMFE_TX_TIMEOUT ((3*HZ)/2)     /* tx packet time-out time 1.5 s" */
+#define DMFE_TX_KICK   (HZ/2)  /* tx packet Kick-out time 0.5 s" */
 
 #define DMFE_DBUG(dbug_now, msg, value) if (dmfe_debug || (dbug_now)) printk(KERN_ERR "<DMFE>: %s %lx\n", (msg), (long) (value))
 
index e0805405b84fa73e6db06aff81d3f7ddfdacc5bd..362a4aad48342b30b6af03370b8e32fd7c75623f 100644 (file)
@@ -16,9 +16,6 @@
  *    Phone: 1-703-847-0040 ext 103
  */
 
-static const char *version = 
-       "Equalizer1996: $Revision: 1.2.1 $ $Date: 1996/09/22 13:52:00 $ Simon Janes (simon@ncm.com)\n";
-
 /*
  * Sources:
  *   skeleton.c by Donald Becker.
@@ -124,6 +121,8 @@ static const char *version =
 
 #include <asm/uaccess.h>
 
+static const char version[] __initdata = 
+       "Equalizer1996: $Revision: 1.2.1 $ $Date: 1996/09/22 13:52:00 $ Simon Janes (simon@ncm.com)\n";
 
 #ifndef EQL_DEBUG
 /* #undef EQL_DEBUG      -* print nothing at all, not even a boot-banner */
@@ -360,9 +359,9 @@ static int eql_slave_xmit(struct sk_buff *skb, struct net_device *dev)
 #endif
                skb->dev = slave_dev;
                skb->priority = 1;
+               slave->bytes_queued += skb->len; 
                dev_queue_xmit (skb);
                eql->stats->tx_packets++;
-               slave->bytes_queued += skb->len; 
        }
        else
        {
index 3d65ef79ee21f8ae48f7453aa98b23eed58dfe30..c92631f3266cb33ab6a1e0f3174ffba43099ae1f 100644 (file)
@@ -354,21 +354,21 @@ static char *version =
 #define RESET                  ID_ROM_0
 
 /* This is the I/O address list to be probed when seeking the card */
-static unsigned int eth16i_portlist[] = {
+static unsigned int eth16i_portlist[] __initdata = {
        0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300, 0 
 };
 
-static unsigned int eth32i_portlist[] = { 
+static unsigned int eth32i_portlist[] __initdata = { 
        0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000,
        0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000, 0xF000, 0 
 };
 
 /* This is the Interrupt lookup table for Eth16i card */
-static unsigned int eth16i_irqmap[] = { 9, 10, 5, 15, 0 };
+static unsigned int eth16i_irqmap[] __initdata = { 9, 10, 5, 15, 0 };
 #define NUM_OF_ISA_IRQS    4
 
 /* This is the Interrupt lookup table for Eth32i card */
-static unsigned int eth32i_irqmap[] = { 3, 5, 7, 9, 10, 11, 12, 15, 0 };  
+static unsigned int eth32i_irqmap[] __initdata = { 3, 5, 7, 9, 10, 11, 12, 15, 0 };  
 #define EISA_IRQ_REG   0xc89
 #define NUM_OF_EISA_IRQS   8
 
@@ -434,7 +434,7 @@ static ushort  eth16i_parse_mediatype(const char* s);
 
 static struct net_device_stats *eth16i_get_stats(struct net_device *dev);
 
-static char cardname[] = "ICL EtherTeam 16i/32";
+static char cardname[] __initdata = "ICL EtherTeam 16i/32";
 
 int __init eth16i_probe(struct net_device *dev)
 {
@@ -832,7 +832,7 @@ static int eth16i_set_irq(struct net_device* dev)
 }
 #endif
 
-static int eth16i_get_irq(int ioaddr)
+static int __init eth16i_get_irq(int ioaddr)
 {
        unsigned char cbyte;
 
@@ -850,7 +850,7 @@ static int eth16i_get_irq(int ioaddr)
        }
 }
 
-static int eth16i_check_signature(int ioaddr)
+static int __init eth16i_check_signature(int ioaddr)
 {
        int i;
        unsigned char creg[4] = { 0 };
index 4b6ce5687657ca47dd43ce52125dd7b35bf5b500..f42ae29954081cd63930756408c40aefbfe95989 100644 (file)
@@ -5,43 +5,26 @@
  *
  * Driver for SGI's IOC3 based Ethernet cards as found in the PCI card.
  *
- * Copyright (C) 1999, 2000 Ralf Baechle
- * Copyright (C) 1995, 1999, 2000 by Silicon Graphics, Inc.
+ * Copyright (C) 1999, 2000, 2001 Ralf Baechle
+ * Copyright (C) 1995, 1999, 2000, 2001 by Silicon Graphics, Inc.
  *
- * Reporting bugs:
- *
- * If you find problems with this drivers, then if possible do the
- * following.  Hook up a terminal to the MSC port, send an NMI to the CPUs
- * by typing ^Tnmi (where ^T stands for <CTRL>-T).  You'll see something
- * like:
- * 1A 000: 
- * 1A 000: *** NMI while in Kernel and no NMI vector installed on node 0
- * 1A 000: *** Error EPC: 0xffffffff800265e4 (0xffffffff800265e4)
- * 1A 000: *** Press ENTER to continue.
- *
- * Next enter the command ``lw i:0x86000f0 0x18'' and include this
- * commands output which will look like below with your bugreport.
- *
- * 1A 000: POD MSC Dex> lw i:0x86000f0 0x18
- * 1A 000: 92000000086000f0: 0021f28c 00000000 00000000 00000000
- * 1A 000: 9200000008600100: a5000000 01cde000 00000000 000004e0
- * 1A 000: 9200000008600110: 00000650 00000000 00110b15 00000000
- * 1A 000: 9200000008600120: 006d0005 77bbca0a a5000000 01ce0000
- * 1A 000: 9200000008600130: 80000500 00000500 00002538 05690008
- * 1A 000: 9200000008600140: 00000000 00000000 000003e1 0000786d
+ * References:
+ *  o IOC3 ASIC specification 4.51, 1996-04-18
+ *  o IEEE 802.3 specification, 2000 edition
+ *  o DP38840A Specification, National Semiconductor, March 1997
  *
  * To do:
  *
- *  - Handle allocation failures in ioc3_alloc_skb() more gracefully.
- *  - Handle allocation failures in ioc3_init_rings().
- *  - Use prefetching for large packets.  What is a good lower limit for
+ *  o Handle allocation failures in ioc3_alloc_skb() more gracefully.
+ *  o Handle allocation failures in ioc3_init_rings().
+ *  o Use prefetching for large packets.  What is a good lower limit for
  *    prefetching?
- *  - We're probably allocating a bit too much memory.
- *  - Workarounds for various PHYs.
- *  - Proper autonegotiation.
- *  - What exactly is net_device_stats.tx_dropped supposed to count?
- *  - Use hardware checksums.
- *  - Convert to using the PCI infrastructure / IOC3 meta driver.
+ *  o We're probably allocating a bit too much memory.
+ *  o Use hardware checksums.
+ *  o Convert to using a IOC3 meta driver.
+ *  o Which PHYs might possibly be attached to the IOC3 in real live,
+ *    which workarounds are required for them?  Do we ever have Lucent's?
+ *  o For the 2.5 branch kill the mii-tool ioctls.
  */
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 
+#ifdef CONFIG_SERIAL
+#include <linux/serial.h>
+#include <asm/serial.h>
+#define IOC3_BAUD (22000000 / (3*16))
+#define IOC3_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+#endif
+
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
 #include <linux/skbuff.h>
+#include <linux/mii.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
+#include <asm/uaccess.h>
 #include <asm/sn/types.h>
 #include <asm/sn/sn0/addrs.h>
 #include <asm/sn/sn0/hubni.h>
 #include <asm/pci/bridge.h>
 
 /*
- * 32 RX buffers.  This is tunable in the range of 16 <= x < 512.  The
+ * 64 RX buffers.  This is tunable in the range of 16 <= x < 512.  The
  * value must be a power of two.
  */
 #define RX_BUFFS 64
 
 /*
- * Private ioctls that de facto are well known and used for examply
- * by mii-tool.
+ * Private ioctls that de facto are well known and used for example
+ * by mii-tool.  These are deprecated and will go away in 2.5.0.
  */
 #define SIOCGMIIPHY (SIOCDEVPRIVATE)   /* Read from current PHY */
 #define SIOCGMIIREG (SIOCDEVPRIVATE+1) /* Read any PHY register */
 #define SIOCGPARAMS (SIOCDEVPRIVATE+3) /* Read operational parameters */
 #define SIOCSPARAMS (SIOCDEVPRIVATE+4) /* Set operational parameters */
 
+/* Timer state engine. */
+enum ioc3_timer_state {
+       arbwait  = 0,   /* Waiting for auto negotiation to complete.          */
+       lupwait  = 1,   /* Auto-neg complete, awaiting link-up status.        */
+       ltrywait = 2,   /* Forcing try of all modes, from fastest to slowest. */
+       asleep   = 3,   /* Time inactive.                                     */
+};
+
 /* Private per NIC data of the driver.  */
 struct ioc3_private {
        struct ioc3 *regs;
@@ -100,8 +101,20 @@ struct ioc3_private {
        int tx_pi;                      /* TX producer index */
        int txqlen;
        u32 emcr, ehar_h, ehar_l;
-       struct timer_list negtimer;
        spinlock_t ioc3_lock;
+       struct net_device *dev;
+
+       /* Members used by autonegotiation  */
+       struct timer_list ioc3_timer;
+       enum ioc3_timer_state timer_state; /* State of auto-neg timer.     */
+       unsigned int timer_ticks;       /* Number of clicks at each state  */
+       unsigned short sw_bmcr;         /* sw copy of MII config register  */
+       unsigned short sw_bmsr;         /* sw copy of MII status register  */
+       unsigned short sw_physid1;      /* sw copy of PHYSID1              */
+       unsigned short sw_physid2;      /* sw copy of PHYSID2              */
+       unsigned short sw_advertise;    /* sw copy of ADVERTISE            */
+       unsigned short sw_lpa;          /* sw copy of LPA                  */
+       unsigned short sw_csconfig;     /* sw copy of CSCONFIG             */
 };
 
 static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -109,8 +122,8 @@ static void ioc3_set_multicast_list(struct net_device *dev);
 static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev);
 static void ioc3_timeout(struct net_device *dev);
 static inline unsigned int ioc3_hash(const unsigned char *addr);
-static inline void ioc3_stop(struct net_device *dev);
-static void ioc3_init(struct net_device *dev);
+static inline void ioc3_stop(struct ioc3_private *ip);
+static void ioc3_init(struct ioc3_private *ip);
 
 static const char ioc3_str[] = "IOC3 Ethernet";
 
@@ -341,8 +354,9 @@ static int nic_init(struct ioc3 *ioc3)
 /*
  * Read the NIC (Number-In-a-Can) device.
  */
-static void ioc3_get_eaddr(struct net_device *dev, struct ioc3 *ioc3)
+static void ioc3_get_eaddr(struct ioc3_private *ip)
 {
+       struct ioc3 *ioc3 = ip->regs;
        u8 nic[14];
        int i;
        int tries = 2; /* There may be some problem with the battery?  */
@@ -370,7 +384,7 @@ static void ioc3_get_eaddr(struct net_device *dev, struct ioc3 *ioc3)
 
        printk("Ethernet address is ");
        for (i = 2; i < 8; i++) {
-               dev->dev_addr[i - 2] = nic[i];
+               ip->dev->dev_addr[i - 2] = nic[i];
                printk("%02x", nic[i]);
                if (i < 7)
                        printk(":");
@@ -378,10 +392,15 @@ static void ioc3_get_eaddr(struct net_device *dev, struct ioc3 *ioc3)
        printk(".\n");
 }
 
-/* Caller must hold the ioc3_lock ever for MII readers.  This is also
-   used to protect the transmitter side but it's low contention.  */
-static u16 mii_read(struct ioc3 *ioc3, int phy, int reg)
+/*
+ * Caller must hold the ioc3_lock ever for MII readers.  This is also
+ * used to protect the transmitter side but it's low contention.
+ */
+static u16 mii_read(struct ioc3_private *ip, int reg)
 {
+       struct ioc3 *ioc3 = ip->regs;
+       int phy = ip->phy;
+
        while (ioc3->micr & MICR_BUSY);
        ioc3->micr = (phy << MICR_PHYADDR_SHIFT) | reg | MICR_READTRIG;
        while (ioc3->micr & MICR_BUSY);
@@ -389,16 +408,18 @@ static u16 mii_read(struct ioc3 *ioc3, int phy, int reg)
        return ioc3->midr_r & MIDR_DATA_MASK;
 }
 
-static void mii_write(struct ioc3 *ioc3, int phy, int reg, u16 data)
+static void mii_write(struct ioc3_private *ip, int reg, u16 data)
 {
+       struct ioc3 *ioc3 = ip->regs;
+       int phy = ip->phy;
+
        while (ioc3->micr & MICR_BUSY);
        ioc3->midr_w = data;
        ioc3->micr = (phy << MICR_PHYADDR_SHIFT) | reg;
        while (ioc3->micr & MICR_BUSY);
 }
 
-static int ioc3_mii_init(struct net_device *dev, struct ioc3_private *ip,
-                         struct ioc3 *ioc3);
+static int ioc3_mii_init(struct ioc3_private *ip);
 
 static struct net_device_stats *ioc3_get_stats(struct net_device *dev)
 {
@@ -410,9 +431,10 @@ static struct net_device_stats *ioc3_get_stats(struct net_device *dev)
 }
 
 static inline void
-ioc3_rx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
+ioc3_rx(struct ioc3_private *ip)
 {
        struct sk_buff *skb, *new_skb;
+       struct ioc3 *ioc3 = ip->regs;
        int rx_entry, n_entry, len;
        struct ioc3_erxbuf *rxb;
        unsigned long *rxr;
@@ -429,12 +451,9 @@ ioc3_rx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
        while (w0 & ERXBUF_V) {
                err = rxb->err;                         /* It's valid ...  */
                if (err & ERXBUF_GOODPKT) {
-                       len = (w0 >> ERXBUF_BYTECNT_SHIFT) & 0x7ff;
+                       len = ((w0 >> ERXBUF_BYTECNT_SHIFT) & 0x7ff) - 4;
                        skb_trim(skb, len);
-                       skb->protocol = eth_type_trans(skb, dev);
-                       netif_rx(skb);
-
-                       ip->rx_skbs[rx_entry] = NULL;   /* Poison  */
+                       skb->protocol = eth_type_trans(skb, ip->dev);
 
                        new_skb = ioc3_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC);
                        if (!new_skb) {
@@ -444,15 +463,18 @@ ioc3_rx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
                                new_skb = skb;
                                goto next;
                        }
+                       netif_rx(skb);
+
+                       ip->rx_skbs[rx_entry] = NULL;   /* Poison  */
 
-                       new_skb->dev = dev;
+                       new_skb->dev = ip->dev;
 
                        /* Because we reserve afterwards. */
                        skb_put(new_skb, (1664 + RX_OFFSET));
                        rxb = (struct ioc3_erxbuf *) new_skb->data;
                        skb_reserve(new_skb, RX_OFFSET);
 
-                       dev->last_rx = jiffies;
+                       ip->dev->last_rx = jiffies;
                        ip->stats.rx_packets++;         /* Statistics */
                        ip->stats.rx_bytes += len;
                } else {
@@ -485,9 +507,10 @@ next:
 }
 
 static inline void
-ioc3_tx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
+ioc3_tx(struct ioc3_private *ip)
 {
        unsigned long packets, bytes;
+       struct ioc3 *ioc3 = ip->regs;
        int tx_entry, o_entry;
        struct sk_buff *skb;
        u32 etcir;
@@ -518,7 +541,7 @@ ioc3_tx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
        ip->txqlen -= packets;
 
        if (ip->txqlen < 128)
-               netif_wake_queue(dev);
+               netif_wake_queue(ip->dev);
 
        ip->tx_ci = o_entry;
        spin_unlock(&ip->ioc3_lock);
@@ -532,31 +555,27 @@ ioc3_tx(struct net_device *dev, struct ioc3_private *ip, struct ioc3 *ioc3)
  * also consider to take the interface down.
  */
 static void
-ioc3_error(struct net_device *dev, struct ioc3_private *ip,
-           struct ioc3 *ioc3, u32 eisr)
+ioc3_error(struct ioc3_private *ip, u32 eisr)
 {
-       if (eisr & EISR_RXOFLO) {
-               printk(KERN_ERR "%s: RX overflow.\n", dev->name);
-       }
-       if (eisr & EISR_RXBUFOFLO) {
-               printk(KERN_ERR "%s: RX buffer overflow.\n", dev->name);
-       }
-       if (eisr & EISR_RXMEMERR) {
-               printk(KERN_ERR "%s: RX PCI error.\n", dev->name);
-       }
-       if (eisr & EISR_RXPARERR) {
-               printk(KERN_ERR "%s: RX SSRAM parity error.\n", dev->name);
-       }
-       if (eisr & EISR_TXBUFUFLO) {
-               printk(KERN_ERR "%s: TX buffer underflow.\n", dev->name);
-       }
-       if (eisr & EISR_TXMEMERR) {
-               printk(KERN_ERR "%s: TX PCI error.\n", dev->name);
-       }
-
-       ioc3_stop(dev);
-       ioc3_init(dev);
-       ioc3_mii_init(dev, ip, ioc3);
+       struct net_device *dev = ip->dev;
+       unsigned char *iface = dev->name;
+
+       if (eisr & EISR_RXOFLO)
+               printk(KERN_ERR "%s: RX overflow.\n", iface);
+       if (eisr & EISR_RXBUFOFLO)
+               printk(KERN_ERR "%s: RX buffer overflow.\n", iface);
+       if (eisr & EISR_RXMEMERR)
+               printk(KERN_ERR "%s: RX PCI error.\n", iface);
+       if (eisr & EISR_RXPARERR)
+               printk(KERN_ERR "%s: RX SSRAM parity error.\n", iface);
+       if (eisr & EISR_TXBUFUFLO)
+               printk(KERN_ERR "%s: TX buffer underflow.\n", iface);
+       if (eisr & EISR_TXMEMERR)
+               printk(KERN_ERR "%s: TX PCI error.\n", iface);
+
+       ioc3_stop(ip);
+       ioc3_init(ip);
+       ioc3_mii_init(ip);
 
        dev->trans_start = jiffies;
        netif_wake_queue(dev);
@@ -566,7 +585,7 @@ ioc3_error(struct net_device *dev, struct ioc3_private *ip,
    after the Tx thread.  */
 static void ioc3_interrupt(int irq, void *_dev, struct pt_regs *regs)
 {
-       struct net_device *dev = _dev;
+       struct net_device *dev = (struct net_device *)_dev;
        struct ioc3_private *ip = dev->priv;
        struct ioc3 *ioc3 = ip->regs;
        const u32 enabled = EISR_RXTIMERINT | EISR_RXOFLO | EISR_RXBUFOFLO |
@@ -582,53 +601,500 @@ static void ioc3_interrupt(int irq, void *_dev, struct pt_regs *regs)
 
                if (eisr & (EISR_RXOFLO | EISR_RXBUFOFLO | EISR_RXMEMERR |
                            EISR_RXPARERR | EISR_TXBUFUFLO | EISR_TXMEMERR))
-                       ioc3_error(dev, ip, ioc3, eisr);
+                       ioc3_error(ip, eisr);
                if (eisr & EISR_RXTIMERINT)
-                       ioc3_rx(dev, ip, ioc3);
+                       ioc3_rx(ip);
                if (eisr & EISR_TXEXPLICIT)
-                       ioc3_tx(dev, ip, ioc3);
+                       ioc3_tx(ip);
 
                eisr = ioc3->eisr & enabled;
        }
 }
 
-static void negotiate(unsigned long data)
+/*
+ * Auto negotiation.  The scheme is very simple.  We have a timer routine that
+ * keeps watching the auto negotiation process as it progresses.  The DP83840
+ * is first told to start doing it's thing, we set up the time and place the
+ * timer state machine in it's initial state.
+ *
+ * Here the timer peeks at the DP83840 status registers at each click to see
+ * if the auto negotiation has completed, we assume here that the DP83840 PHY
+ * will time out at some point and just tell us what (didn't) happen.  For
+ * complete coverage we only allow so many of the ticks at this level to run,
+ * when this has expired we print a warning message and try another strategy.
+ * This "other" strategy is to force the interface into various speed/duplex
+ * configurations and we stop when we see a link-up condition before the
+ * maximum number of "peek" ticks have occurred.
+ *
+ * Once a valid link status has been detected we configure the IOC3 to speak
+ * the most efficient protocol we could get a clean link for.  The priority
+ * for link configurations, highest first is:
+ *
+ *     100 Base-T Full Duplex
+ *     100 Base-T Half Duplex
+ *     10 Base-T Full Duplex
+ *     10 Base-T Half Duplex
+ *
+ * We start a new timer now, after a successful auto negotiation status has
+ * been detected.  This timer just waits for the link-up bit to get set in
+ * the BMCR of the DP83840.  When this occurs we print a kernel log message
+ * describing the link type in use and the fact that it is up.
+ *
+ * If a fatal error of some sort is signalled and detected in the interrupt
+ * service routine, and the chip is reset, or the link is ifconfig'd down
+ * and then back up, this entire process repeats itself all over again.
+ */
+static int ioc3_try_next_permutation(struct ioc3_private *ip)
+{
+       ip->sw_bmcr = mii_read(ip, MII_BMCR);
+
+       /* Downgrade from full to half duplex.  Only possible via ethtool.  */
+       if (ip->sw_bmcr & BMCR_FULLDPLX) {
+               ip->sw_bmcr &= ~BMCR_FULLDPLX;
+               mii_write(ip, MII_BMCR, ip->sw_bmcr);
+
+               return 0;
+       }
+
+       /* Downgrade from 100 to 10. */
+       if (ip->sw_bmcr & BMCR_SPEED100) {
+               ip->sw_bmcr &= ~BMCR_SPEED100;
+               mii_write(ip, MII_BMCR, ip->sw_bmcr);
+
+               return 0;
+       }
+
+       /* We've tried everything. */
+       return -1;
+}
+
+static void
+ioc3_display_link_mode(struct ioc3_private *ip)
+{
+       char *tmode = "";
+
+       ip->sw_lpa = mii_read(ip, MII_LPA);
+
+       if (ip->sw_lpa & (LPA_100HALF | LPA_100FULL)) {
+               if (ip->sw_lpa & LPA_100FULL)
+                       tmode = "100Mb/s, Full Duplex";
+               else
+                       tmode = "100Mb/s, Half Duplex";
+       } else {
+               if (ip->sw_lpa & LPA_10FULL)
+                       tmode = "10Mb/s, Full Duplex";
+               else
+                       tmode = "10Mb/s, Half Duplex";
+       }
+
+       printk(KERN_INFO "%s: Link is up at %s.\n", ip->dev->name, tmode);
+}
+
+static void
+ioc3_display_forced_link_mode(struct ioc3_private *ip)
+{
+       char *speed = "", *duplex = "";
+
+       ip->sw_bmcr = mii_read(ip, MII_BMCR);
+       if (ip->sw_bmcr & BMCR_SPEED100)
+               speed = "100Mb/s, ";
+       else
+               speed = "10Mb/s, ";
+       if (ip->sw_bmcr & BMCR_FULLDPLX)
+               duplex = "Full Duplex.\n";
+       else
+               duplex = "Half Duplex.\n";
+
+       printk(KERN_INFO "%s: Link has been forced up at %s%s", ip->dev->name,
+              speed, duplex);
+}
+
+static int ioc3_set_link_modes(struct ioc3_private *ip)
 {
-       struct net_device *dev = (struct net_device *) data;
-       struct ioc3_private *ip = dev->priv;
        struct ioc3 *ioc3 = ip->regs;
+       int full;
+
+       /*
+        * All we care about is making sure the bigmac tx_cfg has a
+        * proper duplex setting.
+        */
+       if (ip->timer_state == arbwait) {
+               ip->sw_lpa = mii_read(ip, MII_LPA);
+               if (!(ip->sw_lpa & (LPA_10HALF | LPA_10FULL |
+                                   LPA_100HALF | LPA_100FULL)))
+                       goto no_response;
+               if (ip->sw_lpa & LPA_100FULL)
+                       full = 1;
+               else if (ip->sw_lpa & LPA_100HALF)
+                       full = 0;
+               else if (ip->sw_lpa & LPA_10FULL)
+                       full = 1;
+               else
+                       full = 0;
+       } else {
+               /* Forcing a link mode. */
+               ip->sw_bmcr = mii_read(ip, MII_BMCR);
+               if (ip->sw_bmcr & BMCR_FULLDPLX)
+                       full = 1;
+               else
+                       full = 0;
+       }
+
+       if (full)
+               ip->emcr |= EMCR_DUPLEX;
+       else
+               ip->emcr &= ~EMCR_DUPLEX;
+
+       ioc3->emcr = ip->emcr;
+       ioc3->emcr;
+
+       return 0;
+
+no_response:
+
+       return 1;
+}
+
+static int is_lucent_phy(struct ioc3_private *ip)
+{
+       unsigned short mr2, mr3;
+       int ret = 0;
+
+       mr2 = mii_read(ip, MII_PHYSID1);
+       mr3 = mii_read(ip, MII_PHYSID2);
+       if ((mr2 & 0xffff) == 0x0180 && ((mr3 & 0xffff) >> 10) == 0x1d) {
+               ret = 1;
+       }
+
+       return ret;
+}
+
+static void ioc3_timer(unsigned long data)
+{
+       struct ioc3_private *ip = (struct ioc3_private *) data;
+       int restart_timer = 0;
+
+       ip->timer_ticks++;
+       switch (ip->timer_state) {
+       case arbwait:
+               /*
+                * Only allow for 5 ticks, thats 10 seconds and much too
+                * long to wait for arbitration to complete.
+                */
+               if (ip->timer_ticks >= 10) {
+                       /* Enter force mode. */
+       do_force_mode:
+                       ip->sw_bmcr = mii_read(ip, MII_BMCR);
+                       printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful,"
+                              " trying force link mode\n", ip->dev->name);
+                       ip->sw_bmcr = BMCR_SPEED100;
+                       mii_write(ip, MII_BMCR, ip->sw_bmcr);
+
+                       if (!is_lucent_phy(ip)) {
+                               /*
+                                * OK, seems we need do disable the transceiver
+                                * for the first tick to make sure we get an
+                                * accurate link state at the second tick.
+                                */
+                               ip->sw_csconfig = mii_read(ip, MII_CSCONFIG);
+                               ip->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
+                               mii_write(ip, MII_CSCONFIG, ip->sw_csconfig);
+                       }
+                       ip->timer_state = ltrywait;
+                       ip->timer_ticks = 0;
+                       restart_timer = 1;
+               } else {
+                       /* Anything interesting happen? */
+                       ip->sw_bmsr = mii_read(ip, MII_BMSR);
+                       if (ip->sw_bmsr & BMSR_ANEGCOMPLETE) {
+                               int ret;
+
+                               /* Just what we've been waiting for... */
+                               ret = ioc3_set_link_modes(ip);
+                               if (ret) {
+                                       /* Ooops, something bad happened, go to
+                                        * force mode.
+                                        *
+                                        * XXX Broken hubs which don't support
+                                        * XXX 802.3u auto-negotiation make this
+                                        * XXX happen as well.
+                                        */
+                                       goto do_force_mode;
+                               }
+
+                               /*
+                                * Success, at least so far, advance our state
+                                * engine.
+                                */
+                               ip->timer_state = lupwait;
+                               restart_timer = 1;
+                       } else {
+                               restart_timer = 1;
+                       }
+               }
+               break;
 
-       mod_timer(&ip->negtimer, jiffies + 20 * HZ);
+       case lupwait:
+               /*
+                * Auto negotiation was successful and we are awaiting a
+                * link up status.  I have decided to let this timer run
+                * forever until some sort of error is signalled, reporting
+                * a message to the user at 10 second intervals.
+                */
+               ip->sw_bmsr = mii_read(ip, MII_BMSR);
+               if (ip->sw_bmsr & BMSR_LSTATUS) {
+                       /*
+                        * Wheee, it's up, display the link mode in use and put
+                        * the timer to sleep.
+                        */
+                       ioc3_display_link_mode(ip);
+                       ip->timer_state = asleep;
+                       restart_timer = 0;
+               } else {
+                       if (ip->timer_ticks >= 10) {
+                               printk(KERN_NOTICE "%s: Auto negotiation successful, link still "
+                                      "not completely up.\n", ip->dev->name);
+                               ip->timer_ticks = 0;
+                               restart_timer = 1;
+                       } else {
+                               restart_timer = 1;
+                       }
+               }
+               break;
+
+       case ltrywait:
+               /*
+                * Making the timeout here too long can make it take
+                * annoyingly long to attempt all of the link mode
+                * permutations, but then again this is essentially
+                * error recovery code for the most part.
+                */
+               ip->sw_bmsr = mii_read(ip, MII_BMSR);
+               ip->sw_csconfig = mii_read(ip, MII_CSCONFIG);
+               if (ip->timer_ticks == 1) {
+                       if (!is_lucent_phy(ip)) {
+                               /*
+                                * Re-enable transceiver, we'll re-enable the
+                                * transceiver next tick, then check link state
+                                * on the following tick.
+                                */
+                               ip->sw_csconfig |= CSCONFIG_TCVDISAB;
+                               mii_write(ip, MII_CSCONFIG, ip->sw_csconfig);
+                       }
+                       restart_timer = 1;
+                       break;
+               }
+               if (ip->timer_ticks == 2) {
+                       if (!is_lucent_phy(ip)) {
+                               ip->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
+                               mii_write(ip, MII_CSCONFIG, ip->sw_csconfig);
+                       }
+                       restart_timer = 1;
+                       break;
+               }
+               if (ip->sw_bmsr & BMSR_LSTATUS) {
+                       /* Force mode selection success. */
+                       ioc3_display_forced_link_mode(ip);
+                       ioc3_set_link_modes(ip);  /* XXX error? then what? */
+                       ip->timer_state = asleep;
+                       restart_timer = 0;
+               } else {
+                       if (ip->timer_ticks >= 4) { /* 6 seconds or so... */
+                               int ret;
+
+                               ret = ioc3_try_next_permutation(ip);
+                               if (ret == -1) {
+                                       /*
+                                        * Aieee, tried them all, reset the
+                                        * chip and try all over again.
+                                        */
+                                       printk(KERN_NOTICE "%s: Link down, "
+                                              "cable problem?\n",
+                                              ip->dev->name);
+
+                                       ioc3_init(ip);
+                                       return;
+                               }
+                               if (!is_lucent_phy(ip)) {
+                                       ip->sw_csconfig = mii_read(ip,
+                                                           MII_CSCONFIG);
+                                       ip->sw_csconfig |= CSCONFIG_TCVDISAB;
+                                       mii_write(ip, MII_CSCONFIG,
+                                                 ip->sw_csconfig);
+                               }
+                               ip->timer_ticks = 0;
+                               restart_timer = 1;
+                       } else {
+                               restart_timer = 1;
+                       }
+               }
+               break;
+
+       case asleep:
+       default:
+               /* Can't happens.... */
+               printk(KERN_ERR "%s: Aieee, link timer is asleep but we got "
+                      "one anyways!\n", ip->dev->name);
+               restart_timer = 0;
+               ip->timer_ticks = 0;
+               ip->timer_state = asleep; /* foo on you */
+               break;
+       };
+
+       if (restart_timer) {
+               ip->ioc3_timer.expires = jiffies + ((12 * HZ)/10); /* 1.2s */
+               add_timer(&ip->ioc3_timer);
+       }
+}
+
+static void
+ioc3_start_auto_negotiation(struct ioc3_private *ip, struct ethtool_cmd *ep)
+{
+       int timeout;
+
+       /* Read all of the registers we are interested in now. */
+       ip->sw_bmsr      = mii_read(ip, MII_BMSR);
+       ip->sw_bmcr      = mii_read(ip, MII_BMCR);
+       ip->sw_physid1   = mii_read(ip, MII_PHYSID1);
+       ip->sw_physid2   = mii_read(ip, MII_PHYSID2);
+
+       /* XXX Check BMSR_ANEGCAPABLE, should not be necessary though. */
+
+       ip->sw_advertise = mii_read(ip, MII_ADVERTISE);
+       if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) {
+               /* Advertise everything we can support. */
+               if (ip->sw_bmsr & BMSR_10HALF)
+                       ip->sw_advertise |= ADVERTISE_10HALF;
+               else
+                       ip->sw_advertise &= ~ADVERTISE_10HALF;
+
+               if (ip->sw_bmsr & BMSR_10FULL)
+                       ip->sw_advertise |= ADVERTISE_10FULL;
+               else
+                       ip->sw_advertise &= ~ADVERTISE_10FULL;
+               if (ip->sw_bmsr & BMSR_100HALF)
+                       ip->sw_advertise |= ADVERTISE_100HALF;
+               else
+                       ip->sw_advertise &= ~ADVERTISE_100HALF;
+               if (ip->sw_bmsr & BMSR_100FULL)
+                       ip->sw_advertise |= ADVERTISE_100FULL;
+               else
+                       ip->sw_advertise &= ~ADVERTISE_100FULL;
+               mii_write(ip, MII_ADVERTISE, ip->sw_advertise);
+
+               /*
+                * XXX Currently no IOC3 card I know off supports 100BaseT4,
+                * XXX and this is because the DP83840 does not support it,
+                * XXX changes XXX would need to be made to the tx/rx logic in
+                * XXX the driver as well so I completely skip checking for it
+                * XXX in the BMSR for now.
+                */
+
+#ifdef AUTO_SWITCH_DEBUG
+               ASD(("%s: Advertising [ ", ip->dev->name));
+               if (ip->sw_advertise & ADVERTISE_10HALF)
+                       ASD(("10H "));
+               if (ip->sw_advertise & ADVERTISE_10FULL)
+                       ASD(("10F "));
+               if (ip->sw_advertise & ADVERTISE_100HALF)
+                       ASD(("100H "));
+               if (ip->sw_advertise & ADVERTISE_100FULL)
+                       ASD(("100F "));
+#endif
+
+               /* Enable Auto-Negotiation, this is usually on already... */
+               ip->sw_bmcr |= BMCR_ANENABLE;
+               mii_write(ip, MII_BMCR, ip->sw_bmcr);
+
+               /* Restart it to make sure it is going. */
+               ip->sw_bmcr |= BMCR_ANRESTART;
+               mii_write(ip, MII_BMCR, ip->sw_bmcr);
+
+               /* BMCR_ANRESTART self clears when the process has begun. */
+
+               timeout = 64;  /* More than enough. */
+               while (--timeout) {
+                       ip->sw_bmcr = mii_read(ip, MII_BMCR);
+                       if (!(ip->sw_bmcr & BMCR_ANRESTART))
+                               break; /* got it. */
+                       udelay(10);
+               }
+               if (!timeout) {
+                       printk(KERN_ERR "%s: IOC3 would not start auto "
+                              "negotiation BMCR=0x%04x\n",
+                              ip->dev->name, ip->sw_bmcr);
+                       printk(KERN_NOTICE "%s: Performing force link "
+                              "detection.\n", ip->dev->name);
+                       goto force_link;
+               } else {
+                       ip->timer_state = arbwait;
+               }
+       } else {
+force_link:
+               /*
+                * Force the link up, trying first a particular mode.  Either
+                * we are here at the request of ethtool or because the IOC3
+                * would not start to autoneg.
+                */
+
+               /*
+                * Disable auto-negotiation in BMCR, enable the duplex and
+                * speed setting, init the timer state machine, and fire it off.
+                */
+               if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) {
+                       ip->sw_bmcr = BMCR_SPEED100;
+               } else {
+                       if (ep->speed == SPEED_100)
+                               ip->sw_bmcr = BMCR_SPEED100;
+                       else
+                               ip->sw_bmcr = 0;
+                       if (ep->duplex == DUPLEX_FULL)
+                               ip->sw_bmcr |= BMCR_FULLDPLX;
+               }
+               mii_write(ip, MII_BMCR, ip->sw_bmcr);
+
+               if (!is_lucent_phy(ip)) {
+                       /*
+                        * OK, seems we need do disable the transceiver for the
+                        * first tick to make sure we get an accurate link
+                        * state at the second tick.
+                        */
+                       ip->sw_csconfig = mii_read(ip, MII_CSCONFIG);
+                       ip->sw_csconfig &= ~(CSCONFIG_TCVDISAB);
+                       mii_write(ip, MII_CSCONFIG, ip->sw_csconfig);
+               }
+               ip->timer_state = ltrywait;
+       }
+
+       del_timer(&ip->ioc3_timer);
+       ip->timer_ticks = 0;
+       ip->ioc3_timer.expires = jiffies + (12 * HZ)/10;  /* 1.2 sec. */
+       ip->ioc3_timer.data = (unsigned long) ip;
+       ip->ioc3_timer.function = &ioc3_timer;
+       add_timer(&ip->ioc3_timer);
 }
 
-static int ioc3_mii_init(struct net_device *dev, struct ioc3_private *ip,
-                         struct ioc3 *ioc3)
+static int ioc3_mii_init(struct ioc3_private *ip)
 {
-       u16 word, mii0;
-       int i, phy;
+       int i, found;
+       u16 word;
 
+       found = 0;
        spin_lock_irq(&ip->ioc3_lock);
-       phy = -1;
        for (i = 0; i < 32; i++) {
-               word = mii_read(ioc3, i, 2);
+               ip->phy = i;
+               word = mii_read(ip, 2);
                if ((word != 0xffff) && (word != 0x0000)) {
-                       phy = i;
+                       found = 1;
                        break;                  /* Found a PHY          */
                }
        }
-       if (phy == -1) {
+       if (!found) {
                spin_unlock_irq(&ip->ioc3_lock);
                return -ENODEV;
        }
-       ip->phy = phy;
-
-       /* Autonegotiate 100mbit and fullduplex. */
-       mii0 = mii_read(ioc3, ip->phy, 0);
-       mii_write(ioc3, ip->phy, 0, mii0 | 0x3100);
 
-       ip->negtimer.function = &negotiate;
-       ip->negtimer.data = (unsigned long) dev;
-       mod_timer(&ip->negtimer, jiffies);      /* Run it now  */
+       ioc3_start_auto_negotiation(ip, NULL);          // XXX ethtool
 
        spin_unlock_irq(&ip->ioc3_lock);
 
@@ -808,13 +1274,15 @@ ioc3_ssram_disc(struct ioc3_private *ip)
        }
 }
 
-static void ioc3_init(struct net_device *dev)
+static void ioc3_init(struct ioc3_private *ip)
 {
-       struct ioc3_private *ip = dev->priv;
+       struct net_device *dev = ip->dev;
        struct ioc3 *ioc3 = ip->regs;
 
+       del_timer(&ip->ioc3_timer);             /* Kill if running      */
+
        ioc3->emcr = EMCR_RST;                  /* Reset                */
-       ioc3->emcr;                             /* flush WB             */
+       ioc3->emcr;                             /* Flush WB             */
        udelay(4);                              /* Give it time ...     */
        ioc3->emcr = 0;
        ioc3->emcr;
@@ -832,7 +1300,7 @@ static void ioc3_init(struct net_device *dev)
        ioc3->ehar_l = ip->ehar_l;
        ioc3->ersr = 42;                        /* XXX should be random */
 
-       ioc3_init_rings(dev, ip, ioc3);
+       ioc3_init_rings(ip->dev, ip, ioc3);
 
        ip->emcr |= ((RX_OFFSET / 2) << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN |
                     EMCR_TXEN | EMCR_RXDMAEN | EMCR_RXEN;
@@ -843,9 +1311,8 @@ static void ioc3_init(struct net_device *dev)
        ioc3->eier;
 }
 
-static inline void ioc3_stop(struct net_device *dev)
+static inline void ioc3_stop(struct ioc3_private *ip)
 {
-       struct ioc3_private *ip = dev->priv;
        struct ioc3 *ioc3 = ip->regs;
 
        ioc3->emcr = 0;                         /* Shutup */
@@ -856,19 +1323,17 @@ static inline void ioc3_stop(struct net_device *dev)
 static int
 ioc3_open(struct net_device *dev)
 {
-       struct ioc3_private *ip;
+       struct ioc3_private *ip = dev->priv;
 
-       if (request_irq(dev->irq, ioc3_interrupt, 0, ioc3_str, dev)) {
+       if (request_irq(dev->irq, ioc3_interrupt, SA_SHIRQ, ioc3_str, dev)) {
                printk(KERN_ERR "%s: Can't get irq %d\n", dev->name, dev->irq);
 
                return -EAGAIN;
        }
 
-       ip = dev->priv;
-
        ip->ehar_h = 0;
        ip->ehar_l = 0;
-       ioc3_init(dev);
+       ioc3_init(ip);
 
        netif_start_queue(dev);
        return 0;
@@ -879,89 +1344,132 @@ ioc3_close(struct net_device *dev)
 {
        struct ioc3_private *ip = dev->priv;
 
-       del_timer(&ip->negtimer);
+       del_timer(&ip->ioc3_timer);
+
        netif_stop_queue(dev);
 
-       ioc3_stop(dev);                                 /* Flush */
+       ioc3_stop(ip);
        free_irq(dev->irq, dev);
 
        ioc3_free_rings(ip);
        return 0;
 }
 
+/*
+ * MENET cards have four IOC3 chips, which are attached to two sets of
+ * PCI slot resources each: the primary connections are on slots
+ * 0..3 and the secondaries are on 4..7
+ *
+ * All four ethernets are brought out to connectors; six serial ports
+ * (a pair from each of the first three IOC3s) are brought out to
+ * MiniDINs; all other subdevices are left swinging in the wind, leave
+ * them disabled.
+ */
+static inline int ioc3_is_menet(struct pci_dev *pdev)
+{
+       struct pci_dev *dev;
+
+       return pdev->bus->parent == NULL
+              && (dev = pci_find_slot(pdev->bus->number, PCI_DEVFN(0, 0)))
+              && dev->vendor == PCI_VENDOR_ID_SGI
+              && dev->device == PCI_DEVICE_ID_SGI_IOC3
+              && (dev = pci_find_slot(pdev->bus->number, PCI_DEVFN(1, 0)))
+              && dev->vendor == PCI_VENDOR_ID_SGI
+              && dev->device == PCI_DEVICE_ID_SGI_IOC3
+              && (dev = pci_find_slot(pdev->bus->number, PCI_DEVFN(2, 0)))
+              && dev->vendor == PCI_VENDOR_ID_SGI
+              && dev->device == PCI_DEVICE_ID_SGI_IOC3;
+}
+
+static void inline ioc3_serial_probe(struct pci_dev *pdev,
+                               struct ioc3 *ioc3)
+{
+       struct serial_struct req;
+
+       /*
+        * We need to recognice and treat the fourth MENET serial as it
+        * does not have an SuperIO chip attached to it, therefore attempting
+        * to access it will result in bus errors.  We call something an
+        * MENET if PCI slot 0, 1, 2 and 3 of a master PCI bus all have an IOC3
+        * in it.  This is paranoid but we want to avoid blowing up on a
+        * showhorn PCI box that happens to have 4 IOC3 cards in it so it's
+        * not paranoid enough ...
+        */
+       if (ioc3_is_menet(pdev) && PCI_SLOT(pdev->devfn) == 3)
+               return;
+
+       /* Register to interrupt zero because we share the interrupt with
+          the serial driver which we don't properly support yet.  */
+       memset(&req, 0, sizeof(req));
+       req.irq             = 0;
+       req.flags           = IOC3_COM_FLAGS;
+       req.io_type         = SERIAL_IO_MEM;
+       req.iomem_reg_shift = 0;
+       req.baud_base       = IOC3_BAUD;
+
+       req.iomem_base      = (unsigned char *) &ioc3->sregs.uarta;
+       register_serial(&req);
+
+       req.iomem_base      = (unsigned char *) &ioc3->sregs.uartb;
+       register_serial(&req);
+}
+
 static int __devinit ioc3_probe(struct pci_dev *pdev,
                                const struct pci_device_id *ent)
 {
-       u16 mii0, mii_status, mii2, mii3, mii4;
        struct net_device *dev = NULL;
        struct ioc3_private *ip;
        struct ioc3 *ioc3;
        unsigned long ioc3_base, ioc3_size;
        u32 vendor, model, rev;
-       int phy, err;
-
-       dev = init_etherdev(0, sizeof(struct ioc3_private));
+       int err;
 
+       dev = alloc_etherdev(sizeof(struct ioc3_private));
        if (!dev)
                return -ENOMEM;
 
+       err = pci_request_regions(pdev, "ioc3");
+       if (err)
+               goto out_free;
+
        SET_MODULE_OWNER(dev);
        ip = dev->priv;
-       memset(ip, 0, sizeof(*ip));
-
-       /*
-        * This probably needs to be register_netdevice, or call
-        * init_etherdev so that it calls register_netdevice. Quick
-        * hack for now.
-        */
-       netif_device_attach(dev);
+       ip->dev = dev;
 
        dev->irq = pdev->irq;
 
-       ioc3_base = pdev->resource[0].start;
-       ioc3_size = pdev->resource[0].end - ioc3_base;
+       ioc3_base = pci_resource_start(pdev, 0);
+       ioc3_size = pci_resource_len(pdev, 0);
        ioc3 = (struct ioc3 *) ioremap(ioc3_base, ioc3_size);
        if (!ioc3) {
-               printk(KERN_CRIT"%s: Unable to map device I/O.\n", dev->name);
+               printk(KERN_CRIT "ioc3eth(%s): ioremap failed, goodbye.\n",
+                      pdev->slot_name);
                err = -ENOMEM;
-               goto out_free;
+               goto out_res;
        }
        ip->regs = ioc3;
 
+#ifdef CONFIG_SERIAL
+       ioc3_serial_probe(pdev, ioc3);
+#endif
+
        spin_lock_init(&ip->ioc3_lock);
 
-       ioc3_stop(dev);
-       ip->emcr = 0;
-       ioc3_init(dev);
+       ioc3_stop(ip);
+       ioc3_init(ip);
 
-       init_timer(&ip->negtimer);
-       ioc3_mii_init(dev, ip, ioc3);
+       init_timer(&ip->ioc3_timer);
+       ioc3_mii_init(ip);
 
-       phy = ip->phy;
-       if (phy == -1) {
-               printk(KERN_CRIT"%s: Didn't find a PHY, goodbye.\n", dev->name);
+       if (ip->phy == -1) {
+               printk(KERN_CRIT "ioc3-eth(%s): Didn't find a PHY, goodbye.\n",
+                      pdev->slot_name);
                err = -ENODEV;
                goto out_stop;
        }
 
-       mii0 = mii_read(ioc3, phy, 0);
-       mii_status = mii_read(ioc3, phy, 1);
-       mii2 = mii_read(ioc3, phy, 2);
-       mii3 = mii_read(ioc3, phy, 3);
-       mii4 = mii_read(ioc3, phy, 4);
-       vendor = (mii2 << 12) | (mii3 >> 4);
-       model  = (mii3 >> 4) & 0x3f;
-       rev    = mii3 & 0xf;
-       printk(KERN_INFO"Using PHY %d, vendor 0x%x, model %d, rev %d.\n",
-              phy, vendor, model, rev);
-       printk(KERN_INFO "%s:  MII transceiver found at MDIO address "
-              "%d, config %4.4x status %4.4x.\n",
-              dev->name, phy, mii0, mii_status);
-
        ioc3_ssram_disc(ip);
-       printk("IOC3 SSRAM has %d kbyte.\n", ip->emcr & EMCR_BUFSIZ ? 128 : 64);
-
-       ioc3_get_eaddr(dev, ioc3);
+       ioc3_get_eaddr(ip);
 
        /* The IOC3-specific entries in the device structure. */
        dev->open               = ioc3_open;
@@ -973,20 +1481,40 @@ static int __devinit ioc3_probe(struct pci_dev *pdev,
        dev->do_ioctl           = ioc3_ioctl;
        dev->set_multicast_list = ioc3_set_multicast_list;
 
+       err = register_netdev(dev);
+       if (err)
+               goto out_stop;
+
+       vendor = (ip->sw_physid1 << 12) | (ip->sw_physid2 >> 4);
+       model  = (ip->sw_physid2 >> 4) & 0x3f;
+       rev    = ip->sw_physid2 & 0xf;
+       printk(KERN_INFO "%s: Using PHY %d, vendor 0x%x, model %d, "
+              "rev %d.\n", dev->name, ip->phy, vendor, model, rev);
+       printk(KERN_INFO "%s: IOC3 SSRAM has %d kbyte.\n", dev->name,
+              ip->emcr & EMCR_BUFSIZ ? 128 : 64);
+
        return 0;
 
 out_stop:
-       ioc3_stop(dev);
+       ioc3_stop(ip);
        free_irq(dev->irq, dev);
        ioc3_free_rings(ip);
+out_res:
+       pci_release_regions(pdev);
 out_free:
        kfree(dev);
        return err;
 }
 
-static void __devexit eepro100_remove_one (struct pci_dev *pdev)
+static void __devexit ioc3_remove_one (struct pci_dev *pdev)
 {
-       /* Later ... */
+       struct net_device *dev = pci_get_drvdata(pdev);
+       struct ioc3_private *ip = dev->priv;
+       struct ioc3 *ioc3 = ip->regs;
+
+       iounmap(ioc3);
+       /* pci_release_regions(pdev);  Will be used in 2.4.3 */
+       kfree(dev);
 }
 
 static struct pci_device_id ioc3_pci_tbl[] __devinitdata = {
@@ -999,7 +1527,7 @@ static struct pci_driver ioc3_driver = {
        name:           "ioc3-eth",
        id_table:       ioc3_pci_tbl,
        probe:          ioc3_probe,
-       /* remove:              ioc3_remove_one, */
+       remove:         ioc3_remove_one,
 };
 
 static int __init ioc3_init_module(void)
@@ -1080,13 +1608,12 @@ ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev)
 static void ioc3_timeout(struct net_device *dev)
 {
        struct ioc3_private *ip = dev->priv;
-       struct ioc3 *ioc3 = ip->regs;
 
        printk(KERN_ERR "%s: transmit timed out, resetting\n", dev->name);
 
-       ioc3_stop(dev);
-       ioc3_init(dev);
-       ioc3_mii_init(dev, ip, ioc3);
+       ioc3_stop(ip);
+       ioc3_init(ip);
+       ioc3_mii_init(ip);
 
        dev->trans_start = jiffies;
        netif_wake_queue(dev);
@@ -1096,7 +1623,7 @@ static void ioc3_timeout(struct net_device *dev)
  * Given a multicast ethernet address, this routine calculates the
  * address's bit index in the logical address filter mask
  */
-#define CRC_MASK        0xEDB88320
+#define CRC_MASK        0xedb88320
 
 static inline unsigned int
 ioc3_hash(const unsigned char *addr)
@@ -1128,37 +1655,126 @@ ioc3_hash(const unsigned char *addr)
        return temp;
 }
 
-/* Provide ioctl() calls to examine the MII xcvr state. */
+
+/* We provide both the mii-tools and the ethtool ioctls.  */
 static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
        struct ioc3_private *ip = dev->priv;
+       struct ethtool_cmd *ep_user = (struct ethtool_cmd *) rq->ifr_data;
        u16 *data = (u16 *)&rq->ifr_data;
        struct ioc3 *ioc3 = ip->regs;
-       int phy = ip->phy;
+       struct ethtool_cmd ecmd;
 
        switch (cmd) {
        case SIOCGMIIPHY:       /* Get the address of the PHY in use.  */
-               if (phy == -1)
+               if (ip->phy == -1)
                        return -ENODEV;
-               data[0] = phy;
+               data[0] = ip->phy;
                return 0;
 
-       case SIOCGMIIREG:       /* Read any PHY register.  */
+       case SIOCGMIIREG: {     /* Read a PHY register.  */
+               unsigned int phy = data[0];
+               unsigned int reg = data[1];
+
+               if (phy > 0x1f || reg > 0x1f)
+                       return -EINVAL;
+
                spin_lock_irq(&ip->ioc3_lock);
-               data[3] = mii_read(ioc3, data[0], data[1]);
+               while (ioc3->micr & MICR_BUSY);
+               ioc3->micr = (phy << MICR_PHYADDR_SHIFT) | reg | MICR_READTRIG;
+               while (ioc3->micr & MICR_BUSY);
+               data[3] = (ioc3->midr_r & MIDR_DATA_MASK);
                spin_unlock_irq(&ip->ioc3_lock);
+
                return 0;
 
-       case SIOCSMIIREG:       /* Write any PHY register.  */
+       case SIOCSMIIREG:       /* Write a PHY register.  */
+               phy = data[0];
+               reg = data[1];
+
                if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
+
+               if (phy > 0x1f || reg > 0x1f)
+                       return -EINVAL;
+
                spin_lock_irq(&ip->ioc3_lock);
-               mii_write(ioc3, data[0], data[1], data[2]);
+               while (ioc3->micr & MICR_BUSY);
+               ioc3->midr_w = data[2];
+               ioc3->micr = (phy << MICR_PHYADDR_SHIFT) | reg;
+               while (ioc3->micr & MICR_BUSY);
                spin_unlock_irq(&ip->ioc3_lock);
+
                return 0;
+               }
+       case SIOCETHTOOL:
+               if (copy_from_user(&ecmd, ep_user, sizeof(ecmd)))
+                       return -EFAULT;
+
+               if (ecmd.cmd == ETHTOOL_GSET) {
+                       ecmd.supported =
+                               (SUPPORTED_10baseT_Half |
+                                SUPPORTED_10baseT_Full |
+                                SUPPORTED_100baseT_Half |
+                                SUPPORTED_100baseT_Full | SUPPORTED_Autoneg |
+                                SUPPORTED_TP | SUPPORTED_MII);
+
+                       ecmd.port = PORT_TP;
+                       ecmd.transceiver = XCVR_INTERNAL;
+                       ecmd.phy_address = ip->phy;
+
+                       /* Record PHY settings. */
+                       spin_lock_irq(&ip->ioc3_lock);
+                       ip->sw_bmcr = mii_read(ip, MII_BMCR);
+                       ip->sw_lpa = mii_read(ip, MII_LPA);
+                       spin_unlock_irq(&ip->ioc3_lock);
+                       if (ip->sw_bmcr & BMCR_ANENABLE) {
+                               ecmd.autoneg = AUTONEG_ENABLE;
+                               ecmd.speed = (ip->sw_lpa &
+                                    (LPA_100HALF | LPA_100FULL)) ?
+                                    SPEED_100 : SPEED_10;
+                       if (ecmd.speed == SPEED_100)
+                               ecmd.duplex = (ip->sw_lpa & (LPA_100FULL)) ?
+                                             DUPLEX_FULL : DUPLEX_HALF;
+                       else
+                               ecmd.duplex = (ip->sw_lpa & (LPA_10FULL)) ?
+                                             DUPLEX_FULL : DUPLEX_HALF;
+                       } else {
+                               ecmd.autoneg = AUTONEG_DISABLE;
+                               ecmd.speed = (ip->sw_bmcr & BMCR_SPEED100) ?
+                                            SPEED_100 : SPEED_10;
+                               ecmd.duplex = (ip->sw_bmcr & BMCR_FULLDPLX) ?
+                                             DUPLEX_FULL : DUPLEX_HALF;
+                       }
+                       if (copy_to_user(ep_user, &ecmd, sizeof(ecmd)))
+                               return -EFAULT;
+                       return 0;
+               } else if (ecmd.cmd == ETHTOOL_SSET) {
+                       if (!capable(CAP_NET_ADMIN))
+                               return -EPERM;
+
+                       /* Verify the settings we care about. */
+                       if (ecmd.autoneg != AUTONEG_ENABLE &&
+                           ecmd.autoneg != AUTONEG_DISABLE)
+                               return -EINVAL;
+
+                       if (ecmd.autoneg == AUTONEG_DISABLE &&
+                           ((ecmd.speed != SPEED_100 &&
+                             ecmd.speed != SPEED_10) ||
+                            (ecmd.duplex != DUPLEX_HALF &&
+                             ecmd.duplex != DUPLEX_FULL)))
+                               return -EINVAL;
+
+                       /* Ok, do it to it. */
+                       del_timer(&ip->ioc3_timer);
+                       spin_lock_irq(&ip->ioc3_lock);
+                       ioc3_start_auto_negotiation(ip, &ecmd);
+                       spin_unlock_irq(&ip->ioc3_lock);
 
-       default:
-               return -EOPNOTSUPP;
+                       return 0;
+               } else
+               default:
+                       return -EOPNOTSUPP;
        }
 
        return -EOPNOTSUPP;
@@ -1169,10 +1785,11 @@ static void ioc3_set_multicast_list(struct net_device *dev)
        struct dev_mc_list *dmi = dev->mc_list;
        struct ioc3_private *ip = dev->priv;
        struct ioc3 *ioc3 = ip->regs;
-       char *addr = dmi->dmi_addr;
        u64 ehar = 0;
        int i;
 
+       netif_stop_queue(dev);                          /* Lock out others. */
+
        if (dev->flags & IFF_PROMISC) {                 /* Set promiscuous.  */
                /* Unconditionally log net taps.  */
                printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
@@ -1192,6 +1809,7 @@ static void ioc3_set_multicast_list(struct net_device *dev)
                        ip->ehar_l = 0xffffffff;
                } else {
                        for (i = 0; i < dev->mc_count; i++) {
+                               char *addr = dmi->dmi_addr;
                                dmi = dmi->next;
 
                                if (!(*addr & 1))
@@ -1205,6 +1823,8 @@ static void ioc3_set_multicast_list(struct net_device *dev)
                ioc3->ehar_h = ip->ehar_h;
                ioc3->ehar_l = ip->ehar_l;
        }
+
+       netif_wake_queue(dev);                  /* Let us get going again. */
 }
 
 MODULE_AUTHOR("Ralf Baechle <ralf@oss.sgi.com>");
diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c
new file mode 100644 (file)
index 0000000..6ef5286
--- /dev/null
@@ -0,0 +1,1358 @@
+/* Intel Professional Workstation/panther ethernet driver */
+/* lp486e.c: A panther 82596 ethernet driver for linux. */
+/*
+    History and copyrights:
+
+    Driver skeleton
+        Written 1993 by Donald Becker.
+        Copyright 1993 United States Government as represented by the Director,
+        National Security Agency.  This software may only be used and
+       distributed according to the terms of the GNU Public License
+       as modified by SRC, incorporated herein by reference.
+
+        The author may be reached as becker@super.org or
+        C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+    Apricot
+        Written 1994 by Mark Evans.
+        This driver is for the Apricot 82596 bus-master interface
+
+        Modularised 12/94 Mark Evans
+
+    Professional Workstation
+       Derived from apricot.c by Ard van Breemen
+       <ard@murphy.nl>|<ard@cstmel.hobby.nl>|<ard@cstmel.nl.eu.org>
+
+       Credits:
+       Thanks to Murphy Software BV for letting me write this in their time.
+       Well, actually, I get payed doing this...
+       (Also: see http://www.murphy.nl for murphy, and my homepage ~ard for
+       more information on the Professional Workstation)
+
+    Present version
+       aeb@cwi.nl
+*/
+/*
+    There are currently two motherboards that I know of in the
+    professional workstation. The only one that I know is the
+    intel panther motherboard. -- ard
+*/
+/*
+The pws is equipped with an intel 82596. This is a very intelligent controller
+which runs its own micro-code. Communication with the hostprocessor is done
+through linked lists of commands and buffers in the hostprocessors memory.
+A complete description of the 82596 is available from intel. Search for
+a file called "29021806.pdf". It is a complete description of the chip itself.
+To use it for the pws some additions are needed regarding generation of
+the PORT and CA signal, and the interrupt glue needed for a pc.
+I/O map:
+PORT  SIZE ACTION MEANING
+0xCB0    2 WRITE  Lower 16 bits for PORT command
+0xCB2    2 WRITE  Upper 16 bits for PORT command, and issue of PORT command
+0xCB4    1 WRITE  Generation of CA signal
+0xCB8    1 WRITE  Clear interrupt glue
+All other communication is through memory!
+*/
+
+#define SLOW_DOWN_IO udelay(5);
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+/* debug print flags */
+#define LOG_SRCDST    0x80000000
+#define LOG_STATINT   0x40000000
+#define LOG_STARTINT  0x20000000
+
+#define i596_debug debug
+
+static int i596_debug = 0;
+
+static const char * const medianame[] = {
+       "10baseT", "AUI",
+       "10baseT-FD", "AUI-FD",
+};
+
+#define LP486E_TOTAL_SIZE 16
+
+#define I596_NULL (0xffffffff)
+
+#define CMD_EOL                0x8000  /* The last command of the list, stop. */
+#define CMD_SUSP       0x4000  /* Suspend after doing cmd. */
+#define CMD_INTR       0x2000  /* Interrupt after doing cmd. */
+
+#define CMD_FLEX       0x0008  /* Enable flexible memory model */
+
+enum commands {
+       CmdNOP = 0,
+       CmdIASetup = 1,
+       CmdConfigure = 2,
+       CmdMulticastList = 3,
+       CmdTx = 4,
+       CmdTDR = 5,
+       CmdDump = 6,
+       CmdDiagnose = 7
+};
+
+char *CUcmdnames[8] = { "NOP", "IASetup", "Configure", "MulticastList",
+                       "Tx", "TDR", "Dump", "Diagnose" };
+
+/* Status word bits */
+#define        STAT_CX         0x8000  /* The CU finished executing a command
+                                  with the Interrupt bit set */
+#define        STAT_FR         0x4000  /* The RU finished receiving a frame */
+#define        STAT_CNA        0x2000  /* The CU left the active state */
+#define        STAT_RNR        0x1000  /* The RU left the active state */
+#define STAT_ACK       (STAT_CX | STAT_FR | STAT_CNA | STAT_RNR)
+#define        STAT_CUS        0x0700  /* Status of CU: 0: idle, 1: suspended,
+                                  2: active, 3-7: unused */
+#define STAT_RUS       0x00f0  /* Status of RU: 0: idle, 1: suspended,
+                                  2: no resources, 4: ready,
+                                  10: no resources due to no more RBDs,
+                                  12: no more RBDs, other: unused */
+#define        STAT_T          0x0008  /* Bus throttle timers loaded */
+#define        STAT_ZERO       0x0807  /* Always zero */
+
+#if 0
+static char *CUstates[8] = {
+       "idle", "suspended", "active", 0, 0, 0, 0, 0
+};
+static char *RUstates[16] = {
+       "idle", "suspended", "no resources", 0, "ready", 0, 0, 0,
+       0, 0, "no RBDs", 0, "out of RBDs", 0, 0, 0
+};
+
+static void
+i596_out_status(int status) {
+       int bad = 0;
+       char *s;
+
+       printk("status %4.4x:", status);
+       if (status == 0xffff)
+               printk(" strange..\n");
+       else {
+               if (status & STAT_CX)
+                       printk("  CU done");
+               if (status & STAT_CNA)
+                       printk("  CU stopped");
+               if (status & STAT_FR)
+                       printk("  got a frame");
+               if (status & STAT_RNR)
+                       printk("  RU stopped");
+               if (status & STAT_T)
+                       printk("  throttled");
+               if (status & STAT_ZERO)
+                       bad = 1;
+               s = CUstates[(status & STAT_CUS) >> 8];
+               if (!s)
+                       bad = 1;
+               else
+                       printk("  CU(%s)", s);
+               s = RUstates[(status & STAT_RUS) >> 4];
+               if (!s)
+                       bad = 1;
+               else
+                       printk("  RU(%s)", s);
+               if (bad)
+                       printk("  bad status");
+               printk("\n");
+       }
+}
+#endif
+
+/* Command word bits */
+#define ACK_CX         0x8000
+#define ACK_FR         0x4000
+#define ACK_CNA                0x2000
+#define ACK_RNR                0x1000
+
+#define CUC_START      0x0100
+#define CUC_RESUME     0x0200
+#define CUC_SUSPEND    0x0300
+#define CUC_ABORT      0x0400
+
+#define RX_START       0x0010
+#define RX_RESUME      0x0020
+#define RX_SUSPEND     0x0030
+#define RX_ABORT       0x0040
+
+typedef u32 phys_addr;
+
+static inline phys_addr
+va_to_pa(volatile void *x) {
+       return x ? virt_to_bus(x) : I596_NULL;
+}
+
+static inline void *
+pa_to_va(phys_addr x) {
+       return (x == I596_NULL) ? NULL : bus_to_virt(x);
+}
+
+/* status bits for cmd */
+#define CMD_STAT_C     0x8000  /* CU command complete */
+#define CMD_STAT_B     0x4000  /* CU command in progress */
+#define CMD_STAT_OK    0x2000  /* CU command completed without errors */
+#define CMD_STAT_A     0x1000  /* CU command abnormally terminated */
+
+struct i596_cmd {              /* 8 bytes */
+       unsigned short status;
+       unsigned short command;
+       phys_addr pa_next;      /* va_to_pa(struct i596_cmd *next) */
+};
+
+#define EOF            0x8000
+#define SIZE_MASK      0x3fff
+
+struct i596_tbd {
+       unsigned short size;
+       unsigned short pad;
+       phys_addr pa_next;      /* va_to_pa(struct i596_tbd *next) */
+       phys_addr pa_data;      /* va_to_pa(char *data) */
+       struct sk_buff *skb;
+};
+
+struct tx_cmd {
+       struct i596_cmd cmd;
+       phys_addr pa_tbd;       /* va_to_pa(struct i596_tbd *tbd) */
+       unsigned short size;
+       unsigned short pad;
+};
+
+/* status bits for rfd */
+#define RFD_STAT_C     0x8000  /* Frame reception complete */
+#define RFD_STAT_B     0x4000  /* Frame reception in progress */
+#define RFD_STAT_OK    0x2000  /* Frame received without errors */
+#define RFD_STATUS     0x1fff
+#define RFD_LENGTH_ERR 0x1000
+#define RFD_CRC_ERR    0x0800
+#define RFD_ALIGN_ERR  0x0400
+#define RFD_NOBUFS_ERR 0x0200
+#define RFD_DMA_ERR    0x0100  /* DMA overrun failure to acquire system bus */
+#define RFD_SHORT_FRAME_ERR    0x0080
+#define RFD_NOEOP_ERR  0x0040
+#define RFD_TRUNC_ERR  0x0020
+#define RFD_MULTICAST  0x0002  /* 0: destination had our address
+                                  1: destination was broadcast/multicast */
+#define RFD_COLLISION  0x0001
+
+/* receive frame descriptor */
+struct i596_rfd {
+       unsigned short stat;
+       unsigned short cmd;
+       phys_addr pa_next;      /* va_to_pa(struct i596_rfd *next) */
+       phys_addr pa_rbd;       /* va_to_pa(struct i596_rbd *rbd) */
+       unsigned short count;
+       unsigned short size;
+       char data[1532];
+};
+
+#define RBD_EL         0x8000
+#define RBD_P          0x4000
+#define RBD_SIZEMASK   0x3fff
+#define RBD_EOF                0x8000
+#define RBD_F          0x4000
+
+/* receive buffer descriptor */
+struct i596_rbd {
+       unsigned short size;
+       unsigned short pad;
+       phys_addr pa_next;      /* va_to_pa(struct i596_tbd *next) */
+       phys_addr pa_data;      /* va_to_pa(char *data) */
+       phys_addr pa_prev;      /* va_to_pa(struct i596_tbd *prev) */
+       
+       /* Driver private part */
+       struct sk_buff *skb;
+};
+
+#define RX_RING_SIZE 64
+#define RX_SKBSIZE (ETH_FRAME_LEN+10)
+#define RX_RBD_SIZE 32
+
+/* System Control Block - 40 bytes */
+struct i596_scb {
+       u16 status;             /* 0 */
+       u16 command;            /* 2 */
+       phys_addr pa_cmd;       /* 4 - va_to_pa(struct i596_cmd *cmd) */
+       phys_addr pa_rfd;       /* 8 - va_to_pa(struct i596_rfd *rfd) */
+       u32 crc_err;            /* 12 */
+       u32 align_err;          /* 16 */
+       u32 resource_err;       /* 20 */
+       u32 over_err;           /* 24 */
+       u32 rcvdt_err;          /* 28 */
+       u32 short_err;          /* 32 */
+       u16 t_on;               /* 36 */
+       u16 t_off;              /* 38 */
+};
+
+/* Intermediate System Configuration Pointer - 8 bytes */
+struct i596_iscp {
+       u32 busy;               /* 0 */
+       phys_addr pa_scb;       /* 4 - va_to_pa(struct i596_scb *scb) */
+};
+
+/* System Configuration Pointer - 12 bytes */
+struct i596_scp {
+       u32 sysbus;             /* 0 */
+       u32 pad;                /* 4 */
+       phys_addr pa_iscp;      /* 8 - va_to_pa(struct i596_iscp *iscp) */
+};
+
+/* Selftest and dump results - needs 16-byte alignment */
+/*
+ * The size of the dump area is 304 bytes. When the dump is executed
+ * by the Port command an extra word will be appended to the dump area.
+ * The extra word is a copy of the Dump status word (containing the
+ * C, B, OK bits). [I find 0xa006, with a0 for C+OK and 6 for dump]
+ */
+struct i596_dump {
+       u16 dump[153];          /* (304 = 130h) + 2 bytes */
+};
+
+struct i596_private {          /* aligned to a 16-byte boundary */
+       struct i596_scp scp;    /* 0 - needs 16-byte alignment */
+       struct i596_iscp iscp;  /* 12 */
+       struct i596_scb scb;    /* 20 */
+       u32 dummy;              /* 60 */
+       struct i596_dump dump;  /* 64 - needs 16-byte alignment */
+
+       struct i596_cmd set_add;
+       char eth_addr[8];       /* directly follows set_add */
+
+       struct i596_cmd set_conf;
+       char i596_config[16];   /* directly follows set_conf */
+
+       struct i596_cmd tdr;
+       unsigned long tdr_stat; /* directly follows tdr */
+
+       int last_restart;
+       volatile struct i596_rbd *rbd_list;
+       volatile struct i596_rbd *rbd_tail;
+       volatile struct i596_rfd *rx_tail;
+       volatile struct i596_cmd *cmd_tail;
+       volatile struct i596_cmd *cmd_head;
+       int cmd_backlog;
+       unsigned long last_cmd;
+       struct net_device_stats stats;
+};
+
+static char init_setup[14] = {
+       0x8E,   /* length 14 bytes, prefetch on */
+       0xC8,   /* default: fifo to 8, monitor off */
+       0x40,   /* default: don't save bad frames (apricot.c had 0x80) */
+       0x2E,   /* (default is 0x26)
+                  No source address insertion, 8 byte preamble */
+       0x00,   /* default priority and backoff */
+       0x60,   /* default interframe spacing */
+       0x00,   /* default slot time LSB */
+       0xf2,   /* default slot time and nr of retries */
+       0x00,   /* default various bits
+                  (0: promiscuous mode, 1: broadcast disable,
+                   2: encoding mode, 3: transmit on no CRS,
+                   4: no CRC insertion, 5: CRC type,
+                   6: bit stuffing, 7: padding) */
+       0x00,   /* default carrier sense and collision detect */
+       0x40,   /* default minimum frame length */
+       0xff,   /* (default is 0xff, and that is what apricot.c has;
+                  elp486.c has 0xfb: Enable crc append in memory.) */
+       0x00,   /* default: not full duplex */
+       0x7f    /* (default is 0x3f) multi IA */
+};
+
+static int i596_open(struct net_device *dev);
+static int i596_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int i596_close(struct net_device *dev);
+static struct net_device_stats *i596_get_stats(struct net_device *dev);
+static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd);
+static void print_eth(char *);
+static void set_multicast_list(struct net_device *dev);
+static void i596_tx_timeout(struct net_device *dev);
+
+static int
+i596_timeout(struct net_device *dev, char *msg, int ct) {
+       volatile struct i596_private *lp;
+       int boguscnt = ct;
+
+       lp = (struct i596_private *) dev->priv;
+       while (lp->scb.command) {
+               if (--boguscnt == 0) {
+                       printk("%s: %s timed out - stat %4.4x, cmd %4.4x\n",
+                              dev->name, msg,
+                              lp->scb.status, lp->scb.command);
+                       return 1;
+               }
+               udelay(5);
+       }
+       return 0;
+}
+
+static inline int
+init_rx_bufs(struct net_device *dev, int num) {
+       volatile struct i596_private *lp;
+       struct i596_rfd *rfd;
+       int i;
+       // struct i596_rbd *rbd;
+
+       lp = (struct i596_private *) dev->priv;
+       lp->scb.pa_rfd = I596_NULL;
+
+       for (i = 0; i < num; i++) {
+               rfd = kmalloc(sizeof(struct i596_rfd), GFP_KERNEL);
+               if (rfd == NULL)
+                       break;
+
+               rfd->stat = 0;
+               rfd->pa_rbd = I596_NULL;
+               rfd->count = 0;
+               rfd->size = 1532;
+               if (i == 0) {
+                       rfd->cmd = CMD_EOL;
+                       lp->rx_tail = rfd;
+               } else {
+                       rfd->cmd = 0;
+               }
+               rfd->pa_next = lp->scb.pa_rfd;
+               lp->scb.pa_rfd = va_to_pa(rfd);
+               lp->rx_tail->pa_next = lp->scb.pa_rfd;
+       }
+
+#if 0
+       for (i = 0; i<RX_RBD_SIZE; i++) {
+               rbd = kmalloc(sizeof(struct i596_rbd), GFP_KERNEL);
+               if (rbd) {
+                       rbd->pad = 0;
+                       rbd->count = 0;
+                       rbd->skb = dev_alloc_skb(RX_SKB_SIZE);
+                       if (!rbd->skb) {
+                               printk("dev_alloc_skb failed");
+                       }
+                       rbd->next = rfd->rbd;
+                       if (i) {
+                               rfd->rbd->prev = rbd;
+                               rbd->size = RX_SKB_SIZE;
+                       } else {
+                               rbd->size = (RX_SKB_SIZE | RBD_EL);
+                               lp->rbd_tail = rbd;
+                       }
+
+                       rfd->rbd = rbd;
+               } else {
+                       printk("Could not kmalloc rbd\n");
+               }
+       }
+       lp->rbd_tail->next = rfd->rbd;
+#endif
+       return (i);
+}
+
+static inline void
+remove_rx_bufs(struct net_device *dev) {
+       struct i596_private *lp;
+       struct i596_rfd *rfd;
+
+       lp = (struct i596_private *) dev->priv;
+       lp->rx_tail->pa_next = I596_NULL;
+
+       do {
+               rfd = pa_to_va(lp->scb.pa_rfd);
+               lp->scb.pa_rfd = rfd->pa_next;
+               kfree(rfd);
+       } while (rfd != lp->rx_tail);
+
+       lp->rx_tail = 0;
+
+#if 0
+       for (lp->rbd_list) {
+       }
+#endif
+}
+
+#define PORT_RESET              0x00    /* reset 82596 */
+#define PORT_SELFTEST           0x01    /* selftest */
+#define PORT_ALTSCP             0x02    /* alternate SCB address */
+#define PORT_DUMP               0x03    /* dump */
+
+#define IOADDR 0xcb0           /* real constant */
+#define IRQ    10              /* default IRQ - can be changed by ECU */
+
+/* The 82596 requires two 16-bit write cycles for a port command */
+static inline void
+PORT(phys_addr a, unsigned int cmd) {
+       if (a & 0xf)
+               printk("lp486e.c: PORT: address not aligned\n");
+       outw(((a & 0xffff) | cmd), IOADDR);
+       outw(((a>>16) & 0xffff), IOADDR+2);
+}
+
+static inline void
+CA(void) {
+       outb(0, IOADDR+4);
+       udelay(8);
+}
+
+static inline void
+CLEAR_INT(void) {
+       outb(0, IOADDR+8);
+}
+
+#define SIZE(x)        (sizeof(x)/sizeof((x)[0]))
+
+#if 0
+/* selftest or dump */
+static void
+i596_port_do(struct net_device *dev, int portcmd, char *cmdname) {
+       volatile struct i596_private *lp = dev->priv;
+       volatile u16 *outp;
+       int i, m;
+
+       memset((void *)&(lp->dump), 0, sizeof(struct i596_dump));
+       outp = &(lp->dump.dump[0]);
+
+       PORT(va_to_pa(outp), portcmd);
+       mdelay(30);             /* random, unmotivated */
+
+       printk("lp486e i82596 %s result:\n", cmdname);
+       for (m = SIZE(lp->dump.dump); m && lp->dump.dump[m-1] == 0; m--)
+               ;
+       for (i = 0; i < m; i++) {
+               printk(" %04x", lp->dump.dump[i]);
+               if (i%8 == 7)
+                       printk("\n");
+       }
+       printk("\n");
+}
+#endif
+
+static int
+i596_scp_setup(struct net_device *dev) {
+       volatile struct i596_private *lp = dev->priv;
+       int boguscnt;
+
+       /* Setup SCP, ISCP, SCB */
+       /*
+        * sysbus bits:
+        *  only a single byte is significant - here 0x44
+        *  0x80: big endian mode (details depend on stepping)
+        *  0x40: 1
+        *  0x20: interrupt pin is active low
+        *  0x10: lock function disabled
+        *  0x08: external triggering of bus throttle timers
+        *  0x06: 00: 82586 compat mode, 01: segmented mode, 10: linear mode
+        *  0x01: unused
+        */
+       lp->scp.sysbus = 0x00440000;            /* linear mode */
+       lp->scp.pad = 0;                        /* must be zero */
+       lp->scp.pa_iscp = va_to_pa(&(lp->iscp));
+
+       /*
+        * The CPU sets the ISCP to 1 before it gives the first CA()
+        */
+       lp->iscp.busy = 0x0001;
+       lp->iscp.pa_scb = va_to_pa(&(lp->scb));
+
+       lp->scb.command = 0;
+       lp->scb.status = 0;
+       lp->scb.pa_cmd = I596_NULL;
+       /* lp->scb.pa_rfd has been initialised already */
+
+       lp->last_cmd = jiffies;
+       lp->cmd_backlog = 0;
+       lp->cmd_head = NULL;
+
+       /*
+        * Reset the 82596.
+        * We need to wait 10 systemclock cycles, and
+        * 5 serial clock cycles.
+        */
+       PORT(0, PORT_RESET);    /* address part ignored */
+       udelay(100);
+
+       /*
+        * Before the CA signal is asserted, the default SCP address
+        * (0x00fffff4) can be changed to a 16-byte aligned value
+        */
+       PORT(va_to_pa(&lp->scp), PORT_ALTSCP);  /* change the scp address */
+
+       /*
+        * The initialization procedure begins when a
+        * Channel Attention signal is asserted after a reset.
+        */
+
+       CA();
+
+       /*
+        * The ISCP busy is cleared by the 82596 after the SCB address is read.
+        */
+       boguscnt = 100;
+       while (lp->iscp.busy) {
+               if (--boguscnt == 0) {
+                       /* No i82596 present? */
+                       printk("%s: i82596 initialization timed out\n",
+                              dev->name);
+                       return 1;
+               }
+               udelay(5);
+       }
+       /* I find here boguscnt==100, so no delay was required. */
+
+       return 0;
+}
+
+static int
+init_i596(struct net_device *dev) {
+       volatile struct i596_private *lp;
+
+       if (i596_scp_setup(dev))
+               return 1;
+
+       lp = (struct i596_private *) dev->priv;
+       lp->scb.command = 0;
+
+       memcpy ((void *)lp->i596_config, init_setup, 14);
+       lp->set_conf.command = CmdConfigure;
+       i596_add_cmd(dev, (void *)&lp->set_conf);
+
+       memcpy ((void *)lp->eth_addr, dev->dev_addr, 6);
+       lp->set_add.command = CmdIASetup;
+       i596_add_cmd(dev, (struct i596_cmd *)&lp->set_add);
+
+       lp->tdr.command = CmdTDR;
+       i596_add_cmd(dev, (struct i596_cmd *)&lp->tdr);
+
+       if (lp->scb.command && i596_timeout(dev, "i82596 init", 200))
+               return 1;
+
+       lp->scb.command = RX_START;
+       CA();
+
+       if (lp->scb.command && i596_timeout(dev, "Receive Unit start", 100))
+               return 1;
+
+       return 0;
+}
+
+/* Receive a single frame */
+static inline int
+i596_rx_one(struct net_device *dev, volatile struct i596_private *lp,
+           struct i596_rfd *rfd, int *frames) {
+
+       if (rfd->stat & RFD_STAT_OK) {
+               /* a good frame */
+               int pkt_len = (rfd->count & 0x3fff);
+               struct sk_buff *skb = dev_alloc_skb(pkt_len);
+
+               (*frames)++;
+
+               if (rfd->cmd & CMD_EOL)
+                       printk("Received on EOL\n");
+
+               if (skb == NULL) {
+                       printk ("%s: i596_rx Memory squeeze, "
+                               "dropping packet.\n", dev->name);
+                       lp->stats.rx_dropped++;
+                       return 1;
+               }
+
+               skb->dev = dev;         
+               memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len);
+
+               skb->protocol = eth_type_trans(skb,dev);
+               netif_rx(skb);
+               lp->stats.rx_packets++;
+       } else {
+#if 0
+               printk("Frame reception error status %04x\n",
+                      rfd->stat);
+#endif
+               lp->stats.rx_errors++;
+               if (rfd->stat & RFD_COLLISION)
+                       lp->stats.collisions++;
+               if (rfd->stat & RFD_SHORT_FRAME_ERR)
+                       lp->stats.rx_length_errors++;
+               if (rfd->stat & RFD_DMA_ERR)
+                       lp->stats.rx_over_errors++;
+               if (rfd->stat & RFD_NOBUFS_ERR)
+                       lp->stats.rx_fifo_errors++;
+               if (rfd->stat & RFD_ALIGN_ERR)
+                       lp->stats.rx_frame_errors++;
+               if (rfd->stat & RFD_CRC_ERR)
+                       lp->stats.rx_crc_errors++;
+               if (rfd->stat & RFD_LENGTH_ERR)
+                       lp->stats.rx_length_errors++;
+       }
+       rfd->stat = rfd->count = 0;
+       return 0;
+}
+
+static int
+i596_rx(struct net_device *dev) {
+       volatile struct i596_private *lp = (struct i596_private *) dev->priv;
+       struct i596_rfd *rfd;
+       int frames = 0;
+
+       while (1) {
+               rfd = pa_to_va(lp->scb.pa_rfd);
+               if (!rfd) {
+                       printk("i596_rx: NULL rfd?\n");
+                       return 0;
+               }
+#if 1
+               if (rfd->stat && !(rfd->stat & (RFD_STAT_C | RFD_STAT_B)))
+                       printk("SF:%p-%04x\n", rfd, rfd->stat);
+#endif
+               if (!(rfd->stat & RFD_STAT_C))
+                       break;          /* next one not ready */
+               if (i596_rx_one(dev, lp, rfd, &frames))
+                       break;          /* out of memory */
+               rfd->cmd = CMD_EOL;
+               lp->rx_tail->cmd = 0;
+               lp->rx_tail = rfd;
+               lp->scb.pa_rfd = rfd->pa_next;
+       }
+
+       return frames;
+}
+
+static void
+i596_cleanup_cmd(struct net_device *dev) {
+       volatile struct i596_private *lp;
+       struct i596_cmd *cmd;
+
+       lp = (struct i596_private *) dev->priv;
+       while (lp->cmd_head) {
+               cmd = (struct i596_cmd *)lp->cmd_head;
+
+               lp->cmd_head = pa_to_va(lp->cmd_head->pa_next);
+               lp->cmd_backlog--;
+
+               switch ((cmd->command) & 0x7) {
+                       case CmdTx: {
+                               struct tx_cmd *tx_cmd = (struct tx_cmd *) cmd;
+                               struct i596_tbd * tx_cmd_tbd;
+                               tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd);
+
+                               dev_kfree_skb_any(tx_cmd_tbd->skb);
+
+                               lp->stats.tx_errors++;
+                               lp->stats.tx_aborted_errors++;
+
+                               cmd->pa_next = I596_NULL;
+                               kfree((unsigned char *)tx_cmd);
+                               netif_wake_queue(dev);
+                               break;
+                       }
+                       case CmdMulticastList: {
+                               // unsigned short count = *((unsigned short *) (ptr + 1));
+
+                               cmd->pa_next = I596_NULL;
+                               kfree((unsigned char *)cmd);
+                               break;
+                       }
+                       default: {
+                               cmd->pa_next = I596_NULL;
+                               break;
+                       }
+               }
+       }
+
+       if (lp->scb.command && i596_timeout(dev, "i596_cleanup_cmd", 100))
+               ;
+
+       lp->scb.pa_cmd = va_to_pa(lp->cmd_head);
+}
+
+static inline void
+i596_reset(struct net_device *dev,
+          volatile struct i596_private *lp, int ioaddr) {
+
+       if (lp->scb.command && i596_timeout(dev, "i596_reset", 100))
+               ;
+
+       netif_stop_queue(dev);
+
+       lp->scb.command = CUC_ABORT | RX_ABORT;
+       CA();
+
+       /* wait for shutdown */
+       if (lp->scb.command && i596_timeout(dev, "i596_reset(2)", 400))
+               ;
+
+       i596_cleanup_cmd(dev);
+       i596_rx(dev);
+
+       netif_start_queue(dev);
+       /*dev_kfree_skb(skb, FREE_WRITE);*/
+       init_i596(dev);
+}
+
+static void i596_add_cmd(struct net_device *dev, struct i596_cmd *cmd) {
+       volatile struct i596_private *lp = dev->priv;
+       int ioaddr = dev->base_addr;
+       unsigned long flags;
+
+       cmd->status = 0;
+       cmd->command |= (CMD_EOL | CMD_INTR);
+       cmd->pa_next = I596_NULL;
+
+       save_flags(flags);
+       cli();
+       if (lp->cmd_head) {
+               lp->cmd_tail->pa_next = va_to_pa(cmd);
+       } else {
+               lp->cmd_head = cmd;
+               if (lp->scb.command && i596_timeout(dev, "i596_add_cmd", 100))
+                       ;
+               lp->scb.pa_cmd = va_to_pa(cmd);
+               lp->scb.command = CUC_START;
+               CA();
+       }
+       lp->cmd_tail = cmd;
+       lp->cmd_backlog++;
+
+       lp->cmd_head = pa_to_va(lp->scb.pa_cmd);
+       restore_flags(flags);
+
+       if (lp->cmd_backlog > 16) {
+               int tickssofar = jiffies - lp->last_cmd;
+               if (tickssofar < 25) return;
+
+               printk("%s: command unit timed out, status resetting.\n",
+                      dev->name);
+               i596_reset(dev, lp, ioaddr);
+       }
+}
+
+static int
+i596_open(struct net_device *dev) {
+       int i;
+
+       i = request_irq(dev->irq, &i596_interrupt, SA_SHIRQ, dev->name, dev);
+       if (i) {
+               printk("%s: IRQ %d not free\n", dev->name, dev->irq);
+               return i;
+       }
+
+       if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
+               printk("%s: only able to allocate %d receive buffers\n",
+                      dev->name, i);
+
+       if (i < 4) {
+// release buffers
+               free_irq(dev->irq, dev);
+               return -EAGAIN;
+       }
+
+       netif_start_queue(dev);
+
+       init_i596(dev);
+
+       return 0;                       /* Always succeed */
+}
+
+static int
+i596_start_xmit (struct sk_buff *skb, struct net_device *dev) {
+       volatile struct i596_private *lp = dev->priv;
+       struct tx_cmd *tx_cmd;
+       short length;
+
+       /* If some higher level thinks we've missed a tx-done interrupt
+          we are passed NULL. n.b. dev_tint handles the cli()/sti()
+          itself. */
+       if (skb == NULL) {
+               printk ("What about dev_tint\n");
+               /* dev_tint(dev); */
+               return 0;
+       }
+
+       /* shouldn't happen */
+       if (skb->len <= 0)
+               return 0;
+
+       length = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+       dev->trans_start = jiffies;
+
+       tx_cmd = (struct tx_cmd *)
+           kmalloc ((sizeof (struct tx_cmd)
+                     + sizeof (struct i596_tbd)), GFP_ATOMIC);
+       if (tx_cmd == NULL) {
+               printk ("%s: i596_xmit Memory squeeze, dropping packet.\n",
+                       dev->name);
+               lp->stats.tx_dropped++;
+
+               dev_kfree_skb (skb);
+       } else {
+               struct i596_tbd *tx_cmd_tbd;
+               tx_cmd_tbd = (struct i596_tbd *) (tx_cmd + 1);
+               tx_cmd->pa_tbd = va_to_pa (tx_cmd_tbd);
+               tx_cmd_tbd->pa_next = I596_NULL;
+
+               tx_cmd->cmd.command = (CMD_FLEX | CmdTx);
+
+               tx_cmd->pad = 0;
+               tx_cmd->size = 0;
+               tx_cmd_tbd->pad = 0;
+               tx_cmd_tbd->size = (EOF | length);
+
+               tx_cmd_tbd->pa_data = va_to_pa (skb->data);
+               tx_cmd_tbd->skb = skb;
+
+               if (i596_debug & LOG_SRCDST)
+                       print_eth (skb->data);
+
+               i596_add_cmd (dev, (struct i596_cmd *) tx_cmd);
+
+               lp->stats.tx_packets++;
+       }
+
+       return 0;
+}
+
+static void
+i596_tx_timeout (struct net_device *dev) {
+       volatile struct i596_private *lp = dev->priv;
+       int ioaddr = dev->base_addr;
+
+       /* Transmitter timeout, serious problems. */
+       printk ("%s: transmit timed out, status resetting.\n", dev->name);
+       lp->stats.tx_errors++;
+
+       /* Try to restart the adaptor */
+       if (lp->last_restart == lp->stats.tx_packets) {
+               printk ("Resetting board.\n");
+
+               /* Shutdown and restart */
+               i596_reset (dev, lp, ioaddr);
+       } else {
+               /* Issue a channel attention signal */
+               printk ("Kicking board.\n");
+               lp->scb.command = (CUC_START | RX_START);
+               CA();
+               lp->last_restart = lp->stats.tx_packets;
+       }
+       netif_wake_queue(dev);
+}
+
+static void
+print_eth(char *add) {
+       int i;
+
+       printk ("Dest  ");
+       for (i = 0; i < 6; i++)
+               printk(" %2.2X", (unsigned char) add[i]);
+       printk ("\n");
+
+       printk ("Source");
+       for (i = 0; i < 6; i++)
+               printk(" %2.2X", (unsigned char) add[i+6]);
+       printk ("\n");
+
+       printk ("type %2.2X%2.2X\n",
+               (unsigned char) add[12], (unsigned char) add[13]);
+}
+
+int __init
+lp486e_probe(struct net_device *dev) {
+       volatile struct i596_private *lp;
+       unsigned char eth_addr[6] = { 0, 0xaa, 0, 0, 0, 0 };
+       unsigned char *bios;
+       int i, j;
+       int ret = -ENOMEM;
+       static int probed;
+
+       if (probed)
+               return -ENODEV;
+       probed++;
+
+       if (!request_region(IOADDR, LP486E_TOTAL_SIZE, dev->name)) {
+               printk(KERN_ERR "lp486e: IO address 0x%x in use\n", IOADDR);
+               return -EBUSY;
+       }
+
+       /*
+        * Allocate working memory, 16-byte aligned
+        */
+       dev->mem_start = (unsigned long)
+               kmalloc(sizeof(struct i596_private) + 0x0f, GFP_KERNEL);
+       if (!dev->mem_start)
+               goto err_out;
+       dev->priv = (void *)((dev->mem_start + 0xf) & 0xfffffff0);
+       lp = (struct i596_private *) dev->priv;
+       memset((void *)lp, 0, sizeof(struct i596_private));
+
+       /*
+        * Do we really have this thing?
+        */
+       if (i596_scp_setup(dev)) {
+               ret = -ENODEV;
+               goto err_out_kfree;
+       }
+
+       dev->base_addr = IOADDR;
+       dev->irq = IRQ;
+
+       ether_setup(dev);
+
+       /*
+        * How do we find the ethernet address? I don't know.
+        * One possibility is to look at the EISA configuration area
+        * [0xe8000-0xe9fff]. This contains the ethernet address
+        * but not at a fixed address - things depend on setup options.
+        *
+        * If we find no address, or the wrong address, use
+        *   ifconfig eth0 hw ether a1:a2:a3:a4:a5:a6
+        * with the value found in the BIOS setup.
+        */
+       bios = bus_to_virt(0xe8000);
+       for (j = 0; j < 0x2000; j++) {
+               if (bios[j] == 0 && bios[j+1] == 0xaa && bios[j+2] == 0) {
+                       printk("%s: maybe address at BIOS 0x%x:",
+                              dev->name, 0xe8000+j);
+                       for (i = 0; i < 6; i++) {
+                               eth_addr[i] = bios[i+j];
+                               printk(" %2.2X", eth_addr[i]);
+                       }
+                       printk("\n");
+               }
+       }
+
+       printk("%s: lp486e 82596 at %#3lx, IRQ %d,",
+              dev->name, dev->base_addr, dev->irq);
+       for (i = 0; i < 6; i++)
+               printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]);
+       printk("\n");
+
+       /* The LP486E-specific entries in the device structure. */
+       dev->open = &i596_open;
+       dev->stop = &i596_close;
+       dev->hard_start_xmit = &i596_start_xmit;
+       dev->get_stats = &i596_get_stats;
+       dev->set_multicast_list = &set_multicast_list;
+       dev->watchdog_timeo = 5*HZ;
+       dev->tx_timeout = i596_tx_timeout;
+
+#if 0
+       /* selftest reports 0x320925ae - don't know what that means */
+       i596_port_do(dev, PORT_SELFTEST, "selftest");
+       i596_port_do(dev, PORT_DUMP, "dump");
+#endif
+       return 0;
+
+err_out_kfree:
+       kfree ((void *) dev->mem_start);
+err_out:
+       release_region(IOADDR, LP486E_TOTAL_SIZE);
+       return ret;
+}
+
+static void inline
+i596_handle_CU_completion(struct net_device *dev,
+                         volatile struct i596_private *lp,
+                         unsigned short status,
+                         unsigned short *ack_cmdp) {
+       volatile struct i596_cmd *cmd;
+       int frames_out = 0;
+       int commands_done = 0;
+       int cmd_val;
+
+       cmd = lp->cmd_head;
+
+       while (lp->cmd_head && (lp->cmd_head->status & CMD_STAT_C)) {
+               cmd = lp->cmd_head;
+
+               lp->cmd_head = pa_to_va(lp->cmd_head->pa_next);
+               lp->cmd_backlog--;
+
+               commands_done++;
+               cmd_val = cmd->command & 0x7;
+#if 0
+               printk("finished CU %s command (%d)\n",
+                      CUcmdnames[cmd_val], cmd_val);
+#endif
+               switch (cmd_val) {
+               case CmdTx:
+               {
+                       struct tx_cmd *tx_cmd;
+                       struct i596_tbd *tx_cmd_tbd;
+
+                       tx_cmd = (struct tx_cmd *) cmd;
+                       tx_cmd_tbd = pa_to_va(tx_cmd->pa_tbd);
+
+                       frames_out++;
+                       if (cmd->status & CMD_STAT_OK) {
+                               if (i596_debug)
+                                       print_eth(pa_to_va(tx_cmd_tbd->pa_data));
+                       } else {
+                               lp->stats.tx_errors++;
+                               if (i596_debug)
+                                       printk("transmission failure:%04x\n",
+                                              cmd->status);
+                               if (cmd->status & 0x0020)
+                                       lp->stats.collisions++;
+                               if (!(cmd->status & 0x0040))
+                                       lp->stats.tx_heartbeat_errors++;
+                               if (cmd->status & 0x0400)
+                                       lp->stats.tx_carrier_errors++;
+                               if (cmd->status & 0x0800)
+                                       lp->stats.collisions++;
+                               if (cmd->status & 0x1000)
+                                       lp->stats.tx_aborted_errors++;
+                       }
+                       dev_kfree_skb_irq(tx_cmd_tbd->skb);
+
+                       cmd->pa_next = I596_NULL;
+                       kfree((unsigned char *)tx_cmd);
+                       netif_wake_queue(dev);
+                       break;
+               }
+
+               case CmdMulticastList:
+                       cmd->pa_next = I596_NULL;
+                       kfree((unsigned char *)cmd);
+                       break;
+
+               case CmdTDR:
+               {
+                       unsigned long status = *((unsigned long *) (cmd + 1));
+                       if (status & 0x8000) {
+                               if (i596_debug)
+                                       printk("%s: link ok.\n", dev->name);
+                       } else {
+                               if (status & 0x4000)
+                                       printk("%s: Transceiver problem.\n",
+                                              dev->name);
+                               if (status & 0x2000)
+                                       printk("%s: Termination problem.\n",
+                                              dev->name);
+                               if (status & 0x1000)
+                                       printk("%s: Short circuit.\n",
+                                              dev->name);
+                               printk("%s: Time %ld.\n",
+                                      dev->name, status & 0x07ff);
+                       }
+               }
+               default:
+                       cmd->pa_next = I596_NULL;
+                       lp->last_cmd = jiffies;
+                       
+               }
+       }
+
+       cmd = lp->cmd_head;
+       while (cmd && (cmd != lp->cmd_tail)) {
+               cmd->command &= 0x1fff;
+               cmd = pa_to_va(cmd->pa_next);
+       }
+
+       if (lp->cmd_head)
+               *ack_cmdp |= CUC_START;
+       lp->scb.pa_cmd = va_to_pa(lp->cmd_head);
+}
+
+static void
+i596_interrupt (int irq, void *dev_instance, struct pt_regs *regs) {
+       struct net_device *dev = (struct net_device *) dev_instance;
+       volatile struct i596_private *lp;
+       unsigned short status, ack_cmd = 0;
+       int frames_in = 0;
+
+       if (dev == NULL) {
+               printk ("i596_interrupt(): irq %d for unknown device.\n", irq);
+               return;
+       }
+
+       lp = (struct i596_private *) dev->priv;
+
+       /*
+        * The 82596 examines the command, performs the required action,
+        * and then clears the SCB command word.
+        */
+       if (lp->scb.command && i596_timeout(dev, "interrupt", 40))
+               ;
+
+       /*
+        * The status word indicates the status of the 82596.
+        * It is modified only by the 82596.
+        *
+        * [So, we must not clear it. I find often status 0xffff,
+        *  which is not one of the values allowed by the docs.]
+        */
+       status = lp->scb.status;
+#if 0
+       if (i596_debug) {
+               printk("%s: i596 interrupt, ", dev->name);
+               i596_out_status(status);
+       }
+#endif
+       /* Impossible, but it happens - perhaps when we get
+          a receive interrupt but scb.pa_rfd is I596_NULL. */
+       if (status == 0xffff) {
+               printk("%s: i596_interrupt: got status 0xffff\n", dev->name);
+               goto out;
+       }
+
+       ack_cmd = (status & STAT_ACK);
+
+       if (status & (STAT_CX | STAT_CNA))
+               i596_handle_CU_completion(dev, lp, status, &ack_cmd);
+
+       if (status & (STAT_FR | STAT_RNR)) {
+               /* Restart the receive unit when it got inactive somehow */
+               if ((status & STAT_RNR) && netif_running(dev))
+                       ack_cmd |= RX_START;
+
+               if (status & STAT_FR) {
+                       frames_in = i596_rx(dev);
+                       if (!frames_in)
+                               printk("receive frame reported, but no frames\n");
+               }
+       }
+
+       /* acknowledge the interrupt */
+       /*
+       if ((lp->scb.pa_cmd != I596_NULL) && netif_running(dev))
+               ack_cmd |= CUC_START;
+       */
+
+       if (lp->scb.command && i596_timeout(dev, "i596 interrupt", 100))
+               ;
+
+       lp->scb.command = ack_cmd;
+
+       CLEAR_INT();
+       CA();
+
+ out:
+       return;
+}
+
+static int i596_close(struct net_device *dev) {
+       volatile struct i596_private *lp = dev->priv;
+
+       netif_stop_queue(dev);
+
+       if (i596_debug)
+               printk("%s: Shutting down ethercard, status was %4.4x.\n",
+                      dev->name, lp->scb.status);
+
+       lp->scb.command = (CUC_ABORT | RX_ABORT);
+       CA();
+
+       i596_cleanup_cmd(dev);
+
+       if (lp->scb.command && i596_timeout(dev, "i596_close", 200))
+               ;
+
+       free_irq(dev->irq, dev);
+       remove_rx_bufs(dev);
+
+       return 0;
+}
+
+static struct net_device_stats * i596_get_stats(struct net_device *dev) {
+       struct i596_private *lp = dev->priv;
+
+       return &lp->stats;
+}
+
+/*
+*      Set or clear the multicast filter for this adaptor.
+*/
+
+static void set_multicast_list(struct net_device *dev) {
+       volatile struct i596_private *lp = dev->priv;
+       struct i596_cmd *cmd;
+
+       if (i596_debug > 1)
+               printk ("%s: set multicast list %d\n",
+                       dev->name, dev->mc_count);
+
+       if (dev->mc_count > 0) {
+               struct dev_mc_list *dmi;
+               char *cp;
+               cmd = (struct i596_cmd *)
+                       kmalloc(sizeof(struct i596_cmd)+2+dev->mc_count*6,
+                               GFP_ATOMIC);
+               if (cmd == NULL) {
+                       printk ("%s: set_multicast Memory squeeze.\n",
+                               dev->name);
+                       return;
+               }
+               cmd->command = CmdMulticastList;
+               *((unsigned short *) (cmd + 1)) = dev->mc_count * 6;
+               cp = ((char *)(cmd + 1))+2;
+               for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) {
+                       memcpy(cp, dmi,6);
+                       cp += 6;
+               }
+               if (i596_debug & LOG_SRCDST)
+                       print_eth (((char *)(cmd + 1)) + 2);
+               i596_add_cmd(dev, cmd);
+       } else {
+               if (lp->set_conf.pa_next != I596_NULL) {
+                       return;
+               }
+               if (dev->mc_count == 0 &&
+                   !(dev->flags & (IFF_PROMISC | IFF_ALLMULTI))) {
+                       if (dev->flags & IFF_ALLMULTI)
+                               dev->flags |= IFF_PROMISC;
+                       lp->i596_config[8] &= ~0x01;
+               } else {
+                       lp->i596_config[8] |= 0x01;
+               }
+
+               i596_add_cmd(dev, (struct i596_cmd *) &lp->set_conf);
+       }
+}
+
+MODULE_AUTHOR("Ard van Breemen <ard@cstmel.nl.eu.org>");
+MODULE_DESCRIPTION("Intel Panther onboard i82596 driver");
+MODULE_PARM(debug, "i");
+//MODULE_PARM(max_interrupt_work, "i");
+//MODULE_PARM(reverse_probe, "i");
+//MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+
+static struct net_device dev_lp486e;
+static int full_duplex;
+static int options;
+static int io = IOADDR;
+static int irq = IRQ;
+
+static int __init lp486e_init_module(void) {
+       struct net_device *dev = &dev_lp486e;
+       dev->irq = irq;
+       dev->base_addr = io;
+       dev->init = lp486e_probe;
+       if (register_netdev(dev) != 0)
+               return -EIO;
+       full_duplex = 0;
+       options = 0;
+       return 0;
+}
+
+static void __exit lp486e_cleanup_module(void) {
+       unregister_netdev(&dev_lp486e);
+       kfree((void *)dev_lp486e.mem_start);
+       dev_lp486e.priv = NULL;
+       release_region(dev_lp486e.base_addr, LP486E_TOTAL_SIZE);
+}
+
+module_init(lp486e_init_module);
+module_exit(lp486e_cleanup_module);
index 8bfcf6faace131232c39dd3f8272079de2fc6b61..7c237c21c8b1a5b3481f3c0b8ba0aac36dd2c58b 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/netlink.h>
 
 extern int slip_init_ctrl_dev(void);
-extern int strip_init_ctrl_dev(void);
 extern int x25_asy_init_ctrl_dev(void);
   
 extern int dmascc_init(void);
@@ -23,13 +22,13 @@ extern int arcnet_init(void);
 extern int scc_enet_init(void); 
 extern int fec_enet_init(void); 
 extern int dlci_setup(void); 
-extern int lapbeth_init(void);
 extern int sdla_setup(void); 
 extern int sdla_c_setup(void); 
 extern int comx_init(void);
 extern int lmc_setup(void);
 
 extern int madgemc_probe(void);
+extern int uml_net_probe(void);
 
 /* Pad device name to IFNAMSIZ=16. F.e. __PAD6 is string of 9 zeros. */
 #define __PAD6 "\0\0\0\0\0\0\0\0\0"
@@ -64,9 +63,6 @@ static struct net_probe pci_probes[] __initdata = {
 #if defined(CONFIG_SDLA)
        {sdla_c_setup, 0},
 #endif
-#if defined(CONFIG_LAPBETHER)
-       {lapbeth_init, 0},
-#endif
 #if defined(CONFIG_ARCNET)
        {arcnet_init, 0},
 #endif
@@ -107,6 +103,10 @@ static struct net_probe pci_probes[] __initdata = {
 #ifdef CONFIG_MADGEMC
        {madgemc_probe, 0},
 #endif
+#ifdef CONFIG_UML_NET
+       {uml_net_probe, 0},
+#endif
        {NULL, 0},
 };
 
@@ -140,9 +140,6 @@ static void __init network_ldisc_init(void)
 #if defined(CONFIG_X25_ASY)
        x25_asy_init_ctrl_dev();
 #endif
-#if defined(CONFIG_STRIP)
-       strip_init_ctrl_dev();
-#endif
 }
 
 
index eace5f9b6b306f0f62203bf1524ccfd0141b90cf..e8ba145cd2c67cdff5e68764d6958919fec6625e 100644 (file)
@@ -795,6 +795,8 @@ static struct cardbus_override_struct {
        { PD(TI,1251B), &ti_ops },
        { PD(TI,1410),  &ti_ops },
        { PD(TI,1420),  &ti_ops },
+       { PD(TI,4410),  &ti_ops },
+       { PD(TI,4451),  &ti_ops },
 
        { PD(RICOH,RL5C465), &ricoh_ops },
        { PD(RICOH,RL5C466), &ricoh_ops },
index d42150b819076552d76a7fa235bbb97c13b88aef..4ba4f51155691a61381819c4edc3aee87fc65e13 100644 (file)
@@ -22,6 +22,9 @@
  *             Peter Denison <peterd@pnd-pc.demon.co.uk>
  *  2000-06-14 Added isapnp_probe_devs() and isapnp_activate_dev()
  *             Christoph Hellwig <hch@caldera.de>
+ *  2001-06-03  Added release_region calls to correspond with
+ *             request_region calls when a failure occurs.  Also
+ *             added KERN_* constants to printk() calls.
  */
 
 #include <linux/config.h>
@@ -407,7 +410,7 @@ static int __init isapnp_read_tag(unsigned char *type, unsigned short *size)
                *size = tag & 0x07;
        }
 #if 0
-       printk("tag = 0x%x, type = 0x%x, size = %i\n", tag, *type, *size);
+       printk(KERN_DEBUG "tag = 0x%x, type = 0x%x, size = %i\n", tag, *type, *size);
 #endif
        if (type == 0)                          /* wrong type */
                return -1;
@@ -887,7 +890,7 @@ static int __init isapnp_create_device(struct pci_bus *card,
                                isapnp_skip_bytes(size);
                        return 1;
                default:
-                       printk("isapnp: unexpected or unknown tag type 0x%x for logical device %i (device %i), ignored\n", type, dev->devfn, card->number);
+                       printk(KERN_ERR "isapnp: unexpected or unknown tag type 0x%x for logical device %i (device %i), ignored\n", type, dev->devfn, card->number);
                }
              __skip:
                if (size > 0)
@@ -941,7 +944,7 @@ static void __init isapnp_parse_resource_map(struct pci_bus *card)
                                isapnp_skip_bytes(size);
                        return;
                default:
-                       printk("isapnp: unexpected or unknown tag type 0x%x for device %i, ignored\n", type, card->number);
+                       printk(KERN_ERR "isapnp: unexpected or unknown tag type 0x%x for device %i, ignored\n", type, card->number);
                }
              __skip:
                if (size > 0)
@@ -988,10 +991,10 @@ static int __init isapnp_build_device_list(void)
                isapnp_peek(header, 9);
                checksum = isapnp_checksum(header);
 #if 0
-               printk("vendor: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+               printk(KERN_DEBUG "vendor: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
                        header[0], header[1], header[2], header[3],
                        header[4], header[5], header[6], header[7], header[8]);
-               printk("checksum = 0x%x\n", checksum);
+               printk(KERN_DEBUG "checksum = 0x%x\n", checksum);
 #endif
                /* Don't be strict on the checksum, here !
                    e.g. 'SCM SwapBox Plug and Play' has header[8]==0 (should be: b7)*/
@@ -1011,7 +1014,7 @@ static int __init isapnp_build_device_list(void)
                INIT_LIST_HEAD(&card->devices);
                isapnp_parse_resource_map(card);
                if (isapnp_checksum_value != 0x00)
-                       printk("isapnp: checksum for device %i is not valid (0x%x)\n", csn, isapnp_checksum_value);
+                       printk(KERN_ERR "isapnp: checksum for device %i is not valid (0x%x)\n", csn, isapnp_checksum_value);
                card->checksum = isapnp_checksum_value;
 
                list_add_tail(&card->node, &isapnp_cards);
@@ -2181,19 +2184,22 @@ int __init isapnp_init(void)
 
        if (isapnp_disable) {
                isapnp_detected = 0;
-               printk("isapnp: ISA Plug & Play support disabled\n");
+               printk(KERN_INFO "isapnp: ISA Plug & Play support disabled\n");
                return 0;
        }
 #ifdef ISAPNP_REGION_OK
        pidxr_res=request_region(_PIDXR, 1, "isapnp index");
        if(!pidxr_res) {
-               printk("isapnp: Index Register 0x%x already used\n", _PIDXR);
+               printk(KERN_ERR "isapnp: Index Register 0x%x already used\n", _PIDXR);
                return -EBUSY;
        }
 #endif
        pnpwrp_res=request_region(_PNPWRP, 1, "isapnp write");
        if(!pnpwrp_res) {
-               printk("isapnp: Write Data Register 0x%x already used\n", _PNPWRP);
+               printk(KERN_ERR "isapnp: Write Data Register 0x%x already used\n", _PNPWRP);
+#ifdef ISAPNP_REGION_OK
+               release_region(_PIDXR, 1);
+#endif
                return -EBUSY;
        }
        
@@ -2202,12 +2208,16 @@ int __init isapnp_init(void)
         *      so let the user know where.
         */
         
-       printk("isapnp: Scanning for PnP cards...\n");
+       printk(KERN_INFO "isapnp: Scanning for PnP cards...\n");
        if (isapnp_rdp >= 0x203 && isapnp_rdp <= 0x3ff) {
                isapnp_rdp |= 3;
                isapnp_rdp_res=request_region(isapnp_rdp, 1, "isapnp read");
                if(!isapnp_rdp_res) {
-                       printk("isapnp: Read Data Register 0x%x already used\n", isapnp_rdp);
+                       printk(KERN_ERR "isapnp: Read Data Register 0x%x already used\n", isapnp_rdp);
+#ifdef ISAPNP_REGION_OK
+                       release_region(_PIDXR, 1);
+#endif
+                       release_region(isapnp_rdp, 1);
                        return -EBUSY;
                }
                isapnp_set_rdp();
@@ -2219,7 +2229,7 @@ int __init isapnp_init(void)
                    (isapnp_rdp < 0x203 || isapnp_rdp > 0x3ff)) {
                        isapnp_free_all_resources();
                        isapnp_detected = 0;
-                       printk("isapnp: No Plug & Play device found\n");
+                       printk(KERN_INFO "isapnp: No Plug & Play device found\n");
                        return 0;
                }
                isapnp_rdp_res=request_region(isapnp_rdp, 1, "isapnp read");
@@ -2231,19 +2241,19 @@ int __init isapnp_init(void)
                cards++;
                if (isapnp_verbose) {
                        struct list_head *devlist;
-                       printk( "isapnp: Card '%s'\n", card->name[0]?card->name:"Unknown");
+                       printk(KERN_INFO "isapnp: Card '%s'\n", card->name[0]?card->name:"Unknown");
                        if (isapnp_verbose < 2)
                                continue;
                        for (devlist = card->devices.next; devlist != &card->devices; devlist = devlist->next) {
                                struct pci_dev *dev = pci_dev_b(devlist);
-                               printk("isapnp:   Device '%s'\n", dev->name[0]?card->name:"Unknown");
+                               printk(KERN_INFO "isapnp:   Device '%s'\n", dev->name[0]?card->name:"Unknown");
                        }
                }
        }
        if (cards) {
-               printk("isapnp: %i Plug & Play card%s detected total\n", cards, cards>1?"s":"");
+               printk(KERN_INFO "isapnp: %i Plug & Play card%s detected total\n", cards, cards>1?"s":"");
        } else {
-               printk("isapnp: No Plug & Play card found\n");
+               printk(KERN_INFO "isapnp: No Plug & Play card found\n");
        }
 #ifdef CONFIG_PROC_FS
        isapnp_proc_init();
index ef9638f97330fc4366463dbce06b306b742f2c2e..da09031973bdf440e917a57c6b23b22582b9b1e9 100644 (file)
@@ -101,7 +101,7 @@ if [ "$CONFIG_MCA" = "y" ]; then
    fi
 fi
 if [ "$CONFIG_X86" = "y" ]; then
-   dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI
+   dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI $CONFIG_PCI
 fi
 dep_tristate 'Initio 9100U(W) support' CONFIG_SCSI_INITIO $CONFIG_SCSI $CONFIG_PCI
 dep_tristate 'Initio INI-A100U2W support' CONFIG_SCSI_INIA100 $CONFIG_SCSI $CONFIG_PCI
index 1c2960cd595534b82a4973832900370c0633d7ff..1a5fde3489e66324a8e2841b2ae733a336661768 100644 (file)
@@ -321,7 +321,8 @@ static      Scsi_Request * osst_do_scsi(Scsi_Request *SRpnt, OS_Scsi_Tape *STp,
                }
        }
 
-       cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0;
+        if (SRpnt->sr_device->scsi_level <= SCSI_2)
+                cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0;
        init_MUTEX_LOCKED(&STp->sem);
        SRpnt->sr_use_sg = (bytes > (STp->buffer)->sg[0].length) ?
                                    (STp->buffer)->use_sg : 0;
@@ -712,7 +713,7 @@ static int osst_wait_frame(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int curr,
                result = osst_get_frame_position (STp, aSRpnt);
                if (result == -EIO)
                        if ((result = osst_write_error_recovery(STp, aSRpnt, 0)) == 0)
-                               return 0;       /* successfull recovery leaves drive ready for frame */
+                               return 0;       /* successful recovery leaves drive ready for frame */
                if (result < 0) break;
                if (STp->first_frame_position == curr &&
                    ((minlast < 0 &&
@@ -1379,7 +1380,7 @@ static int osst_reposition_and_retry(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt,
        unsigned char   cmd[MAX_COMMAND_SIZE];
        Scsi_Request  * SRpnt;
        int             dev       = TAPE_NR(STp->devt);
-       int             expected  = 0;
+       int             expected  __attribute__ ((__unused__));
        int             attempts  = 1000 / skip;
        int             flag      = 1;
        long            startwait = jiffies;
index fe1cb597c009aee20f7a5ef840ef1a3b97cab585..b120123c7919889cafb2e0967335f4d794dad8e1 100644 (file)
@@ -595,7 +595,7 @@ typedef struct {
                                                * has been read into STp->buffer and is valid */
   int      frame_seq_number;                   /* logical frame number */
   int      logical_blk_num;                    /* logical block number */
-  unsigned first_frame_position;               /* physical frame to be transfered to/from host */
+  unsigned first_frame_position;               /* physical frame to be transferred to/from host */
   unsigned last_frame_position;                /* physical frame to be transferd to/from tape */
   int      cur_frames;                         /* current number of frames in internal buffer */
   int      max_frames;                         /* max number of frames in internal buffer */
index c064dfde4e3bc38161e0d2da97a44645aac543df..f08b680f69ee13831cfe55f9dc253353e0e21162 100644 (file)
@@ -7,7 +7,7 @@
  * Original driver (sg.c):
  *        Copyright (C) 1992 Lawrence Foard
  * Version 2 and 3 extensions to driver:
- *        Copyright (C) 1998 - 2000 Douglas Gilbert
+ *        Copyright (C) 1998 - 2001 Douglas Gilbert
  *
  *  Modified  19-JAN-1998  Richard Gooch <rgooch@atnf.csiro.au>  Devfs support
  *
@@ -19,9 +19,9 @@
  */
 #include <linux/config.h>
 #ifdef CONFIG_PROC_FS
- static char * sg_version_str = "Version: 3.1.17 (20001002)";
+ static char sg_version_str[] = "Version: 3.1.19 (20010623)";
 #endif
- static int sg_version_num = 30117; /* 2 digits for each component */
+ static int sg_version_num = 30119; /* 2 digits for each component */
 /*
  *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
  *      - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
@@ -76,8 +76,9 @@ static void sg_proc_cleanup(void);
 #include <linux/version.h>
 #endif /* LINUX_VERSION_CODE */
 
-/* #define SG_ALLOW_DIO */
-#ifdef SG_ALLOW_DIO
+#define SG_ALLOW_DIO_DEF 0
+#define SG_ALLOW_DIO_CODE      /* compile out be commenting this define */
+#ifdef SG_ALLOW_DIO_CODE
 #include <linux/iobuf.h>
 #endif
 
@@ -89,6 +90,7 @@ int sg_big_buff = SG_DEF_RESERVED_SIZE;
    readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into
    the kernel (i.e. it is not a module).] */
 static int def_reserved_size = -1;      /* picks up init parameter */
+static int sg_allow_dio = SG_ALLOW_DIO_DEF;
 
 #define SG_SECTOR_SZ 512
 #define SG_SECTOR_MSK (SG_SECTOR_SZ - 1)
@@ -112,7 +114,7 @@ static void sg_finish(void);
 static int sg_detect(Scsi_Device *);
 static void sg_detach(Scsi_Device *);
 
-static Scsi_Request * dummy_cmdp = 0;    /* only used for sizeof */
+static Scsi_Request * dummy_cmdp;      /* only used for sizeof */
 
 static rwlock_t sg_dev_arr_lock = RW_LOCK_UNLOCKED;  /* Also used to lock
                        file descriptor list for device */
@@ -157,7 +159,7 @@ typedef struct sg_request  /* SG_MAX_QUEUE requests outstanding per file */
     char res_used;              /* 1 -> using reserve buffer, 0 -> not ... */
     char orphan;                /* 1 -> drop on sight, 0 -> normal */
     char sg_io_owned;           /* 1 -> packet belongs to SG_IO */
-    char done;                  /* 0->before bh, 1->before read, 2->read */
+    volatile char done;         /* 0->before bh, 1->before read, 2->read */
 } Sg_request; /* 168 bytes long on i386 */
 
 typedef struct sg_fd /* holds the state of a file descriptor */
@@ -174,12 +176,12 @@ typedef struct sg_fd /* holds the state of a file descriptor */
     Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */
     char low_dma;       /* as in parent but possibly overridden to 1 */
     char force_packid;  /* 1 -> pack_id input to read(), 0 -> ignored */
-    char closed;        /* 1 -> fd closed but request(s) outstanding */
+    volatile char closed; /* 1 -> fd closed but request(s) outstanding */
     char fd_mem_src;    /* heap whereabouts of this Sg_fd object */
     char cmd_q;         /* 1 -> allow command queuing, 0 -> don't */
     char next_cmd_len;  /* 0 -> automatic (def), >0 -> use on next write() */
     char keep_orphan;   /* 0 -> drop orphan (def), 1 -> keep for read() */
-} Sg_fd; /* 2768 bytes long on i386 */
+} Sg_fd; /* 2760 bytes long on i386 */
 
 typedef struct sg_device /* holds the state of each scsi generic device */
 {
@@ -189,10 +191,10 @@ typedef struct sg_device /* holds the state of each scsi generic device */
     Sg_fd * headfp;     /* first open fd belonging to this device */
     devfs_handle_t de;
     kdev_t i_rdev;      /* holds device major+minor number */
-    char exclude;       /* opened for exclusive access */
+    volatile char detached;  /* 0->attached, 1->detached pending removal */
+    volatile char exclude;   /* opened for exclusive access */
     char sgdebug;       /* 0->off, 1->sense, 9->dump dev, 10-> all devs */
-    char detached;      /* 0->attached, 1->detached pending removal */
-} Sg_device; /* 44 bytes long on i386 */
+} Sg_device; /* 36 bytes long on i386 */
 
 
 static int sg_fasync(int fd, struct file * filp, int mode);
@@ -225,13 +227,12 @@ static char * sg_low_malloc(int rqSz, int lowDma, int mem_src,
 static void sg_low_free(char * buff, int size, int mem_src);
 static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev);
 static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);
+static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);
 static Sg_request * sg_get_rq_mark(Sg_fd * sfp, int pack_id);
 static Sg_request * sg_add_request(Sg_fd * sfp);
 static int sg_remove_request(Sg_fd * sfp, Sg_request * srp);
 static int sg_res_in_use(Sg_fd * sfp);
-static int sg_dio_in_use(Sg_fd * sfp);
 static void sg_clr_srpnt(Scsi_Request * SRpnt);
-static void sg_shorten_timeout(Scsi_Request * srpnt);
 static int sg_ms_to_jif(unsigned int msecs);
 static unsigned sg_jif_to_ms(int jifs);
 static int sg_allow_access(unsigned char opcode, char dev_type);
@@ -244,10 +245,10 @@ static int sg_last_dev(void);
 
 static Sg_device ** sg_dev_arr = NULL;
 
-static const int size_sg_header = sizeof(struct sg_header);
-static const int size_sg_io_hdr = sizeof(sg_io_hdr_t);
-static const int size_sg_iovec = sizeof(sg_iovec_t);
-static const int size_sg_req_info = sizeof(sg_req_info_t);
+#define SZ_SG_HEADER sizeof(struct sg_header)
+#define SZ_SG_IO_HDR sizeof(sg_io_hdr_t)
+#define SZ_SG_IOVEC sizeof(sg_iovec_t)
+#define SZ_SG_REQ_INFO sizeof(sg_req_info_t)
 
 
 static int sg_open(struct inode * inode, struct file * filp)
@@ -257,43 +258,56 @@ static int sg_open(struct inode * inode, struct file * filp)
     Sg_device * sdp;
     Sg_fd * sfp;
     int res;
+    int retval = -EBUSY;
 
+    SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));
     sdp = sg_get_dev(dev);
-    if ((! sdp) || (! sdp->device) || (! sdp->device->host))
-        return -ENXIO;
-    if (sdp->i_rdev != inode->i_rdev)
-        printk("sg_open: inode maj=%d, min=%d   sdp maj=%d, min=%d\n",
-               MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
-               MAJOR(sdp->i_rdev), MINOR(sdp->i_rdev));
-    /* If we are in the middle of error recovery, don't let anyone
-     * else try and use this device.  Also, if error recovery fails, it
-     * may try and take the device offline, in which case all further
-     * access to the device is prohibited.  */
-    if (! ((flags & O_NONBLOCK) || 
-          scsi_block_when_processing_errors(sdp->device)))
+    if ((! sdp) || (! sdp->device))
         return -ENXIO;
+    if (sdp->detached)
+       return -ENODEV;
 
-    SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));
+     /* This driver's module count bumped by fops_get in <linux/fs.h> */
+     /* Prevent the device driver from vanishing while we sleep */
+     if (sdp->device->host->hostt->module)
+        __MOD_INC_USE_COUNT(sdp->device->host->hostt->module);
+
+    if (! ((flags & O_NONBLOCK) ||
+          scsi_block_when_processing_errors(sdp->device))) {
+        retval = -ENXIO;
+       /* we are in error recovery for this device */
+       goto error_out;
+    }
 
     if (flags & O_EXCL) {
-        if (O_RDONLY == (flags & O_ACCMODE))
-            return -EACCES;   /* Can't lock it with read only access */
-        if (sdp->headfp && (flags & O_NONBLOCK))
-            return -EBUSY;
-        res = 0;  /* following is a macro that beats race condition */
+        if (O_RDONLY == (flags & O_ACCMODE))  {
+            retval = -EACCES;   /* Can't lock it with read only access */
+           goto error_out;
+       }
+       if (sdp->headfp && (flags & O_NONBLOCK))
+            goto error_out;
+        res = 0; 
        __wait_event_interruptible(sdp->o_excl_wait,
               ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)),
                                    res);
-        if (res)
-            return res; /* -ERESTARTSYS because signal hit process */
+        if (res) {
+            retval = res; /* -ERESTARTSYS because signal hit process */
+           goto error_out;
+        }
     }
     else if (sdp->exclude) { /* some other fd has an exclusive lock on dev */
         if (flags & O_NONBLOCK)
-            return -EBUSY;
-        res = 0;  /* following is a macro that beats race condition */
+            goto error_out;
+        res = 0; 
         __wait_event_interruptible(sdp->o_excl_wait, (! sdp->exclude), res);
-        if (res)
-            return res; /* -ERESTARTSYS because signal hit process */
+        if (res) {
+            retval = res; /* -ERESTARTSYS because signal hit process */
+           goto error_out;
+        }
+    }
+    if (sdp->detached) {
+       retval = -ENODEV;
+       goto error_out;
     }
     if (! sdp->headfp) { /* no existing opens on this device */
         sdp->sgdebug = 0;
@@ -303,12 +317,15 @@ static int sg_open(struct inode * inode, struct file * filp)
         filp->private_data = sfp;
     else {
         if (flags & O_EXCL) sdp->exclude = 0; /* undo if error */
-        return -ENOMEM;
+        retval = -ENOMEM;
+       goto error_out;
     }
-
-    if (sdp->device->host->hostt->module)
-        __MOD_INC_USE_COUNT(sdp->device->host->hostt->module);
     return 0;
+
+error_out:
+    if ((! sdp->detached) && sdp->device->host->hostt->module)
+        __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module);
+    return retval;
 }
 
 /* Following function was formerly called 'sg_close' */
@@ -324,14 +341,12 @@ static int sg_release(struct inode * inode, struct file * filp)
     }
     SCSI_LOG_TIMEOUT(3, printk("sg_release: dev=%d\n", MINOR(sdp->i_rdev)));
     sg_fasync(-1, filp, 0);   /* remove filp from async notification list */
-    sg_remove_sfp(sdp, sfp);
-    if (! sdp->headfp)
-        filp->private_data = NULL;
-
-    if (sdp->device->host->hostt->module)
-        __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module);
-    sdp->exclude = 0;
-    wake_up_interruptible(&sdp->o_excl_wait);
+    if (0 == sg_remove_sfp(sdp, sfp)) { /* Returns 1 when sdp gone */
+       if ((! sdp->detached) && sdp->device->host->hostt->module)
+           __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module);
+       sdp->exclude = 0;
+       wake_up_interruptible(&sdp->o_excl_wait);
+    }
     unlock_kernel();
     return 0;
 }
@@ -356,11 +371,11 @@ static ssize_t sg_read(struct file * filp, char * buf,
         ; /* FIXME: Hmm.  Seek to the right place, or fail?  */
     if ((k = verify_area(VERIFY_WRITE, buf, count)))
         return k;
-    if (sfp->force_packid && (count >= size_sg_header)) {
-       __copy_from_user(&old_hdr, buf, size_sg_header);
+    if (sfp->force_packid && (count >= SZ_SG_HEADER)) {
+       __copy_from_user(&old_hdr, buf, SZ_SG_HEADER);
        if (old_hdr.reply_len < 0) {
-           if (count >= size_sg_io_hdr) {
-               __copy_from_user(&new_hdr, buf, size_sg_io_hdr);
+           if (count >= SZ_SG_IO_HDR) {
+               __copy_from_user(&new_hdr, buf, SZ_SG_IO_HDR);
                req_pack_id = new_hdr.pack_id;
            }
        }
@@ -369,25 +384,26 @@ static ssize_t sg_read(struct file * filp, char * buf,
     }
     srp = sg_get_rq_mark(sfp, req_pack_id);
     if (! srp) { /* now wait on packet to arrive */
+       if (sdp->detached)
+           return -ENODEV;
         if (filp->f_flags & O_NONBLOCK)
             return -EAGAIN;
        while (1) {
-           int dio = sg_dio_in_use(sfp);
            res = 0;  /* following is a macro that beats race condition */
-           __wait_event_interruptible(sfp->read_wait,
-                                   (srp = sg_get_rq_mark(sfp, req_pack_id)),
-                                   res);
+           __wait_event_interruptible(sfp->read_wait, (sdp->detached || 
+                   (srp = sg_get_rq_mark(sfp, req_pack_id))), res);
+           if (sdp->detached)
+               return -ENODEV;
            if (0 == res)
                break;
-           else if (! dio)     /* only let signal out if no dio */
-               return res; /* -ERESTARTSYS because signal hit process */
+           return res; /* -ERESTARTSYS because signal hit process */
        }
     }
     if (srp->header.interface_id != '\0')
        return sg_new_read(sfp, buf, count, srp);
 
     hp = &srp->header;
-    memset(&old_hdr, 0, size_sg_header);
+    memset(&old_hdr, 0, SZ_SG_HEADER);
     old_hdr.reply_len = (int)hp->timeout;
     old_hdr.pack_len = old_hdr.reply_len;   /* very old, strange behaviour */
     old_hdr.pack_id = hp->pack_id;
@@ -430,13 +446,13 @@ static ssize_t sg_read(struct file * filp, char * buf,
     }
 
     /* Now copy the result back to the user buffer.  */
-    if (count >= size_sg_header) {
-       __copy_to_user(buf, &old_hdr, size_sg_header);
-        buf += size_sg_header;
+    if (count >= SZ_SG_HEADER) {
+       __copy_to_user(buf, &old_hdr, SZ_SG_HEADER);
+        buf += SZ_SG_HEADER;
        if (count > old_hdr.reply_len)
            count = old_hdr.reply_len;
-       if (count > size_sg_header)
-           sg_read_oxfer(srp, buf, count - size_sg_header);
+       if (count > SZ_SG_HEADER)
+           sg_read_oxfer(srp, buf, count - SZ_SG_HEADER);
     }
     else
        count = (old_hdr.result == 0) ? 0 : -EIO;
@@ -447,11 +463,14 @@ static ssize_t sg_read(struct file * filp, char * buf,
 static ssize_t sg_new_read(Sg_fd * sfp, char * buf, size_t count,
                           Sg_request * srp)
 {
-    sg_io_hdr_t         * hp = &srp->header;
-    int                   k, len;
+    sg_io_hdr_t * hp = &srp->header;
+    int err = 0;
+    int len;
 
-    if (count < size_sg_io_hdr)
-       return -EINVAL;
+    if (count < SZ_SG_IO_HDR) {
+       err = -EINVAL;
+       goto err_out;
+    }
     hp->sb_len_wr = 0;
     if ((hp->mx_sb_len > 0) && hp->sbp) {
        if ((CHECK_CONDITION & hp->masked_status) ||
@@ -460,20 +479,19 @@ static ssize_t sg_new_read(Sg_fd * sfp, char * buf, size_t count,
            sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len;
            len = 8 + (int)srp->sense_b[7]; /* Additional sense length field */
            len = (len > sb_len) ? sb_len : len;
-           if ((k = verify_area(VERIFY_WRITE, hp->sbp, len)))
-               return k;
+           if ((err = verify_area(VERIFY_WRITE, hp->sbp, len)))
+               goto err_out;
            __copy_to_user(hp->sbp, srp->sense_b, len);
            hp->sb_len_wr = len;
        }
     }
     if (hp->masked_status || hp->host_status || hp->driver_status)
        hp->info |= SG_INFO_CHECK;
-    copy_to_user(buf, hp, size_sg_io_hdr);
-
-    k = sg_read_xfer(srp);
-    if (k) return k; /* probably -EFAULT, bad addr in dxferp or iovec list */
+    copy_to_user(buf, hp, SZ_SG_IO_HDR);
+    err = sg_read_xfer(srp);
+err_out:
     sg_finish_rem_req(srp);
-    return count;
+    return (0 == err) ? count : err;
 }
 
 
@@ -494,7 +512,8 @@ static ssize_t sg_write(struct file * filp, const char * buf,
         return -ENXIO;
     SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n",
                                MINOR(sdp->i_rdev), (int)count));
-
+    if (sdp->detached)
+       return -ENODEV;
     if (! ((filp->f_flags & O_NONBLOCK) ||
            scsi_block_when_processing_errors(sdp->device)))
         return -ENXIO;
@@ -503,20 +522,20 @@ static ssize_t sg_write(struct file * filp, const char * buf,
 
     if ((k = verify_area(VERIFY_READ, buf, count)))
         return k;  /* protects following copy_from_user()s + get_user()s */
-    if (count < size_sg_header)
+    if (count < SZ_SG_HEADER)
        return -EIO;
-    __copy_from_user(&old_hdr, buf, size_sg_header);
+    __copy_from_user(&old_hdr, buf, SZ_SG_HEADER);
     blocking = !(filp->f_flags & O_NONBLOCK);
     if (old_hdr.reply_len < 0)
        return sg_new_write(sfp, buf, count, blocking, 0, NULL);
-    if (count < (size_sg_header + 6))
+    if (count < (SZ_SG_HEADER + 6))
        return -EIO;   /* The minimum scsi command length is 6 bytes. */
 
     if (! (srp = sg_add_request(sfp))) {
        SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n"));
        return -EDOM;
     }
-    buf += size_sg_header;
+    buf += SZ_SG_HEADER;
     __get_user(opcode, buf);
     if (sfp->next_cmd_len > 0) {
         if (sfp->next_cmd_len > MAX_COMMAND_SIZE) {
@@ -539,8 +558,8 @@ static ssize_t sg_write(struct file * filp, const char * buf,
     input_size = count - cmd_size;
     mxsize = (input_size > old_hdr.reply_len) ? input_size :
                                                old_hdr.reply_len;
-    mxsize -= size_sg_header;
-    input_size -= size_sg_header;
+    mxsize -= SZ_SG_HEADER;
+    input_size -= SZ_SG_HEADER;
     if (input_size < 0) {
         sg_remove_request(sfp, srp);
         return -EIO; /* User did not pass enough bytes for this command. */
@@ -550,16 +569,12 @@ static ssize_t sg_write(struct file * filp, const char * buf,
     hp->cmd_len = (unsigned char)cmd_size;
     hp->iovec_count = 0;
     hp->mx_sb_len = 0;
-#if 0
-    hp->dxfer_direction = SG_DXFER_UNKNOWN;
-#else
     if (input_size > 0)
-       hp->dxfer_direction = ((old_hdr.reply_len - size_sg_header) > 0) ?
+       hp->dxfer_direction = ((old_hdr.reply_len - SZ_SG_HEADER) > 0) ?
                              SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV;
     else
        hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV :
                                             SG_DXFER_NONE;
-#endif
     hp->dxfer_len = mxsize;
     hp->dxferp = (unsigned char *)buf + cmd_size;
     hp->sbp = NULL;
@@ -581,7 +596,7 @@ static ssize_t sg_new_write(Sg_fd * sfp, const char * buf, size_t count,
     unsigned char         cmnd[sizeof(dummy_cmdp->sr_cmnd)];
     int                   timeout;
 
-    if (count < size_sg_io_hdr)
+    if (count < SZ_SG_IO_HDR)
        return -EINVAL;
     if ((k = verify_area(VERIFY_READ, buf, count)))
        return k;  /* protects following copy_from_user()s + get_user()s */
@@ -592,7 +607,7 @@ static ssize_t sg_new_write(Sg_fd * sfp, const char * buf, size_t count,
        return -EDOM;
     }
     hp = &srp->header;
-    __copy_from_user(hp, buf, size_sg_io_hdr);
+    __copy_from_user(hp, buf, SZ_SG_IO_HDR);
     if (hp->interface_id != 'S') {
        sg_remove_request(sfp, srp);
        return -ENOSYS;
@@ -648,7 +663,10 @@ static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
        sg_finish_rem_req(srp);
        return k;
     }
-/*  SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */
+    if (sdp->detached) {
+       sg_finish_rem_req(srp);
+       return -ENODEV;
+    }
     SRpnt = scsi_allocate_request(sdp->device);
     if(SRpnt == NULL) {
        SCSI_LOG_TIMEOUT(1, printk("sg_write: no mem\n"));
@@ -656,17 +674,15 @@ static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
        return -ENOMEM;
     }
 
-/*  SCSI_LOG_TIMEOUT(7, printk("sg_write: device allocated\n")); */
     srp->my_cmdp = SRpnt;
     SRpnt->sr_request.rq_dev = sdp->i_rdev;
     SRpnt->sr_request.rq_status = RQ_ACTIVE;
     SRpnt->sr_sense_buffer[0] = 0;
     SRpnt->sr_cmd_len = hp->cmd_len;
-/* Set the LUN field in the command structure, overriding user input  */
-    if (! (hp->flags & SG_FLAG_LUN_INHIBIT))
-       cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5);
-
-/*  SCSI_LOG_TIMEOUT(7, printk("sg_write: do cmd\n")); */
+    if (! (hp->flags & SG_FLAG_LUN_INHIBIT)) {
+       if (sdp->device->scsi_level <= SCSI_2)
+           cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5);
+    }
     SRpnt->sr_use_sg = srp->data.k_use_sg;
     SRpnt->sr_sglist_len = srp->data.sglist_len;
     SRpnt->sr_bufflen = srp->data.bufflen;
@@ -719,30 +735,31 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
        {
            int blocking = 1;   /* ignore O_NONBLOCK flag */
 
+           if (sdp->detached)
+               return -ENODEV;
            if(! scsi_block_when_processing_errors(sdp->device) )
                return -ENXIO;
-           result = verify_area(VERIFY_WRITE, (void *)arg, size_sg_io_hdr);
+           result = verify_area(VERIFY_WRITE, (void *)arg, SZ_SG_IO_HDR);
            if (result) return result;
-           result = sg_new_write(sfp, (const char *)arg, size_sg_io_hdr,
+           result = sg_new_write(sfp, (const char *)arg, SZ_SG_IO_HDR,
                                  blocking, read_only, &srp);
            if (result < 0) return result;
            srp->sg_io_owned = 1;
            while (1) {
-               int dio = sg_dio_in_use(sfp);
                result = 0;  /* following macro to beat race condition */
                __wait_event_interruptible(sfp->read_wait,
-                                  (sfp->closed || srp->done), result);
+                      (sdp->detached || sfp->closed || srp->done), result);
+               if (sdp->detached)
+                   return -ENODEV;
                if (sfp->closed)
                    return 0;       /* request packet dropped already */
                if (0 == result)
                    break;
-               else if (! dio) {       /* only let signal out if no dio */
-                   srp->orphan = 1;
-                   return result; /* -ERESTARTSYS because signal hit process */
-               }
+               srp->orphan = 1;
+               return result; /* -ERESTARTSYS because signal hit process */
            }
            srp->done = 2;
-           result = sg_new_read(sfp, (char *)arg, size_sg_io_hdr, srp);
+           result = sg_new_read(sfp, (char *)arg, SZ_SG_IO_HDR, srp);
            return (result < 0) ? result : 0;
        }
     case SG_SET_TIMEOUT:
@@ -765,8 +782,11 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
                 sg_build_reserve(sfp, val);
             }
         }
-        else
+        else {
+           if (sdp->detached)
+               return -ENODEV;
             sfp->low_dma = sdp->device->host->unchecked_isa_dma;
+       }
         return 0;
     case SG_GET_LOW_DMA:
         return put_user((int)sfp->low_dma, (int *)arg);
@@ -775,6 +795,9 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
         if (result) return result;
         else {
            sg_scsi_id_t * sg_idp = (sg_scsi_id_t *)arg;
+
+           if (sdp->detached)
+               return -ENODEV;
             __put_user((int)sdp->device->host->host_no, &sg_idp->host_no);
             __put_user((int)sdp->device->channel, &sg_idp->channel);
             __put_user((int)sdp->device->id, &sg_idp->scsi_id);
@@ -853,7 +876,7 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
         return put_user(sg_version_num, (int *)arg);
     case SG_GET_REQUEST_TABLE:
        result = verify_area(VERIFY_WRITE, (void *) arg,
-                            size_sg_req_info * SG_MAX_QUEUE);
+                            SZ_SG_REQ_INFO * SG_MAX_QUEUE);
        if (result) return result;
        else {
            sg_req_info_t rinfo[SG_MAX_QUEUE];
@@ -861,7 +884,7 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
            read_lock_irqsave(&sfp->rq_list_lock, iflags);
            for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE;
                 ++val, srp = srp ? srp->nextrp : srp) {
-               memset(&rinfo[val], 0, size_sg_req_info);
+               memset(&rinfo[val], 0, SZ_SG_REQ_INFO);
                if (srp) {
                    rinfo[val].req_state = srp->done + 1;
                    rinfo[val].problem = srp->header.masked_status &
@@ -876,12 +899,16 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
                }
            }
            read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-           __copy_to_user((void *)arg, rinfo, size_sg_req_info * SG_MAX_QUEUE);
+           __copy_to_user((void *)arg, rinfo, SZ_SG_REQ_INFO * SG_MAX_QUEUE);
            return 0;
        }
     case SG_EMULATED_HOST:
+       if (sdp->detached)
+           return -ENODEV;
         return put_user(sdp->device->host->hostt->emulated, (int *)arg);
     case SG_SCSI_RESET:
+       if (sdp->detached)
+           return -ENODEV;
         if (filp->f_flags & O_NONBLOCK) {
            if (sdp->device->host->in_recovery)
                return -EBUSY;
@@ -907,13 +934,16 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
        default:
            return -EINVAL;
        }
-       if(! capable(CAP_SYS_ADMIN))  return -EACCES;
+       if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+           return -EACCES;
        return (scsi_reset_provider(sdp->device, val) == SUCCESS) ? 0 : -EIO;
 #else
        SCSI_LOG_TIMEOUT(1, printk("sg_ioctl: SG_RESET_SCSI not supported\n"));
        result = -EINVAL;
 #endif
     case SCSI_IOCTL_SEND_COMMAND:
+       if (sdp->detached)
+           return -ENODEV;
        if (read_only) {
            unsigned char opcode = WRITE_6;
            Scsi_Ioctl_Command * siocp = (void *)arg;
@@ -932,6 +962,8 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
     case SCSI_IOCTL_GET_BUS_NUMBER:
     case SCSI_IOCTL_PROBE_HOST:
     case SG_GET_TRANSFORM:
+       if (sdp->detached)
+           return -ENODEV;
         return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
     default:
        if (read_only)
@@ -949,7 +981,8 @@ static unsigned int sg_poll(struct file * filp, poll_table * wait)
     int count = 0;
     unsigned long iflags;
 
-    if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)))
+    if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp))
+       || sfp->closed)
         return POLLERR;
     poll_wait(filp, &sfp->read_wait, wait);
     read_lock_irqsave(&sfp->rq_list_lock, iflags);
@@ -960,7 +993,10 @@ static unsigned int sg_poll(struct file * filp, poll_table * wait)
         ++count;
     }
     read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-    if (! sfp->cmd_q) {
+
+    if (sdp->detached)
+       res |= POLLHUP;
+    else if (! sfp->cmd_q) {
         if (0 == count)
             res |= POLLOUT | POLLWRNORM;
     }
@@ -1001,9 +1037,9 @@ static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
        if (dev < sg_template.dev_max)
            sdp = sg_dev_arr[dev];
     }
-    if (NULL == sdp) {
+    if ((NULL == sdp) || sdp->detached) {
        read_unlock(&sg_dev_arr_lock);
-       SCSI_LOG_TIMEOUT(1, printk("sg...bh: bad args dev=%d\n", dev));
+       SCSI_LOG_TIMEOUT(1, printk("sg...bh: dev=%d gone\n", dev));
         scsi_release_request(SRpnt);
         SRpnt = NULL;
         return;
@@ -1020,8 +1056,8 @@ static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
             break;
         sfp = sfp->nextfp;
     }
-    read_unlock(&sg_dev_arr_lock);
     if (! srp) {
+       read_unlock(&sg_dev_arr_lock);
        SCSI_LOG_TIMEOUT(1, printk("sg...bh: req missing, dev=%d\n", dev));
         scsi_release_request(SRpnt);
         SRpnt = NULL;
@@ -1035,6 +1071,7 @@ static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
     sg_clr_srpnt(SRpnt);
     srp->my_cmdp = NULL;
     srp->done = 1;
+    read_unlock(&sg_dev_arr_lock);
 
     SCSI_LOG_TIMEOUT(4, printk("sg...bh: dev=%d, pack_id=%d, res=0x%x\n",
                     dev, srp->header.pack_id, (int)SRpnt->sr_result));
@@ -1071,7 +1108,6 @@ static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
     if (sfp->closed) { /* whoops this fd already released, cleanup */
         SCSI_LOG_TIMEOUT(1,
               printk("sg...bh: already closed, freeing ...\n"));
-       /* should check if module is unloaded <<<<<<< */
        sg_finish_rem_req(srp);
        srp = NULL;
        if (NULL == sfp->headrp) {
@@ -1080,6 +1116,10 @@ static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt)
             sg_remove_sfp(sdp, sfp);
            sfp = NULL;
         }
+       if (sg_template.module)
+               __MOD_DEC_USE_COUNT(sg_template.module);
+       if (sdp->device->host->hostt->module)
+           __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module);
     }
     else if (srp && srp->orphan) {
        if (sfp->keep_orphan)
@@ -1110,19 +1150,6 @@ static struct file_operations sg_fops = {
 
 static int sg_detect(Scsi_Device * scsidp)
 {
-    switch (scsidp->type) {
-        case TYPE_DISK:
-        case TYPE_MOD:
-        case TYPE_ROM:
-        case TYPE_WORM:
-        case TYPE_TAPE: break;
-        default:
-        printk("Detected scsi generic sg%d at scsi%d,"
-                " channel %d, id %d, lun %d, type %d\n",
-               sg_template.dev_noticed,
-              scsidp->host->host_no, scsidp->channel,
-               scsidp->id, scsidp->lun, scsidp->type);
-    }
     sg_template.dev_noticed++;
     return 1;
 }
@@ -1241,51 +1268,73 @@ static int sg_attach(Scsi_Device * scsidp)
     sg_template.nr_dev++;
     sg_dev_arr[k] = sdp;
     write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+    switch (scsidp->type) {
+       case TYPE_DISK:
+       case TYPE_MOD:
+       case TYPE_ROM:
+       case TYPE_WORM:
+       case TYPE_TAPE: break;
+       default:
+           printk("Attached scsi generic sg%d at scsi%d, channel %d, id %d,"
+                  " lun %d,  type %d\n", k, scsidp->host->host_no, 
+                  scsidp->channel, scsidp->id, scsidp->lun, scsidp->type);
+    }
     return 0;
 }
 
 /* Called at 'finish' of init process, after all attaches */
 static void sg_finish(void)
-{
-    SCSI_LOG_TIMEOUT(3, printk("sg_finish: dma_free_sectors=%u\n",
-                     scsi_dma_free_sectors));
-}
+{ }
 
 static void sg_detach(Scsi_Device * scsidp)
 {
     Sg_device * sdp;
     unsigned long iflags;
     Sg_fd * sfp;
+    Sg_fd * tsfp;
     Sg_request * srp;
-    int k;
+    Sg_request * tsrp;
+    int k, delay;
 
     if (NULL == sg_dev_arr)
        return;
+    delay = 0;
     write_lock_irqsave(&sg_dev_arr_lock, iflags);
     for (k = 0; k < sg_template.dev_max; k++) {
        sdp = sg_dev_arr[k];
         if ((NULL == sdp) || (sdp->device != scsidp))
             continue;   /* dirty but lowers nesting */
         if (sdp->headfp) {
-           for (sfp = sdp->headfp; sfp; sfp = sfp->nextfp) {
-               /* no lock on request list here */
-               for (srp = sfp->headrp; srp; srp = srp->nextrp) {
-                   if (! srp->done) {
-                       write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
-                        sg_shorten_timeout(srp->my_cmdp);
-                       write_lock_irqsave(&sg_dev_arr_lock, iflags);
-                }
-            }
+           sdp->detached = 1;
+           for (sfp = sdp->headfp; sfp; sfp = tsfp) {
+               tsfp = sfp->nextfp;
+               for (srp = sfp->headrp; srp; srp = tsrp) {
+                   tsrp = srp->nextrp;
+                   if (sfp->closed || (0 == srp->done))
+                       sg_finish_rem_req(srp);
+               }
+               if (sfp->closed) {
+                   if (sg_template.module)
+                       __MOD_DEC_USE_COUNT(sg_template.module);
+                   if (sdp->device->host->hostt->module)
+                       __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module);
+                   __sg_remove_sfp(sdp, sfp);
+               }
+               else {
+                   delay = 1;
+                   wake_up_interruptible(&sfp->read_wait);
+                   kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP);
+               }
             }
-           write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
-    SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty, sleep(3)\n", k));
-            scsi_sleep(3); /* sleep 3 jiffies, hoping for timeout to go off */
+           SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty\n", k));
            devfs_unregister (sdp->de);
            sdp->de = NULL;
-           sdp->detached = 1;
-           write_lock_irqsave(&sg_dev_arr_lock, iflags);
+           if (NULL == sdp->headfp) {
+               kfree((char *)sdp);
+               sg_dev_arr[k] = NULL;
+           }
         }
-        else {
+        else { /* nothing active, simple case */
             SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k));
            devfs_unregister (sdp->de);
            kfree((char *)sdp);
@@ -1293,13 +1342,12 @@ static void sg_detach(Scsi_Device * scsidp)
         }
         scsidp->attached--;
         sg_template.nr_dev--;
-/* avoid associated device /dev/sg? being incremented
- * each time module is inserted/removed , <dan@lectra.fr> */
-        sg_template.dev_noticed--;
+        sg_template.dev_noticed--;     /* from <dan@lectra.fr> */
         break;
     }
     write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
-    return;
+    if (delay)
+       scsi_sleep(2);  /* dirty detach so delay device destruction */
 }
 
 MODULE_AUTHOR("Douglas Gilbert");
@@ -1322,8 +1370,6 @@ static void __exit exit_sg( void)
     scsi_unregister_module(MODULE_SCSI_DEV, &sg_template);
     devfs_unregister_chrdev(SCSI_GENERIC_MAJOR, "sg");
     if(sg_dev_arr != NULL) {
-/* Really worrying situation of writes still pending and get here */
-/* Strategy: shorten timeout on release + wait on detach ... */
        kfree((char *)sg_dev_arr);
         sg_dev_arr = NULL;
     }
@@ -1331,29 +1377,6 @@ static void __exit exit_sg( void)
 }
 
 
-#if 0
-extern void scsi_times_out (Scsi_Cmnd * SCpnt);
-extern void scsi_old_times_out (Scsi_Cmnd * SCpnt);
-#endif
-
-/* Can't see clean way to abort a command so shorten timeout to 1 jiffy */
-static void sg_shorten_timeout(Scsi_Request * srpnt)
-{
-#if 0 /* scsi_syms.c is very miserly about exported functions */
-    scsi_delete_timer(scpnt);
-    if (! scpnt)
-        return;
-    scpnt->timeout_per_command = 1; /* try 1 jiffy (perhaps 0 jiffies) */
-    if (scpnt->host->hostt->use_new_eh_code)
-        scsi_add_timer(scpnt, scpnt->timeout_per_command, scsi_times_out);
-    else
-        scsi_add_timer(scpnt, scpnt->timeout_per_command,
-                       scsi_old_times_out);
-#else
-    scsi_sleep(HZ); /* just sleep 1 second and hope ... */
-#endif
-}
-
 static int sg_start_req(Sg_request * srp)
 {
     int res;
@@ -1367,9 +1390,8 @@ static int sg_start_req(Sg_request * srp)
     SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len));
     if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE))
        return 0;
-    if ((hp->flags & SG_FLAG_DIRECT_IO) && 
-       (dxfer_dir != SG_DXFER_UNKNOWN) &&
-       (0 == hp->iovec_count) &&
+    if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && 
+       (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) &&
        (! sfp->parentdp->device->host->unchecked_isa_dma)) {
        res = sg_build_dir(srp, sfp, dxfer_len);
        if (res <= 0)   /* -ve -> error, 0 -> done, 1 -> try indirect */
@@ -1427,7 +1449,7 @@ static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp)
 
 static void sg_unmap_and(Sg_scatter_hold * schp, int free_also)
 {
-#ifdef SG_ALLOW_DIO
+#ifdef SG_ALLOW_DIO_CODE
     if (schp && schp->kiobp) {
        if (schp->mapped) {
            unmap_kiobuf(schp->kiobp);
@@ -1443,7 +1465,7 @@ static void sg_unmap_and(Sg_scatter_hold * schp, int free_also)
 
 static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len)
 {
-#ifdef SG_ALLOW_DIO
+#ifdef SG_ALLOW_DIO_CODE
     int res, k, split, offset, num, mx_sc_elems, rem_sz;
     struct kiobuf * kp;
     char * mem_src_arr;
@@ -1519,7 +1541,7 @@ static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len)
     return 0;
 #else
     return 1;
-#endif /* SG_ALLOW_DIO */
+#endif /* SG_ALLOW_DIO_CODE */
 }
 
 static int sg_build_indi(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size)
@@ -1629,7 +1651,7 @@ static int sg_write_xfer(Sg_request * srp)
     if (iovec_count) {
        onum = iovec_count;
        if ((k = verify_area(VERIFY_READ, hp->dxferp,
-                            size_sg_iovec * onum)))
+                            SZ_SG_IOVEC * onum)))
            return k;
     }
     else
@@ -1707,8 +1729,8 @@ static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind,
     }
     else {
        __copy_from_user(&u_iovec,
-                        (unsigned char *)hp->dxferp + (ind * size_sg_iovec),
-                        size_sg_iovec);
+                        (unsigned char *)hp->dxferp + (ind * SZ_SG_IOVEC),
+                        SZ_SG_IOVEC);
        p = (unsigned char *)u_iovec.iov_base;
        count = (int)u_iovec.iov_len;
     }
@@ -1778,7 +1800,7 @@ static int sg_read_xfer(Sg_request * srp)
     if (iovec_count) {
        onum = iovec_count;
        if ((k = verify_area(VERIFY_READ, hp->dxferp,
-                            size_sg_iovec * onum)))
+                            SZ_SG_IOVEC * onum)))
            return k;
     }
     else
@@ -2124,6 +2146,34 @@ static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev)
     return sfp;
 }
 
+static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
+{
+    Sg_fd * fp;
+    Sg_fd * prev_fp;
+
+    prev_fp =  sdp->headfp;
+    if (sfp == prev_fp)
+       sdp->headfp = prev_fp->nextfp;
+    else {
+       while ((fp = prev_fp->nextfp)) {
+           if (sfp == fp) {
+               prev_fp->nextfp = fp->nextfp;
+               break;
+           }
+           prev_fp = fp;
+       }
+    }
+    if (sfp->reserve.bufflen > 0) {
+    SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp:    bufflen=%d, k_use_sg=%d\n",
+            (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg));
+       sg_remove_scat(&sfp->reserve);
+    }
+    sfp->parentdp = NULL;
+    SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp:    sfp=0x%p\n", sfp));
+    sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->fd_mem_src);
+}
+
+/* Returns 0 in normal case, 1 when detached and sdp object removed */
 static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
 {
     Sg_request * srp;
@@ -2131,44 +2181,18 @@ static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
     int dirty = 0;
     int res = 0;
 
-    srp = sfp->headrp;
-    if (srp) {
-       while (srp) {
-           tsrp = srp->nextrp;
-           if (srp->done)
-               sg_finish_rem_req(srp);
-           else
-               ++dirty;
-           srp = tsrp;
-       }
+    for (srp = sfp->headrp; srp; srp = tsrp) {
+       tsrp = srp->nextrp;
+       if (srp->done)
+           sg_finish_rem_req(srp);
+       else
+           ++dirty;
     }
     if (0 == dirty) {
-       Sg_fd * fp;
-       Sg_fd * prev_fp;
        unsigned long iflags;
 
        write_lock_irqsave(&sg_dev_arr_lock, iflags);
-       prev_fp =  sdp->headfp;
-       if (sfp == prev_fp)
-           sdp->headfp = prev_fp->nextfp;
-       else {
-           while ((fp = prev_fp->nextfp)) {
-               if (sfp == fp) {
-                   prev_fp->nextfp = fp->nextfp;
-                   break;
-               }
-               prev_fp = fp;
-           }
-       }
-       write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
-       if (sfp->reserve.bufflen > 0) {
-SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    bufflen=%d, k_use_sg=%d\n",
-                (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg));
-           sg_remove_scat(&sfp->reserve);
-        }
-        sfp->parentdp = NULL;
-       SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    sfp=0x%p\n", sfp));
-       sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->fd_mem_src);
+       __sg_remove_sfp(sdp, sfp);
        if (sdp->detached && (NULL == sdp->headfp)) {
            int k, maxd;
 
@@ -2180,11 +2204,17 @@ SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    bufflen=%d, k_use_sg=%d\n",
            if (k < maxd)
                sg_dev_arr[k] = NULL;
            kfree((char *)sdp);
+           res = 1;
        }
-        res = 1;
+       write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
     }
     else {
         sfp->closed = 1; /* flag dirty state on this fd */
+       /* MOD_INC's to inhibit unloading sg and associated adapter driver */
+       if (sg_template.module)
+           __MOD_INC_USE_COUNT(sg_template.module);
+        if (sdp->device->host->hostt->module)
+           __MOD_INC_USE_COUNT(sdp->device->host->hostt->module);
         SCSI_LOG_TIMEOUT(1, printk(
           "sg_remove_sfp: worrisome, %d writes pending\n", dirty));
     }
@@ -2203,31 +2233,15 @@ static int sg_res_in_use(Sg_fd * sfp)
     return srp ? 1 : 0;
 }
 
-static int sg_dio_in_use(Sg_fd * sfp)
-{
-    const Sg_request * srp;
-    unsigned long iflags;
-
-    read_lock_irqsave(&sfp->rq_list_lock, iflags);
-    for (srp = sfp->headrp; srp; srp = srp->nextrp)
-       if ((! srp->done) && srp->data.kiobp) break;
-    read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-    return srp ? 1 : 0;
-}
-
 /* If retSzp==NULL want exact size or fail */
-/* sg_low_malloc() should always be called from a process context allowing
-   GFP_KERNEL to be used instead of GFP_ATOMIC */
 static char * sg_low_malloc(int rqSz, int lowDma, int mem_src, int * retSzp)
 {
     char * resp = NULL;
-    int page_mask = lowDma ? (GFP_KERNEL | GFP_DMA) : GFP_KERNEL;
+    int page_mask = lowDma ? (GFP_ATOMIC | GFP_DMA) : GFP_ATOMIC;
 
     if (rqSz <= 0)
         return resp;
     if (SG_HEAP_KMAL == mem_src) {
-        page_mask = lowDma ? (GFP_ATOMIC | GFP_DMA) : GFP_ATOMIC;
-        /* Seen kmalloc(..,GFP_KERNEL) hang for 40 secs! */
         resp = kmalloc(rqSz, page_mask);
         if (resp && retSzp) *retSzp = rqSz;
         return resp;
@@ -2355,7 +2369,7 @@ static void sg_low_free(char * buff, int size, int mem_src)
     case SG_USER_MEM:
        break; /* nothing to do */
     default:
-       printk("sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%df\n",
+       printk("sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%d\n",
                mem_src, buff, size);
        break;
     }
@@ -2451,11 +2465,17 @@ static Sg_device * sg_get_dev(int dev)
 
 static struct proc_dir_entry * sg_proc_sgp = NULL;
 
-static const char * sg_proc_sg_dirname = "sg";
-static const char * sg_proc_leaf_names[] = {"def_reserved_size", "debug",
-                           "devices", "device_hdr", "device_strs",
-                           "hosts", "host_hdr", "host_strs", "version"};
+static char sg_proc_sg_dirname[] = "sg";
+static const char * sg_proc_leaf_names[] = {"allow_dio", "def_reserved_size",
+               "debug", "devices", "device_hdr", "device_strs",
+               "hosts", "host_hdr", "host_strs", "version"};
 
+static int sg_proc_adio_read(char * buffer, char ** start, off_t offset,
+                            int size, int * eof, void * data);
+static int sg_proc_adio_info(char * buffer, int * len, off_t * begin,
+                            off_t offset, int size);
+static int sg_proc_adio_write(struct file * filp, const char * buffer,
+                             unsigned long count, void * data);
 static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset,
                               int size, int * eof, void * data);
 static int sg_proc_dressz_info(char * buffer, int * len, off_t * begin,
@@ -2495,12 +2515,12 @@ static int sg_proc_version_read(char * buffer, char ** start, off_t offset,
 static int sg_proc_version_info(char * buffer, int * len, off_t * begin,
                                off_t offset, int size);
 static read_proc_t * sg_proc_leaf_reads[] = {
-            sg_proc_dressz_read, sg_proc_debug_read,
+            sg_proc_adio_read, sg_proc_dressz_read, sg_proc_debug_read,
             sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read,
             sg_proc_host_read, sg_proc_hosthdr_read, sg_proc_hoststrs_read,
             sg_proc_version_read};
 static write_proc_t * sg_proc_leaf_writes[] = {
-            sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0};
+            sg_proc_adio_write, sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0};
 
 #define PRINT_PROC(fmt,args...)                                 \
     do {                                                        \
@@ -2562,6 +2582,32 @@ static void sg_proc_cleanup()
     remove_proc_entry(sg_proc_sg_dirname, proc_scsi);
 }
 
+static int sg_proc_adio_read(char * buffer, char ** start, off_t offset,
+                              int size, int * eof, void * data)
+{ SG_PROC_READ_FN(sg_proc_adio_info); }
+
+static int sg_proc_adio_info(char * buffer, int * len, off_t * begin,
+                            off_t offset, int size)
+{
+    PRINT_PROC("%d\n", sg_allow_dio);
+    return 1;
+}
+
+static int sg_proc_adio_write(struct file * filp, const char * buffer,
+                             unsigned long count, void * data)
+{
+    int num;
+    char buff[11];
+
+    if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+       return -EACCES;
+    num = (count < 10) ? count : 10;
+    copy_from_user(buff, buffer, num);
+    buff[num] = '\0';
+    sg_allow_dio = simple_strtoul(buff, 0, 10) ? 1 : 0;
+    return count;
+}
+
 static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset,
                               int size, int * eof, void * data)
 { SG_PROC_READ_FN(sg_proc_dressz_info); }
@@ -2580,7 +2626,7 @@ static int sg_proc_dressz_write(struct file * filp, const char * buffer,
     unsigned long k = ULONG_MAX;
     char buff[11];
 
-    if (! capable(CAP_SYS_ADMIN))
+    if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
        return -EACCES;
     num = (count < 10) ? count : 10;
     copy_from_user(buff, buffer, num);
@@ -2620,8 +2666,9 @@ static int sg_proc_debug_info(char * buffer, int * len, off_t * begin,
            Sg_request * srp;
            struct scsi_device * scsidp;
            int dev, k, m, blen, usg;
-
-           if (! (scsidp = sdp->device)) {
+           scsidp = sdp->device;
+           if (NULL == scsidp) {
                PRINT_PROC("device %d detached ??\n", j);
                continue;
            }
@@ -2629,10 +2676,14 @@ static int sg_proc_debug_info(char * buffer, int * len, off_t * begin,
 
            if (sg_get_nth_sfp(sdp, 0)) {
                PRINT_PROC(" >>> device=sg%d ", dev);
-               PRINT_PROC("scsi%d chan=%d id=%d lun=%d   em=%d sg_tablesize=%d"
-                      " excl=%d\n", scsidp->host->host_no, scsidp->channel,
-                      scsidp->id, scsidp->lun, scsidp->host->hostt->emulated,
-                      sdp->sg_tablesize, sdp->exclude);
+               if (sdp->detached)
+                   PRINT_PROC("detached pending close ");
+               else
+                   PRINT_PROC("scsi%d chan=%d id=%d lun=%d   em=%d",
+                      scsidp->host->host_no, scsidp->channel,
+                      scsidp->id, scsidp->lun, scsidp->host->hostt->emulated);
+               PRINT_PROC(" sg_tablesize=%d excl=%d\n", sdp->sg_tablesize, 
+                          sdp->exclude);
            }
            for (k = 0; (fp = sg_get_nth_sfp(sdp, k)); ++k) {
                PRINT_PROC("   FD(%d): timeout=%dms bufflen=%d "
@@ -2683,13 +2734,14 @@ static int sg_proc_dev_info(char * buffer, int * len, off_t * begin,
     max_dev = sg_last_dev();
     for (j = 0; j < max_dev; ++j) {
        sdp = sg_get_dev(j);
-       if (sdp && (scsidp = sdp->device))
-           PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
+       if (sdp && (scsidp = sdp->device) && (! sdp->detached))
+           PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
               scsidp->host->host_no, scsidp->channel, scsidp->id,
               scsidp->lun, (int)scsidp->type, (int)scsidp->access_count,
-              (int)scsidp->queue_depth, (int)scsidp->device_busy);
+              (int)scsidp->queue_depth, (int)scsidp->device_busy,
+              (int)scsidp->online);
        else
-           PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n");
+           PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n");
     }
     return 1;
 }
@@ -2701,7 +2753,7 @@ static int sg_proc_devhdr_read(char * buffer, char ** start, off_t offset,
 static int sg_proc_devhdr_info(char * buffer, int * len, off_t * begin,
                               off_t offset, int size)
 {
-    PRINT_PROC("host\tchan\tid\tlun\ttype\tbopens\tqdepth\tbusy\n");
+    PRINT_PROC("host\tchan\tid\tlun\ttype\tbopens\tqdepth\tbusy\tonline\n");
     return 1;
 }
 
@@ -2719,7 +2771,7 @@ static int sg_proc_devstrs_info(char * buffer, int * len, off_t * begin,
     max_dev = sg_last_dev();
     for (j = 0; j < max_dev; ++j) {
        sdp = sg_get_dev(j);
-       if (sdp && (scsidp = sdp->device))
+       if (sdp && (scsidp = sdp->device) && (! sdp->detached))
            PRINT_PROC("%8.8s\t%16.16s\t%4.4s\n",
                       scsidp->vendor, scsidp->model, scsidp->rev);
        else
index ea2bd825b5eab94e9b94bff3f3cacc8d644ee64d..afef733dc87d94ab5d41d2239842c1c724bfff60 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * acm.c  Version 0.18
+ * acm.c  Version 0.19
  *
  * Copyright (c) 1999 Armin Fuerst     <fuerst@in.tum.de>
  * Copyright (c) 1999 Pavel Machek     <pavel@suse.cz>
@@ -21,6 +21,7 @@
  *     v0.16 - added code for modems with swapped data and control interfaces
  *     v0.17 - added new style probing
  *     v0.18 - fixed new style probing for devices with more configurations
+ *     v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
  */
 
 /*
@@ -51,6 +52,7 @@
 #include <linux/tty_driver.h>
 #include <linux/tty_flip.h>
 #include <linux/module.h>
+#include <linux/smp_lock.h>
 #undef DEBUG
 #include <linux/usb.h>
 
@@ -202,13 +204,10 @@ static void acm_ctrl_irq(struct urb *urb)
 
                        newctrl = le16_to_cpup((__u16 *) data);
 
-#if 0
-                       /* Please someone tell me how to do this properly to kill pppd and not kill minicom */
                        if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
                                dbg("calling hangup");
                                tty_hangup(acm->tty);
                        }
-#endif
 
                        acm->ctrlin = newctrl;
 
@@ -305,7 +304,14 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 
        MOD_INC_USE_COUNT;
 
-       if (acm->used++) return 0;
+        lock_kernel();
+
+       if (acm->used++) {
+                unlock_kernel();
+                return 0;
+        }
+
+        unlock_kernel();
 
        acm->ctrlurb.dev = acm->dev;
        if (usb_submit_urb(&acm->ctrlurb))
@@ -410,7 +416,7 @@ static void acm_tty_break_ctl(struct tty_struct *tty, int state)
 static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct acm *acm = tty->driver_data;
-       unsigned int retval, mask, newctrl;
+       unsigned int mask, newctrl;
 
        if (!ACM_READY(acm)) return -EINVAL;
 
@@ -429,7 +435,8 @@ static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int
                case TIOCMBIS:
                case TIOCMBIC:
 
-                       if ((retval = get_user(mask, (unsigned long *) arg))) return retval;
+                       if (get_user(mask, (unsigned long *) arg))
+                               return -EFAULT;
 
                        newctrl = acm->ctrlout;
                        mask = (mask & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (mask & TIOCM_RTS ? ACM_CTRL_RTS : 0);
@@ -475,7 +482,7 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_
                (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0;
        newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4];
 
-       acm->clocal = termios->c_cflag & CLOCAL;
+       acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
 
        if (!newline.speed) {
                newline.speed = acm->line.speed;
index e2b53b45cd65ec46c98701ba1a6caa7429ce4b2e..07b593e1ba48b6c74d459a2e855068f33393c3cb 100644 (file)
@@ -475,6 +475,7 @@ static ssize_t usb_device_read(struct file *file, char *buf, size_t nbytes, loff
                return -EFAULT;
 
        /* enumerate busses */
+       read_lock_irq (&usb_bus_list_lock);
        for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) {
                /* print devices for this bus */
                bus = list_entry(buslist, struct usb_bus, bus_list);
@@ -484,6 +485,7 @@ static ssize_t usb_device_read(struct file *file, char *buf, size_t nbytes, loff
                        return ret;
                total_written += ret;
        }
+       read_unlock_irq (&usb_bus_list_lock);
        return total_written;
 }
 
index df60a9dd80544f1ef4bd4f3041ee5818c6469d36..5f5111d6d99e5179787ef6fcdabb2aa55f1397e8 100644 (file)
@@ -260,11 +260,15 @@ static struct usb_bus *usbdevfs_findbus(int busnr)
         struct list_head *list;
         struct usb_bus *bus;
 
+       read_lock_irq (&usb_bus_list_lock);
         for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) {
                 bus = list_entry(list, struct usb_bus, bus_list);
-                if (bus->busnum == busnr)
+                if (bus->busnum == busnr) {
+                       read_unlock_irq (&usb_bus_list_lock);
                         return bus;
+               }
         }
+       read_unlock_irq (&usb_bus_list_lock);
         return NULL;
 }
 
@@ -412,7 +416,7 @@ static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t fill
                if (i < 2+NRSPECIAL)
                        return 0;
                i -= 2+NRSPECIAL;
-               lock_kernel();
+               read_lock_irq (&usb_bus_list_lock);
                for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) {
                        if (i > 0) {
                                i--;
@@ -424,7 +428,7 @@ static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t fill
                                break;
                        filp->f_pos++;
                }
-               unlock_kernel();
+               read_unlock_irq (&usb_bus_list_lock);
                return 0;
        }
 }
@@ -635,13 +639,13 @@ struct super_block *usbdevfs_read_super(struct super_block *s, void *data, int s
                list_add_tail(&inode->u.usbdev_i.slist, &s->u.usbdevfs_sb.ilist);
                list_add_tail(&inode->u.usbdev_i.dlist, &special[i].inodes);
        }
-       lock_kernel();
+       read_lock_irq (&usb_bus_list_lock);
        for (blist = usb_bus_list.next; blist != &usb_bus_list; blist = blist->next) {
                bus = list_entry(blist, struct usb_bus, bus_list);
                new_bus_inode(bus, s);
                recurse_new_dev_inode(bus->root_hub, s);
        }
-       unlock_kernel();
+       read_unlock_irq (&usb_bus_list_lock);
         return s;
 
  out_no_root:
index 8a0379831ef8935a19d032220be8c32448f80952..495605ca734a31de65c3cf63e5f9bcb0d77ac1ad 100644 (file)
@@ -716,6 +716,7 @@ static ssize_t mdc800_device_read (struct file *file, char *buf, size_t len, lof
 static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos)
 {
        int i=0;
+       char c;
 
        spin_lock (&mdc800->io_lock);
        if (mdc800->state != READY)
@@ -738,7 +739,14 @@ static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t l
                }
 
                /* check for command start */
-               if (buf [i] == (char) 0x55)
+               
+               if(get_user(c, buf+i))
+               {
+                       spin_unlock (&mdc800->io_lock);
+                       return -EFAULT;
+               }
+               
+               if (c == (char) 0x55)
                {
                        mdc800->in_count=0;
                        mdc800->out_count=0;
@@ -749,7 +757,7 @@ static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t l
                /* save command byte */
                if (mdc800->in_count < 8)
                {
-                       mdc800->in[mdc800->in_count]=buf[i];
+                       mdc800->in[mdc800->in_count]=c;
                        mdc800->in_count++;
                }
                else
index 7f4b1ea3eb105fb2b1bf19bbe12f52113be7437f..fc17731b5d7ce1d5164432ca7e192722c3b5a46f 100644 (file)
@@ -96,7 +96,7 @@ struct freecom_status {
 #define FCM_PACKET_OUTPUT 0x01
 
 /* Write a value to an ide register.  Or the ide register to write after
- * munging the addres a bit. */
+ * munging the address a bit. */
 #define FCM_PACKET_IDE_WRITE    0x40
 #define FCM_PACKET_IDE_READ     0xC0
 
@@ -412,7 +412,7 @@ int freecom_transport(Scsi_Cmnd *srb, struct us_data *us)
 
         US_DEBUG(pdump ((void *) fst, partial));
 
-       /* while we haven't recieved the IRQ */
+       /* while we haven't received the IRQ */
        while (!(fst->Status & 0x2)) {
                /* send a command to re-fetch the status */
                US_DEBUGP("Re-attempting to get status...\n");
index a5339ab2be4f89dccf4746c875b700dd15fa97c0..1edd3b1ae1ea4ccba403183be317adb7428995e1 100644 (file)
@@ -62,7 +62,7 @@
  * Version Information
  */
 #define DRIVER_VERSION ""
-#define DRIVER_AUTHOR "Linus Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
+#define DRIVER_AUTHOR "Linus 'Frodo Rabbit' Torvalds, Johannes Erdfelt, Randy Dunlap, Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
 #define DRIVER_DESC "USB Universal Host Controller Interface driver"
 
 
@@ -305,8 +305,7 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
        mb();
        td->link = UHCI_PTR_TERM;
 
-       list_del(&td->fl_list);
-       INIT_LIST_HEAD(&td->fl_list);
+       list_del_init(&td->fl_list);
        td->frame = -1;
 
        spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
@@ -438,8 +437,7 @@ static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
        mb();
        qh->element = qh->link = UHCI_PTR_TERM;
 
-       list_del(&qh->list);
-       INIT_LIST_HEAD(&qh->list);
+       list_del_init(&qh->list);
 
        spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
 
@@ -624,8 +622,7 @@ static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb)
                        pltd->link = UHCI_PTR_TERM;
        }
 
-       list_del(&urbp->queue_list);
-       INIT_LIST_HEAD(&urbp->queue_list);
+       list_del_init(&urbp->queue_list);
 
 out:
        spin_unlock_irqrestore(&uhci->frame_list_lock, flags);
@@ -689,8 +686,7 @@ static void uhci_remove_td_from_urb(struct uhci_td *td)
        if (list_empty(&td->list))
                return;
 
-       list_del(&td->list);
-       INIT_LIST_HEAD(&td->list);
+       list_del_init(&td->list);
 
        td->urb = NULL;
 }
@@ -1665,8 +1661,7 @@ static void uhci_transfer_result(struct uhci *uhci, struct urb *urb)
                        usb_pipetype(urb->pipe), urb);
        }
 
-       list_del(&urb->urb_list);
-       INIT_LIST_HEAD(&urb->urb_list);
+       list_del_init(&urb->urb_list);
 
        uhci_add_complete(urb);
 }
@@ -1723,8 +1718,7 @@ static int uhci_unlink_urb(struct urb *urb)
                return 0;
 
        spin_lock_irqsave(&uhci->urb_list_lock, flags);
-       list_del(&urb->urb_list);
-       INIT_LIST_HEAD(&urb->urb_list);
+       list_del_init(&urb->urb_list);
        spin_unlock_irqrestore(&uhci->urb_list_lock, flags);
 
        uhci_unlink_generic(uhci, urb);
@@ -2206,8 +2200,7 @@ static void uhci_free_pending_qhs(struct uhci *uhci)
 
                tmp = tmp->next;
 
-               list_del(&qh->remove_list);
-               INIT_LIST_HEAD(&qh->remove_list);
+               list_del_init(&qh->remove_list);
 
                uhci_free_qh(uhci, qh);
        }
@@ -2300,8 +2293,7 @@ static void uhci_finish_completion(struct uhci *uhci)
 
                tmp = tmp->next;
 
-               list_del(&urbp->complete_list);
-               INIT_LIST_HEAD(&urbp->complete_list);
+               list_del_init(&urbp->complete_list);
 
                uhci_call_completion(urb);
        }
@@ -2322,8 +2314,7 @@ static void uhci_remove_pending_qhs(struct uhci *uhci)
 
                tmp = tmp->next;
 
-               list_del(&urb->urb_list);
-               INIT_LIST_HEAD(&urb->urb_list);
+               list_del_init(&urb->urb_list);
 
                urbp->status = urb->status = -ECONNRESET;
                uhci_call_completion(urb);
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
new file mode 100644 (file)
index 0000000..bc146d4
--- /dev/null
@@ -0,0 +1,652 @@
+/*
+ * USB Skeleton driver
+ *
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *     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 driver is to be used as a skeleton driver to be able to create a
+ * USB driver quickly.  The design of it is based on the usb-serial and
+ * dc2xx drivers.
+ *
+ * Thanks to Oliver Neukum and David Brownell for their help in debugging
+ * this driver.
+ *
+ * TODO:
+ *     - fix urb->status race condition in write sequence
+ *     - move minor_table to a dynamic list.
+ *
+ * History:
+ *
+ * 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel
+ * 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people
+ * 2001_05_01 - 0.1 - first version
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/usb.h>
+
+#ifdef CONFIG_USB_DEBUG
+       static int debug = 1;
+#else
+       static int debug;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0)
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.3"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
+#define DRIVER_DESC "USB Skeleton Driver"
+
+/* Module paramaters */
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+
+/* Define these values to match your device */
+#define USB_SKEL_VENDOR_ID     0xfff0
+#define USB_SKEL_PRODUCT_ID    0xfff0
+
+/* table of devices that work with this driver */
+static struct usb_device_id skel_table [] = {
+       { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
+       { }                                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, skel_table);
+
+
+
+/* Get a minor range for your devices from the usb maintainer */
+#define USB_SKEL_MINOR_BASE    200     
+
+/* we can have up to this number of device plugged in at once */
+#define MAX_DEVICES            16
+
+/* Structure to hold all of our device specific stuff */
+struct usb_skel {
+       struct usb_device *     udev;                   /* save off the usb device pointer */
+       struct usb_interface *  interface;              /* the interface for this device */
+       devfs_handle_t          devfs;                  /* devfs device node */
+       unsigned char           minor;                  /* the starting minor number for this device */
+       unsigned char           num_ports;              /* the number of ports this device has */
+       char                    num_interrupt_in;       /* number of interrupt in endpoints we have */
+       char                    num_bulk_in;            /* number of bulk in endpoints we have */
+       char                    num_bulk_out;           /* number of bulk out endpoints we have */
+
+       unsigned char *         bulk_in_buffer;         /* the buffer to receive data */
+       int                     bulk_in_size;           /* the size of the receive buffer */
+       __u8                    bulk_in_endpointAddr;   /* the address of the bulk in endpoint */
+
+       unsigned char *         bulk_out_buffer;        /* the buffer to send data */
+       int                     bulk_out_size;          /* the size of the send buffer */
+       struct urb *            write_urb;              /* the urb used to send data */
+       __u8                    bulk_out_endpointAddr;  /* the address of the bulk out endpoint */
+
+       struct tq_struct        tqueue;                 /* task queue for line discipline waking up */
+       int                     open_count;             /* number of times this port has been opened */
+       struct semaphore        sem;                    /* locks this structure */
+};
+
+
+/* the global usb devfs handle */
+extern devfs_handle_t usb_devfs_handle;
+
+
+/* local function prototypes */
+static ssize_t skel_read       (struct file *file, char *buffer, size_t count, loff_t *ppos);
+static ssize_t skel_write      (struct file *file, const char *buffer, size_t count, loff_t *ppos);
+static int skel_ioctl          (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int skel_open           (struct inode *inode, struct file *file);
+static int skel_release                (struct inode *inode, struct file *file);
+       
+static void * skel_probe       (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);
+static void skel_disconnect    (struct usb_device *dev, void *ptr);
+
+static void skel_write_bulk_callback   (struct urb *urb);
+
+
+/* array of pointers to our devices that are currently connected */
+static struct usb_skel         *minor_table[MAX_DEVICES];
+
+/* lock to protect the minor_table structure */
+static DECLARE_MUTEX (minor_table_mutex);
+
+/* file operations needed when we register this driver */
+static struct file_operations skel_fops = {
+       owner:          THIS_MODULE,
+       read:           skel_read,
+       write:          skel_write,
+       ioctl:          skel_ioctl,
+       open:           skel_open,
+       release:        skel_release,
+};      
+
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver skel_driver = {
+       name:           "skeleton",
+       probe:          skel_probe,
+       disconnect:     skel_disconnect,
+       fops:           &skel_fops,
+       minor:          USB_SKEL_MINOR_BASE,
+       id_table:       skel_table,
+};
+
+
+
+
+
+/**
+ *     usb_skel_debug_data
+ */
+static inline void usb_skel_debug_data (const char *function, int size, const unsigned char *data)
+{
+       int i;
+
+       if (!debug)
+               return;
+       
+       printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", 
+               function, size);
+       for (i = 0; i < size; ++i) {
+               printk ("%.2x ", data[i]);
+       }
+       printk ("\n");
+}
+
+
+/**
+ *     skel_open
+ */
+static int skel_open (struct inode *inode, struct file *file)
+{
+       struct usb_skel *dev = NULL;
+       int subminor;
+       int retval = 0;
+       
+       dbg(__FUNCTION__);
+
+       subminor = MINOR (inode->i_rdev) - USB_SKEL_MINOR_BASE;
+       if ((subminor < 0) ||
+           (subminor >= MAX_DEVICES)) {
+               return -ENODEV;
+       }
+
+       /* increment our usage count for the module */
+       MOD_INC_USE_COUNT;
+
+       /* lock our minor table and get our local data for this minor */
+       down (&minor_table_mutex);
+       dev = minor_table[subminor];
+       if (dev == NULL) {
+               up (&minor_table_mutex);
+               MOD_DEC_USE_COUNT;
+               return -ENODEV;
+       }
+
+       /* lock this device */
+       down (&dev->sem);
+
+       /* unlock the minor table */
+       up (&minor_table_mutex);
+
+       /* increment our usage count for the driver */
+       ++dev->open_count;
+
+       /* save our object in the file's private structure */
+       file->private_data = dev;
+
+       /* unlock this device */
+       up (&dev->sem);
+
+       return retval;
+}
+
+
+/**
+ *     skel_release
+ */
+static int skel_release (struct inode *inode, struct file *file)
+{
+       struct usb_skel *dev;
+       int retval = 0;
+
+       dev = (struct usb_skel *)file->private_data;
+       if (dev == NULL) {
+               dbg (__FUNCTION__ " - object is NULL");
+               return -ENODEV;
+       }
+
+       dbg(__FUNCTION__ " - minor %d", dev->minor);
+
+       /* lock our minor table */
+       down (&minor_table_mutex);
+
+       /* lock our device */
+       down (&dev->sem);
+
+       if (dev->open_count <= 0) {
+               dbg (__FUNCTION__ " - device not opened");
+               retval = -ENODEV;
+               goto exit_not_opened;
+       }
+
+       if (dev->udev == NULL) {
+               /* the device was unplugged before the file was released */
+               minor_table[dev->minor] = NULL;
+               if (dev->bulk_in_buffer != NULL)
+                       kfree (dev->bulk_in_buffer);
+               if (dev->bulk_out_buffer != NULL)
+                       kfree (dev->bulk_out_buffer);
+               if (dev->write_urb != NULL)
+                       usb_free_urb (dev->write_urb);
+               kfree (dev);
+               goto exit;
+       }
+
+       /* decrement our usage count for the device */
+       --dev->open_count;
+       if (dev->open_count <= 0) {
+               /* shutdown any bulk writes that might be going on */
+               dev->write_urb->transfer_flags |= USB_ASYNC_UNLINK;
+               usb_unlink_urb (dev->write_urb);
+               dev->open_count = 0;
+       }
+
+exit:
+       /* decrement our usage count for the module */
+       MOD_DEC_USE_COUNT;
+
+exit_not_opened:
+       up (&dev->sem);
+       up (&minor_table_mutex);
+
+       return retval;
+}
+
+
+/**
+ *     skel_read
+ */
+static ssize_t skel_read (struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+       struct usb_skel *dev;
+       int retval = 0;
+
+       dev = (struct usb_skel *)file->private_data;
+       
+       dbg(__FUNCTION__ " - minor %d, count = %d", dev->minor, count);
+
+       /* lock this object */
+       down (&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (dev->udev == NULL) {
+               up (&dev->sem);
+               return -ENODEV;
+       }
+       
+       /* do an immediate bulk read to get data from the device */
+       retval = usb_bulk_msg (dev->udev,
+                              usb_rcvbulkpipe (dev->udev, 
+                                               dev->bulk_in_endpointAddr),
+                              dev->bulk_in_buffer, dev->bulk_in_size,
+                              &count, HZ*10);
+
+       /* if the read was successful, copy the data to userspace */
+       if (!retval) {
+               if (copy_to_user (buffer, dev->bulk_in_buffer, count))
+                       retval = -EFAULT;
+               else
+                       retval = count;
+       }
+       
+       /* unlock the device */
+       up (&dev->sem);
+       return retval;
+}
+
+
+/**
+ *     skel_write
+ */
+static ssize_t skel_write (struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+       struct usb_skel *dev;
+       ssize_t bytes_written = 0;
+       int retval = 0;
+
+       dev = (struct usb_skel *)file->private_data;
+
+       dbg(__FUNCTION__ " - minor %d, count = %d", dev->minor, count);
+
+       /* lock this object */
+       down (&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (dev->udev == NULL) {
+               retval = -ENODEV;
+               goto exit;
+       }
+
+       /* verify that we actually have some data to write */
+       if (count == 0) {
+               dbg(__FUNCTION__ " - write request of 0 bytes");
+               goto exit;
+       }
+
+       /* see if we are already in the middle of a write */
+       if (dev->write_urb->status == -EINPROGRESS) {
+               dbg (__FUNCTION__ " - already writing");
+               goto exit;
+       }
+
+       /* we can only write as much as 1 urb will hold */
+       bytes_written = (count > dev->bulk_out_size) ? 
+                               dev->bulk_out_size : count;
+
+       /* copy the data from userspace into our urb */
+       if (copy_from_user(dev->write_urb->transfer_buffer, buffer, 
+                          bytes_written)) {
+               retval = -EFAULT;
+               goto exit;
+       }
+
+       usb_skel_debug_data (__FUNCTION__, bytes_written, 
+                            dev->write_urb->transfer_buffer);
+
+       /* set up our urb */
+       FILL_BULK_URB(dev->write_urb, dev->udev, 
+                     usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
+                     dev->write_urb->transfer_buffer, bytes_written,
+                     skel_write_bulk_callback, dev);
+
+       /* send the data out the bulk port */
+       retval = usb_submit_urb(dev->write_urb);
+       if (retval) {
+               err(__FUNCTION__ " - failed submitting write urb, error %d",
+                   retval);
+       } else {
+               retval = bytes_written;
+       }
+
+exit:
+       /* unlock the device */
+       up (&dev->sem);
+
+       return retval;
+}
+
+
+/**
+ *     skel_ioctl
+ */
+static int skel_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct usb_skel *dev;
+
+       dev = (struct usb_skel *)file->private_data;
+
+       /* lock this object */
+       down (&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (dev->udev == NULL) {
+               up (&dev->sem);
+               return -ENODEV;
+       }
+
+       dbg(__FUNCTION__ " - minor %d, cmd 0x%.4x, arg %ld", 
+           dev->minor, cmd, arg);
+
+
+       /* fill in your device specific stuff here */
+       
+       /* unlock the device */
+       up (&dev->sem);
+       
+       /* return that we did not understand this ioctl call */
+       return -ENOTTY;
+}
+
+
+/**
+ *     skel_write_bulk_callback
+ */
+static void skel_write_bulk_callback (struct urb *urb)
+{
+       struct usb_skel *dev = (struct usb_skel *)urb->context;
+
+       dbg(__FUNCTION__ " - minor %d", dev->minor);
+
+       if ((urb->status != -ENOENT) && 
+           (urb->status != -ECONNRESET)) {
+               dbg(__FUNCTION__ " - nonzero write bulk status received: %d",
+                   urb->status);
+               return;
+       }
+
+       return;
+}
+
+
+
+
+
+static void * skel_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
+{
+       struct usb_skel *dev = NULL;
+       struct usb_interface *interface;
+       struct usb_interface_descriptor *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       int minor;
+       int buffer_size;
+       int i;
+       char name[10];
+
+       
+       /* See if the device offered us matches what we can accept */
+       if ((udev->descriptor.idVendor != USB_SKEL_VENDOR_ID) ||
+           (udev->descriptor.idProduct != USB_SKEL_PRODUCT_ID)) {
+               return NULL;
+       }
+
+       /* select a "subminor" number (part of a minor number) */
+       down (&minor_table_mutex);
+       for (minor = 0; minor < MAX_DEVICES; i++) {
+               if (minor_table[minor] == NULL)
+                       break;
+       }
+       if (minor >= MAX_DEVICES) {
+               info ("Too many devices plugged in, can not handle this device.");
+               goto exit;
+       }
+
+       /* allocate memory for our device state and intialize it */
+       dev = kmalloc (sizeof(struct usb_skel), GFP_KERNEL);
+       if (dev == NULL) {
+               err ("Out of memory");
+               goto exit;
+       }
+       minor_table[minor] = dev;
+
+       interface = &udev->actconfig->interface[ifnum];
+
+       init_MUTEX (&dev->sem);
+       dev->udev = udev;
+       dev->interface = interface;
+       dev->minor = minor;
+
+       /* set up the endpoint information */
+       /* check out the endpoints */
+       iface_desc = &interface->altsetting[0];
+       for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i];
+
+               if ((endpoint->bEndpointAddress & 0x80) &&
+                   ((endpoint->bmAttributes & 3) == 0x02)) {
+                       /* we found a bulk in endpoint */
+                       buffer_size = endpoint->wMaxPacketSize;
+                       dev->bulk_in_size = buffer_size;
+                       dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+                       dev->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+                       if (!dev->bulk_in_buffer) {
+                               err("Couldn't allocate bulk_in_buffer");
+                               goto error;
+                       }
+               }
+               
+               if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
+                   ((endpoint->bmAttributes & 3) == 0x02)) {
+                       /* we found a bulk out endpoint */
+                       dev->write_urb = usb_alloc_urb(0);
+                       if (!dev->write_urb) {
+                               err("No free urbs available");
+                               goto error;
+                       }
+                       buffer_size = endpoint->wMaxPacketSize;
+                       dev->bulk_out_size = buffer_size;
+                       dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+                       dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
+                       if (!dev->bulk_out_buffer) {
+                               err("Couldn't allocate bulk_out_buffer");
+                               goto error;
+                       }
+                       FILL_BULK_URB(dev->write_urb, udev, 
+                                     usb_sndbulkpipe(udev, 
+                                                     endpoint->bEndpointAddress),
+                                     dev->bulk_out_buffer, buffer_size,
+                                     skel_write_bulk_callback, dev);
+               }
+       }
+
+       /* initialize the devfs node for this device and register it */
+       sprintf(name, "skel%d", dev->minor);
+       
+       dev->devfs = devfs_register (usb_devfs_handle, name,
+                                    DEVFS_FL_DEFAULT, USB_MAJOR,
+                                    USB_SKEL_MINOR_BASE + dev->minor,
+                                    S_IFCHR | S_IRUSR | S_IWUSR | 
+                                    S_IRGRP | S_IWGRP | S_IROTH, 
+                                    &skel_fops, NULL);
+
+       /* let the user know what node this device is now attached to */
+       info ("USB Skeleton device now attached to USBSkel%d", dev->minor);
+       goto exit;
+       
+error:
+       minor_table [dev->minor] = NULL;
+       if (dev->bulk_in_buffer != NULL)
+               kfree (dev->bulk_in_buffer);
+       if (dev->bulk_out_buffer != NULL)
+               kfree (dev->bulk_out_buffer);
+       if (dev->write_urb != NULL)
+               usb_free_urb (dev->write_urb);
+       kfree (dev);
+       dev = NULL;
+
+exit:
+       up (&minor_table_mutex);
+       return dev;
+}
+
+
+/**
+ *     skel_disconnect
+ */
+static void skel_disconnect(struct usb_device *udev, void *ptr)
+{
+       struct usb_skel *dev;
+       int minor;
+
+       dev = (struct usb_skel *)ptr;
+       
+       down (&minor_table_mutex);
+       down (&dev->sem);
+               
+       minor = dev->minor;
+
+       /* if the device is not opened, then we clean up right now */
+       if (!dev->open_count) {
+               minor_table[dev->minor] = NULL;
+               if (dev->bulk_in_buffer != NULL)
+                       kfree (dev->bulk_in_buffer);
+               if (dev->bulk_out_buffer != NULL)
+                       kfree (dev->bulk_out_buffer);
+               if (dev->write_urb != NULL)
+                       usb_free_urb (dev->write_urb);
+               kfree (dev);
+       } else {
+               dev->udev = NULL;
+               up (&dev->sem);
+       }
+
+       /* remove our devfs node */
+       devfs_unregister(dev->devfs);
+
+       info("USB Skeleton #%d now disconnected", minor);
+       up (&minor_table_mutex);
+}
+
+
+
+/**
+ *     usb_skel_init
+ */
+static int __init usb_skel_init(void)
+{
+       int result;
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&skel_driver);
+       if (result < 0) {
+               err("usb_register failed for the "__FILE__" driver. Error number %d",
+                   result);
+               return -1;
+       }
+
+       info(DRIVER_VERSION " " DRIVER_AUTHOR);
+       info(DRIVER_DESC);
+       return 0;
+}
+
+
+/**
+ *     usb_skel_exit
+ */
+static void __exit usb_skel_exit(void)
+{
+       /* deregister this driver with the USB subsystem */
+       usb_deregister(&skel_driver);
+}
+
+
+module_init (usb_skel_init);
+module_exit (usb_skel_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
index 71b46cf3858f03d85aa92d9efbbda086f14bcd8e..c7462d463d6a42d0705f1091a6ffed29d081d18e 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/kmod.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
+#include <linux/spinlock.h>
 
 #ifdef CONFIG_USB_DEBUG
        #define DEBUG
@@ -59,6 +60,7 @@ static void usb_check_support(struct usb_device *);
  */
 LIST_HEAD(usb_driver_list);
 LIST_HEAD(usb_bus_list);
+rwlock_t usb_bus_list_lock = RW_LOCK_UNLOCKED;
 
 devfs_handle_t usb_devfs_handle;       /* /dev/usb dir. */
 
@@ -110,6 +112,7 @@ void usb_scan_devices(void)
 {
        struct list_head *tmp;
 
+       read_lock_irq (&usb_bus_list_lock);
        tmp = usb_bus_list.next;
        while (tmp != &usb_bus_list) {
                struct usb_bus *bus = list_entry(tmp,struct usb_bus, bus_list);
@@ -117,6 +120,7 @@ void usb_scan_devices(void)
                tmp = tmp->next;
                usb_check_support(bus->root_hub);
        }
+       read_unlock_irq (&usb_bus_list_lock);
 }
 
 /*
@@ -178,6 +182,7 @@ void usb_deregister(struct usb_driver *driver)
         */
        list_del(&driver->driver_list);
 
+       read_lock_irq (&usb_bus_list_lock);
        tmp = usb_bus_list.next;
        while (tmp != &usb_bus_list) {
                struct usb_bus *bus = list_entry(tmp,struct usb_bus,bus_list);
@@ -185,6 +190,7 @@ void usb_deregister(struct usb_driver *driver)
                tmp = tmp->next;
                usb_drivers_purge(driver, bus->root_hub);
        }
+       read_unlock_irq (&usb_bus_list_lock);
 }
 
 struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
@@ -415,6 +421,7 @@ void usb_register_bus(struct usb_bus *bus)
 {
        int busnum;
 
+       write_lock_irq (&usb_bus_list_lock);
        busnum = find_next_zero_bit(busmap.busmap, USB_MAXBUS, 1);
        if (busnum < USB_MAXBUS) {
                set_bit(busnum, busmap.busmap);
@@ -426,6 +433,7 @@ void usb_register_bus(struct usb_bus *bus)
 
        /* Add it to the list of buses */
        list_add(&bus->bus_list, &usb_bus_list);
+       write_unlock_irq (&usb_bus_list_lock);
 
        usbdevfs_add_bus(bus);
 
@@ -447,9 +455,11 @@ void usb_deregister_bus(struct usb_bus *bus)
         * controller code, as well as having it call this when cleaning
         * itself up
         */
+       write_lock_irq (&usb_bus_list_lock);
        list_del(&bus->bus_list);
+       write_unlock_irq (&usb_bus_list_lock);
 
-       usbdevfs_remove_bus(bus);
+       usbdevfs_remove_bus(bus);
 
        clear_bit(bus->busnum, busmap.busmap);
 
index 9e41f7f3851e3c171ef2376f9e60b7fa49277998..3eb54b7874d1ad5f841976f9f3064b9dac4862c5 100644 (file)
@@ -60,7 +60,7 @@ static struct tgafb_par current_par;
 static int current_par_valid = 0;
 static struct display disp;
 
-static char default_fontname[40] = { 0 };
+static char default_fontname[40] __initdata = { 0 };
 static struct fb_var_screeninfo default_var;
 static int default_var_valid = 0;
 
@@ -281,9 +281,9 @@ static int tgafb_blank(int blank, struct fb_info_gen *info);
 static void tgafb_set_disp(const void *fb_par, struct display *disp, 
                struct fb_info_gen *info);
 
+#ifndef MODULE
 int tgafb_setup(char*);
-int tgafb_init(void);
-void tgafb_cleanup(struct fb_info *info);
+#endif
 
 static void tgafb_set_pll(int f);
 #if 1
@@ -879,6 +879,7 @@ static struct fb_ops tgafb_ops = {
 };
 
 
+#ifndef MODULE
     /*
      *  Setup
      */
@@ -910,6 +911,7 @@ int __init tgafb_setup(char *options) {
     }
     return 0;
 }
+#endif
 
 
     /*
@@ -990,9 +992,9 @@ int __init tgafb_init(void)
      *  Cleanup
      */
 
-void tgafb_cleanup(struct fb_info *info)
+void __exit tgafb_cleanup(void)
 {
-    unregister_framebuffer(info);
+    unregister_framebuffer(&fb_info.gen.info);
 }
 
 
@@ -1001,14 +1003,7 @@ void tgafb_cleanup(struct fb_info *info)
      */
 
 #ifdef MODULE
-int init_module(void)
-{
-    return tgafb_init();
-}
-
-void cleanup_module(void)
-{
-    tgafb_cleanup(void);
-}
-#endif /* MODULE */
+module_init(tgafb_init);
+#endif
 
+module_exit(tgafb_cleanup);
index c9fbe82dc1a1cf8343daf36cfccd0d47b94d2a3f..9cdbe270464fbf0a3f1ef9a4d383b5ecb5a9d3c1 100644 (file)
  *
  *  Leases and LOCK_MAND
  *  Matthew Wilcox <willy@linuxcare.com>, June, 2000.
- *  Stephen Rothwell <sfr@linuxcare.com>, June, 2000.
+ *  Stephen Rothwell <sfr@canb.auug.org.au>, June, 2000.
  */
 
 #include <linux/slab.h>
index a65ec3fe65c386ae0a7a5b120e13b5c7972866c9..5e55da5cfa0c3de02a8202c546ba7e569900b6e4 100644 (file)
@@ -48,16 +48,7 @@ extern __inline__ unsigned long ___mem_isa(unsigned long a)
 #define __arch_getw(a)         (*(volatile unsigned short *)(a))
 #define __arch_putw(v,a)       (*(volatile unsigned short *)(a) = (v))
 
-#include <asm/hardware/dec21285.h>
-
-/*
- * ioremap support - validate a PCI memory address,
- * and convert a PCI memory address to a physical
- * address for the page tables.
- */
-#define iomem_valid_addr(iomem,sz) \
-       ((iomem) < 0x80000000 && (iomem) + (sz) <= 0x80000000)
-
-#define iomem_to_phys(iomem)   ((iomem) + DC21285_PCI_MEM)
+#define iomem_valid_addr(iomem,sz)     (1)
+#define iomem_to_phys(iomem)           (iomem)
 
 #endif
index bad7b07ea0f1be98c003827e2c758fea158981cb..607147a34b334d4892c654964ce0ae82d67752a8 100644 (file)
 /*
  * Validate the pci memory address for ioremap.
  */
-#define iomem_valid_addr(iomem,size)   \
-       ((iomem) > 0 && (iomem) + (size) <= 0x20000000)
+#define iomem_valid_addr(iomem,size)   (1)
 
 /*
  * Convert PCI memory space to a CPU physical address
  */
-#define iomem_to_phys(iomem)   ((iomem) + PHYS_PCI_MEM_BASE)
+#define iomem_to_phys(iomem)   (iomem)
 
 #endif
index 550310ffeffe2c82fda2500a234576b589b95c99..1c080f179268f327718b4a214ccd66e088d7cead 100644 (file)
@@ -4,10 +4,9 @@
 #include <linux/config.h>
 #include <linux/threads.h>
 
-/* entry.S is sensitive to the offsets of these fields */
+/* softirq.h is sensitive to the offsets of these fields */
 typedef struct {
-       unsigned int __softirq_active;
-       unsigned int __softirq_mask;
+       unsigned int __softirq_pending;
        unsigned int __local_irq_count;
        unsigned int __local_bh_count;
        unsigned int __syscall_count;
index 7f118bc75576a2c3480a8d54bfbbd21d322a71f7..b3f4d4b17cc70f0f7d5cbcca73173871d7b261c1 100644 (file)
@@ -7,36 +7,26 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#define MAX_NR_BUS     2
+struct hw_pci {
+       /* Initialise the hardware */
+       void            (*init)(void *);
+
+       /* Setup bus resources */
+       void            (*setup_resources)(struct resource **);
 
-struct arm_bus_sysdata {
-       /*
-        * bitmask of features we can turn.
-        * See PCI command register for more info.
-        */
-       u16             features;
-       /*
-        * Maximum devsel for this bus.
-        */
-       u16             maxdevsel;
        /*
-        * The maximum latency that devices on this
-        * bus can withstand.
+        * This is the offset of PCI memory base registers
+        * to physical memory.
         */
-       u8              max_lat;
-};
-
-struct arm_pci_sysdata {
-       struct arm_bus_sysdata bus[MAX_NR_BUS];
-};
+       unsigned long   mem_offset;
 
-struct hw_pci {
-       void            (*init)(struct arm_pci_sysdata *);
+       /* IRQ swizzle */
        u8              (*swizzle)(struct pci_dev *dev, u8 *pin);
+
+       /* IRQ mapping */
        int             (*map_irq)(struct pci_dev *dev, u8 slot, u8 pin);
 };
 
 extern u8 no_swizzle(struct pci_dev *dev, u8 *pin);
-
-void __init dc21285_init(struct arm_pci_sysdata *);
-void __init plx90x0_init(struct arm_pci_sysdata *);
+extern void __init dc21285_setup_resources(struct resource **resource);
+extern void __init dc21285_init(void *sysdata);
index 01e0d73d1062fb748b852b04e5f68b2251f0a938..78dd609063a1ec45cd6bb26a59d90ad3cfc2fe40 100644 (file)
@@ -4,12 +4,23 @@
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
 
-#define cpu_bh_disable(cpu)    do { local_bh_count(cpu)++; barrier(); } while (0)
-#define cpu_bh_enable(cpu)     do { barrier(); local_bh_count(cpu)--; } while (0)
+#define __cpu_bh_enable(cpu) \
+               do { barrier(); local_bh_count(cpu)--; } while (0)
+#define cpu_bh_disable(cpu) \
+               do { local_bh_count(cpu)++; barrier(); } while (0)
 
 #define local_bh_disable()     cpu_bh_disable(smp_processor_id())
-#define local_bh_enable()      cpu_bh_enable(smp_processor_id())
+#define __local_bh_enable()    __cpu_bh_enable(smp_processor_id())
+#define __cpu_raise_softirq(cpu,nr) set_bit((nr), &softirq_pending(cpu))
+#define raise_softirq(nr)      __cpu_raise_softirq(smp_processor_id(), (nr))
 
 #define in_softirq()           (local_bh_count(smp_processor_id()) != 0)
 
+#define local_bh_enable()                                              \
+do {                                                                   \
+       unsigned int *ptr = &local_bh_count(smp_processor_id());        \
+       if (!--*ptr && ptr[-2])                                         \
+               __asm__("bl%? __do_softirq": : : "lr");/* out of line */\
+} while (0)
+
 #endif /* __ASM_SOFTIRQ_H */
index 21296d96140ad4b2af313d676c2cee7f23282ee1..2ce1df92e274674fab6ba69ba5bb8a8e4342a5b9 100644 (file)
@@ -222,6 +222,7 @@ static inline void clear_in_cr4 (unsigned long mask)
 #define CX86_CCR4 0xe8
 #define CX86_CCR5 0xe9
 #define CX86_CCR6 0xea
+#define CX86_CCR7 0xeb
 #define CX86_DIR0 0xfe
 #define CX86_DIR1 0xff
 #define CX86_ARR_BASE 0xc4
index f171e5d836563f7b633438d36e7ce08494eb94b5..7f3cdc0d7b00290b1a1f0ddb68bf9ce82e87cc8b 100644 (file)
@@ -58,7 +58,6 @@
 #define FIONCLEX       0x6602          /* these numbers need to be adjusted. */
 #define FIOASYNC       0x667d
 #define FIONBIO                0x667e
-#define FIOQSIZE       0x667f
 
 #if defined(__USE_MISC) || defined (__KERNEL__)
 #define TIOCGLTC       (tIOC | 116)            /* get special local chars */
diff --git a/include/linux/meye.h b/include/linux/meye.h
new file mode 100644 (file)
index 0000000..b614696
--- /dev/null
@@ -0,0 +1,57 @@
+/* 
+ * Motion Eye video4linux driver for Sony Vaio PictureBook
+ *
+ * Copyright (C) 2001 Stelian Pop <stelian.pop@fr.alcove.com>, AlcĂ´ve
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ * 
+ * Some parts borrowed from various video4linux drivers, especially
+ * bttv-driver.c and zoran.c, see original files for credits.
+ * 
+ * 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.
+ */
+
+#ifndef _MEYE_H_
+#define _MEYE_H_
+
+/****************************************************************************/
+/* Private API for handling mjpeg capture / playback.                       */
+/****************************************************************************/
+
+struct meye_params {
+       unsigned char subsample;
+       unsigned char quality;
+       unsigned char sharpness;
+       unsigned char agc;
+       unsigned char picture;
+       unsigned char framerate;
+};
+
+/* query the extended parameters */
+#define MEYEIOC_G_PARAMS       _IOR ('v', BASE_VIDIOCPRIVATE+0, struct meye_params)
+/* set the extended parameters */
+#define MEYEIOC_S_PARAMS       _IOW ('v', BASE_VIDIOCPRIVATE+1, struct meye_params)
+/* queue a buffer for mjpeg capture */
+#define MEYEIOC_QBUF_CAPT      _IOW ('v', BASE_VIDIOCPRIVATE+2, int)
+/* sync a previously queued mjpeg buffer */
+#define MEYEIOC_SYNC           _IOWR('v', BASE_VIDIOCPRIVATE+3, int)
+/* get a still uncompressed snapshot */
+#define MEYEIOC_STILLCAPT      _IO  ('v', BASE_VIDIOCPRIVATE+4)
+/* get a jpeg compressed snapshot */
+#define MEYEIOC_STILLJCAPT     _IOR ('v', BASE_VIDIOCPRIVATE+5, int)
+
+#endif
index 68f20493499808d7771da3fe930dd9207858a1ff..f47902bcfdb01d4a7f307c96cccbbdafee9c2aac 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef _LINUX_NLS_H
+#define _LINUX_NLS_H
+
 #include <linux/init.h>
 
 /* unicode character */
@@ -28,3 +31,6 @@ extern int utf8_mbtowc(wchar_t *, const __u8 *, int);
 extern int utf8_mbstowcs(wchar_t *, const __u8 *, int);
 extern int utf8_wctomb(__u8 *, wchar_t, int);
 extern int utf8_wcstombs(__u8 *, const wchar_t *, int);
+
+#endif /* _LINUX_NLS_H */
+
index 4afedc1502a77aed90c6d86aea9fd1153297edbf..43186de10f0e355669f8fa43f839d9731274d268 100644 (file)
@@ -680,13 +680,16 @@ static inline int pci_module_init(struct pci_driver *drv)
 #if defined(CONFIG_HOTPLUG) && !defined(MODULE)
        if (rc == 0)
                return 0;
+#else
+       if (rc == 0)
+               rc = -ENODEV;           
 #endif
 
        /* if we get here, we need to clean up pci driver instance
         * and return some sort of error */
        pci_unregister_driver (drv);
        
-       return -ENODEV;
+       return rc;
 }
 
 #endif /* !CONFIG_PCI */
index 72d29d9b5aab1c9fe33741886ba93c9380a33381..d6f2700e36308ddf111b598b631bb3c6460e8afe 100644 (file)
@@ -171,6 +171,8 @@ static inline void proc_net_remove(const char *name)
 
 #else
 
+#define proc_root_driver NULL
+
 static inline struct proc_dir_entry *proc_net_create(const char *name, mode_t mode, 
        get_info_t *get_info) {return NULL;}
 static inline void proc_net_remove(const char *name) {}
diff --git a/include/linux/sonypi.h b/include/linux/sonypi.h
new file mode 100644 (file)
index 0000000..b563a29
--- /dev/null
@@ -0,0 +1,103 @@
+/* 
+ * Sony Programmable I/O Control Device driver for VAIO
+ *
+ * Copyright (C) 2001 Stelian Pop <stelian.pop@fr.alcove.com>, AlcĂ´ve
+ *
+ * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au>
+ *
+ * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp>
+ *
+ * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp>
+ *
+ * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com>
+ *
+ * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras.
+ * 
+ * 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.
+ *
+ */
+
+#ifndef _SONYPI_H_ 
+#define _SONYPI_H_
+
+#include <linux/types.h>
+
+/* events the user application reading /dev/sonypi can use */
+
+#define SONYPI_EVENT_JOGDIAL_DOWN               1
+#define SONYPI_EVENT_JOGDIAL_UP                         2
+#define SONYPI_EVENT_JOGDIAL_DOWN_PRESSED       3
+#define SONYPI_EVENT_JOGDIAL_UP_PRESSED                 4
+#define SONYPI_EVENT_JOGDIAL_PRESSED            5
+#define SONYPI_EVENT_JOGDIAL_RELEASED           6
+#define SONYPI_EVENT_CAPTURE_PRESSED            7
+#define SONYPI_EVENT_CAPTURE_RELEASED           8
+#define SONYPI_EVENT_CAPTURE_PARTIALPRESSED     9
+#define SONYPI_EVENT_CAPTURE_PARTIALRELEASED   10
+#define SONYPI_EVENT_FNKEY_ESC                 11
+#define SONYPI_EVENT_FNKEY_F1                  12
+#define SONYPI_EVENT_FNKEY_F2                  13
+#define SONYPI_EVENT_FNKEY_F3                  14
+#define SONYPI_EVENT_FNKEY_F4                  15
+#define SONYPI_EVENT_FNKEY_F5                  16
+#define SONYPI_EVENT_FNKEY_F6                  17
+#define SONYPI_EVENT_FNKEY_F7                  18
+#define SONYPI_EVENT_FNKEY_F8                  19
+#define SONYPI_EVENT_FNKEY_F9                  20
+#define SONYPI_EVENT_FNKEY_F10                 21
+#define SONYPI_EVENT_FNKEY_F11                 22
+#define SONYPI_EVENT_FNKEY_F12                 23
+#define SONYPI_EVENT_FNKEY_1                   24
+#define SONYPI_EVENT_FNKEY_2                   25
+#define SONYPI_EVENT_FNKEY_D                   26
+#define SONYPI_EVENT_FNKEY_E                   27
+#define SONYPI_EVENT_FNKEY_F                   28
+#define SONYPI_EVENT_FNKEY_S                   29
+#define SONYPI_EVENT_FNKEY_B                   30
+#define SONYPI_EVENT_BLUETOOTH_PRESSED         31
+
+/* brightness etc. ioctls */
+#define SONYPI_IOCGBRT _IOR('v', 0, __u8)
+#define SONYPI_IOCSBRT _IOW('v', 0, __u8)
+
+#ifdef __KERNEL__
+
+/* used only for communication between v4l and sonypi */
+
+#define SONYPI_COMMAND_GETCAMERA                1
+#define SONYPI_COMMAND_SETCAMERA                2
+#define SONYPI_COMMAND_GETCAMERABRIGHTNESS      3
+#define SONYPI_COMMAND_SETCAMERABRIGHTNESS      4
+#define SONYPI_COMMAND_GETCAMERACONTRAST        5
+#define SONYPI_COMMAND_SETCAMERACONTRAST        6
+#define SONYPI_COMMAND_GETCAMERAHUE             7
+#define SONYPI_COMMAND_SETCAMERAHUE             8
+#define SONYPI_COMMAND_GETCAMERACOLOR           9
+#define SONYPI_COMMAND_SETCAMERACOLOR          10
+#define SONYPI_COMMAND_GETCAMERASHARPNESS      11
+#define SONYPI_COMMAND_SETCAMERASHARPNESS      12
+#define SONYPI_COMMAND_GETCAMERAPICTURE                13
+#define SONYPI_COMMAND_SETCAMERAPICTURE                14
+#define SONYPI_COMMAND_GETCAMERAAGC            15
+#define SONYPI_COMMAND_SETCAMERAAGC            16
+#define SONYPI_COMMAND_GETCAMERADIRECTION      17
+#define SONYPI_COMMAND_GETCAMERAROMVERSION     18
+#define SONYPI_COMMAND_GETCAMERAREVISION       19
+
+u8 sonypi_camera_command(int command, u8 value);
+
+#endif /* __KERNEL__ */
+
+#endif /* _SONYPI_H_ */
index 8bb2d1c5d10dc1771dd2daec1fbf6f98558b62e1..1d49bb8e265ac95e3093b39a560e4788b94f3039 100644 (file)
@@ -34,7 +34,8 @@
 #ifdef CONFIG_SMP
 #include <asm/spinlock.h>
 
-#else /* !SMP */
+#elif !defined(spin_lock_init) /* !SMP and spin_lock_init not previously
+                                  defined (e.g. by including asm/spinlock.h */
 
 #define DEBUG_SPINLOCKS        0       /* 0 == no debugging, 1 == maintain lock state, 2 == full debug */
 
index d43f5d981d7c5db2ae56fea52fcaaacfa24b3795..3f4a3c3c33564054ddd088c955b0b983ab3c7451 100644 (file)
 
 #define MAX_SWAPFILES 8
 
+/*
+ * Magic header for a swap area. The first part of the union is
+ * what the swap magic looks like for the old (limited to 128MB)
+ * swap area format, the second part of the union adds - in the
+ * old reserved area - some extra information. Note that the first
+ * kilobyte is reserved for boot loader or disk label stuff...
+ *
+ * Having the magic at the end of the PAGE_SIZE makes detecting swap
+ * areas somewhat tricky on machines that support multiple page sizes.
+ * For 2.5 we'll probably want to move the magic to just beyond the
+ * bootbits...
+ */
 union swap_header {
        struct 
        {
                char reserved[PAGE_SIZE - 10];
-               char magic[10];
+               char magic[10];                 /* SWAP-SPACE or SWAPSPACE2 */
        } magic;
        struct 
        {
@@ -46,6 +58,9 @@ union swap_header {
 #define SWAP_MAP_MAX   0x7fff
 #define SWAP_MAP_BAD   0x8000
 
+/*
+ * The in-memory structure used to track swap areas.
+ */
 struct swap_info_struct {
        unsigned int flags;
        kdev_t swap_device;
index 5e8c283da482d845c12ecefaf7c6fcbcbf1fab85..3a00a26e2da1dcf30283d0437f8fce2c5605f17d 100644 (file)
@@ -60,7 +60,9 @@
  * OSF/1 kernel. The SHIFT_HZ define expresses the same value as the
  * nearest power of two in order to avoid hardware multiply operations.
  */
-#if HZ >= 24 && HZ < 48
+#if HZ >= 12 && HZ < 24
+# define SHIFT_HZ      4
+#elif HZ >= 24 && HZ < 48
 # define SHIFT_HZ      5
 #elif HZ >= 48 && HZ < 96
 # define SHIFT_HZ      6
index b396b93b26eb554a24b829fe11a61ed2a186f6cb..389c32372e2bc1f4bbf3baeb3306caaa198e5856 100644 (file)
@@ -366,6 +366,7 @@ extern int stli_init(void);
 extern int specialix_init(void);
 extern int espserial_init(void);
 extern int macserial_init(void);
+extern int a2232board_init(void);
 
 extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
                              const char *routine);
index 2e1217ad1b27b57adf45d9f85e728a12b5487ce3..d478980ebfbe0493e368834eae753b647e1894ac 100644 (file)
@@ -838,6 +838,7 @@ void usb_show_string(struct usb_device *dev, char *id, int index);
 
 extern struct list_head usb_driver_list;
 extern struct list_head usb_bus_list;
+extern rwlock_t usb_bus_list_lock;
 
 /*
  * USB device fs stuff
index 487ee821526f355f7823863ecd53aefe66b6838c..fd8a34b4571d85aa86e045714ee429e32c08096a 100644 (file)
@@ -162,18 +162,6 @@ struct usbdevfs_hub_portinfo {
 
 #define IROOT      1
 
-/*
- * sigh. rwsemaphores do not (yet) work from modules
- */
-
-#define rw_semaphore semaphore
-#define init_rwsem init_MUTEX
-#define down_read down
-#define down_write down
-#define up_read up
-#define up_write up
-
-
 struct dev_state {
        struct list_head list;      /* state list */
        struct rw_semaphore devsem; /* protects modifications to dev (dev == NULL indicating disconnect) */ 
index 33524af3cb4c7b6a6294ba80ed788ffaec23d173..6dbe38adfa929df90b8347f27c474c0267a9bcd8 100644 (file)
@@ -375,6 +375,7 @@ struct video_code
 #define VID_HARDWARE_W9966     29
 #define VID_HARDWARE_SE401     30      /* SE401 USB webcams */
 #define VID_HARDWARE_PWC       31      /* Philips webcams */
+#define VID_HARDWARE_MEYE      32      /* Sony Vaio MotionEye cameras */
 
 /*
  *     Initialiser list
index 9423ba1dbfe457ad3effb093f90033cbf314ceaf..eb118a2dcce846312caeb229b261b4a1459829a0 100644 (file)
@@ -9,89 +9,43 @@
 Original driver (sg.h):
 *       Copyright (C) 1992 Lawrence Foard
 Version 2 and 3 extensions to driver:
-*       Copyright (C) 1998 - 2000 Douglas Gilbert
+*       Copyright (C) 1998 - 2001 Douglas Gilbert
 
-    Version: 3.1.17 (20000921)
+    Version: 3.1.19 (20010623)
     This version is for 2.4 series kernels.
 
-    Changes since 3.1.16 (20000716)
-       - changes for new scsi subsystem initialization
-       - change Scsi_Cmnd usage to Scsi_Request
-       - cleanup for no procfs
-    Changes since 3.1.15 (20000528)
-       - further (scatter gather) buffer length changes
-    Changes since 3.1.14 (20000503)
-        - fix aha1542 odd length buffer problem
-        - make multiple readers on same fd safe
-    Changes since 3.1.13 (20000324)
-        - revert change so sg_header interface doesn't send _UNKNOWN
-        - "discon" and "tq" in /proc/scsi/sg/devices replaced with
-          "bopens" and "busy"; correct duration output in procfs
-        - provision for SG_RESET
-        - lock file descriptor and request lists
-    Changes since 3.1.12 (20000222)
-       - make sg_header interface use SCSI_DATA_UNKNOWN
-       - add SG_DXFER_UNKNOWN define to sg interface
-       - stop allocating data buffers to non data transfer commands
-    Changes since 3.1.10 (20000123)
-       - make device allocation dynamic (get rid of SG_EXTRA_DEVS)
-       - move to sg0,sg1,sg2 rather than sga,sgb,sgc
-       - make sg_io_hdr_t safer across architectures
-    Changes since 2.1.34 (990603) and 2.3.35 (990708)
-       - add new interface structure: sg_io_hdr_t
-         - supports larger sense buffer, DMA residual count + direct IO
-       - add SG_IO ioctl (combines function of write() + read() )
-       - remove SG_SET_MERGE_FD, UNDERRUN_FLAG + _GET_ ioctls + logic
-       - add proc_fs support in /proc/scsi/sg/ directory
-        - add queuing info into struct sg_scsi_id
-       - def_reserved_size can be given at driver or module load time
-    Changes since 2.1.33 (990521)
-        - implement SG_SET_RESERVED_SIZE and associated memory re-org.
-        - add SG_NEXT_CMD_LEN to override SCSI command lengths
-        - add SG_GET_VERSION_NUM to get version expressed as an integer
-    Changes since 2.1.32 (990501)
-        - fix race condition in sg_read() and sg_open()
-    Changes since 2.1.31 (990327)
-        - add ioctls SG_GET_UNDERRUN_FLAG and _SET_. Change the default
-          to _not_ flag underruns (affects aic7xxx driver)
-        - clean up logging of pointers to use %p (for 64 bit architectures)
-        - rework usage of get_user/copy_to_user family of kernel calls
-        - "disown" scsi_command blocks before releasing them
+    Changes since 3.1.18 (20010505)
+       - fix bug that caused long wait when large buffer requested
+       - fix leak in error case of sg_new_read() [report: Eric Barton]
+       - add 'online' column to /proc/scsi/sg/devices
+    Changes since 3.1.17 (20000921)
+       - add CAP_SYS_RAWIO capability for sensitive stuff
+       - compile in dio stuff, procfs 'allow_dio' defaulted off (0)
+       - make premature close and detach more robust
+       - lun masked into commands <= SCSI_2
+       - poll() and async notification now yield POLL_HUP on detach
+       - various 3rd party tweaks tracking lk 2.4 internal changes
 
 Map of SG verions to the Linux kernels in which they appear:
        ----------        ----------------------------------
        original          all kernels < 2.2.6
-       2.1.31            2.2.6 and 2.2.7
-       2.1.32            2.2.8 and 2.2.9
-       2.1.34            2.2.10 to 2.2.13
-       2.1.36            2.2.14 and 2.2.15
+       2.1.38            2.2.16
+       2.1.39            2.2.17 - 2.2.19
        3.0.x             optional version 3 sg driver for 2.2 series
-       3.1.x             first appeared in lk 2.3.43
+       3.1.17            2.4.0 ++
 
 Major new features in SG 3.x driver (cf SG 2.x drivers)
        - SG_IO ioctl() combines function if write() and read()
        - new interface (sg_io_hdr_t) but still supports old interface
        - scatter/gather in user space and direct IO supported
 
-Major features in SG 2.x driver (cf original SG driver)
-       - per file descriptor (fd) write-read sequencing
-       - command queuing supported
-       - scatter-gather supported at kernel level allowing potentially
-         large transfers
-       - more SCSI status information returned
-       - asynchronous notification support added (SIGPOLL, SIGIO)
-       - read() can fetch by given pack_id
-       - uses kernel memory as appropriate for SCSI adapter being used
-       - single SG_BIG_BUFF replaced by per file descriptor "reserve
-         buffer" whose size can be manipulated by ioctls()
-
  The term "indirect IO" refers a method by which data is DMAed into kernel
  buffers from the hardware and afterwards is transferred into the user
  space (or vice versa if you are writing). Transfer speeds of up to 20 to
  30MBytes/sec have been measured using indirect IO. For faster throughputs
  "direct IO" which cuts out the double handling of data is required.
- Direct IO is supported by the SG 3.x drivers on 2.3 series Linux kernels
(or later) and requires the use of the new interface.
+ Direct IO is supported by the SG 3.x drivers on 2.4 series Linux kernels
+ and requires the use of the new interface.
 
  Requests for direct IO with the new interface will automatically fall back
  to indirect IO mode if they cannot be fulfilled. An example of such a case
@@ -107,6 +61,9 @@ Major features in SG 2.x driver (cf original SG driver)
  kernel memory that is suitable for DMA may be constrained by the
  architecture of the SCSI adapter (e.g. ISA adapters).
 
+ ** N.B. To use direct IO 'echo 1 > /proc/scsi/sg/allow_dio' may be
+         needed. That pseudo file's content is defaulted to 0. **
+
  Documentation
  =============
  A web site for SG device drivers can be found at:
@@ -116,9 +73,10 @@ Major features in SG 2.x driver (cf original SG driver)
        http://www.torque.net/sg/p/scsi-generic_long.txt
  Documentation on the changes and additions in 3.x version of the sg driver
  can be found at: http://www.torque.net/sg/p/scsi-generic_v3.txt
- This document can also be found in the kernel source tree, probably at:
+ A version of this document (potentially out of date) may also be found in
+ the kernel source tree, probably at:
         /usr/src/linux/Documentation/scsi-generic.txt .
- Utility and test programs are also available at that web site.
+ Utility and test programs are available at the sg web site.
 */
 
 /* New interface introduced in the 3.x SG drivers follows */
index fabc9a01d959ba8a0e96179987055914217fc1e7..e356977156fa1eb70ae878ed70b2b24c920c5ef1 100644 (file)
@@ -502,6 +502,21 @@ static void __init smp_init(void)
 
 #endif
 
+/*
+ * We need to finalize in a non-__init function or else race conditions
+ * between the root thread and the init thread may cause start_kernel to
+ * be reaped by free_initmem before the root thread has proceeded to
+ * cpu_idle.
+ */
+
+static void rest_init(void)
+{
+       kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
+       unlock_kernel();
+       current->need_resched = 1;
+       cpu_idle();
+} 
+
 /*
  *     Activate the first processor.
  */
@@ -584,10 +599,7 @@ asmlinkage void __init start_kernel(void)
         *      make syscalls (and thus be locked).
         */
        smp_init();
-       kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL);
-       unlock_kernel();
-       current->need_resched = 1;
-       cpu_idle();
+       rest_init();
 }
 
 #ifdef CONFIG_BLK_DEV_INITRD