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
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
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
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
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
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
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
--- /dev/null
+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.
--- /dev/null
+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>
--- /dev/null
+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 ?).
--- /dev/null
+
+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
VERSION = 2
PATCHLEVEL = 4
SUBLEVEL = 7
-EXTRAVERSION =-pre1
+EXTRAVERSION =-pre2
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
#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;
}
/*
- * 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;
+ }
}
}
{
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,
}, { 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;
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
*/
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) ?
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
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)
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)
* 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)
{
#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);
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;
"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
* 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
}
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(®s);
enable_fiq(dma->dma_irq);
* 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
* 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
* 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.
irq_exit(cpu, irq);
- if (softirq_active(cpu) & softirq_mask(cpu))
+ if (softirq_pending(cpu))
do_softirq();
return;
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);
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);
"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");
}
#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
#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 };
}
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,
};
#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 };
}
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,
};
#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)
}
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,
};
#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,
}
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,
};
# 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
#
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
+#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
};
#define dmi_printk(x)
+//#define dmi_printk(x) printk(x)
static char * __init dmi_string(struct dmi_header *dm, u8 s)
{
}
+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.
*/
{
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:
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);
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)
{ "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 }
};
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)
{
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;
/* 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;
}
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
*/
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]);
* romfs
* gzip
*/
-int __init
+static int __init
identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)
{
const int size = 512;
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
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
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
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'
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
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
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
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
{
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)
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)
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
{
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
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++;
unsigned long arg)
{
struct dtlk_settings *sp;
- int err;
char portval;
TRACE_TEXT(" dtlk_ioctl");
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;
* 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
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)
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;
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;
* 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,
struct gs_port *port = ptr;
long end_jiffies;
int jiffies_to_transmit, charsleft = 0, rv = 0;
- int to, rcib;
+ int rcib;
func_enter();
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;
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!
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);
EXPORT_SYMBOL(gs_init_port);
EXPORT_SYMBOL(gs_setserial);
EXPORT_SYMBOL(gs_getserial);
-
+EXPORT_SYMBOL(gs_got_break);
/*
* 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);
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.
#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;
}
/* 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;
}
/* 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;
return 0;
}
-int
-h8_alloc_queues(void)
+static int h8_alloc_queues(void)
{
h8_cmd_q_t *qp;
unsigned long flags;
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 */
{
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);
}
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);
*/
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);
}
*/
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 */
}
/*
* Function called when H8 has performed requested command.
*/
-void
+static void
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);
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;
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:
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;
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);
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:
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;
*/
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);
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)) {
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);
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);
}
#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 */
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;
}
//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
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;
#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
* 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:
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");
}
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);
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)){
* 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);
/* ...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");
}
}
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);
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,
{
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;
}
zf_show_action(action);
spin_lock_init(&zf_lock);
+ spin_lock_init(&zf_port_lock);
ret = misc_register(&zf_miscdev);
if (ret){
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",
unsigned long baseAddr;
int i;
+ if(len > sizeof(moxaBuff))
+ return -EINVAL;
if(copy_from_user(moxaBuff, tmp, len))
return -EFAULT;
baseAddr = moxaBaseAddr[cardno];
* 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)
#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>
/* 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,
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;
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,
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);
}
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);
/* 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) {
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 ();
}
PortP->gs.count = 0;
}
-
+ PortP->gs.tty = NULL;
rio_dec_mod_count ();
-
func_exit ();
}
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) {
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) {
((ival & TIOCM_RTS) ? 1 : 0));
}
break;
-
+#endif
default:
rc = -ENOIOCTLCMD;
break;
}
- /* func_exit(); */
+ func_exit();
return rc;
-#else
- return -ENOIOCTLCMD;
-#endif
-
}
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);
tty_unregister_driver (&rio_driver);
func_exit();
}
-#endif
#ifdef TWO_ZERO
#define PDEV unsigned char pci_bus, unsigned pci_fun
#endif
-#ifdef MODULE
-#define rio_init init_module
-#endif
-
-int rio_init(void)
+static int __init rio_init(void)
{
int found = 0;
int i;
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);
* 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");
}
-#ifdef MODULE
-void cleanup_module(void)
+static void __exit rio_exit (void)
{
int i;
struct Host *hp;
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.
#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)
#define rio_memcpy_fromio memcpy_fromio
#endif
-#define DEBUG
+#define DEBUG 1
/*
#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>
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)) {
*/
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)))) {
struct CmdBlk *CmdBlkP;
CmdBlkP = (struct CmdBlk *)sysbrk(sizeof(struct CmdBlk));
- bzero(CmdBlkP, sizeof(struct CmdBlk));
+ if (CmdBlkP)
+ bzero(CmdBlkP, sizeof(struct CmdBlk));
return CmdBlkP;
}
#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)
}
RIODefaultName(p, HostP, rup);
}
- HostP->UnixRups[rup].RupLock = -1;
+ HostP->UnixRups[rup].RupLock = SPIN_LOCK_UNLOCKED;
}
}
}
#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>
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);
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;
}
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;
}
}
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;
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");
work_done++;
}
}
- rio_spin_unlock_irqrestore(&HostP->HostLock, flags);
+ rio_spin_unlock_irqrestore(&HostP->HostLock, lock_flags);
}
/* XXXXX lock me up */
#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);
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
func_exit ();
return -EINTR;
}
-#endif
}
PortP->State &= ~RIO_WOPEN;
}
#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;
/* 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,
** 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
*/
rio_dprintk (RIO_DEBUG_TTY, "Timeout 1 starts\n");
-#if 0
if (!deleted)
while ( (PortP->InUse != NOT_INUSE) && !p->RIOHalted &&
(PortP->TxBufferIn != PortP->TxBufferOut) ) {
}
rio_spin_lock_irqsave(&PortP->portSem, flags);
}
-#endif
+
PortP->TxBufferIn = PortP->TxBufferOut = 0;
repeat_this = 0xff;
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;
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 );
** 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;
--- /dev/null
+/* 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 *********************/
--- /dev/null
+/* 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
--- /dev/null
+;.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
+
+
+
--- /dev/null
+/* 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,
+};
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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_ */
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));
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;
--- /dev/null
+/*
+ * 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);
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");
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
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
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
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.
+++ /dev/null
-/*
- 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
+++ /dev/null
-/*
- 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
int disp_mode;
int virtual_mode;
struct i2c_client *client;
+ struct semaphore lock;
};
}
memset(t, 0, sizeof(*t));
strcpy(client->name, IF_NAME);
+ init_MUTEX(&t->lock);
/*
* Now create a video4linux device
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));
t->vdau[pgbuf].stopped = TRUE;
t->is_searching[pgbuf] = FALSE;
}
- vd->priv=t;
+ vd->priv=t;
+
/*
* Register it
* 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)
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)
{
t->is_searching[pgbuf] = FALSE;
}
t->virtual_mode=FALSE;
- MOD_INC_USE_COUNT;
return 0;
}
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;
}
static struct video_device saa_template =
{
+ owner: THIS_MODULE,
name: IF_NAME,
type: VID_TYPE_TELETEXT, /*| VID_TYPE_TUNER ?? */
hardware: VID_HARDWARE_SAA5249,
--- /dev/null
+/*
+ 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
--- /dev/null
+#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
+}
/*
- 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
/*
- 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_
--- /dev/null
+#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);
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, ®s->IfMtu);
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)
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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 */
+};
#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))
* 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.
#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 */
#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
{
#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
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)
{
}
#endif
-static int eth16i_get_irq(int ioaddr)
+static int __init eth16i_get_irq(int ioaddr)
{
unsigned char cbyte;
}
}
-static int eth16i_check_signature(int ioaddr)
+static int __init eth16i_check_signature(int ioaddr)
{
int i;
unsigned char creg[4] = { 0 };
*
* 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;
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);
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";
/*
* 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? */
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(":");
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);
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)
{
}
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;
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) {
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 {
}
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;
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);
* 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);
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 |
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);
}
}
-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;
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;
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 */
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;
{
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;
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 = {
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)
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);
* 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)
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;
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);
ip->ehar_l = 0xffffffff;
} else {
for (i = 0; i < dev->mc_count; i++) {
+ char *addr = dmi->dmi_addr;
dmi = dmi->next;
if (!(*addr & 1))
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>");
--- /dev/null
+/* 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);
#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);
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"
#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
#ifdef CONFIG_MADGEMC
{madgemc_probe, 0},
#endif
+#ifdef CONFIG_UML_NET
+ {uml_net_probe, 0},
+#endif
+
{NULL, 0},
};
#if defined(CONFIG_X25_ASY)
x25_asy_init_ctrl_dev();
#endif
-#if defined(CONFIG_STRIP)
- strip_init_ctrl_dev();
-#endif
}
{ 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 },
* 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>
*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;
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)
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)
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)*/
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);
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;
}
* 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();
(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");
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();
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
}
}
- 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;
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 &&
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;
* 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 */
* 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
*
*/
#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
#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
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)
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 */
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 */
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 */
{
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);
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);
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)
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;
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' */
}
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;
}
; /* 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;
}
}
}
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;
}
/* 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;
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) ||
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;
}
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;
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) {
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. */
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;
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 */
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;
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"));
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;
{
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:
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);
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);
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];
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 &
}
}
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;
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;
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)
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);
++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;
}
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;
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;
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));
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) {
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)
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;
}
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);
}
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");
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;
}
}
-#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;
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 */
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);
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;
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)
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
}
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;
}
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
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;
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;
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));
}
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;
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;
}
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,
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 { \
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); }
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);
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;
}
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 "
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;
}
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;
}
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
/*
- * 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>
* 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)
*/
/*
#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>
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;
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))
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;
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);
(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;
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);
return ret;
total_written += ret;
}
+ read_unlock_irq (&usb_bus_list_lock);
return total_written;
}
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;
}
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--;
break;
filp->f_pos++;
}
- unlock_kernel();
+ read_unlock_irq (&usb_bus_list_lock);
return 0;
}
}
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:
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)
}
/* 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;
/* 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
#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
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");
* 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"
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);
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);
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);
if (list_empty(&td->list))
return;
- list_del(&td->list);
- INIT_LIST_HEAD(&td->list);
+ list_del_init(&td->list);
td->urb = NULL;
}
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);
}
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);
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);
}
tmp = tmp->next;
- list_del(&urbp->complete_list);
- INIT_LIST_HEAD(&urbp->complete_list);
+ list_del_init(&urbp->complete_list);
uhci_call_completion(urb);
}
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);
--- /dev/null
+/*
+ * 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);
+
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/devfs_fs_kernel.h>
+#include <linux/spinlock.h>
#ifdef CONFIG_USB_DEBUG
#define DEBUG
*/
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. */
{
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);
tmp = tmp->next;
usb_check_support(bus->root_hub);
}
+ read_unlock_irq (&usb_bus_list_lock);
}
/*
*/
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);
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)
{
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);
/* 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);
* 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);
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;
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
};
+#ifndef MODULE
/*
* Setup
*/
}
return 0;
}
+#endif
/*
* Cleanup
*/
-void tgafb_cleanup(struct fb_info *info)
+void __exit tgafb_cleanup(void)
{
- unregister_framebuffer(info);
+ unregister_framebuffer(&fb_info.gen.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);
*
* 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>
#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
/*
* 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
#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;
* 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);
#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 */
#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
#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 */
--- /dev/null
+/*
+ * 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
+#ifndef _LINUX_NLS_H
+#define _LINUX_NLS_H
+
#include <linux/init.h>
/* unicode character */
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 */
+
#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 */
#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) {}
--- /dev/null
+/*
+ * 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_ */
#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 */
#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
{
#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;
* 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
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);
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
#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) */
#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
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
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:
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 */
#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.
*/
* 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