D: Initial implementation of VC's, pty's and select()
N: Pavel Machek
-E: pavel@atrey.karlin.mff.cuni.cz
-D: Softcursor for vga, hypertech cdrom support, vcsa bugfix
-D: Network block device, sun4/330 port
+E: pavel@ucw.cz
+E: pavel@suse.cz
+D: Softcursor for vga, hypertech cdrom support, vcsa bugfix, nbd
+D: sun4/330 port, capabilities for elf, speedup for rm on ext2, USB
S: Volkova 1131
S: 198 00 Praha 9
S: Czech Republic
If you are compiling a kernel which will never run on a machine
with more than 1 Gigabyte total physical RAM, answer "off"
here (default choice). This will result in the old "3GB/1GB"
- virtual/physical memory split. 3BG are mapped so as each processus
+ virtual/physical memory split. 3GB are mapped so as each processus
sees a 3GB virtual memory space.
- The remaining part of the 4G virtual memory space is used by the
+ The remaining part of the 4GB virtual memory space is used by the
kernel to 'permanently map' as much physical memory as possible.
Certain types of applications perform better if there is more
'permanently mapped' kernel memory.
about anything having "SCSI" in its name other than hard disks,
CDROMs or tapes, say Y here. These won't be supported by the kernel
directly, so you need some additional software which knows how to
- talk to these devices using the SCSI protocol. For CD-writers, you
- would need the program cdwrite, available from
- ftp://metalab.unc.edu/pub/Linux/utils/disk-management ; for other
- devices, it's possible that you'll have to write the driver software
- yourself, so have a look at the SCSI-HOWTO and at the
- SCSI-Programming-HOWTO, both available from
- http://www.linuxdoc.org/docs.html#howto . Please read the file
- Documentation/scsi-generic.txt for more information.
+ talk to these devices using the SCSI protocol. For scanners, look at
+ SANE (www.mostang.com/sane). For CD writer software look at cdrecord
+ (www.fokus.gmd.de/research/cc/glone/employees/joerg.schilling/private
+ /cdrecord.html) and for burning a "disk at once": cdrdao
+ (www.ping.de/sites/daneb/cdrdao.html). Cdparanoia is a high quality
+ digital reader of audio CDs (www.xiph.org/paranoia).
+ For other devices, it's possible that you'll have to write the driver
+ software yourself. Please read the file Documentation/scsi-generic.txt
+ for more information.
If you want to compile this as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
substantial, so users of MultiMaster Host Adapters may wish to omit
it.
+DMX3191D SCSI support
+CONFIG_SCSI_DMX3191D
+ This is support for Domex DMX3191D SCSI Host Adapters.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called dmx3191d.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt.
+
DTC3180/3280 SCSI support
CONFIG_SCSI_DTC3280
This is support for DTC 3180/3280 SCSI Host Adapters. Please read
The module will be called usbcore.o. If you want to compile it as a
module, say M here and read Documentation/modules.txt.
+USB verbose debug messages
+CONFIG_USB_DEBUG
+ Say Y here if you want the USB core drivers to produce a bunch of
+ debug messages to the system log. Select this if you are having a
+ problem with USB support and want to see more of what is going on.
+
UHCI (intel PIIX4, VIA, ...) support?
CONFIG_USB_UHCI
The Universal Host Controller Interface is a standard by Intel for
The module will be called printer.o. If you want to compile it as a
module, say M here and read Documentation/modules.txt.
-USB CPiA Camera support
-CONFIG_USB_CPIA
- Say Y here if you want to connect this type of camera to your
- computer's USB port.
-
- This driver uses the Video For Linux API. You must enable
- (Y or M in config) Video For Linux (under Character Devices)
- to use this driver. Information on this API and pointers to
- "v4l" programs may be found on the WWW at
- http://roadrunner.swansea.uk.linux.org/v4l.shtml .
-
- This code is also available as a module ( = code which can be
- inserted in and removed from the running kernel whenever you want).
- The module will be called cpia.o. If you want to compile it as a
- module, say M here and read Documentation/modules.txt.
-
USB IBM (Xirlink) C-It Camera support
CONFIG_USB_IBMCAM
Say Y here if you want to connect this type of camera to your
Say Y here if you want to connect this type of still camera to
your computer's USB port. This driver can be used with gphoto 0.4.3
and higher (look at www.gphoto.org).
- To use it create a devicenode with mknod /dev/mustek c 10 171 and
+ To use it create a devicenode with mknod /dev/mustek c 180 32 and
configure it in your software.
This code is also available as a module ( = code which can be
files, usually found in the /dev directory on your system. They
make it possible to have user-space programs use the I2C bus.
+CPiA Video For Linux
+CONFIG_VIDEO_CPIA
+ This is the video4linux driver for cameras based on Vision's CPiA
+ (Colour Processor Interface ASIC), such as the Creative Labs Video
+ Blaster Webcam II. If you have one of these cameras, say Y here
+ and select parallel port and/or USB lowlevel support below,
+ otherwise say N. This will not work with the Creative Webcam III.
+ It is also available as a module (cpia.o).
+
+CPiA Parallel Port Lowlevel Support
+CONFIG_VIDEO_CPIA_PP
+ This is the lowlevel parallel port support for cameras based on
+ Vision's CPiA (Colour Processor Interface ASIC), such as the
+ Creative Webcam II. If you have the parallel port version of one
+ of these cameras, say Y here, otherwise say N. It is also available
+ as a module (cpia_pp.o).
+
+CPiA USB Lowlevel Support
+CONFIG_VIDEO_CPIA_USB
+ This is the lowlevel USB support for cameras based on Vision's CPiA
+ (Colour Processor Interface ASIC), such as the Creative Webcam II.
+ If you have the USB version of one of these cameras, say Y here,
+ otherwise say N. This will not work with the Creative Webcam III.
+ It is also available as a module (cpia_usb.o).
+
#
# A couple of things I keep forgetting:
# capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet,
BOOKS := wanbook.sgml z8530book.sgml mcabook.sgml videobook.sgml kernel-api.sgml
-
+
+PS := $(patsubst %.sgml, %.ps, $(BOOKS))
+PDF := $(patsubst %.sgml, %.pdf, $(BOOKS))
+
books: docproc $(BOOKS)
+ps: $(PS)
+
+pdf: $(PDF)
+
docproc:
$(MAKE) -C $(TOPDIR)/scripts docproc
$(TOPDIR)/kernel/pm.c \
<kernel-api.tmpl >kernel-api.sgml
+DVI := $(patsubst %.sgml, %.dvi, $(BOOKS))
+AUX := $(patsubst %.sgml, %.aux, $(BOOKS))
+TEX := $(patsubst %.sgml, %.tex, $(BOOKS))
+LOG := $(patsubst %.sgml, %.log, $(BOOKS))
+
clean:
- rm -f core *~
- rm -f $(BOOKS)
+ $(RM) core *~
+ $(RM) $(BOOKS)
+ $(RM) $(DVI) $(AUX) $(TEX) $(LOG)
+
+mrproper: clean
+ $(RM) $(PS) $(PDF)
+
+%.ps : %.sgml
+ db2ps $<
+
+%.pdf : %.sgml
+ db2pdf $<
include $(TOPDIR)/Rules.make
- RS232 serial ports
- audio output
- LCD screen
- - keyboard (needs to be cleaned up badly... any volunteer?)
+ - keyboard
The actual Brutus support may not be complete without extra patches.
If such patches exist, they should be found from
--- /dev/null
+Thin Client / Single Board Computer
+
+The Thin Client, a low cost high power single board computer, has been
+designed to provide intuitive graphical displays in embedded systems.
+
+For more details, contact Applied Data Systems or see
+http://www.flatpanels.com/products.html
+
+Current Linux support for this product has been provided by Nicolas Pitre
+<nico@cam.org>.
+
+It's currently possible to mount a root filesystem via NFS providing a
+complete Linux environment. Otherwyse a ramdisk image may be used. Use
+'make thinclient_config' before any 'make config'. This will set up
+defaults for ThinClient support.
+
+Supported peripherals:
+- SA1100 LCD frame buffer (only 8bpp yet)
+- on-board SMC 92C94 ethernet NIC
+- SA1100 serial port
+- possibly UCB1200 audio (not tested yet)
+
+To do:
+- touchscreen driver
+- flash memory access
+- 16bpp frame buffer support
+- extra (external) serial port driver
+- pcmcia
+- some console keyboard support (maybe IR?)
+- everything else! :-)
+
+Any contribution can be sent to nico@cam.org and will be greatly welcome!
+
-------------------------------------
Alessandro Zummo <azummo@ita.flashnet.it>
+( Be sure to read Documentation/sound/SoundPro too )
+
+
This adapter is now directly supported by the sb driver.
The only thing you have to do is to compile the kernel sound
for MPU401 support.
-CONFIG_SOUND_YM3812=m
-
- for OPL3 support. Please note that there are better ways to play midi files, like
- timidity or the softoss2 module.
-
-
-CONFIG_JOYSTICK=y
-
- to activate the joystick port.
-
-
(I suggest you to use "make menuconfig" or "make xconfig"
for a more comfortable configuration editing)
sb: CMI8330 detected.
sb: CMI8330 sb base located at 0x220
sb: CMI8330 mpu base located at 0x330
-sb: CMI8330 gameport base located at 0x200
-sb: CMI8330 opl3 base located at 0x388
sb: CMI8330 mail reports to Alessandro Zummo <azummo@ita.flashnet.it>
sb: ISAPnP reports CMI 8330 SoundPRO at i/o 0x220, irq 7, dma 1,5
-To activate the OPL3 support, you need these lines in /etc/modules.conf
-or in a file in /etc/modutils
-
-alias synth0 opl3
-options opl3 io=0x388
-
-and then you can do:
-
- modprobe opl3
-
-
-
-
-
-
The old documentation file follows for reference
purposes.
Documentation for the SoundPro CMI8330 extensions in the WSS driver (ad1848.o)
------------------------------------------------------------------------------
+( Be sure to read Documentation/sound/CMI8330 too )
+
Ion Badulescu, ionut@cs.columbia.edu
February 24, 1999
xawtv. Other utilities may work but have not yet been tested.
NEW IN THIS VERSION:
- o Preliminary snapshot support
- o Experimental red-blue misalignment fixes
- o Better YUV420 color conversion
- o Module options
- o Finer-grained debug message control
- o Support for new cameras (4, 36)
- o Uses initcalls
-
-SUPPORTED CAMERAS:
-_________________________________________________________
-Manufacturer | Model | Custom ID | Status
------------------+-----------------+-----------+---------
-MediaForte | MV300 | 0 | Working
-Aiptek | HyperVCam ? | 0 | Working
-NetView | NV300M | 0 | Working
-D-Link | DSB-C300 | 3 | Working
-Hawking Tech. | ??? | 3 | Working
-??? | Generic | 4 | Untested
-Puretek | PT-6007 | 5 | Working
-Creative Labs | WebCam 3 | 21 | Working
-??? | Koala-Cam | 36 | Untested
-Lifeview | RoboCam | 100 | Untested
-AverMedia | InterCam Elite | 102 | Working
-MediaForte | MV300 | 112 | Working
-Omnivision | OV7110 EV board | 112 | Working*
----------------------------------------------------------
-(*) uses OV7110 (monochrome)
-
-Any camera using the OV511 and the OV7610 CCD should work with this driver. The
+ o Autoadjust disable works, but is not perfect yet
+ o Partial support for OV511+
+ o Detection and support for OV7610, OV7620, and OV7620AE sensors
+ o Various code cleanups and improvements
+
+KNOWN CAMERAS:
+____________________________________________________________________
+Manufacturer | Model | ID | Bridge | Sensor | Status
+--------------+-----------------+-----+--------+----------+-----------
+MediaForte | MV300 | 0 | OV511 | OV7610 * | Working
+Aiptek | HyperVCam ? | 0 | OV511 | OV7610 * | Working
+NetView | NV300M | 0 | OV511 | OV7610 * | Working
+D-Link | DSB-C300 | 3 | OV511 | OV7620AE | Working
+Hawking Tech. | ??? | 3 | OV511 | OV7610 * | Working
+??? | Generic | 4 | OV511 | OV7610 * | Untested
+Puretek | PT-6007 | 5 | OV511 | OV7610 * | Working
+Creative Labs | WebCam 3 | 21 | OV511 | OV7610 * | Working
+??? | Koala-Cam | 36 | OV511 | OV7610 * | Untested
+Lifeview | USB Life TV | 38 | OV511 | N/A | Unsupported
+Lifeview | RoboCam | 100 | OV511 | OV7610 * | Untested
+AverMedia | InterCam Elite | 102 | OV511 | OV7610 * | Working
+MediaForte | MV300 | 112 | OV511 | OV7610 * | Working
+Omnivision | OV7110 EV board | 112 | OV511 | OV7110 | Working
+---------------------------------------------------------------------
+
+(*) These have not been verified. If you have one of these, please report
+ the sensor type to me.
+
+NOTE - the OV511+ is not yet supported
+
+Any camera using the OV511 and the OV7610 or OV7620AE CCD should work. The
driver only detects known cameras though, based on their custom id number. If
you have a currently unsupported camera, the ID number should be reported to you
in the kernel logs. Please send me the model, manufacturer and ID number and I
DESC: The camera normally adjusts exposure, gain, and hue automatically. This
can be set to 0 to disable this automatic adjustment. Note that there is
currently no way to set these parameters manually once autoadjust is
- disabled. (This feature is not working yet)
+ disabled.
NAME: debug
TYPE: integer (0-6)
o Color streaming/capture at 640x480 and 320x240
o YUV420 color
o Monochrome
- o Setting/getting of saturation, contrast and brightness (no color yet)
+ o Setting/getting of saturation, contrast and brightness (no hue yet; only
+ works with OV7610, not the OV7620 or OV7620AE)
EXPERIMENTAL FEATURES:
o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
corrupted frames.
o Snapshot mode (only works with some read() based apps; see below for more)
+ o OV511+ support; not complete yet.
TODO:
o Fix the noise / grainy image problem.
o Get hue (red/blue channel balance) adjustment working (in ov511_get_picture()
and ov511_set_picture())
o Get autoadjust disable working
- o Devise some clean way to support different types of CCDs (based on Custom ID)
- o OV511A support
+ o OV7620 support
o V4L2 support (Probably not until it goes into the kernel)
o Fix I2C initialization. Some people are reporting problems with reading the
7610 registers. This could be due to timing differences, an excessive I2C
clock rate, or a problem with ov511_i2c_read().
o Get rid of the memory management functions (put them in videodev.c??)
+ o Setting of contrast and brightness not working with 7620
+ o Driver/camera state save/restore for when USB supports suspend/resume
HOW TO CONTACT ME:
/proc/bus/usb filesystem output
===============================
-(version 19991218)
+(version 2000.03.24)
The /proc filesystem for USB devices generates
/proc/bus/usb/drivers and /proc/bus/usb/devices.
-/proc/bus/usb/drivers just lists the registered drivers,
-one per line. Not very interesting or pretty.
+/proc/bus/usb/drivers lists the registered drivers,
+one per line, with each driver's USB minor dev node
+number range if applicable.
In /proc/bus/usb/devices, each device's output has multiple
-lines (except for a root hub) of ASCII output.
+lines of ASCII output.
I made it ASCII instead of binary on purpose, so that someone
can obtain some useful data from it without the use of an
auxiliary program. However, with an auxiliary program, the numbers
Each line is tagged with a one-character ID for that line:
T = Topology (etc.)
-B = Bandwidth
+B = Bandwidth (applies only to USB host controllers, which are
+ virtualized as root hubs)
D = Device descriptor info.
P = Product ID info. (from Device descriptor, but they won't fit
together on one line)
-S = String info
+S = String descriptors.
C = Configuration descriptor info. (* = active configuration)
I = Interface descriptor info.
E = Endpoint descriptor info.
|__String info tag
S: Product=ssss
-| |__Product description of this device as read from the device.
+| |__Product description of this device as read from the device,
+| except that it is a generated string for USB host controllers
+| (virtual root hubs), in the form "USB *HCI Root Hub".
+|__String info tag
+
+S: SerialNumber=ssss
+| |__Serial Number of this device as read from the device,
+| except that it is a generated string for USB host controllers
+| (virtual root hubs), and represent's the host controller's
+| unique identification in the system (currently I/O or
+| memory-mapped base address).
|__String info tag
script, it can display any selected lines (for example, only T, D,
and P lines) and change their output format. (The "procusb"
Perl script is the beginning of this idea. It will list only
-selected lines [selected from TDPCIE] or "All" lines from
+selected lines [selected from TBDPSCIE] or "All" lines from
/proc/bus/usb/devices.)
The Topology lines can be used to generate a graphic/pictorial
T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2
B: Alloc= 28/900 us ( 3%), #Int= 2, #Iso= 0
+D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
+P: Vendor=0000 ProdID=0000 Rev= 0.00
+S: Product=USB UHCI Root Hub
+S: SerialNumber=dce0
+C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA
+I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub
+E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms
T: Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 4
D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1
P: Vendor=0451 ProdID=1446 Rev= 1.00
Copyright (C) 1999, 2000 David E. Nelson
-Jan. 22, 2000
+Mar. 23, 2000
CHANGES
- Amended for Linux-2.3.40
+- Updated for multiple scanner support
INTRODUCTION
version SANE-1.0.1. For instructions on building and installing SANE,
refer to the various README files within the SANE distribution.
-The latest SANE HP backend available from http://www.kirchgessner.net.
+The latest SANE HP backend is available from http://www.kirchgessner.net.
At the time of this writing, version 0.83 was available.
OK, I'VE INSTALLED SANE. SO WHAT DO I DO NOW?
-NOTE: $INSTALL_DIR is the location where SANE was installed. It may
+NOTE: $INSTALL_DIR is the location where SANE is installed. It may
be /usr/local, /usr, /opt or somewhere else. If you don't know, ask
your system administrator.
/dev/usbscanner
option connect-device
+NOTE: If you are using multiple scanners, make sure to have the correct
+devince, ie /dev/usbscanner0. See scanner.txt for more info.
+
3) You should now be able to use SANE (xscanimage or scanimage).
Don't forget to read any relevant man pages regarding the usage of
Copyright (C) 1999, 2000 David E. Nelson
-Jan. 22, 2000
+Mar. 23, 2000
CHANGES
- Amended for linux-2.3.40
- Appended hp_scan.c to end of this README
- Removed most references to HP
+- Updated uhci/ohci host controller info
+- Updated support for multiple scanner support
+- Updated supported scanners list
OVERVIEW
REQUIREMENTS
A host with a USB port. Ideally, either a UHCI (Intel) or OHCI
-(Compaq and others) hardware port should work. However, I've only
-been able to really use an OHCI controller. At the time of this
-writing, both uhci and ohci work with scanner.c *except* for the HP
-4100C which only works with ohci. This problem is being investigated.
+(Compaq and others) hardware port should work. At the time of this
+writing, there are two UHCI drivers and one OHCI.
A Linux development kernel (2.3.x) with USB support enabled or a
backported version to linux-2.2.x. See http://www.linux-usb.org for
(you may need to execute `depmod -a` to update the module
dependencies). Testing was performed only as modules, YMMV.
-Add a device for the USB scanner:
- `mknod /dev/usbscanner c 180 48`
+Beginning with version 0.4 of the driver, up to 16 scanners can be
+connected/used simultaneously. If you intend to use more than
+one scanner at a time:
-Set appropriate permissions for /dev/usbscanner (don't forget about
-group and world permissions). Both read and write permissions are
-required for proper operation.
+ Add a device for the USB scanner:
+ `mknod /dev/usbscanner0 c 180 48`
+ `mknod /dev/usbscanner1 c 180 49`
+ .
+ .
+ `mknod /dev/usb/scanner15 180 63`
+
+
+If you forsee using only one scanner:
+ `mknod /dev/usbscanner0 c 180 48`
+ `ln -s /dev/usbscanner0 /dev/usbscanner`
+
+
+Set appropriate permissions for /dev/usbscanner[0-15] (don't forget
+about group and world permissions). Both read and write permissions
+are required for proper operation.
Load the appropriate modules (if compiled as modules):
At the time of this writing, the following scanners were supported by
scanner.c:
+ Acer
+
+ Prisa AcerScan 620U
+
+ Agfa
+
+ SnapScan 1212U, SnapScan Touch
+
+ Genius
+
+ ColorPage Vivid Pro
+
Hewlett Packard
3300, 4100, 4200, 5200, 6200, 6300, PhotoSmart S20
- AGFA
+ Microtek
- SnapScan 1212U
+ ScanMaker X6-X6U, Phantom 336CX - C3, Phantom C6, ScanMaker V6USL,
+ ScanMaker V6UL - SpicyU
- Umax
+ Mustek
+
+ 1200 CU
- Astra 2000U
+ Primax/Colorado
+
+ G2-300, G2-600, G2E-300, G2E-600, ReadyScan 636i, Colorado USB
+ 19200, Colorado 600u, Colorado 1200u
Seiko/Epson
- Perfection 636, Perfection 1200U
+ Perfection Perfection 610, Perfection 636U/636Photo, Perfection
+ 1200U/1200Photo
- Mustek
+ Umax
+
+ Astra 1220U, 1236U, 2000U
+
+ Visioneer
+
+ OneTouch 5300, OneTouch 7600, 6100,
- 1200 CU
User Specified. See MODULE PARAMETERS for details.
product=0x****' to the conf.modules/modules.conf file replacing the
#'s and the *'s with the correct ID's. The ID's can be retrieved from
the messages file or using `cat /proc/bus/usb/devices` if USB /proc
-support was selected during kernel configuration. In later kernels
-(2.3.38+), a new filesystem was introduced, usbdevfs. To mount the
-filesystem, issue the command `mount -t usbdevfs /proc/bus/usb
-/proc/bus/usb`. You can then issue ` cat /proc/bus/usb/devices` to
-extract USB device information.
+support was selected during kernel configuration. **NOTE**:In later
+kernels (2.3.38+), a new filesystem was introduced, usbdevfs. To
+mount the filesystem, issue the command `mount -t usbdevfs
+/proc/bus/usb /proc/bus/usb`. You can then issue ` cat
+/proc/bus/usb/devices` to extract USB device information.
BUGS
--- /dev/null
+usb-help.txt
+2000-March-24
+
+For USB help other than the readme files that are located in
+linux/Documentation/usb/*, see the following:
+
+Linux-USB project: http://www.linux-usb.org
+ mirrors at http://www.suse.cz/development/linux-usb/
+ and http://usb.in.tum.de/linux-usb/
+Linux USB Guide: http://linuxusbguide.sourceforge.net
+Linux-USB device overview (working devices and drivers):
+ http://www.qbik.ch/usb/devices/
+
+The Linux-USB mailing list is linux-usb@suse.com .
+
+###
--- /dev/null
+This is a driver for the CPiA PPC2 driven parallel connected
+Camera. For example the Creative WebcamII is CPiA driven.
+
+ ) [1]Peter Pregler, Linz 2000, published under the [2]GNU GPL
+
+---------------------------------------------------------------------------
+
+USAGE:
+
+General:
+========
+
+1) Make sure you have created the video devices (/dev/video*):
+
+- if you have a recent MAKEDEV do a 'cd /dev;./MAKEDEV video'
+- otherwise do a:
+
+cd /dev
+mknod video0 c 81 0
+ln -s video0 video
+
+2) Compile the kernel (see below for the list of options to use),
+ configure your parport and reboot.
+
+3) If all worked well you should get messages similar
+ to the following (your versions may be different) on the console:
+
+V4L-Driver for Vision CPiA based cameras v0.7.4
+parport0: read2 timeout.
+parport0: Multimedia device, VLSI Vision Ltd PPC2
+Parallel port driver for Vision CPiA based camera
+ CPIA Version: 1.20 (2.0)
+ CPIA PnP-ID: 0553:0002:0100
+ VP-Version: 1.0 0100
+ 1 camera(s) found
+
+
+As modules:
+===========
+
+Make sure you have selected the following kernel options (you can
+select all stuff as modules):
+
+The cpia-stuff is in the section 'Character devices -> Video For Linux'.
+
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_PARPORT_PC_FIFO=y
+CONFIG_PARPORT_1284=y
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_CPIA=m
+CONFIG_VIDEO_CPIA_PP=m
+
+For autoloading of all those modules you need to tell kerneld some
+stuff. Add the following line to your kerneld config-file
+(e.g. /etc/modules.conf or wherever your distribution does store that
+stuff):
+
+options parport_pc dma=3 irq=7
+alias char-major-81 cpia_pp
+
+The first line tells the dma/irq channels to use. Those _must_ match
+the settings of your BIOS. Do NOT simply use the values above. See
+Documentation/parport.txt for more information about this. The second
+line associates the video-device file with the driver. Of cause you
+can also load the modules once upon boot (usually done in /etc/modules).
+
+Linked into the kernel:
+=======================
+
+Make sure you have selected the following kernel options. Note that
+you cannot compile the parport-stuff as modules and the cpia-driver
+statically (the other way round is okay though).
+
+The cpia-stuff is in the section 'Character devices -> Video For Linux'.
+
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+CONFIG_PARPORT_PC_FIFO=y
+CONFIG_PARPORT_1284=y
+CONFIG_VIDEO_DEV=y
+CONFIG_VIDEO_CPIA=y
+CONFIG_VIDEO_CPIA_PP=y
+
+To use DMA/irq you will need to tell the kernel upon boot time the
+hardware configuration of the parport. You can give the boot-parameter
+at the LILO-prompt or specify it in lilo.conf. I use the following
+append-line in lilo.conf:
+
+ append="parport=0x378,7,3"
+
+See Documentation/parport.txt for more information about the
+configuration of the parport and the values given above. Do not simply
+use the values given above.
+
+---------------------------------------------------------------------------
+FEATURES:
+
+- mmap/read v4l-interface (but no overlay)
+- image formats: CIF/QCIF, SIF/QSIF, various others used by isabel;
+ note: all sizes except CIF/QCIF are implemented by clipping, i.e.
+ pixels are not uploaded from the camera
+- palettes: VIDEO_PALETTE_GRAY, VIDEO_PALETTE_RGB565, VIDEO_PALETTE_RGB555,
+ VIDEO_PALETTE_RGB24, VIDEO_PALETTE_RGB32, VIDEO_PALETTE_YUYV,
+ VIDEO_PALETTE_UYVY, VIDEO_PALETTE_YUV422
+- state information (color balance, exposure, ...) is preserved between
+ device opens
+- complete control over camera via proc-interface (_all_ camera settings are
+ supported), there is also a python-gtk application available for this [3]
+- works under SMP (but the driver is completly serialized and synchronous)
+ so you get no benefit from SMP, but at least it does not crash your box
+- might work for non-Intel architecture, let us know about this
+
+---------------------------------------------------------------------------
+TESTED APPLICATIONS:
+
+- a simple test application based on Xt is available at [3]
+- another test-application based on gqcam-0.4 (uses GTK)
+- gqcam-0.6 should work
+- xawtv-3.x (also the webcam software)
+- xawtv-2.46
+- w3cam (cgi-interface and vidcat, e.g. you may try out 'vidcat |xv
+ -maxpect -root -quit +noresetroot -rmode 5 -')
+- vic, the MBONE video conferencing tool (version 2.8ucl4-1)
+- isabel 3R4beta (barely working, but AFAICT all the problems are on
+ their side)
+- camserv-0.40
+
+See [3] for pointers to v4l-applications.
+
+---------------------------------------------------------------------------
+KNOWN PROBLEMS:
+
+- some applications do not handle the image format correctly, you will
+ see strange horizontal stripes instead of a nice picture -> make sure
+ your application does use a supported image size or queries the driver
+ for the actually used size (reason behind this: the camera cannot
+ provide any image format, so if size NxM is requested the driver will
+ use a format to the closest fitting N1xM1, the application should now
+ query for this granted size, most applications do not).
+- all the todo ;)
+- if there is not enough light and the picture is too dark try to
+ adjust the SetSensorFPS setting, automatic frame rate adjustment
+ has its price
+- do not try out isabel 3R4beta (built 135), you will be disappointed
+
+---------------------------------------------------------------------------
+TODO:
+
+- multiple camera support (struct camera or something) - This should work,
+ but hasn't been tested yet.
+- architecture independence?
+- SMP-safe asynchronous mmap interface
+- nibble mode for old parport interfaces
+- streaming capture, this should give a performance gain
+
+---------------------------------------------------------------------------
+IMPLEMENTATION NOTES:
+
+The camera can act in two modes, streaming or grabbing. Right now a
+polling grab-scheme is used. Maybe interrupt driven streaming will be
+used for a ansychronous mmap interface in the next major release of the
+driver. This might give a better frame rate.
+
+---------------------------------------------------------------------------
+THANKS (in no particular order):
+
+- Scott J. Bertin <sbertin@mindspring.com> for cleanups, the proc-filesystem
+ and much more
+- Henry Bruce <whb@vvl.co.uk> for providing developers information about
+ the CPiA chip, I wish all companies would treat Linux as seriously
+- Karoly Erdei <Karoly.Erdei@risc.uni-linz.ac.at> and RISC-Linz for being
+ my boss ;) resp. my employer and for providing me the hardware and
+ allow me to devote some working time to this project
+- Manuel J. Petit de Gabriel <mpetit@dit.upm.es> for providing help
+ with Isabel (http://isabel.dit.upm.es/)
+- Bas Huisman <bhuism@cs.utwente.nl> for writing the initial parport code
+- Jarl Totland <Jarl.Totland@bdc.no> for setting up the mailing list
+ and maintaining the web-server[3]
+- Chris Whiteford <Chris@informinteractive.com> for fixes related to the
+ 1.02 firmware
+- special kudos to all the tester whose machines crashed and/or
+ will crash. :)
+
+---------------------------------------------------------------------------
+REFERENCES
+
+ 1. http://www.risc.uni-linz.ac.at/people/ppregler
+ mailto:Peter_Pregler@email.com
+ 2. see the file COPYING in the top directory of the kernel tree
+ 3. http://webcam.sourceforge.net/
VERSION = 2
PATCHLEVEL = 3
SUBLEVEL = 99
-EXTRAVERSION = -pre3
+EXTRAVERSION = -pre4
KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
endif
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
-AFLAGS := $(CPPFLAGS)
+AFLAGS := $(CPPFLAGS) -D__ASSEMBLY__ -traditional
# use '-fno-strict-aliasing', but only if the compiler can take it
CFLAGS += $(shell if $(CC) -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-fno-strict-aliasing"; fi)
export NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS
.S.s:
- $(CPP) -D__ASSEMBLY__ $(AFLAGS) -traditional -o $*.s $<
+ $(CPP) $(AFLAGS) -o $*.s $<
.S.o:
- $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c -o $*.o $<
+ $(CC) $(AFLAGS) -c -o $*.o $<
Version: dummy
@rm -f include/linux/compile.h
rm -f net/khttpd/times.h
rm -f submenu*
rm -rf modules
+ $(MAKE) -C Documentation/DocBook clean
mrproper: clean archmrproper
rm -f include/linux/autoconf.h include/linux/version.h
rm -f .hdepend scripts/mkdep scripts/split-include scripts/docproc
rm -f $(TOPDIR)/include/linux/modversions.h
rm -rf $(TOPDIR)/include/linux/modules
- make clean TOPDIR=$(TOPDIR) -C Documentation/DocBook
-
+ $(MAKE) -C Documentation/DocBook mrproper
distclean: mrproper
rm -f core `find . \( -name '*.orig' -o -name '*.rej' -o -name '*~' \
-o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
sgmldocs:
$(MAKE) -C $(TOPDIR)/Documentation/DocBook books
+psdocs: sgmldocs
+ $(MAKE) -C scripts docproc
+ $(MAKE) -C Documentation/DocBook ps
+
+pdfdocs: sgmldocs
+ $(MAKE) -C scripts docproc
+ $(MAKE) -C Documentation/DocBook pdf
+
sums:
find . -type f -print | sort | xargs sum > .SUMS
# Most of these machines have ISA slots; not exactly sure which don't,
# and this doesn't activate hordes of code, so do it always.
define_bool CONFIG_ISA y
+define_bool CONFIG_SBUS n
if [ "$CONFIG_ALPHA_JENSEN" = "y" ]
then
if [ "$CONFIG_HOTPLUG" = "y" ] ; then
source drivers/pcmcia/Config.in
+else
+ define_bool CONFIG_PCMCIA n
fi
bool 'Networking support' CONFIG_NET
source drivers/char/Config.in
-source drivers/usb/Config.in
#source drivers/misc/Config.in
fi
endmenu
+source drivers/usb/Config.in
+
mainmenu_option next_comment
comment 'Kernel hacking'
# CONFIG_ALPHA_SABLE is not set
# CONFIG_ALPHA_TAKARA is not set
CONFIG_ISA=y
+# CONFIG_SBUS is not set
CONFIG_PCI=y
CONFIG_ALPHA_BROKEN_IRQ_MASK=y
# CONFIG_SMP is not set
CONFIG_PCI_NAMES=y
# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
CONFIG_NET=y
CONFIG_SYSVIPC=y
# CONFIG_BSD_PROCESS_ACCT is not set
*/
int j = *(vuip) IACK_SC;
j &= 0xff;
- if (j == 7) {
- if (!(inb(0x20) & 0x80)) {
- /* It's only a passive release... */
- return;
- }
- }
handle_irq(j, regs);
}
#endif
LIBGCC := $(shell $(CC) $(CFLAGS) --print-libgcc-file-name)
-export LIBGCC
+export LIBGCC MACHINE PROCESSOR TEXTADDR
ifeq ($(CONFIG_ARCH_A5K),y)
MACHINE = a5k
@$(MAKEBOOT) clean
$(RM) arch/arm/lib/constants.h arch/arm/vmlinux.lds
-archdep:
+archdep: symlinks
@$(MAKEBOOT) dep
# My testing targets (that short circuit a few dependencies)
empeg_config:
$(RM) arch/arm/defconfig
cp arch/arm/def-configs/empeg arch/arm/defconfig
+
+thinclient_config:
+ $(RM) arch/arm/defconfig
+ cp arch/arm/def-configs/thinclient arch/arm/defconfig
+
#error What am I doing here...
#endif
-#ifdef CONFIG_SA1100_BRUTUS
-@ need to enter SVC mode
+#if defined( CONFIG_SA1100_BRUTUS ) || \
+ defined( CONFIG_SA1100_THINCLIENT )
+@ Booting from Angel -- need to enter SVC mode
#define angel_SWIreason_EnterSVC 0x17 /* from arm.h, in angel source */
#define angel_SWI_ARM (0xEF123456 & 0xffffff)
mov r0, #angel_SWIreason_EnterSVC
Itsy CONFIG_SA1100_ITSY \
LART CONFIG_SA1100_LART \
PLEB CONFIG_SA1100_PLEB \
+ ThinClient CONFIG_SA1100_THINCLIENT \
Victor CONFIG_SA1100_VICTOR \
Tifon CONFIG_SA1100_TIFON" Brutus
fi
define_bool CONFIG_ISA_DMA n
fi
+define_bool CONFIG_SBUS n
+define_bool CONFIG_PCMCIA n
+
if [ "$CONFIG_CPU_32" = "y" -a "$CONFIG_ARCH_EBSA110" != "y" ]; then
bool 'Kernel-mode alignment trap handler' CONFIG_ALIGNMENT_TRAP
fi
fi
fi
-source drivers/usb/Config.in
#source drivers/misc/Config.in
source fs/Config.in
+source drivers/usb/Config.in
+
mainmenu_option next_comment
comment 'Kernel hacking'
# Automatically generated make config: don't edit
#
CONFIG_ARM=y
+CONFIG_UID16=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
#
# System and processor type
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_FOOTBRIDGE is not set
CONFIG_ARCH_SA1100=y
-CONFIG_CPU_SA1100=y
CONFIG_SA1100_BRUTUS=y
# CONFIG_SA1100_EMPEG is not set
# CONFIG_SA1100_ITSY is not set
# CONFIG_SA1100_LART is not set
# CONFIG_SA1100_PLEB is not set
+# CONFIG_SA1100_THINCLIENT is not set
# CONFIG_SA1100_VICTOR is not set
# CONFIG_SA1100_TIFON is not set
+CONFIG_DISCONTIGMEM=y
# CONFIG_ARCH_ACORN is not set
CONFIG_CPU_32=y
# CONFIG_CPU_26 is not set
CONFIG_CPU_32v4=y
-CONFIG_CPU_SA110=y
+CONFIG_CPU_SA1100=y
# CONFIG_ISA_DMA is not set
-
-#
-# Code maturity level options
-#
-CONFIG_EXPERIMENTAL=y
# CONFIG_ALIGNMENT_TRAP is not set
#
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_SYSCTL is not set
CONFIG_NWFPE=y
+CONFIG_KCORE_ELF=y
+# CONFIG_KCORE_AOUT is not set
CONFIG_BINFMT_AOUT=y
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_MISC is not set
# CONFIG_ARTHUR is not set
+
+#
+# Parallel port support
+#
# CONFIG_PARPORT is not set
CONFIG_CMDLINE=""
# CONFIG_I2O is not set
# CONFIG_I2O_PCI is not set
# CONFIG_I2O_BLOCK is not set
-# CONFIG_I2O_LAN is not set
# CONFIG_I2O_SCSI is not set
# CONFIG_I2O_PROC is not set
# Please see Documentation/ide.txt for help/info on IDE drives
#
# CONFIG_BLK_DEV_HD_ONLY is not set
-# CONFIG_BLK_CPQ_DA is not set
#
# Additional Block Devices
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_BLK_DEV_XD is not set
-CONFIG_PARIDE_PARPORT=y
# CONFIG_PARIDE is not set
# CONFIG_BLK_DEV_IDE_MODES is not set
# CONFIG_BLK_DEV_HD is not set
# Character devices
#
CONFIG_VT=y
-CONFIG_VT_CONSOLE=y
+# CONFIG_VT_CONSOLE is not set
CONFIG_SERIAL_SA1100=y
-# CONFIG_SERIAL_SA1100_CONSOLE is not set
+CONFIG_SERIAL_SA1100_CONSOLE=y
# CONFIG_SERIAL is not set
# CONFIG_SERIAL_EXTENDED is not set
# CONFIG_SERIAL_NONSTANDARD is not set
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=32
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
#
# Mice
#
# CONFIG_BUSMOUSE is not set
# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
# CONFIG_WATCHDOG is not set
# CONFIG_NVRAM is not set
# CONFIG_RTC is not set
# Video For Linux
#
# CONFIG_VIDEO_DEV is not set
-
-#
-# Joystick support
-#
-# CONFIG_JOYSTICK is not set
# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
# CONFIG_APPLICOM is not set
#
# CONFIG_FTAPE is not set
# CONFIG_DRM is not set
+# CONFIG_DRM_TDFX is not set
+# CONFIG_AGP is not set
#
-# USB drivers - not for the faint of heart
+# USB support
#
# CONFIG_USB is not set
#
# CONFIG_VGA_CONSOLE is not set
CONFIG_FB=y
+
+#
+# Frame-buffer support
+#
+CONFIG_FB=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_FB_SA1100=y
# CONFIG_FB_MATROX is not set
# CONFIG_FB_ATY is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_3DFX is not set
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FBCON_ADVANCED is not set
CONFIG_FBCON_CFB2=y
CONFIG_FBCON_CFB4=y
CONFIG_FBCON_CFB8=y
-CONFIG_FBCON_CFB16=y
CONFIG_FBCON_FONTWIDTH8_ONLY=y
CONFIG_FBCON_FONTS=y
CONFIG_FONT_8x8=y
# CONFIG_SCSI is not set
#
-# Filesystems
+# File systems
#
# CONFIG_QUOTA is not set
# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
# CONFIG_FAT_FS is not set
# CONFIG_MSDOS_FS is not set
# CONFIG_UMSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
# CONFIG_ISO9660_FS is not set
# CONFIG_JOLIET is not set
-# CONFIG_UDF_FS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_NTFS_FS is not set
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
CONFIG_DEVPTS_FS=y
# CONFIG_QNX4FS_FS is not set
# CONFIG_ROMFS_FS is not set
CONFIG_EXT2_FS=y
# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
# CONFIG_UFS_FS is not set
#
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
-# CONFIG_SGI_PARTITION is not set
-# CONFIG_SUN_PARTITION is not set
# CONFIG_NLS is not set
#
# Automatically generated make config: don't edit
#
CONFIG_ARM=y
+CONFIG_UID16=y
#
# Code maturity level options
CONFIG_HOST_FOOTBRIDGE=y
# CONFIG_ADDIN_FOOTBRIDGE is not set
CONFIG_ARCH_EBSA285=y
-# CONFIG_CATS is not set
+# CONFIG_ARCH_CATS is not set
CONFIG_ARCH_NETWINDER=y
+# CONFIG_ARCH_PERSONAL_SERVER is not set
# CONFIG_ARCH_ACORN is not set
CONFIG_CPU_32=y
# CONFIG_CPU_26 is not set
CONFIG_CPU_32v4=y
CONFIG_CPU_SA110=y
CONFIG_PCI=y
+CONFIG_PCI_NAMES=y
CONFIG_ISA_DMA=y
-# CONFIG_ALIGNMENT_TRAP is not set
+CONFIG_ALIGNMENT_TRAP=y
#
# Loadable module support
#
CONFIG_NET=y
CONFIG_SYSVIPC=y
-# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_BSD_PROCESS_ACCT=y
CONFIG_SYSCTL=y
CONFIG_NWFPE=y
CONFIG_KCORE_ELF=y
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_MISC is not set
# CONFIG_ARTHUR is not set
+
+#
+# Parallel port support
+#
CONFIG_PARPORT=y
CONFIG_PARPORT_PC=y
CONFIG_PARPORT_PC_FIFO=y
-# CONFIG_PARPORT_PC_PCMCIA is not set
+# CONFIG_PARPORT_PC_SUPERIO is not set
# CONFIG_PARPORT_ARC is not set
# CONFIG_PARPORT_AMIGA is not set
# CONFIG_PARPORT_MFC3 is not set
CONFIG_LEDS_TIMER=y
# CONFIG_LEDS_CPU is not set
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
#
# I2O device support
#
# Block devices
#
# CONFIG_BLK_DEV_FD is not set
-CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_IDEDISK_MULTI_MODE=y
-# CONFIG_BLK_DEV_IDECD is not set
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-
-#
-# IDE chipset support/bugfixes
-#
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_IDEDMA_PCI=y
-CONFIG_IDEDMA_PCI_AUTO=y
-CONFIG_IDEDMA_NEW_DRIVE_LISTINGS=y
-CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
-CONFIG_BLK_DEV_OFFBOARD=y
-# CONFIG_BLK_DEV_AEC6210 is not set
-# CONFIG_BLK_DEV_ALI15X3 is not set
-# CONFIG_BLK_DEV_CMD646 is not set
-CONFIG_BLK_DEV_CY82C693=y
-# CONFIG_BLK_DEV_HPT34X is not set
-# CONFIG_BLK_DEV_HPT366 is not set
-# CONFIG_BLK_DEV_NS87415 is not set
-# CONFIG_BLK_DEV_OPTI621 is not set
-CONFIG_BLK_DEV_PDC202XX=y
-# CONFIG_PDC202XX_FORCE_BURST_BIT is not set
-# CONFIG_PDC202XX_FORCE_MASTER_MODE is not set
-# CONFIG_BLK_DEV_TRM290 is not set
-CONFIG_BLK_DEV_SL82C105=y
-CONFIG_BLK_DEV_IDEDMA=y
-CONFIG_IDEDMA_AUTO=y
-# CONFIG_IDE_CHIPSETS is not set
-# CONFIG_BLK_CPQ_DA is not set
-
-#
-# Additional Block Devices
-#
-CONFIG_BLK_DEV_LOOP=m
-CONFIG_BLK_DEV_NBD=m
-CONFIG_BLK_DEV_MD=y
-CONFIG_MD_LINEAR=m
-CONFIG_MD_STRIPED=m
-CONFIG_MD_MIRRORING=m
-CONFIG_MD_RAID5=m
-CONFIG_BLK_DEV_RAM=y
-# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_BLK_DEV_XD is not set
-# CONFIG_BLK_DEV_DAC960 is not set
-CONFIG_PARIDE_PARPORT=y
CONFIG_PARIDE=m
+CONFIG_PARIDE_PARPORT=y
#
# Parallel IDE high-level drivers
CONFIG_PARIDE_KTTI=m
CONFIG_PARIDE_ON20=m
CONFIG_PARIDE_ON26=m
-CONFIG_BLK_DEV_IDE_MODES=y
-# CONFIG_BLK_DEV_HD is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+
+#
+# Additional Block Devices
+#
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+# CONFIG_BLK_DEV_INITRD is not set
#
# Character devices
CONFIG_SERIAL=y
CONFIG_SERIAL_CONSOLE=y
# CONFIG_SERIAL_EXTENDED is not set
-# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_COMPUTONE is not set
+# CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
+CONFIG_SERIAL_21285=y
+CONFIG_SERIAL_21285_CONSOLE=y
+# CONFIG_DIGIEPCA is not set
+# CONFIG_DIGI is not set
+# CONFIG_ESPSERIAL is not set
+# CONFIG_MOXA_INTELLIO is not set
+# CONFIG_MOXA_SMARTIO is not set
+# CONFIG_ISI is not set
+# CONFIG_SYNCLINK is not set
+# CONFIG_N_HDLC is not set
+# CONFIG_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_STALDRV is not set
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=256
CONFIG_PRINTER=m
# CONFIG_LP_CONSOLE is not set
# CONFIG_PPDEV is not set
+#
+# I2C support
+#
+CONFIG_I2C=m
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_CHARDEV is not set
+
#
# Mice
#
CONFIG_SOFT_WATCHDOG=y
# CONFIG_PCWATCHDOG is not set
# CONFIG_ACQUIRE_WDT is not set
-# CONFIG_21285_WATCHDOG is not set
+# CONFIG_MIXCOMWD is not set
+CONFIG_21285_WATCHDOG=m
CONFIG_977_WATCHDOG=m
CONFIG_DS1620=y
CONFIG_NWBUTTON=y
CONFIG_NWBUTTON_REBOOT=y
CONFIG_NWFLASH=m
-# CONFIG_NVRAM is not set
+CONFIG_NVRAM=m
CONFIG_RTC=y
#
# CONFIG_I2C_PARPORT is not set
#
-# Radio/Video Adapters
+# Radio Adapters
#
# CONFIG_RADIO_CADET is not set
# CONFIG_RADIO_RTRACK is not set
# CONFIG_RADIO_RTRACK2 is not set
# CONFIG_RADIO_AZTECH is not set
-# CONFIG_VIDEO_BT848 is not set
# CONFIG_RADIO_GEMTEK is not set
-# CONFIG_VIDEO_PMS is not set
# CONFIG_RADIO_MIROPCM20 is not set
-# CONFIG_VIDEO_BWQCAM is not set
-# CONFIG_VIDEO_CQCAM is not set
-# CONFIG_VIDEO_SAA5249 is not set
# CONFIG_RADIO_SF16FMI is not set
-# CONFIG_VIDEO_STRADIS is not set
# CONFIG_RADIO_TERRATEC is not set
# CONFIG_RADIO_TRUST is not set
# CONFIG_RADIO_TYPHOON is not set
# CONFIG_RADIO_ZOLTRIX is not set
+
+#
+# Video Adapters
+#
+# CONFIG_VIDEO_PMS is not set
+# CONFIG_VIDEO_BWQCAM is not set
+# CONFIG_VIDEO_CQCAM is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+# CONFIG_VIDEO_STRADIS is not set
# CONFIG_VIDEO_ZORAN is not set
# CONFIG_VIDEO_BUZ is not set
# CONFIG_VIDEO_ZR36120 is not set
# CONFIG_FTAPE is not set
# CONFIG_DRM is not set
# CONFIG_DRM_TDFX is not set
-
-#
-# PCMCIA character device support
-#
-# CONFIG_PCMCIA_SERIAL_CS is not set
# CONFIG_AGP is not set
#
-# Support for USB
+# USB support
#
CONFIG_USB=m
# USB Controllers
#
# CONFIG_USB_UHCI is not set
+# CONFIG_USB_UHCI_ALT is not set
CONFIG_USB_OHCI=m
-CONFIG_USB_OHCI_DEBUG=y
-CONFIG_USB_OHCI_HCD=m
-CONFIG_USB_OHCI_VROOTHUB=y
#
# Miscellaneous USB options
#
-# CONFIG_USB_DEBUG_ISOC is not set
-CONFIG_USB_PROC=y
-# CONFIG_USB_EZUSB is not set
+# CONFIG_USB_DEVICEFS is not set
#
# USB Devices
#
-CONFIG_USB_MOUSE=m
-# CONFIG_USB_HP_SCANNER is not set
-CONFIG_USB_KBD=m
-CONFIG_USB_AUDIO=m
-CONFIG_USB_ACM=m
CONFIG_USB_PRINTER=m
+CONFIG_USB_SCANNER=m
+CONFIG_USB_AUDIO=m
+# CONFIG_USB_ACM is not set
# CONFIG_USB_SERIAL is not set
# CONFIG_USB_CPIA is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_OV511 is not set
# CONFIG_USB_DC2XX is not set
-# CONFIG_USB_SCSI is not set
+# CONFIG_USB_STORAGE is not set
# CONFIG_USB_USS720 is not set
+# CONFIG_USB_DABUSB is not set
+# CONFIG_USB_PLUSB is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_DSBR is not set
+
+#
+# USB HID
+#
+# CONFIG_USB_HID is not set
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+# CONFIG_USB_WACOM is not set
+# CONFIG_USB_WMFORCE is not set
+# CONFIG_INPUT_KEYBDEV is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
#
# Console drivers
# CONFIG_FBCON_MAC is not set
# CONFIG_FBCON_VGA_PLANES is not set
CONFIG_FBCON_VGA=y
+# CONFIG_FBCON_HGA is not set
# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
CONFIG_FBCON_FONTS=y
-CONFIG_FONT_8x8=y
-CONFIG_FONT_8x16=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
# CONFIG_FONT_SUN8x16 is not set
# CONFIG_FONT_SUN12x22 is not set
# CONFIG_FONT_6x11 is not set
# Networking options
#
CONFIG_PACKET=y
-# CONFIG_PACKET_MMAP is not set
+CONFIG_PACKET_MMAP=y
# CONFIG_NETLINK is not set
# CONFIG_NETFILTER is not set
# CONFIG_FILTER is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
CONFIG_IP_ALIAS=y
-# CONFIG_SYN_COOKIES is not set
+CONFIG_SYN_COOKIES=y
#
# (it is safe to leave these untouched)
#
-CONFIG_SKB_LARGE=y
+# CONFIG_SKB_LARGE is not set
# CONFIG_IPV6 is not set
# CONFIG_KHTTPD is not set
# CONFIG_ATM is not set
#
# IrDA (infrared) support
#
-# CONFIG_IRDA is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRCOMM=m
+CONFIG_IRDA_ULTRA=y
+CONFIG_IRDA_OPTIONS=y
+
+#
+# IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+# CONFIG_IRDA_DEBUG is not set
+# CONFIG_IRDA_COMPRESSION is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+# CONFIG_IRPORT_SIR is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_NSC_FIR is not set
+CONFIG_WINBOND_FIR=m
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
#
# Network device support
#
# CONFIG_ARCNET is not set
# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_NET_SB1000 is not set
# CONFIG_LANCE is not set
# CONFIG_NET_VENDOR_SMC is not set
# CONFIG_NET_VENDOR_RACAL is not set
-# CONFIG_RTL8139 is not set
-# CONFIG_DM9102 is not set
# CONFIG_AT1700 is not set
# CONFIG_DEPCA is not set
# CONFIG_NET_ISA is not set
-CONFIG_NET_EISA=y
+CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
# CONFIG_ADAPTEC_STARFIRE is not set
# CONFIG_AC3200 is not set
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
# CONFIG_DE4X5 is not set
-CONFIG_DEC_ELCP=m
+CONFIG_TULIP=m
# CONFIG_DGRS is not set
-# CONFIG_EEXPRESS_PRO100 is not set
+# CONFIG_DM9102 is not set
+# CONFIG_EEPRO100 is not set
# CONFIG_LNE390 is not set
# CONFIG_NE3210 is not set
CONFIG_NE2K_PCI=y
+# CONFIG_RTL8129 is not set
+# CONFIG_8139TOO is not set
# CONFIG_SIS900 is not set
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set
# CONFIG_ES3210 is not set
# CONFIG_EPIC100 is not set
-# CONFIG_ZNET is not set
# CONFIG_NET_POCKET is not set
#
# CONFIG_NET_RADIO is not set
#
-# Token Ring driver support
+# Token Ring devices
#
# CONFIG_TR is not set
# CONFIG_NET_FC is not set
# CONFIG_WAN is not set
#
-# PCMCIA network device support
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+# CONFIG_BLK_DEV_IDECS is not set
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+
+#
+# IDE chipset support/bugfixes
#
-# CONFIG_NET_PCMCIA is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+CONFIG_BLK_DEV_OFFBOARD=y
+CONFIG_IDEDMA_PCI_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDEDMA_PCI_EXPERIMENTAL=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+# CONFIG_BLK_DEV_AEC6210 is not set
+# CONFIG_AEC6210_TUNING is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD7409 is not set
+# CONFIG_AMD7409_OVERRIDE is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_CMD64X_RAID is not set
+CONFIG_BLK_DEV_CY82C693=y
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_HPT366_FIP is not set
+# CONFIG_HPT366_MODE3 is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_PDC202XX=y
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_MASTER is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+CONFIG_BLK_DEV_SL82C105=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
+CONFIG_BLK_DEV_IDE_MODES=y
#
# SCSI support
# CONFIG_SOUND_MSNDCLAS is not set
# CONFIG_SOUND_MSNDPIN is not set
CONFIG_SOUND_OSS=m
+# CONFIG_SOUND_TRACEINIT is not set
+# CONFIG_SOUND_DMAP is not set
# CONFIG_SOUND_AD1816 is not set
# CONFIG_SOUND_SGALAXY is not set
+CONFIG_SOUND_ADLIB=m
+# CONFIG_SOUND_ACI_MIXER is not set
# CONFIG_SOUND_CS4232 is not set
# CONFIG_SOUND_SSCAPE is not set
# CONFIG_SOUND_GUS is not set
# CONFIG_SOUND_NM256 is not set
# CONFIG_SOUND_MAD16 is not set
# CONFIG_SOUND_PAS is not set
+# CONFIG_PAS_JOYSTICK is not set
# CONFIG_SOUND_PSS is not set
# CONFIG_SOUND_SOFTOSS is not set
CONFIG_SOUND_SB=m
+# CONFIG_SOUND_AWE32_SYNTH is not set
# CONFIG_SOUND_WAVEFRONT is not set
# CONFIG_SOUND_MAUI is not set
# CONFIG_SOUND_VIA82CXXX is not set
# CONFIG_SOUND_OPL3SA1 is not set
# CONFIG_SOUND_OPL3SA2 is not set
# CONFIG_SOUND_UART6850 is not set
+# CONFIG_SOUND_AEDSP16 is not set
# CONFIG_SOUND_VIDC is not set
CONFIG_SOUND_WAVEARTIST=m
-CONFIG_WAVEARTIST_BASE=250
-CONFIG_WAVEARTIST_IRQ=12
-CONFIG_WAVEARTIST_DMA=3
-CONFIG_WAVEARTIST_DMA2=7
#
-# Additional low level sound drivers
-#
-# CONFIG_LOWLEVEL_SOUND is not set
-
-#
-# Filesystems
+# File systems
#
# CONFIG_QUOTA is not set
CONFIG_AUTOFS_FS=y
-CONFIG_ADFS_FS=y
+# CONFIG_AUTOFS4_FS is not set
+CONFIG_ADFS_FS=m
+# CONFIG_ADFS_FS_RW is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_UMSDOS_FS is not set
CONFIG_VFAT_FS=m
# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
CONFIG_ISO9660_FS=m
CONFIG_JOLIET=y
# CONFIG_MINIX_FS is not set
# CONFIG_NTFS_FS is not set
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
CONFIG_DEVPTS_FS=y
# CONFIG_QNX4FS_FS is not set
# CONFIG_ROMFS_FS is not set
# Partition Types
#
CONFIG_PARTITION_ADVANCED=y
+CONFIG_ACORN_PARTITION=y
+# CONFIG_ACORN_PARTITION_ICS is not set
+CONFIG_ACORN_PARTITION_ADFS=y
+# CONFIG_ACORN_PARTITION_POWERTEC is not set
+# CONFIG_ACORN_PARTITION_RISCIX is not set
# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
# CONFIG_MAC_PARTITION is not set
CONFIG_MSDOS_PARTITION=y
# CONFIG_BSD_DISKLABEL is not set
# CONFIG_UNIXWARE_DISKLABEL is not set
# CONFIG_SGI_PARTITION is not set
# CONFIG_SUN_PARTITION is not set
-# CONFIG_AMIGA_PARTITION is not set
-# CONFIG_ATARI_PARTITION is not set
-CONFIG_ACORN_PARTITION=y
-CONFIG_ACORN_PARTITION_ADFS=y
-# CONFIG_ACORN_PARTITION_ICS is not set
-# CONFIG_ACORN_PARTITION_POWERTEC is not set
-# CONFIG_ACORN_PARTITION_RISCIX is not set
CONFIG_NLS=y
#
--- /dev/null
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_ARM=y
+CONFIG_UID16=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# System and processor type
+#
+# CONFIG_ARCH_ARC is not set
+# CONFIG_ARCH_A5K is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_FOOTBRIDGE is not set
+CONFIG_ARCH_SA1100=y
+# CONFIG_SA1100_BRUTUS is not set
+# CONFIG_SA1100_EMPEG is not set
+# CONFIG_SA1100_ITSY is not set
+# CONFIG_SA1100_LART is not set
+# CONFIG_SA1100_PLEB is not set
+CONFIG_SA1100_THINCLIENT=y
+# CONFIG_SA1100_VICTOR is not set
+# CONFIG_SA1100_TIFON is not set
+CONFIG_DISCONTIGMEM=y
+# CONFIG_ARCH_ACORN is not set
+CONFIG_CPU_32=y
+# CONFIG_CPU_26 is not set
+CONFIG_CPU_32v4=y
+CONFIG_CPU_SA1100=y
+# CONFIG_ISA_DMA is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_KMOD is not set
+
+#
+# General setup
+#
+CONFIG_NET=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+CONFIG_NWFPE=y
+CONFIG_KCORE_ELF=y
+# CONFIG_KCORE_AOUT is not set
+CONFIG_BINFMT_AOUT=y
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+# CONFIG_PARPORT is not set
+CONFIG_CMDLINE="root=nfs"
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+# CONFIG_I2O_PCI is not set
+# CONFIG_I2O_BLOCK is not set
+# CONFIG_I2O_LAN is not set
+# CONFIG_I2O_SCSI is not set
+# CONFIG_I2O_PROC is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_IDE is not set
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_ONLY is not set
+
+#
+# Additional Block Devices
+#
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+# CONFIG_VT_CONSOLE is not set
+CONFIG_SERIAL_SA1100=y
+CONFIG_SERIAL_SA1100_CONSOLE=y
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=32
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_EFI_RTC is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_DRM is not set
+# CONFIG_DRM_TDFX is not set
+# CONFIG_AGP is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Misc devices
+#
+
+#
+# Console drivers
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_FB=y
+
+#
+# Frame-buffer support
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FB_SA1100=y
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FBCON_ADVANCED is not set
+CONFIG_FBCON_CFB2=y
+CONFIG_FBCON_CFB4=y
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_FONTWIDTH8_ONLY=y
+CONFIG_FBCON_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+# (it is safe to leave these untouched)
+#
+CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+
+#
+#
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_ARM_AM79C961A is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+CONFIG_NET_VENDOR_SMC=y
+# CONFIG_WD80x3 is not set
+# CONFIG_ULTRA is not set
+# CONFIG_ULTRA32 is not set
+CONFIG_SMC9194=y
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring driver support
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UFS_FS is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_FRAME_POINTER=y
+CONFIG_DEBUG_ERRORS=y
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_DEBUG_LL is not set
CONFIG_PCI_NAMES=y
CONFIG_ISA=y
CONFIG_ISA_DMA=y
+# CONFIG_SBUS is not set
+# CONFIG_PCMCIA is not set
# CONFIG_ALIGNMENT_TRAP is not set
#
# CONFIG_BLK_DEV_AEC6210 is not set
# CONFIG_AEC6210_TUNING is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD7409 is not set
# CONFIG_AMD7409_OVERRIDE is not set
# CONFIG_BLK_DEV_CMD64X is not set
unsigned int number_mfm_drives;
#endif
+extern void setup_initrd(unsigned int start, unsigned int size);
+extern void setup_ramdisk(int doload, int prompt, int start, unsigned int rd_sz);
+
/*
* Architecture specific fixups. This is where any
* parameters in the params struct are fixed up, or
#if defined(CONFIG_SA1100_BRUTUS)
ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
setup_ramdisk( 1, 0, 0, 8192 );
- setup_initrd( __phys_to_virt(0xd8000000), 0x00400000 );
+ setup_initrd( __phys_to_virt(0xd8000000), 3*1024*1024 );
#elif defined(CONFIG_SA1100_EMPEG)
ROOT_DEV = MKDEV( 3, 1 ); /* /dev/hda1 */
setup_ramdisk( 1, 0, 0, 4096 );
setup_initrd( 0xd0000000+((1024-320)*1024), (320*1024) );
+#elif defined(CONFIG_SA1100_THINCLIENT)
+ ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
+ setup_ramdisk( 1, 0, 0, 8192 );
+ setup_initrd( __phys_to_virt(0xc0800000), 4*1024*1024 );
#elif defined(CONFIG_SA1100_TIFON)
ROOT_DEV = MKDEV(UNNAMED_MAJOR, 0);
setup_ramdisk(1, 0, 0, 4096);
"EBSA110", /* RMK */
0x00000400,
NO_VIDEO,
- 1, 0, 1, 1, 1,
+ 1, 0, 1, 0, 1,
NULL
},
#endif
EXPORT_SYMBOL(uaccess_user);
#endif
+ /* consistent area handling */
+EXPORT_SYMBOL(pci_alloc_consistent);
EXPORT_SYMBOL(consistent_alloc);
EXPORT_SYMBOL(consistent_free);
EXPORT_SYMBOL(consistent_sync);
{
}
+void pcibios_set_master(struct pci_dev *dev)
+{
+}
+
int pcibios_enable_device(struct pci_dev *dev)
{
u16 cmd, old_cmd;
.byte 6, 6, 6, 6, 2, 2, 2, 2, 3, 3, 6, 6, 2, 2, 2, 2
.endm
+#elif defined(CONFIG_ARCH_SHARK)
+
+ .macro disable_fiq
+ .endm
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base
+ mov r4, #0xe0000000
+ orr r4, r4, #0x20
+
+ mov \irqstat, #0x0C
+ strb \irqstat, [r4] @outb(0x0C, 0x20) /* Poll command */
+ ldrb \irqnr, [r4] @irq = inb(0x20) & 7
+ and \irqstat, \irqnr, #0x80
+ teq \irqstat, #0
+ beq 43f
+ and \irqnr, \irqnr, #7
+ teq \irqnr, #2
+ bne 44f
+43: mov \irqstat, #0x0C
+ strb \irqstat, [r4, #0x80] @outb(0x0C, 0xA0) /* Poll command */
+ ldrb \irqnr, [r4, #0x80] @irq = (inb(0xA0) & 7) + 8
+ and \irqstat, \irqnr, #0x80
+ teq \irqstat, #0
+ beq 44f
+ and \irqnr, \irqnr, #7
+ add \irqnr, \irqnr, #8
+44: teq \irqstat, #0
+ .endm
+
+ .macro irq_prio_table
+ .endm
+
#elif defined(CONFIG_HOST_FOOTBRIDGE) || defined(CONFIG_ADDIN_FOOTBRIDGE)
#include <asm/dec21285.h>
.endm
.macro get_irqnr_and_base, irqnr, irqstat, base
- ldr r4, =0xffe00000
- ldr \irqstat, [r4, #0x180] @ get interrupts
+ ldr \irqstat, =INTCONT_BASE
+ ldr \base, =soft_irq_mask
+ ldr \irqstat, [\irqstat] @ get interrupts
+ ldr \base, [\base]
mov \irqnr, #0
+ and \irqstat, \irqstat, \base @ mask out disabled ones
1001: tst \irqstat, #1
addeq \irqnr, \irqnr, #1
moveq \irqstat, \irqstat, lsr #1
.endm
.macro irq_prio_table
+ .ltorg
+ .bss
+ENTRY(soft_irq_mask)
+ .word 0
+ .text
+ .endm
+
+#elif defined(CONFIG_ARCH_TBOX)
+
+ .macro disable_fiq
+ .endm
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base
+ ldr \irqstat, =0xffff7000
+ ldr \irqstat, [\irqstat] @ get interrupts
+ ldr \base, =soft_irq_mask
+ ldr \base, [\base]
+ mov \irqnr, #0
+ and \irqstat, \irqstat, \base @ mask out disabled ones
+1001: tst \irqstat, #1
+ addeq \irqnr, \irqnr, #1
+ moveq \irqstat, \irqstat, lsr #1
+ tsteq \irqnr, #32
+ beq 1001b
+ teq \irqnr, #32
+ .endm
+
+ .macro irq_prio_table
+ .ltorg
+ .bss
+ENTRY(soft_irq_mask)
+ .word 0
+ .text
.endm
#elif defined(CONFIG_ARCH_SA1100)
#include <asm/uaccess.h>
#include <asm/system.h>
-#include <asm/arch/system.h>
#include <asm/io.h>
+/*
+ * Values for cpu_do_idle()
+ */
+#define IDLE_WAIT_SLOW 0
+#define IDLE_WAIT_FAST 1
+#define IDLE_CLOCK_SLOW 2
+#define IDLE_CLOCK_FAST 3
+
extern char *processor_modes[];
extern void setup_mm_for_reboot(char mode);
asmlinkage void ret_from_sys_call(void) __asm__("ret_from_sys_call");
-static int hlt_counter;
+static volatile int hlt_counter;
+
+#include <asm/arch/system.h>
void disable_hlt(void)
{
__setup("hlt", hlt_setup);
/*
- * The idle loop on an ARM...
+ * The idle thread. We try to conserve power, while trying to keep
+ * overall latency low. The architecture specific idle is passed
+ * a value to indicate the level of "idleness" of the system.
*/
void cpu_idle(void)
{
init_idle();
current->priority = 0;
current->counter = -100;
+
while (1) {
- if (!hlt_counter)
- arch_do_idle();
- if (current->need_resched) {
- schedule();
+ arch_idle();
+ schedule();
#ifndef CONFIG_NO_PGT_CACHE
- check_pgt_cache();
+ check_pgt_cache();
#endif
- }
}
}
int __init reboot_setup(char *str)
{
reboot_mode = str[0];
- return 0;
+ return 1;
}
__setup("reboot=", reboot_setup);
*cmdline_p = command_line;
}
-static void __init
+void __init
setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz)
{
#ifdef CONFIG_BLK_DEV_RAM
/*
* initial ram disk
*/
-static void __init setup_initrd(unsigned int start, unsigned int size)
+void __init setup_initrd(unsigned int start, unsigned int size)
{
#ifdef CONFIG_BLK_DEV_INITRD
if (start == 0)
*/
void setup_mm_for_reboot(char mode)
{
- pgd_t *pgd = current->mm->pgd;
+ pgd_t *pgd;
pmd_t pmd;
int i;
+ if (current->mm && current->mm->pgd)
+ pgd = current->mm->pgd;
+ else
+ pgd = init_mm.pgd;
+
for (i = 0; i < FIRST_USER_PGD_NR + USER_PTRS_PER_PGD; i++) {
pmd_val(pmd) = (i << PGDIR_SHIFT) |
PMD_SECT_AP_WRITE | PMD_SECT_AP_READ |
#if defined(CONFIG_SA1100_BRUTUS)
{ 0xc0000000, 0x00400000 }, /* 4MB */
{ 0xc8000000, 0x00400000 }, /* 4MB */
-#if 0 /* only two banks until the bootmem stuff is fixed... */
{ 0xd0000000, 0x00400000 }, /* 4MB */
{ 0xd8000000, 0x00400000 } /* 4MB */
-#endif
#elif defined(CONFIG_SA1100_EMPEG)
{ 0xc0000000, 0x00400000 }, /* 4MB */
{ 0xc8000000, 0x00400000 } /* 4MB */
{ 0xc9000000, 0x00800000 } /* 8MB */
#elif defined(CONFIG_SA1100_VICTOR)
{ 0xc0000000, 0x00400000 } /* 4MB */
+#elif defined(CONFIG_SA1100_THINCLIENT)
+ { 0xc0000000, 0x01000000 } /* 16MB */
#elif defined(CONFIG_SA1100_TIFON)
{ 0xc0000000, 0x01000000 }, /* 16MB */
{ 0xc8000000, 0x01000000 } /* 16MB */
{ 0xd0000000, 0x00000000, 0x00200000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash */
#elif defined(CONFIG_SA1100_EMPEG)
{ EMPEG_FLASHBASE, 0x00000000, 0x00200000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash */
+#elif defined(CONFIG_SA1100_THINCLIENT)
+#if 1
+ /* ThinClient: only one of those... */
+// { 0xd0000000, 0x00000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 when JP1 2-4 */
+ { 0xd0000000, 0x08000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 when JP1 3-4 */
+#else
+ /* GraphicsClient: */
+ { 0xd0000000, 0x08000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 */
+ { 0xd0800000, 0x18000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 3 */
+#endif
#elif defined(CONFIG_SA1100_TIFON)
{ 0xd0000000, 0x00000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 1 */
{ 0xd0800000, 0x08000000, 0x00800000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 2 */
#endif
-#ifdef CONFIG_SA1101
+#if defined( CONFIG_SA1101 )
{ 0xdc000000, SA1101_BASE, 0x00400000, DOMAIN_IO, 1, 1, 0, 0 }, /* SA1101 */
+#elif defined( CONFIG_SA1100_THINCLIENT )
+ { 0xdc000000, 0x10000000, 0x00400000, DOMAIN_IO, 0, 1, 0, 0 }, /* CPLD */
#endif
{ 0xe0000000, 0x20000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA0 IO */
{ 0xe4000000, 0x30000000, 0x04000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCMCIA1 IO */
--- /dev/null
+/*
+ * arch/arm/mm/mm-shark.c
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ */
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/io.h>
+
+#include "map.h"
+
+struct map_desc io_desc[] __initdata = {
+ { IO_BASE , IO_START , IO_SIZE , DOMAIN_IO, 0, 1, 0, 0 },
+ { FB_BASE , FB_START , FB_SIZE , DOMAIN_IO, 0, 1, 0, 0 },
+ { FBREG_BASE , FBREG_START , FBREG_SIZE , DOMAIN_IO, 0, 1, 0, 0 }
+};
+
+
+#define SIZEOFMAP (sizeof(io_desc) / sizeof(io_desc[0]))
+
+unsigned int __initdata io_desc_size = SIZEOFMAP;
ldmfd sp!, {r1, pc}
.align 5
+idle: mcr p15, 0, r0, c15, c8, 2 @ Wait for interrupt
+ mov r0, r0 @ safety
+ mov pc, lr
+/*
+ * Function: *_do_idle
+ * Params : r0 = call type:
+ * 0 = slow idle
+ * 1 = fast idle
+ * 2 = switch to slow processor clock
+ * 3 = switch to fast processor clock
+ */
ENTRY(cpu_sa110_do_idle)
ENTRY(cpu_sa1100_do_idle)
- mov r0, #0
- mcr p15, 0, r0, c15, c2, 2 @ Disable clock switching
- ldr r1, =FLUSH_BASE+FLUSH_OFFSET*2 @ load from uncacheable loc
- ldr r1, [r1, #0]
- b 1f
+ mov ip, #0
+ cmp r0, #4
+ addcc pc, pc, r0, lsl #2
+ mov pc, lr
- .align 5
-1: mcr p15, 0, r0, c15, c8, 2 @ Wait for interrupt
- mcr p15, 0, r0, c15, c1, 2 @ Enable clock switching
+ b idle
+ b idle
+ b slow_clock
+ b fast_clock
+
+fast_clock: mcr p15, 0, ip, c15, c1, 2 @ enable clock switching
+ mov pc, lr
+
+slow_clock: mcr p15, 0, ip, c15, c2, 2 @ disable clock switching
+ ldr r1, =UNCACHEABLE_ADDR @ load from uncacheable loc
+ ldr r1, [r1, #0] @ force switch to MCLK
mov pc, lr
/*
*/
.text
-#define __ASSEMBLY__
#include <linux/linkage.h>
#include <asm/segment.h>
define_bool CONFIG_X86 y
define_bool CONFIG_ISA y
+define_bool CONFIG_SBUS n
define_bool CONFIG_UID16 y
if [ "$CONFIG_HOTPLUG" = "y" ] ; then
source drivers/pcmcia/Config.in
+else
+ define_bool CONFIG_PCMCIA n
fi
bool 'System V IPC' CONFIG_SYSVIPC
source drivers/char/Config.in
-source drivers/usb/Config.in
#source drivers/misc/Config.in
fi
endmenu
+source drivers/usb/Config.in
+
mainmenu_option next_comment
comment 'Kernel hacking'
#
CONFIG_X86=y
CONFIG_ISA=y
+# CONFIG_SBUS is not set
CONFIG_UID16=y
#
# CONFIG_MK7 is not set
CONFIG_X86_WP_WORKS_OK=y
CONFIG_X86_INVLPG=y
+CONFIG_X86_CMPXCHG=y
CONFIG_X86_BSWAP=y
CONFIG_X86_POPAD_OK=y
+CONFIG_X86_L1_CACHE_BYTES=32
CONFIG_X86_TSC=y
CONFIG_X86_GOOD_APIC=y
CONFIG_X86_PGE=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
# CONFIG_MICROCODE is not set
CONFIG_NOHIGHMEM=y
# CONFIG_HIGHMEM4G is not set
# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_STRIPED is not set
# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
#
# Networking options
# CONFIG_BLK_DEV_AEC6210 is not set
# CONFIG_AEC6210_TUNING is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD7409 is not set
# CONFIG_AMD7409_OVERRIDE is not set
# CONFIG_BLK_DEV_CMD64X is not set
# CONFIG_SCSI_AM53C974 is not set
# CONFIG_SCSI_MEGARAID is not set
# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_DTC3280 is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_EATA_DMA is not set
# CONFIG_AUTOFS_FS is not set
CONFIG_AUTOFS4_FS=y
# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_JOLIET is not set
# CONFIG_MINIX_FS is not set
# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
# CONFIG_HPFS_FS is not set
CONFIG_PROC_FS=y
# CONFIG_DEVFS_FS is not set
# CONFIG_DEVFS_DEBUG is not set
CONFIG_DEVPTS_FS=y
# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
# CONFIG_ROMFS_FS is not set
CONFIG_EXT2_FS=y
# CONFIG_SYSV_FS is not set
+# CONFIG_SYSV_FS_WRITE is not set
# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
#
# Network File Systems
CONFIG_LOCKD=y
# CONFIG_SMB_FS is not set
# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
+# CONFIG_NCPFS_NDS_DOMAINS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
#
# Partition Types
*/
/**
- * probe_irq_mask
+ * probe_irq_mask - scan a bitmap of interrupt lines
* @val: mask of interrupts to consider
*
* Scan the ISA bus interrupt lines and return a bitmap of
} /* End Function mtrr_add */
/**
- * mtrr_del
+ * mtrr_del - delete a memory type region
* @reg: Register returned by mtrr_add
* @base: Physical base address
* @size: Size of region
if (!pr || request_resource(pr, r) < 0) {
printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, dev->slot_name);
/* We'll assign a new address later */
- r->start -= r->end;
+ r->end -= r->start;
r->start = 0;
}
}
if (r->flags & IORESOURCE_MEM)
cmd |= PCI_COMMAND_MEMORY;
}
+ if (dev->resource[PCI_ROM_RESOURCE].start)
+ cmd |= PCI_COMMAND_MEMORY;
if (cmd != old_cmd) {
printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd);
pci_write_config_word(dev, PCI_COMMAND, cmd);
} else if (newirq) {
DBG(" -> [VIA] set to %02x\n", newirq);
x = (pirq & 1) ? ((x & 0x0f) | (newirq << 4)) : ((x & 0xf0) | newirq);
- pci_write_config_byte(router, reg, y);
+ pci_write_config_byte(router, reg, x);
msg = "VIA-NEW";
} else DBG(" -> [VIA] sink\n");
break;
rdmsr(0xC0000082, l, h);
if((l&0x0000FFFF)==0)
{
- l=(1<<0)|(mbytes/4);
+ l=(1<<0)|((mbytes/4)<<1);
save_flags(flags);
__cli();
__asm__ __volatile__ ("wbinvd": : :"memory");
comment 'General setup'
define_bool CONFIG_IA64 y
+define_bool CONFIG_ISA n
+define_bool CONFIG_SBUS n
choice 'IA-64 system type' \
"Generic CONFIG_IA64_GENERIC \
bool 'PCI support' CONFIG_PCI
source drivers/pci/Config.in
-source drivers/pcmcia/Config.in
+bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG
+if [ "$CONFIG_HOTPLUG" = "y" ]; then
+ source drivers/pcmcia/Config.in
+else
+ define_bool CONFIG_PCMCIA n
+fi
mainmenu_option next_comment
comment 'Code maturity level options'
endmenu
source drivers/char/Config.in
-source drivers/usb/Config.in
#source drivers/misc/Config.in
source fs/Config.in
fi
endmenu
+source drivers/usb/Config.in
+
mainmenu_option next_comment
comment 'Kernel hacking'
# General setup
#
CONFIG_IA64=y
+# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
# CONFIG_IA64_GENERIC is not set
CONFIG_IA64_HP_SIM=y
# CONFIG_IA64_SGI_SN1_SIM is not set
# CONFIG_BINFMT_MISC is not set
CONFIG_PCI=y
CONFIG_PCI_NAMES=y
-
-#
-# PCMCIA/CardBus support
-#
+# CONFIG_HOTPLUG is not set
# CONFIG_PCMCIA is not set
#
# CONFIG_BLK_DEV_AEC6210 is not set
# CONFIG_AEC6210_TUNING is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD7409 is not set
# CONFIG_AMD7409_OVERRIDE is not set
# CONFIG_BLK_DEV_CMD64X is not set
comment 'Platform dependent setup'
define_bool CONFIG_ISA n
+define_bool CONFIG_PCMCIA n
+
bool 'Amiga support' CONFIG_AMIGA
bool 'Atari support' CONFIG_ATARI
-if [ "$CONFIG_ATARI" = "y" ]; then
- bool ' Hades support' CONFIG_HADES
- if [ "$CONFIG_HADES" = "y" ]; then
- define_bool CONFIG_PCI y
- fi
-fi
-if [ "$CONFIG_HADES" != "y" ]; then
- define_bool CONFIG_PCI n
+dep_bool ' Hades support' CONFIG_HADES $CONFIG_ATARI
+if [ "$CONFIG_HADES" = "y" ]; then
+ define_bool CONFIG_PCI y
+else
+ define_bool CONFIG_PCI n
fi
bool 'Macintosh support' CONFIG_MAC
if [ "$CONFIG_MAC" = "y" ]; then
if [ "$CONFIG_HP300" = "y" -a "$CONFIG_DIO" = "y" ]; then
tristate 'HP DCA serial support' CONFIG_HPDCA
fi
-if [ "$CONFIG_SUN3X" = "y" ]; then
- bool 'Sun3x builtin serial support' CONFIG_SUN3X_ZS
- if [ "$CONFIG_SUN3X_ZS" = "y" ]; then
- bool ' Sun keyboard support' CONFIG_SUN_KEYBOARD
- bool ' Sun mouse support' CONFIG_SUN_MOUSE
- if [ "$CONFIG_SUN_MOUSE" != "n" ]; then
- define_bool CONFIG_BUSMOUSE y
- fi
- define_bool CONFIG_SBUS y
- define_bool CONFIG_SBUSCHAR y
- define_bool CONFIG_SUN_SERIAL y
- fi
+
+dep_bool 'Sun3x builtin serial support' CONFIG_SUN3X_ZS $CONFIG_SUN3X
+dep_bool ' Sun keyboard support' CONFIG_SUN_KEYBOARD $CONFIG_SUN3X_ZS
+dep_bool ' Sun mouse support' CONFIG_SUN_MOUSE $CONFIG_SUN3X_ZS
+if [ "$CONFIG_SUN_MOUSE" = "y" ]; then
+ define_bool CONFIG_BUSMOUSE y
+fi
+if [ "$CONFIG_SUN3X_ZS" = "y" ]; then
+ define_bool CONFIG_SBUS y
+ define_bool CONFIG_SBUSCHAR y
+ define_bool CONFIG_SUN_SERIAL y
+else
+ define_bool CONFIG_SBUS n
fi
+
if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_ATARI" = "y" -o \
"$CONFIG_MAC" = "y" -o "$CONFIG_HP300" = "y" -o \
"$CONFIG_SUN3X" = "y" ]; then
# Platform dependent setup
#
# CONFIG_ISA is not set
+# CONFIG_PCMCIA is not set
CONFIG_AMIGA=y
# CONFIG_ATARI is not set
+# CONFIG_HADES is not set
# CONFIG_PCI is not set
# CONFIG_MAC is not set
# CONFIG_APOLLO is not set
# CONFIG_GVPIOEXT_LP is not set
# CONFIG_GVPIOEXT_PLIP is not set
# CONFIG_MULTIFACE_III_TTY is not set
+# CONFIG_SUN3X_ZS is not set
+# CONFIG_SUN_KEYBOARD is not set
+# CONFIG_SUN_MOUSE is not set
+# CONFIG_SBUS is not set
# CONFIG_SERIAL_CONSOLE is not set
# CONFIG_USERIAL is not set
# CONFIG_WATCHDOG is not set
unset CONFIG_MIPS_JAZZ
unset CONFIG_VIDEO_G364
+define_bool CONFIG_SBUS n
+
if [ "$CONFIG_ALGOR_P4032" = "y" -o "$CONFIG_SNI_RM200_PCI" = "y" -o \
"$CONFIG_DDB5074" = "y" ]; then
define_bool CONFIG_PCI y
bool 'Networking support' CONFIG_NET
source drivers/pci/Config.in
-source drivers/pcmcia/Config.in
+
+bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG
+if [ "$CONFIG_HOTPLUG" = "y" ]; then
+ source drivers/pcmcia/Config.in
+else
+ define_bool CONFIG_PCMCIA n
+fi
bool 'System V IPC' CONFIG_SYSVIPC
bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
fi
if [ "$CONFIG_SGI" != "y" -a "$CONFIG_DECSTATION" != "y" -a "$CONFIG_BAGET_MIPS" != "y" ]; then
- source drivers/net/hamradio/Config.in
+ mainmenu_option next_comment
+ # comment 'AX.25 network device drivers'
+ source drivers/net/hamradio/Config.in
+ endmenu
mainmenu_option next_comment
comment 'ISDN subsystem'
endmenu
fi
-source drivers/usb/Config.in
#source drivers/misc/Config.in
source drivers/sgi/Config.in
fi
+source drivers/usb/Config.in
+
mainmenu_option next_comment
comment 'Kernel hacking'
# CONFIG_OLIVETTI_M700 is not set
CONFIG_SGI_IP22=y
# CONFIG_SNI_RM200_PCI is not set
+# CONFIG_SBUS is not set
# CONFIG_PCI is not set
# CONFIG_ISA is not set
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_MISC is not set
CONFIG_NET=y
-
-#
-# PCMCIA/CardBus support
-#
+# CONFIG_HOTPLUG is not set
# CONFIG_PCMCIA is not set
CONFIG_SYSVIPC=y
# CONFIG_BSD_PROCESS_ACCT is not set
bool ' Discontiguous Memory Support' CONFIG_DISCONTIGMEM
#bool ' IP27 XXL' CONFIG_SGI_SN0_XXL
fi
-endmenu
-
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'Symmetric Multi-Processing support' CONFIG_SMP
fi
+endmenu
#
# Select some configuration options automatically based on user selections
unset CONFIG_BINFMT_ELF32
define_bool CONFIG_ISA n
+define_bool CONFIG_SBUS n
if [ "$CONFIG_SGI_IP22" = "y" ]; then
define_bool CONFIG_BOOT_ELF32 y
bool 'Networking support' CONFIG_NET
source drivers/pci/Config.in
-source drivers/pcmcia/Config.in
+
+bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG
+
+if [ "$CONFIG_HOTPLUG" = "y" ] ; then
+ source drivers/pcmcia/Config.in
+else
+ define_bool CONFIG_PCMCIA n
+fi
bool 'System V IPC' CONFIG_SYSVIPC
bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
source drivers/char/Config.in
-source drivers/usb/Config.in
#source drivers/misc/Config.in
source drivers/sgi/Config.in
fi
+source drivers/usb/Config.in
+
mainmenu_option next_comment
comment 'Kernel hacking'
# CONFIG_SGI_SN0_N_MODE is not set
# CONFIG_DISCONTIGMEM is not set
# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
CONFIG_BOOT_ELF64=y
CONFIG_ARC64=y
CONFIG_COHERENT_IO=y
# CONFIG_CPU_LITTLE_ENDIAN is not set
CONFIG_NET=y
CONFIG_PCI_NAMES=y
-
-#
-# PCMCIA/CardBus support
-#
+# CONFIG_HOTPLUG is not set
# CONFIG_PCMCIA is not set
CONFIG_SYSVIPC=y
# CONFIG_BSD_PROCESS_ACCT is not set
PLAT_NODE_DATA(node)->physstart = (start_pfn << PAGE_SHIFT);
PLAT_NODE_DATA(node)->size = (zones_size[ZONE_DMA] << PAGE_SHIFT);
free_area_init_node(node, NODE_DATA(node), zones_size,
- start_pfn << PAGE_SHIFT);
+ start_pfn << PAGE_SHIFT, 0);
PLAT_NODE_DATA(node)->start_mapnr =
(NODE_DATA(node)->node_mem_map - mem_map);
if ((PLAT_NODE_DATA(node)->start_mapnr +
* Like the LANCE driver:
* The driver runs as two independent, single-threaded flows of control. One
* is the send-packet routine, which enforces single-threaded use by the
- * dev->tbusy flag. The other thread is the interrupt handler, which is single
- * threaded by the hardware and other software.
+ * cep->tx_busy flag. The other thread is the interrupt handler, which is
+ * single threaded by the hardware and other software.
*
- * The send packet thread has partial control over the Tx ring and 'dev->tbusy'
- * flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
- * queue slot is empty, it clears the tbusy flag when finished otherwise it sets
- * the 'lp->tx_full' flag.
+ * The send packet thread has partial control over the Tx ring and the
+ * 'cep->tx_busy' flag. It sets the tx_busy flag whenever it's queuing a Tx
+ * packet. If the next queue slot is empty, it clears the tx_busy flag when
+ * finished otherwise it sets the 'lp->tx_full' flag.
*
* The MBX has a control register external to the MPC8xx that has some
* control of the Ethernet interface. Control Register 1 has the
cbd_t *dirty_tx; /* The ring entries to be free()ed. */
scc_t *sccp;
struct net_device_stats stats;
- char tx_full;
+ uint tx_full;
+ uint tx_busy;
unsigned long lock;
+ int interrupt;
};
static int cpm_enet_open(struct net_device *dev);
* a simple way to do that.
*/
- dev->tbusy = 0;
- dev->interrupt = 0;
- dev->start = 1;
-
+ netif_start_queue(dev);
return 0; /* Always succeed */
}
unsigned long flags;
/* Transmitter timeout, serious problems. */
- if (dev->tbusy) {
+ if (cep->tx_busy) {
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < 200)
return 1;
}
#endif
- dev->tbusy=0;
+ cep->tx_busy=0;
dev->trans_start = jiffies;
return 0;
}
/* Block a timer-based transmit from overlapping. This could better be
- done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+ * done with atomic_swap(1, cep->tx_busy), but set_bit() works as well.
+ */
+ if (test_and_set_bit(0, (void*)&cep->tx_busy) != 0) {
printk("%s: Transmitter access conflict.\n", dev->name);
return 1;
}
if (test_and_set_bit(0, (void*)&cep->lock) != 0) {
printk("%s: tx queue lock!.\n", dev->name);
- /* don't clear dev->tbusy flag. */
+ /* don't clear cep->tx_busy flag. */
return 1;
}
#ifndef final_version
if (bdp->cbd_sc & BD_ENET_TX_READY) {
/* Ooops. All transmit buffers are full. Bail out.
- * This should not happen, since dev->tbusy should be set.
+ * This should not happen, since cep->tx_busy should be set.
*/
printk("%s: tx queue full!.\n", dev->name);
cep->lock = 0;
if (bdp->cbd_sc & BD_ENET_TX_READY)
cep->tx_full = 1;
else
- dev->tbusy=0;
+ cep->tx_busy=0;
restore_flags(flags);
cep->cur_tx = (cbd_t *)bdp;
int must_restart;
cep = (struct cpm_enet_private *)dev->priv;
- if (dev->interrupt)
+ if (cep->interrupt)
printk("%s: Re-entering the interrupt handler.\n", dev->name);
- dev->interrupt = 1;
+ cep->interrupt = 1;
/* Get the interrupt events that caused us to be here.
*/
/* Free the sk buffer associated with this last transmit.
*/
- dev_kfree_skb(cep->tx_skbuff[cep->skb_dirty]/*, FREE_WRITE*/);
+ dev_kfree_skb_irq(cep->tx_skbuff[cep->skb_dirty]);
cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK;
/* Update pointer to next buffer descriptor to be transmitted.
/* Since we have freed up a buffer, the ring is no longer
* full.
*/
- if (cep->tx_full && dev->tbusy) {
+ if (cep->tx_full && cep->tx_busy) {
cep->tx_full = 0;
- dev->tbusy = 0;
- mark_bh(NET_BH);
+ cep->tx_busy = 0;
+ netif_wake_queue(dev);
}
cep->dirty_tx = (cbd_t *)bdp;
printk("CPM ENET: BSY can't happen.\n");
}
- dev->interrupt = 0;
+ cep->interrupt = 0;
return;
}
{
/* Don't know what to do yet.
*/
+ netif_stop_queue(dev);
return 0;
}
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
+#include <linux/serialP.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
ifdef CONFIG_8xx
SUBDIRS += arch/ppc/8xx_io
-DRIVERS += arch/ppc/8xx_io/8xx_io.a drivers/net/net.a
+DRIVERS += arch/ppc/8xx_io/8xx_io.a
endif
ifdef CONFIG_APUS
comment 'General setup'
define_bool CONFIG_ISA n
+define_bool CONFIG_SBUS n
if [ "$CONFIG_APUS" = "y" -o "$CONFIG_4xx" = "y" -o \
"$CONFIG_82xx" = "y" ]; then
define_bool CONFIG_PCI n
else
- if [ "$CONFIG_6xx" = "y" -o CONFIG_PPC64 ]; then
+ if [ "$CONFIG_6xx" = "y" -o "$CONFIG_PPC64" = "y" ]; then
define_bool CONFIG_PCI y
else
# CONFIG_8xx
if [ "$CONFIG_HOTPLUG" = "y" ]; then
source drivers/pcmcia/Config.in
+else
+ define_bool CONFIG_PCMCIA n
fi
source drivers/parport/Config.in
endmenu
source drivers/char/Config.in
-source drivers/usb/Config.in
source fs/Config.in
mainmenu_option next_comment
source arch/ppc/8xx_io/Config.in
fi
+source drivers/usb/Config.in
+
mainmenu_option next_comment
comment 'Kernel hacking'
# General setup
#
# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
CONFIG_PCI=y
CONFIG_NET=y
CONFIG_SYSCTL=y
# CONFIG_BINFMT_MISC is not set
# CONFIG_PCI_NAMES is not set
# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
#
# Parallel port support
# CONFIG_BLK_DEV_AEC6210 is not set
# CONFIG_AEC6210_TUNING is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD7409 is not set
# CONFIG_AMD7409_OVERRIDE is not set
# CONFIG_BLK_DEV_CMD64X is not set
int i;
void cpm_interrupt_init(void);
- ppc8xx_pic.irq_offset = 0;
for ( i = 0 ; i < NR_SIU_INTS ; i++ )
irq_desc[i].handler = &ppc8xx_pic;
case LFD:
idx = (insn >> 16) & 0x1f;
sdisp = (insn & 0xffff);
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
lfd(op0, op1, op2, op3);
break;
case LFDU:
idx = (insn >> 16) & 0x1f;
sdisp = (insn & 0xffff);
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
lfd(op0, op1, op2, op3);
regs->gpr[idx] = (unsigned long)op1;
case STFD:
idx = (insn >> 16) & 0x1f;
sdisp = (insn & 0xffff);
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
stfd(op0, op1, op2, op3);
break;
case STFDU:
idx = (insn >> 16) & 0x1f;
sdisp = (insn & 0xffff);
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
stfd(op0, op1, op2, op3);
regs->gpr[idx] = (unsigned long)op1;
break;
case OP63:
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)¤t->tss.fpr[(insn >> 11) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
+ op1 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f];
fmr(op0, op1, op2, op3);
break;
default:
switch (type) {
case AB:
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)¤t->tss.fpr[(insn >> 16) & 0x1f];
- op2 = (void *)¤t->tss.fpr[(insn >> 11) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
+ op1 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f];
+ op2 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f];
break;
case AC:
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)¤t->tss.fpr[(insn >> 16) & 0x1f];
- op2 = (void *)¤t->tss.fpr[(insn >> 6) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
+ op1 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f];
+ op2 = (void *)¤t->thread.fpr[(insn >> 6) & 0x1f];
break;
case ABC:
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)¤t->tss.fpr[(insn >> 16) & 0x1f];
- op2 = (void *)¤t->tss.fpr[(insn >> 11) & 0x1f];
- op3 = (void *)¤t->tss.fpr[(insn >> 6) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
+ op1 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f];
+ op2 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f];
+ op3 = (void *)¤t->thread.fpr[(insn >> 6) & 0x1f];
break;
case D:
idx = (insn >> 16) & 0x1f;
sdisp = (insn & 0xffff);
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
op1 = (void *)((idx ? regs->gpr[idx] : 0) + sdisp);
break;
goto illegal;
sdisp = (insn & 0xffff);
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
op1 = (void *)(regs->gpr[idx] + sdisp);
break;
case X:
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
break;
case XA:
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)¤t->tss.fpr[(insn >> 16) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
+ op1 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f];
break;
case XB:
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
- op1 = (void *)¤t->tss.fpr[(insn >> 11) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
+ op1 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f];
break;
case XE:
if (!idx)
goto illegal;
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
op1 = (void *)(regs->gpr[idx] + regs->gpr[(insn >> 11) & 0x1f]);
break;
case XEU:
idx = (insn >> 16) & 0x1f;
- op0 = (void *)¤t->tss.fpr[(insn >> 21) & 0x1f];
+ op0 = (void *)¤t->thread.fpr[(insn >> 21) & 0x1f];
op1 = (void *)((idx ? regs->gpr[idx] : 0)
+ regs->gpr[(insn >> 11) & 0x1f]);
break;
case XCR:
op0 = (void *)®s->ccr;
op1 = (void *)((insn >> 23) & 0x7);
- op2 = (void *)¤t->tss.fpr[(insn >> 16) & 0x1f];
- op3 = (void *)¤t->tss.fpr[(insn >> 11) & 0x1f];
+ op2 = (void *)¤t->thread.fpr[(insn >> 16) & 0x1f];
+ op3 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f];
break;
case XCRL:
case XFLB:
op0 = (void *)((insn >> 17) & 0xff);
- op1 = (void *)¤t->tss.fpr[(insn >> 11) & 0x1f];
+ op1 = (void *)¤t->thread.fpr[(insn >> 11) & 0x1f];
break;
default:
#include <linux/kernel.h>
#include <linux/sched.h>
-#define __FPU_FPSCR (current->tss.fpscr)
+#define __FPU_FPSCR (current->thread.fpscr)
/* We only actually write to the destination register
* if exceptions signalled (if any) will not trap.
ISZ = 0
TFTPIMAGE=/tftpboot/zImage.mbx
-ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00100000
+ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00180000
OBJECTS := head.o misc.o ../coffboot/zlib.o m8xx_tty.o
CFLAGS = $(CPPFLAGS) -O2 -DSTDC_HEADERS -fno-builtin -DCONFIG_8xx
#ifdef CONFIG_8xx
-unsigned long va_to_phys(unsigned long address)
+/* The pgtable.h claims some functions generically exist, but I
+ * can't find them......
+ */
+pte_t *find_pte(struct mm_struct *mm, unsigned long address)
{
pgd_t *dir;
pmd_t *pmd;
pte_t *pte;
-
- dir = pgd_offset(current->mm, address & PAGE_MASK);
- if (dir)
- {
+
+ dir = pgd_offset(mm, address & PAGE_MASK);
+ if (dir) {
pmd = pmd_offset(dir, address & PAGE_MASK);
- if (pmd && pmd_present(*pmd))
- {
+ if (pmd && pmd_present(*pmd)) {
pte = pte_offset(pmd, address & PAGE_MASK);
- if (pte && pte_present(*pte))
- {
- return(pte_page(*pte) | (address & ~(PAGE_MASK-1)));
+ if (pte && pte_present(*pte)) {
+ return(pte);
}
- } else
- {
+ }
+ else {
return (0);
}
- } else
- {
+ }
+ else {
return (0);
}
return (0);
}
+unsigned long va_to_phys(unsigned long address)
+{
+ pte_t *pte;
+
+ pte = find_pte(current->mm, address);
+ if (pte)
+ return((unsigned long)(pte_page(*pte)) | (address & ~(PAGE_MASK-1)));
+ return (0);
+}
+
void
print_8xx_pte(struct mm_struct *mm, unsigned long addr)
{
printk(" (0x%08lx)->(0x%08lx)->0x%08lx\n",
(long)pgd, (long)pte, (long)pte_val(*pte));
#define pp ((long)pte_val(*pte))
- printk(" RPN: %05x PP: %x SPS: %x SH: %x "
- "CI: %x v: %x\n",
+ printk(" RPN: %05x PP: %x SPS: %x SH: %lx "
+ "CI: %lx v: %lx\n",
pp>>12, /* rpn */
(pp>>10)&3, /* pp */
(pp>>3)&1, /* small */
-# $Id: Makefile,v 1.2 1999/12/23 12:13:53 gniibe Exp gniibe $
+# $Id: Makefile,v 1.4 2000/03/08 15:14:14 gniibe Exp $
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
comment 'General setup'
define_bool CONFIG_ISA n
+define_bool CONFIG_SBUS n
bool 'Networking support' CONFIG_NET
if [ "$CONFIG_HOTPLUG" = "y" ] ; then
source drivers/pcmcia/Config.in
+else
+ define_bool CONFIG_PCMCIA n
fi
bool 'System V IPC' CONFIG_SYSVIPC
source drivers/char/pcmcia/Config.in
fi
-#source drivers/misc/Config.in
-
source fs/Config.in
if [ "$CONFIG_VT" = "y" ]; then
# General setup
#
# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
# CONFIG_NET is not set
CONFIG_CF_ENABLER=y
# CONFIG_PCI is not set
# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
# CONFIG_SYSVIPC is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_SYSCTL is not set
-/* $Id: entry.S,v 1.55 2000/03/05 01:48:58 gniibe Exp $
+/* $Id: entry.S,v 1.71 2000/03/22 13:29:33 gniibe Exp $
*
* linux/arch/sh/entry.S
*
*
* syscall #
* ssr
- * r15 = stack pointer
* r0
* ...
- * r14
+ * r15 = stack pointer
* gbr
* mach
* macl
*/
/*
- * these are offsets into the task-struct.
+ * These are offsets into the task-struct.
*/
state = 0
flags = 4
/* Offsets to the stack */
SYSCALL_NR = 0
SR = 4
-SP = 8
-R0 = 12
+R0 = 8
+SP = (8+15*4)
#define k0 r0
#define k1 r1
k2 scratch (Exception code)
k3 scratch (Return address)
k4 Stack base = current+8192
- k5 reserved
+ k5 Global Interrupt Mask (0--15)
k6 reserved
k7 reserved
*/
! this first version depends *much* on C implementation.
!
-#define DO_FAULT(write) \
- mov.l 4f,r0; \
- mov.l @r0,r6; \
- /* STI */ \
- mov.l 3f,r1; \
- stc sr,r0; \
- and r1,r0; \
- ldc r0,sr; \
- /* */ \
- mov r15,r4; \
- mov.l 2f,r0; \
- jmp @r0; \
- mov #write,r5;
+#define RESTORE_FLAGS() \
+ mov.l @(SR,$r15), $r0; \
+ and #0xf0, $r0; \
+ shlr8 $r0; \
+ cmp/eq #0x0f, $r0; \
+ bt 9f; \
+ mov.l __INV_IMASK, $r1; \
+ stc $sr, $r0; \
+ and $r1, $r0; \
+ stc $r5_bank, $r1; \
+ or $r1, $r0; \
+ ldc $r0, $sr
.balign 4
tlb_protection_violation_load:
tlb_miss_load:
- mov #-1,r0
- mov.l r0,@r15 ! syscall nr = -1
- DO_FAULT(0)
+ mov #-1, $r0
+ mov.l $r0, @$r15 ! syscall nr = -1
+ mov.l 2f, $r0
+ mov.l @$r0, $r6
+ RESTORE_FLAGS()
+9: mov $r15, $r4
+ mov.l 1f, $r0
+ jmp @$r0
+ mov #0, $r5
.balign 4
tlb_protection_violation_store:
tlb_miss_store:
initial_page_write:
- mov #-1,r0
- mov.l r0,@r15 ! syscall nr = -1
- DO_FAULT(1)
+ mov #-1, $r0
+ mov.l $r0, @$r15 ! syscall nr = -1
+ mov.l 2f, $r0
+ mov.l @$r0, $r6
+ RESTORE_FLAGS()
+9: mov $r15, $r4
+ mov.l 1f, $r0
+ jmp @$r0
+ mov #1, $r5
.balign 4
-2: .long SYMBOL_NAME(do_page_fault)
-3: .long 0xefffffff ! BL=0
-4: .long MMU_TEA
+1: .long SYMBOL_NAME(do_page_fault)
+2: .long MMU_TEA
#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
.balign 4
/* Unwind the stack and jmp to the debug entry */
debug:
- add #4,r15 ! skip syscall number
- mov.l @r15+,r11 ! SSR
- mov.l @r15+,r10 ! original stack
- mov.l @r15+,r0
- mov.l @r15+,r1
- mov.l @r15+,r2
- mov.l @r15+,r3
- mov.l @r15+,r4
- mov.l @r15+,r5
- mov.l @r15+,r6
- mov.l @r15+,r7
- stc sr,r14
- mov.l 8f,r9 ! BL =1, RB=1
- or r9,r14
- ldc r14,sr ! here, change the register bank
- mov r10,k0
- mov r11,k1
- mov.l @r15+,r8
- mov.l @r15+,r9
- mov.l @r15+,r10
- mov.l @r15+,r11
- mov.l @r15+,r12
- mov.l @r15+,r13
- mov.l @r15+,r14
- ldc.l @r15+,gbr
- lds.l @r15+,mach
- lds.l @r15+,macl
- lds.l @r15+,pr
- ldc.l @r15+,spc
- mov k0,r15
+ add #4, $r15 ! skip syscall number
+ mov.l @$r15+, $r11 ! SSR
+ mov.l @$r15+, $r0
+ mov.l @$r15+, $r1
+ mov.l @$r15+, $r2
+ mov.l @$r15+, $r3
+ mov.l @$r15+, $r4
+ mov.l @$r15+, $r5
+ mov.l @$r15+, $r6
+ mov.l @$r15+, $r7
+ stc $sr, $r14
+ mov.l 1f, $r9 ! BL =1, RB=1
+ or $r9, $r14
+ ldc $r14, $sr ! here, change the register bank
+ mov $r11, $k1
+ mov.l @$r15+, $r8
+ mov.l @$r15+, $r9
+ mov.l @$r15+, $r10
+ mov.l @$r15+, $r11
+ mov.l @$r15+, $r12
+ mov.l @$r15+, $r13
+ mov.l @$r15+, $r14
+ mov.l @$r15+, $k0
+ ldc.l @$r15+, $gbr
+ lds.l @$r15+, $mach
+ lds.l @$r15+, $macl
+ lds.l @$r15+, $pr
+ ldc.l @$r15+, $spc
+ mov $k0, $r15
!
- mov.l 9f,k0
- jmp @k0
- ldc k1,ssr
+ mov.l 2f, $k0
+ jmp @$k0
+ ldc $k1, $ssr
.balign 4
-8: .long 0x300000f0
-9: .long 0xa0000100
+1: .long 0x300000f0
+2: .long 0xa0000100
#endif
.balign 4
error:
- ! STI
- mov.l 2f,r1
- stc sr,r0
- and r1,r0
- ldc r0,sr
!
- mov.l 1f,r1
- mov #-1,r0
- jmp @r1
- mov.l r0,@r15 ! syscall nr = -1
+ RESTORE_FLAGS()
+9: mov.l 1f, $r1
+ mov #-1, $r0
+ jmp @$r1
+ mov.l $r0, @$r15 ! syscall nr = -1
.balign 4
1: .long SYMBOL_NAME(do_exception_error)
-2: .long 0xefffffff ! BL=0
-badsys: mov #-ENOSYS,r0
+badsys: mov #-ENOSYS, $r0
rts ! go to ret_from_syscall..
- mov.l r0,@(R0,r15)
+ mov.l $r0, @(R0,$r15)
!
!
!
ENTRY(ret_from_fork)
bra SYMBOL_NAME(ret_from_syscall)
- add #4,r15 ! pop down bogus r0 (see switch_to MACRO)
+ add #4, $r15 ! pop down bogus r0 (see switch_to MACRO)
!
! The immediate value of "trapa" indicates the number of arguments
! Note that TRA register contains the value = Imm x 4.
!
system_call:
- mov.l 1f,r2
- mov.l @r2,r8
+ mov.l 1f, $r2
+ mov.l @$r2, $r8
!
! DEBUG DEBUG
- ! mov.l led,r1
- ! mov r0,r2
- ! mov.b r2,@r1
+ ! mov.l led, $r1
+ ! mov $r0, $r2
+ ! mov.b $r2, @$r1
!
#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
- mov #0x20,r1
- extu.b r1,r1
- shll2 r1
- cmp/hs r1,r8
+ mov #0x20, $r1
+ extu.b $r1, $r1
+ shll2 $r1
+ cmp/hs $r1, $r8
bt debug
#endif
- ! STI
- mov.l 2f,r1
- stc sr,r2
- and r1,r2
- ldc r2,sr
!
- mov.l __n_sys,r1
- cmp/hs r1,r0
- bt/s badsys
- mov r0,r2
+ mov $r0, $r2
+ RESTORE_FLAGS()
+9: mov.l __n_sys, $r1
+ cmp/hs $r1, $r2
+ bt badsys
!
- stc ksp,r1 !
- mov.l __tsk_flags,r0 !
- add r0,r1 !
- mov.l @r1,r0 ! Is it trace?
- tst #PF_TRACESYS,r0
+ stc $ksp, $r1
+ mov.l __tsk_flags, $r0
+ add $r0, $r1 !
+ mov.l @$r1, $r0 ! Is it trace?
+ tst #PF_TRACESYS, $r0
bt 5f
! Trace system call
- mov #-ENOSYS,r1
- mov.l r1,@(R0,r15)
- mov.l 3f,r1
- jsr @r1
+ mov #-ENOSYS, $r1
+ mov.l $r1, @(R0,$r15)
+ mov.l 3f, $r1
+ jsr @$r1
nop
- mova 4f,r0
+ mova 3f, $r0
bra 6f
- lds r0,pr
- !
-5: mova ret,r0 ! normal case
- lds r0,pr
- ! Build the stack frame if TRA > 0
+ lds $r0, $pr
!
-6: mov r2,r3
- mov r8,r2
- cmp/pl r8
- bf 9f
- mov.l @(SP,r15),r0 ! get original stack
-7: add #-4,r8
-8: mov.l @(r0,r8),r1 ! May cause address error exception..
- mov.l r1,@-r15
- cmp/pl r8
+5: mova syscall_ret, $r0
+ lds $r0, $pr
+ ! Build the stack frame if TRA > 0
+6: mov $r2, $r3
+ mov $r8, $r2
+ cmp/pl $r8
+ bf 0f
+ mov #SP, $r0
+ mov.l @($r0,$r15), $r0 ! get original stack
+7: add #-4, $r8
+4: mov.l @($r0,$r8), $r1 ! May cause address error exception..
+ mov.l $r1, @-$r15
+ cmp/pl $r8
bt 7b
!
-9: mov r3,r0
- shll2 r0 ! x4
- mov.l __sct,r1
- add r1,r0
- mov.l @r0,r1
- jmp @r1
- mov r2,r8
-
+0: mov $r3, $r0
+ shll2 $r0 ! x4
+ mov.l __sct, $r1
+ add $r1, $r0
+ mov.l @$r0, $r1
+ jmp @$r1
+ mov $r2, $r8
! In case of trace
.balign 4
-4: add r8,r15 ! pop off the arguments
- mov.l r0,@(R0,r15) ! save the return value
- mov.l 3f,r1
- mova SYMBOL_NAME(ret_from_syscall),r0
- jmp @r1
- lds r0,pr
+3: add $r8, $r15 ! pop off the arguments
+ mov.l $r0, @(R0,$r15) ! save the return value
+ mov.l 2f, $r1
+ mova SYMBOL_NAME(ret_from_syscall), $r0
+ jmp @$r1
+ lds $r0, $pr
.balign 4
-3: .long SYMBOL_NAME(syscall_trace)
-2: .long 0xefffffff ! BL=0
1: .long TRA
+2: .long SYMBOL_NAME(syscall_trace)
__n_sys: .long NR_syscalls
__sct: .long SYMBOL_NAME(sys_call_table)
__tsk_flags: .long flags-8192 ! offset from stackbase to tsk->flags
.section .fixup,"ax"
fixup_syscall_argerr:
rts
- mov.l 1f,r0
+ mov.l 1f, $r0
1: .long -22 ! -EINVAL
.previous
.section __ex_table, "a"
.balign 4
- .long 8b,fixup_syscall_argerr
+ .long 4b,fixup_syscall_argerr
.previous
+ .balign 4
reschedule:
- mova SYMBOL_NAME(ret_from_syscall),r0
- mov.l 1f,r1
- jmp @r1
- lds r0,pr
+ mova SYMBOL_NAME(ret_from_syscall), $r0
+ mov.l 1f, $r1
+ jmp @$r1
+ lds $r0, $pr
.balign 4
1: .long SYMBOL_NAME(schedule)
ENTRY(ret_from_irq)
- mov.l @(SR,r15),r0 ! get status register
- shll r0
- shll r0 ! kernel space?
+ mov.l @(SR,$r15), $r0 ! get status register
+ shll $r0
+ shll $r0 ! kernel space?
bt restore_all ! Yes, it's from kernel, go back soon
- ! STI
- mov.l 1f, $r1
- stc $sr, $r2
- and $r1, $r2
- ldc $r2, $sr
!
- bra ret_with_reschedule
+ RESTORE_FLAGS()
+9: bra ret_with_reschedule
nop
ENTRY(ret_from_exception)
- mov.l @(SR,r15),r0 ! get status register
- shll r0
- shll r0 ! kernel space?
+ mov.l @(SR,$r15), $r0 ! get status register
+ shll $r0
+ shll $r0 ! kernel space?
bt restore_all ! Yes, it's from kernel, go back soon
- ! STI
- mov.l 1f, $r1
- stc $sr, $r2
- and $r1, $r2
- ldc $r2, $sr
!
- bra ret_from_syscall
+ RESTORE_FLAGS()
+9: bra ret_from_syscall
nop
.balign 4
-1: .long 0xefffffff ! BL=0
+__INV_IMASK:
+ .long 0xffffff0f ! ~(IMASK)
.balign 4
-ret: add r8,r15 ! pop off the arguments
- mov.l r0,@(R0,r15) ! save the return value
+syscall_ret:
+ add $r8, $r15 ! pop off the arguments
+ mov.l $r0, @(R0,$r15) ! save the return value
/* fall through */
ENTRY(ret_from_syscall)
- mov.l __softirq_state,r0
- mov.l @r0,r1
- mov.l @(4,r0),r2
- tst r2,r1
+ mov.l __softirq_state, $r0
+ mov.l @$r0, $r1
+ mov.l @(4,$r0), $r2
+ tst $r2, $r1
bt ret_with_reschedule
handle_softirq:
- mov.l __do_softirq,r0
- jsr @r0
+ mov.l __do_softirq, $r0
+ jsr @$r0
nop
ret_with_reschedule:
- stc ksp,r1
- mov.l __minus8192,r0
- add r0,r1
- mov.l @(need_resched,r1),r0
- tst #0xff,r0
+ stc $ksp, $r1
+ mov.l __minus8192, $r0
+ add $r0, $r1
+ mov.l @(need_resched,$r1), $r0
+ tst #0xff, $r0
bf reschedule
- mov.l @(sigpending,r1),r0
- tst #0xff,r0
+ mov.l @(sigpending,$r1), $r0
+ tst #0xff, $r0
bt restore_all
signal_return:
- mov r15,r4
- mov #0,r5
- mov.l __do_signal,r1
- mova restore_all,r0
- jmp @r1
- lds r0,pr
+ mov $r15, $r4
+ mov #0, $r5
+ mov.l __do_signal, $r1
+ mova restore_all, $r0
+ jmp @$r1
+ lds $r0, $pr
.balign 4
__do_signal:
.long SYMBOL_NAME(do_signal)
jsr @$r1
stc $sr, $r4
#endif
- add #4,r15 ! Skip syscall number
- mov.l @r15+,r11 ! Got SSR into R11
+ add #4, $r15 ! Skip syscall number
+ mov.l @$r15+, $r11 ! Got SSR into R11
#if defined(__SH4__)
mov $r11, $r12
#endif
!
- mov.l 1f,r1
- stc sr,r0
- and r1,r0 ! Get IMASK+FD
- mov.l 2f,r1
- and r1,r11
- or r0,r11 ! Inherit the IMASK+FD value of SR
+ mov.l 1f, $r1
+ stc $sr, $r0
+ and $r1, $r0 ! Get FD
+ mov.l 2f, $r1
+ and $r1, $r11
+ or $r0, $r11 ! Inherit the FD value of SR
+ stc $r5_bank, $r0
+ or $r0, $r11 ! Inherit the IMASK value
!
- mov.l @r15+,r10 ! original stack
- mov.l @r15+,r0
- mov.l @r15+,r1
- mov.l @r15+,r2
- mov.l @r15+,r3
- mov.l @r15+,r4
- mov.l @r15+,r5
- mov.l @r15+,r6
- mov.l @r15+,r7
- stc sr,r14
- mov.l __blrb_flags,r9 ! BL =1, RB=1
- or r9,r14
- ldc r14,sr ! here, change the register bank
- mov r10,k0
- mov r11,k1
+ mov.l @$r15+, $r0
+ mov.l @$r15+, $r1
+ mov.l @$r15+, $r2
+ mov.l @$r15+, $r3
+ mov.l @$r15+, $r4
+ mov.l @$r15+, $r5
+ mov.l @$r15+, $r6
+ mov.l @$r15+, $r7
+ stc $sr, $r14
+ mov.l __blrb_flags, $r9 ! BL =1, RB=1
+ or $r9, $r14
+ ldc $r14, $sr ! here, change the register bank
+ mov $r11, $k1
#if defined(__SH4__)
mov $r12, $k2
#endif
- mov.l @r15+,r8
- mov.l @r15+,r9
- mov.l @r15+,r10
- mov.l @r15+,r11
- mov.l @r15+,r12
- mov.l @r15+,r13
- mov.l @r15+,r14
- ldc.l @r15+,gbr
- lds.l @r15+,mach
- lds.l @r15+,macl
- lds.l @r15+,pr
- ldc.l @r15+,spc
- ldc k1,ssr
+ mov.l @$r15+, $r8
+ mov.l @$r15+, $r9
+ mov.l @$r15+, $r10
+ mov.l @$r15+, $r11
+ mov.l @$r15+, $r12
+ mov.l @$r15+, $r13
+ mov.l @$r15+, $r14
+ mov.l @$r15+, $k0 ! original stack
+ ldc.l @$r15+, $gbr
+ lds.l @$r15+, $mach
+ lds.l @$r15+, $macl
+ lds.l @$r15+, $pr
+ ldc.l @$r15+, $spc
+ ldc $k1, $ssr
#if defined(__SH4__)
shll $k1
shll $k1
bf 9f ! user mode
/* Kernel to kernel transition */
- mov.l 3f, $k1
+ mov.l 1f, $k1
tst $k1, $k2
bf 9f ! it hadn't FPU
! Kernel to kernel and FPU was used
lds.l @$r15+, $fpul
9:
#endif
- mov k0,r15
+ mov $k0, $r15
rte
nop
__PF_USEDFPU:
.long PF_USEDFPU
#endif
-1: .long 0x000080f0 ! IMASK+FD
+1: .long 0x00008000 ! FD
2: .long 0xffff7f0f ! ~(IMASK+FD)
-3: .long 0x00008000 ! FD=1
! Exception Vector Base
!
!
.balign 256,0,256
general_exception:
- mov.l 1f,k2
- mov.l 2f,k3
+ mov.l 1f, $k2
+ mov.l 2f, $k3
bra handle_exception
- mov.l @k2,k2
+ mov.l @$k2, $k2
.balign 4
2: .long SYMBOL_NAME(ret_from_exception)
1: .long EXPEVT
!
.balign 1024,0,1024
tlb_miss:
- mov.l 1f,k2
- mov.l 4f,k3
+ mov.l 1f, $k2
+ mov.l 4f, $k3
bra handle_exception
- mov.l @k2,k2
+ mov.l @$k2, $k2
!
.balign 512,0,512
interrupt:
- mov.l 2f,k2
- mov.l 3f,k3
+ mov.l 2f, $k2
+ mov.l 3f, $k3
bra handle_exception
- mov.l @k2,k2
+ mov.l @$k2, $k2
.balign 4
1: .long EXPEVT
! Using k0, k1 for scratch registers (r0_bank1, r1_bank),
! save all registers onto stack.
!
- stc ssr,k0 ! from kernel space?
- shll k0 ! Check MD bit (bit30)
- shll k0
+ stc $ssr, $k0 ! from kernel space?
+ shll $k0 ! Check MD bit (bit30)
+ shll $k0
#if defined(__SH4__)
bf/s 8f ! it's from user to kernel transition
mov $r15, $k0 ! save original stack to k0
mov.l 2f, $k1
stc $ssr, $k0
tst $k1, $k0
+ mov.l 4f, $k1
bf/s 9f ! FPU is not used
mov $r15, $k0 ! save original stack to k0
! FPU is used, save FPU
fmov.s $fr1, @-$r15
fmov.s $fr0, @-$r15
bra 9f
- mov #0, $k1
+ mov.l 3f, $k1
#else
+ mov.l 3f, $k1
bt/s 9f ! it's from kernel to kernel transition
- mov r15,k0 ! save original stack to k0 anyway
+ mov $r15, $k0 ! save original stack to k0 anyway
#endif
8: /* User space to kernel */
- mov kernel_sp, $r15 ! change to kernel stack
-#if defined(__SH4__)
- mov.l 2f, $k1 ! let kernel release FPU
-#endif
-9: stc.l spc,@-r15
- sts.l pr,@-r15
+ mov $kernel_sp, $r15 ! change to kernel stack
+ mov.l 4f, $k1 ! let kernel release FPU
+9: stc.l $spc, @-$r15
+ sts.l $pr, @-$r15
!
- lds k3,pr ! Set the return address to pr
+ lds $k3, $pr ! Set the return address to pr
!
- sts.l macl,@-r15
- sts.l mach,@-r15
- stc.l gbr,@-r15
- mov.l r14,@-r15
+ sts.l $macl, @-$r15
+ sts.l $mach, @-$r15
+ stc.l $gbr, @-$r15
+ mov.l $k0, @-$r15 ! save orignal stack
+ mov.l $r14, @-$r15
!
- stc sr,r14 ! Back to normal register bank, and
-#if defined(__SH4__)
- or $k1, $r14 ! may release FPU
-#endif
- mov.l 3f,k1
- and k1,r14 ! ...
- ldc r14,sr ! ...changed here.
+ stc $sr, $r14 ! Back to normal register bank, and
+ or $k1, $r14 ! Block all interrupts, may release FPU
+ mov.l 5f, $k1
+ and $k1, $r14 ! ...
+ ldc $r14, $sr ! ...changed here.
!
- mov.l r13,@-r15
- mov.l r12,@-r15
- mov.l r11,@-r15
- mov.l r10,@-r15
- mov.l r9,@-r15
- mov.l r8,@-r15
- mov.l r7,@-r15
- mov.l r6,@-r15
- mov.l r5,@-r15
- mov.l r4,@-r15
- mov.l r3,@-r15
- mov.l r2,@-r15
- mov.l r1,@-r15
- mov.l r0,@-r15
- stc.l r0_bank,@-r15 ! save orignal stack
- stc.l ssr,@-r15
- mov.l r0,@-r15 ! push r0 again (for syscall number)
+ mov.l $r13, @-$r15
+ mov.l $r12, @-$r15
+ mov.l $r11, @-$r15
+ mov.l $r10, @-$r15
+ mov.l $r9, @-$r15
+ mov.l $r8, @-$r15
+ mov.l $r7, @-$r15
+ mov.l $r6, @-$r15
+ mov.l $r5, @-$r15
+ mov.l $r4, @-$r15
+ mov.l $r3, @-$r15
+ mov.l $r2, @-$r15
+ mov.l $r1, @-$r15
+ mov.l $r0, @-$r15
+ stc.l $ssr, @-$r15
+ mov.l $r0, @-$r15 ! push $r0 again (for syscall number)
! Then, dispatch to the handler, according to the excepiton code.
- stc k_ex_code,r1
- shlr2 r1
- shlr r1
- mov.l 1f,r0
- add r1,r0
- mov.l @r0,r0
- jmp @r0
- mov.l @r15,r0 ! recovering r0..
+ stc $k_ex_code, $r1
+ shlr2 $r1
+ shlr $r1
+ mov.l 1f, $r0
+ add $r1, $r0
+ mov.l @$r0, $r0
+ jmp @$r0
+ mov.l @$r15, $r0 ! recovering $r0..
.balign 4
1: .long SYMBOL_NAME(exception_handling_table)
2: .long 0x00008000 ! FD=1
-3: .long 0xdfffffff ! RB=0, leave BL=1
+3: .long 0x000000f0 ! FD=0, IMASK=15
+4: .long 0x000080f0 ! FD=1, IMASK=15
+5: .long 0xcfffffff ! RB=0, BL=0
none:
rts
ENTRY(nmi_slot)
.long none ! Not implemented yet
ENTRY(user_break_point_trap)
- .long error ! Not implemented yet
+ .long break_point_trap
ENTRY(interrupt_table)
! external hardware
.long SYMBOL_NAME(do_IRQ) ! 0000
.long SYMBOL_NAME(sys_setfsuid) /* 215 */
.long SYMBOL_NAME(sys_setfsgid)
.long SYMBOL_NAME(sys_pivot_root)
+ .long SYMBOL_NAME(sys_mincore)
+ .long SYMBOL_NAME(sys_madvise)
/*
* NOTE!! This doesn't have to be exact - we just have
* entries. Don't panic if you notice that this hasn't
* been shrunk every time we add a new system call.
*/
- .rept NR_syscalls-217
+ .rept NR_syscalls-219
.long SYMBOL_NAME(sys_ni_syscall)
.endr
-/* $Id: fpu.c,v 1.27 2000/03/05 01:48:34 gniibe Exp $
+/* $Id: fpu.c,v 1.29 2000/03/22 13:42:10 gniibe Exp $
*
* linux/arch/sh/kernel/fpu.c
*
* has the property that no matter wether considered as single or as
* double precission represents signaling NANS.
*/
-/* Double presision, NANS as NANS, rounding to nearest, no exceptions */
-#define FPU_DEFAULT 0x00080000
void fpu_init(void)
{
"fsts $fpul, $fr15\n\t"
"frchg"
: /* no output */
- : "r" (0), "r" (FPU_DEFAULT));
+ : "r" (0), "r" (FPSCR_INIT));
}
asmlinkage void
-/* $Id: head.S,v 1.16 2000/03/02 00:01:15 gniibe Exp $
+/* $Id: head.S,v 1.17 2000/03/06 12:44:24 gniibe Exp $
*
* arch/sh/kernel/head.S
*
*/
ENTRY(_stext)
! Initialize Status Register
- mov.l 1f, $r0 ! MD=1, RB=0, BL=1
+ mov.l 1f, $r0 ! MD=1, RB=0, BL=0, IMASK=0xF
ldc $r0, $sr
+ ! Initialize global interrupt mask
+ mov #0, $r0
+ ldc $r0, $r5_bank
!
mov.l 2f, $r0
mov $r0, $r15 ! Set initial r15 (stack pointer)
nop
.balign 4
-1: .long 0x50000000 ! MD=1, RB=0, BL=1, FD=0
+1: .long 0x400000F0 ! MD=1, RB=0, BL=0, FD=0, IMASK=0xF
2: .long SYMBOL_NAME(stack)
3: .long SYMBOL_NAME(__bss_start)
4: .long SYMBOL_NAME(_end)
-/* $Id: irq.c,v 1.11 2000/02/29 11:03:40 gniibe Exp $
+/* $Id: irq.c,v 1.12 2000/03/06 14:07:50 gniibe Exp $
*
* linux/arch/sh/kernel/irq.c
*
regs.syscall_nr = -1; /* It's not system call */
/* Get IRQ number */
- asm volatile("stc r2_bank,%0\n\t"
+ asm volatile("stc $r2_bank, %0\n\t"
"shlr2 %0\n\t"
"shlr2 %0\n\t"
"shlr %0\n\t"
- "add #-16,%0\n\t"
+ "add #-16, %0\n\t"
:"=z" (irq));
kstat.irqs[cpu][irq]++;
-/* $Id: irq_imask.c,v 1.2 2000/02/11 04:57:40 gniibe Exp $
+/* $Id: irq_imask.c,v 1.6 2000/03/06 14:11:32 gniibe Exp $
*
* linux/arch/sh/kernel/irq_imask.c
*
- * Copyright (C) 1999 Niibe Yutaka
+ * Copyright (C) 1999, 2000 Niibe Yutaka
*
* Simple interrupt handling using IMASK of SR register.
*
end_imask_irq
};
-void disable_imask_irq(unsigned int irq)
+void static inline set_interrupt_registers(int ip)
{
unsigned long __dummy;
+ asm volatile("ldc %2, $r5_bank\n\t"
+ "stc $sr, %0\n\t"
+ "and #0xf0, %0\n\t"
+ "shlr8 %0\n\t"
+ "cmp/eq #0x0f, %0\n\t"
+ "bt 1f ! CLI-ed\n\t"
+ "stc $sr, %0\n\t"
+ "and %1, %0\n\t"
+ "or %2, %0\n\t"
+ "ldc %0, $sr\n"
+ "1:"
+ : "=&z" (__dummy)
+ : "r" (~0xf0), "r" (ip << 4));
+}
+
+void disable_imask_irq(unsigned int irq)
+{
clear_bit(irq, &imask_mask);
if (interrupt_priority < IMASK_PRIORITY - irq)
interrupt_priority = IMASK_PRIORITY - irq;
- asm volatile("stc sr,%0\n\t"
- "and %1,%0\n\t"
- "or %2,%0\n\t"
- "ldc %0,sr"
- : "=&r" (__dummy)
- : "r" (0xffffff0f), "r" (interrupt_priority << 4));
+ set_interrupt_registers(interrupt_priority);
}
static void enable_imask_irq(unsigned int irq)
{
- unsigned long __dummy;
-
set_bit(irq, &imask_mask);
interrupt_priority = IMASK_PRIORITY - ffz(imask_mask);
- asm volatile("stc sr,%0\n\t"
- "and %1,%0\n\t"
- "or %2,%0\n\t"
- "ldc %0,sr"
- : "=&r" (__dummy)
- : "r" (0xffffff0f), "r" (interrupt_priority << 4));
+ set_interrupt_registers(interrupt_priority);
}
static void mask_and_ack_imask(unsigned int irq)
-/* $Id: process.c,v 1.28 2000/03/05 02:16:15 gniibe Exp $
+/* $Id: process.c,v 1.33 2000/03/25 00:06:15 gniibe Exp $
*
* linux/arch/sh/kernel/process.c
*
{
printk("\n");
printk("PC : %08lx SP : %08lx SR : %08lx TEA : %08lx\n",
- regs->pc, regs->sp, regs->sr, ctrl_inl(MMU_TEA));
+ regs->pc, regs->regs[15], regs->sr, ctrl_inl(MMU_TEA));
printk("R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n",
regs->regs[0],regs->regs[1],
regs->regs[2],regs->regs[3]);
struct task_struct *p, struct pt_regs *regs)
{
struct pt_regs *childregs;
+#if defined(__SH4__)
struct task_struct *tsk = current;
- childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long) p)) - 1;
- struct_cpy(childregs, regs);
-
-#if defined(__SH4__)
if (tsk != &init_task) {
unlazy_fpu(tsk);
- struct_cpy(&p->thread.fpu, ¤t->thread.fpu);
+ p->thread.fpu = current->thread.fpu;
p->used_math = tsk->used_math;
}
#endif
+ childregs = ((struct pt_regs *)(THREAD_SIZE + (unsigned long) p)) - 1;
+ *childregs = *regs;
+
if (user_mode(regs)) {
- childregs->sp = usp;
+ childregs->regs[15] = usp;
} else {
- childregs->sp = (unsigned long)p+2*PAGE_SIZE;
+ childregs->regs[15] = (unsigned long)p+2*PAGE_SIZE;
}
childregs->regs[0] = 0; /* Set return value for child */
childregs->sr |= SR_FD; /* Invalidate FPU flag */
dump->magic = CMAGIC;
dump->start_code = current->mm->start_code;
dump->start_data = current->mm->start_data;
- dump->start_stack = regs->sp & ~(PAGE_SIZE - 1);
+ dump->start_stack = regs->regs[15] & ~(PAGE_SIZE - 1);
dump->u_tsize = (current->mm->end_code - dump->start_code) >> PAGE_SHIFT;
dump->u_dsize = (current->mm->brk + (PAGE_SIZE-1) - dump->start_data) >> PAGE_SHIFT;
dump->u_ssize = (current->mm->start_stack - dump->start_stack +
unsigned long r6, unsigned long r7,
struct pt_regs regs)
{
- return do_fork(SIGCHLD, regs.sp, ®s);
+ return do_fork(SIGCHLD, regs.regs[15], ®s);
}
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
struct pt_regs regs)
{
if (!newsp)
- newsp = regs.sp;
+ newsp = regs.regs[15];
return do_fork(clone_flags, newsp, ®s);
}
unsigned long r6, unsigned long r7,
struct pt_regs regs)
{
- return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.sp, ®s);
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.regs[15], ®s);
}
/*
(init_task.flags&PF_USEDFPU)?'K':' ', (sr&SR_FD)?' ':'F');
restore_flags(flags);
}
+
+asmlinkage void break_point_trap(void)
+{
+ /* Clear traicng. */
+ ctrl_outw(0, UBC_BBRA);
+ ctrl_outw(0, UBC_BBRB);
+
+ force_sig(SIGTRAP, current);
+}
-/*
- * Surely this doesn't work... (we need to design ptrace for SupreH)
+/* $Id: ptrace.c,v 1.4 2000/03/22 13:59:01 gniibe Exp $
+ *
* linux/arch/sh/kernel/ptrace.c
+ *
+ * Original x86 implementation:
+ * By Ross Biro 1/23/92
+ * edited by Linus Torvalds
+ *
+ * SuperH version: Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
+ *
*/
#include <linux/kernel.h>
#include <linux/ptrace.h>
#include <linux/user.h>
+#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/processor.h>
+#include <asm/mmu_context.h>
/*
* does not yet catch signals sent when the child dies.
* in exit.c or in signal.c.
*/
-/* determines which flags the user has access to. */
-/* 1 = access 0 = no access */
-#define FLAG_MASK 0x00044dd5
-
-/* set's the trap flag. */
-#define TRAP_FLAG 0x100
-
/*
- * Offset of eflags on child stack..
+ * This routine will get a word off of the process kernel stack.
*/
-#define EFL_OFFSET ((EFL-2)*4-sizeof(struct pt_regs))
-
-/*
- * this routine will get a word off of the processes privileged stack.
- * the offset is how far from the base addr as stored in the TSS.
- * this routine assumes that all the privileged stacks are in our
- * data space.
- */
static inline int get_stack_long(struct task_struct *task, int offset)
{
unsigned char *stack;
- stack = (unsigned char *)task->thread.sp;
+ stack = (unsigned char *)task + THREAD_SIZE - sizeof(struct pt_regs);
stack += offset;
return (*((int *)stack));
}
/*
- * this routine will put a word on the processes privileged stack.
- * the offset is how far from the base addr as stored in the TSS.
- * this routine assumes that all the privileged stacks are in our
- * data space.
+ * This routine will put a word on the process kernel stack.
*/
static inline int put_stack_long(struct task_struct *task, int offset,
- unsigned long data)
+ unsigned long data)
{
- unsigned char * stack;
+ unsigned char *stack;
- stack = (unsigned char *) task->thread.sp;
+ stack = (unsigned char *)task + THREAD_SIZE - sizeof(struct pt_regs);
stack += offset;
*(unsigned long *) stack = data;
return 0;
}
-static int putreg(struct task_struct *child,
- unsigned long regno, unsigned long value)
+static void
+compute_next_pc(struct pt_regs *regs, unsigned short inst,
+ unsigned long *pc1, unsigned long *pc2)
{
-#if 0
- switch (regno >> 2) {
- case ORIG_EAX:
- return -EIO;
- case FS:
- if (value && (value & 3) != 3)
- return -EIO;
- child->thread.fs = value;
- return 0;
- case GS:
- if (value && (value & 3) != 3)
- return -EIO;
- child->thread.gs = value;
- return 0;
- case DS:
- case ES:
- if (value && (value & 3) != 3)
- return -EIO;
- value &= 0xffff;
- break;
- case SS:
- case CS:
- if ((value & 3) != 3)
- return -EIO;
- value &= 0xffff;
- break;
- case EFL:
- value &= FLAG_MASK;
- value |= get_stack_long(child, EFL_OFFSET) & ~FLAG_MASK;
+ int nib[4]
+ = { (inst >> 12) & 0xf,
+ (inst >> 8) & 0xf,
+ (inst >> 4) & 0xf,
+ inst & 0xf};
+
+ /* bra & bsr */
+ if (nib[0] == 0xa || nib[0] == 0xb) {
+ *pc1 = regs->pc + 4 + ((short) ((inst & 0xfff) << 4) >> 3);
+ *pc2 = (unsigned long) -1;
+ return;
}
- if (regno > GS*4)
- regno -= 2*4;
- put_stack_long(child, regno - sizeof(struct pt_regs), value);
-#endif
- return 0;
+
+ /* bt & bf */
+ if (nib[0] == 0x8 && (nib[1] == 0x9 || nib[1] == 0xb)) {
+ *pc1 = regs->pc + 4 + ((char) (inst & 0xff) << 1);
+ *pc2 = regs->pc + 2;
+ return;
+ }
+
+ /* bt/s & bf/s */
+ if (nib[0] == 0x8 && (nib[1] == 0xd || nib[1] == 0xf)) {
+ *pc1 = regs->pc + 4 + ((char) (inst & 0xff) << 1);
+ *pc2 = regs->pc + 4;
+ return;
+ }
+
+ /* jmp & jsr */
+ if (nib[0] == 0x4 && nib[3] == 0xb
+ && (nib[2] == 0x0 || nib[2] == 0x2)) {
+ *pc1 = regs->regs[nib[1]];
+ *pc2 = (unsigned long) -1;
+ return;
+ }
+
+ /* braf & bsrf */
+ if (nib[0] == 0x0 && nib[3] == 0x3
+ && (nib[2] == 0x0 || nib[2] == 0x2)) {
+ *pc1 = regs->pc + 4 + regs->regs[nib[1]];
+ *pc2 = (unsigned long) -1;
+ return;
+ }
+
+ if (inst == 0x000b) {
+ *pc1 = regs->pr;
+ *pc2 = (unsigned long) -1;
+ return;
+ }
+
+ *pc1 = regs->pc + 2;
+ *pc2 = (unsigned long) -1;
+ return;
}
-static unsigned long getreg(struct task_struct *child,
- unsigned long regno)
+/* Tracing by user break controller. */
+static void
+ubc_set_tracing(int asid, unsigned long nextpc1, unsigned nextpc2)
{
- unsigned long retval = ~0UL;
-
-#if 0
- switch (regno >> 2) {
- case FS:
- retval = child->thread.fs;
- break;
- case GS:
- retval = child->thread.gs;
- break;
- case DS:
- case ES:
- case SS:
- case CS:
- retval = 0xffff;
- /* fall through */
- default:
- if (regno > GS*4)
- regno -= 2*4;
- regno = regno - sizeof(struct pt_regs);
- retval &= get_stack_long(child, regno);
+ ctrl_outl(nextpc1, UBC_BARA);
+ ctrl_outb(asid, UBC_BASRA);
+ ctrl_outb(BAMR_12, UBC_BAMRA);
+ ctrl_outw(BBR_INST | BBR_READ, UBC_BBRA);
+
+ if (nextpc2 != (unsigned long) -1) {
+ ctrl_outl(nextpc2, UBC_BARB);
+ ctrl_outb(asid, UBC_BASRB);
+ ctrl_outb(BAMR_12, UBC_BAMRB);
+ ctrl_outw(BBR_INST | BBR_READ, UBC_BBRB);
}
-#endif
- return retval;
+ ctrl_outw(BRCR_PCBA | BRCR_PCBB, UBC_BRCR);
}
asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
{
- struct task_struct *child;
+ struct task_struct *child, *tsk = current;
struct user * dummy = NULL;
unsigned long flags;
- int i, ret;
+ int ret;
lock_kernel();
ret = -EPERM;
if (pid == 1) /* you may not mess with init */
goto out;
if (request == PTRACE_ATTACH) {
- if (child == current)
+ if (child == tsk)
goto out;
if ((!child->dumpable ||
- (current->uid != child->euid) ||
- (current->uid != child->suid) ||
- (current->uid != child->uid) ||
- (current->gid != child->egid) ||
- (current->gid != child->sgid) ||
- (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
- (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+ (tsk->uid != child->euid) ||
+ (tsk->uid != child->suid) ||
+ (tsk->uid != child->uid) ||
+ (tsk->gid != child->egid) ||
+ (tsk->gid != child->sgid) ||
+ (!cap_issubset(child->cap_permitted, tsk->cap_permitted)) ||
+ (tsk->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
goto out;
/* the same process cannot be attached many times */
if (child->flags & PF_PTRACED)
child->flags |= PF_PTRACED;
write_lock_irqsave(&tasklist_lock, flags);
- if (child->p_pptr != current) {
+ if (child->p_pptr != tsk) {
REMOVE_LINKS(child);
- child->p_pptr = current;
+ child->p_pptr = tsk;
SET_LINKS(child);
}
write_unlock_irqrestore(&tasklist_lock, flags);
if (request != PTRACE_KILL)
goto out;
}
- if (child->p_pptr != current)
+ if (child->p_pptr != tsk)
goto out;
switch (request) {
/* when I and D space are separate, these will need to be fixed. */
- case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA: {
- unsigned long tmp;
- int copied;
-
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- ret = -EIO;
- if (copied != sizeof(tmp))
- goto out;
- ret = put_user(tmp,(unsigned long *) data);
- goto out;
- }
+ case PTRACE_PEEKTEXT: /* read word at location addr. */
+ case PTRACE_PEEKDATA: {
+ unsigned long tmp;
+ int copied;
+
+ copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+ ret = -EIO;
+ if (copied != sizeof(tmp))
+ break;
+ ret = put_user(tmp,(unsigned long *) data);
+ break;
+ }
/* read the word at location addr in the USER area. */
- case PTRACE_PEEKUSR: {
- unsigned long tmp;
-
- ret = -EIO;
- if ((addr & 3) || addr < 0 ||
- addr > sizeof(struct user) - 3)
- goto out;
-
- tmp = 0; /* Default return condition */
- if(addr < 17*sizeof(long))
- tmp = getreg(child, addr);
-#if 0
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
- addr -= (long) &dummy->u_debugreg[0];
- addr = addr >> 2;
- tmp = child->thread.debugreg[addr];
- };
-#endif
- ret = put_user(tmp,(unsigned long *) data);
- goto out;
- }
+ case PTRACE_PEEKUSR: {
+ unsigned long tmp;
- /* when I and D space are separate, this will have to be fixed. */
- case PTRACE_POKETEXT: /* write the word at location addr. */
- case PTRACE_POKEDATA:
- ret = 0;
- if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
- goto out;
- ret = -EIO;
- goto out;
+ ret = -EIO;
+ if ((addr & 3) || addr < 0 ||
+ addr > sizeof(struct user) - 3)
+ break;
- case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
- ret = -EIO;
- if ((addr & 3) || addr < 0 ||
- addr > sizeof(struct user) - 3)
- goto out;
-
- if (addr < 17*sizeof(long)) {
- ret = putreg(child, addr, data);
- goto out;
- }
-
- /* We need to be very careful here. We implicitly
- want to modify a portion of the task_struct, and we
- have to be selective about what portions we allow someone
- to modify. */
-#if 0
- if(addr >= (long) &dummy->u_debugreg[0] &&
- addr <= (long) &dummy->u_debugreg[7]){
-
- if(addr == (long) &dummy->u_debugreg[4]) return -EIO;
- if(addr == (long) &dummy->u_debugreg[5]) return -EIO;
- if(addr < (long) &dummy->u_debugreg[4] &&
- ((unsigned long) data) >= TASK_SIZE-3) return -EIO;
-
- ret = -EIO;
- if(addr == (long) &dummy->u_debugreg[7]) {
- data &= ~DR_CONTROL_RESERVED;
- for(i=0; i<4; i++)
- if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
- goto out;
- };
-
- addr -= (long) &dummy->u_debugreg;
- addr = addr >> 2;
- child->thread.debugreg[addr] = data;
- ret = 0;
- goto out;
- };
-#endif
- ret = -EIO;
- goto out;
-
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: { /* restart after signal. */
- long tmp;
-
- ret = -EIO;
- if ((unsigned long) data > _NSIG)
- goto out;
- if (request == PTRACE_SYSCALL)
- child->flags |= PF_TRACESYS;
- else
- child->flags &= ~PF_TRACESYS;
- child->exit_code = data;
- /* make sure the single step bit is not set. */
-#if 0
- tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
- put_stack_long(child, EFL_OFFSET,tmp);
-#endif
- wake_up_process(child);
+ if (addr < sizeof(struct pt_regs))
+ tmp = get_stack_long(child, addr);
+ else if (addr >= (long) &dummy->fpu &&
+ addr < (long) &dummy->u_fpvalid) {
+ if (!child->used_math) {
+ if (addr == (long)&dummy->fpu.fpscr)
+ tmp = FPSCR_INIT;
+ else
+ tmp = 0;
+ } else
+ tmp = ((long *)&child->thread.fpu)
+ [(addr - (long)&dummy->fpu) >> 2];
+ } else if (addr == (long) &dummy->u_fpvalid)
+ tmp = child->used_math;
+ else
+ tmp = 0;
+ ret = put_user(tmp, (unsigned long *)data);
+ break;
+ }
+
+ /* when I and D space are separate, this will have to be fixed. */
+ case PTRACE_POKETEXT: /* write the word at location addr. */
+ case PTRACE_POKEDATA:
+ ret = 0;
+ if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
+ break;
+ ret = -EIO;
+ break;
+
+ case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
+ ret = -EIO;
+ if ((addr & 3) || addr < 0 ||
+ addr > sizeof(struct user) - 3)
+ break;
+
+ if (addr < sizeof(struct pt_regs))
+ ret = put_stack_long(child, addr, data);
+ else if (addr >= (long) &dummy->fpu &&
+ addr < (long) &dummy->u_fpvalid) {
+ child->used_math = 1;
+ ((long *)&child->thread.fpu)
+ [(addr - (long)&dummy->fpu) >> 2] = data;
+ ret = 0;
+ } else if (addr == (long) &dummy->u_fpvalid) {
+ child->used_math = data?1:0;
ret = 0;
- goto out;
}
+ break;
+
+ case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
+ case PTRACE_CONT: { /* restart after signal. */
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ if (request == PTRACE_SYSCALL)
+ child->flags |= PF_TRACESYS;
+ else
+ child->flags &= ~PF_TRACESYS;
+ child->exit_code = data;
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
/*
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
- case PTRACE_KILL: {
- long tmp;
+ case PTRACE_KILL: {
+ ret = 0;
+ if (child->state == TASK_ZOMBIE) /* already dead */
+ break;
+ child->exit_code = SIGKILL;
+ wake_up_process(child);
+ break;
+ }
- ret = 0;
- if (child->state == TASK_ZOMBIE) /* already dead */
- goto out;
- child->exit_code = SIGKILL;
- /* make sure the single step bit is not set. */
-#if 0
- tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
- put_stack_long(child, EFL_OFFSET, tmp);
-#endif
- wake_up_process(child);
- goto out;
- }
+ case PTRACE_SINGLESTEP: { /* set the trap flag. */
+ long tmp, pc;
+ struct pt_regs *dummy = NULL;
+ struct pt_regs *regs;
+ unsigned long nextpc1, nextpc2;
+ unsigned short insn;
- case PTRACE_SINGLESTEP: { /* set the trap flag. */
- long tmp;
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ child->flags &= ~PF_TRACESYS;
+ if ((child->flags & PF_DTRACE) == 0) {
+ /* Spurious delayed TF traps may occur */
+ child->flags |= PF_DTRACE;
+ }
- ret = -EIO;
- if ((unsigned long) data > _NSIG)
- goto out;
- child->flags &= ~PF_TRACESYS;
- if ((child->flags & PF_DTRACE) == 0) {
- /* Spurious delayed TF traps may occur */
- child->flags |= PF_DTRACE;
- }
-#if 0
- tmp = get_stack_long(child, EFL_OFFSET) | TRAP_FLAG;
- put_stack_long(child, EFL_OFFSET, tmp);
+ /* Compute next pc. */
+ pc = get_stack_long(child, (long)&dummy->pc);
+ regs = (struct pt_regs *)((unsigned long)child + THREAD_SIZE - sizeof(struct pt_regs));
+ if (access_process_vm(child, pc&~3, &tmp, sizeof(tmp), 1) != sizeof(data))
+ break;
+
+#ifdef __LITTLE_ENDIAN__
+ if (pc & 3)
+ insn = tmp >> 16;
+ else
+ insn = tmp & 0xffff;
+#else
+ if (pc & 3)
+ insn = tmp & 0xffff;
+ else
+ insn = tmp >> 16;
#endif
- child->exit_code = data;
- /* give it a chance to run. */
- wake_up_process(child);
- ret = 0;
- goto out;
- }
+ compute_next_pc (regs, insn, &nextpc1, &nextpc2);
- case PTRACE_DETACH: { /* detach a process that was attached. */
- long tmp;
+ if (nextpc1 & 0x80000000)
+ break;
+ if (nextpc2 != (unsigned long) -1 && (nextpc2 & 0x80000000))
+ break;
- ret = -EIO;
- if ((unsigned long) data > _NSIG)
- goto out;
- child->flags &= ~(PF_PTRACED|PF_TRACESYS);
- child->exit_code = data;
- write_lock_irqsave(&tasklist_lock, flags);
- REMOVE_LINKS(child);
- child->p_pptr = child->p_opptr;
- SET_LINKS(child);
- write_unlock_irqrestore(&tasklist_lock, flags);
- /* make sure the single step bit is not set. */
-#if 0
- tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
- put_stack_long(child, EFL_OFFSET, tmp);
-#endif
- wake_up_process(child);
- ret = 0;
- goto out;
- }
-#if 0
- case PTRACE_GETREGS: { /* Get all gp regs from the child. */
- if (!access_ok(VERIFY_WRITE, (unsigned *)data,
- 17*sizeof(long)))
- {
- ret = -EIO;
- goto out;
- }
- for ( i = 0; i < 17*sizeof(long); i += sizeof(long) )
- {
- __put_user(getreg(child, i),(unsigned long *) data);
- data += sizeof(long);
- }
- ret = 0;
- goto out;
- };
-
- case PTRACE_SETREGS: { /* Set all gp regs in the child. */
- unsigned long tmp;
- if (!access_ok(VERIFY_READ, (unsigned *)data,
- 17*sizeof(long)))
- {
- ret = -EIO;
- goto out;
- }
- for ( i = 0; i < 17*sizeof(long); i += sizeof(long) )
- {
- __get_user(tmp, (unsigned long *) data);
- putreg(child, i, tmp);
- data += sizeof(long);
- }
- ret = 0;
- goto out;
- };
-
- case PTRACE_GETFPREGS: { /* Get the child FPU state. */
- if (!access_ok(VERIFY_WRITE, (unsigned *)data,
- sizeof(struct user_i387_struct)))
- {
- ret = -EIO;
- goto out;
- }
- ret = 0;
- if ( !child->used_math ) {
- /* Simulate an empty FPU. */
- child->thread.i387.hard.cwd = 0xffff037f;
- child->thread.i387.hard.swd = 0xffff0000;
- child->thread.i387.hard.twd = 0xffffffff;
- }
- __copy_to_user((void *)data, &child->thread.i387.hard,
- sizeof(struct user_i387_struct));
- goto out;
- };
-
- case PTRACE_SETFPREGS: { /* Set the child FPU state. */
- if (!access_ok(VERIFY_READ, (unsigned *)data,
- sizeof(struct user_i387_struct)))
- {
- ret = -EIO;
- goto out;
- }
- child->used_math = 1;
- __copy_from_user(&child->thread.i387.hard, (void *)data,
- sizeof(struct user_i387_struct));
- ret = 0;
- goto out;
- };
-#endif
- default:
- ret = -EIO;
- goto out;
+ ubc_set_tracing(child->mm->context & MMU_CONTEXT_ASID_MASK,
+ nextpc1, nextpc2);
+
+ child->exit_code = data;
+ /* give it a chance to run. */
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ case PTRACE_DETACH: { /* detach a process that was attached. */
+ ret = -EIO;
+ if ((unsigned long) data > _NSIG)
+ break;
+ child->flags &= ~(PF_PTRACED|PF_TRACESYS);
+ child->exit_code = data;
+ write_lock_irqsave(&tasklist_lock, flags);
+ REMOVE_LINKS(child);
+ child->p_pptr = child->p_opptr;
+ SET_LINKS(child);
+ write_unlock_irqrestore(&tasklist_lock, flags);
+ wake_up_process(child);
+ ret = 0;
+ break;
+ }
+
+ default:
+ ret = -EIO;
+ break;
}
out:
unlock_kernel();
asmlinkage void syscall_trace(void)
{
- if ((current->flags & (PF_PTRACED|PF_TRACESYS))
- != (PF_PTRACED|PF_TRACESYS))
+ struct task_struct *tsk = current;
+
+ if ((tsk->flags & (PF_PTRACED|PF_TRACESYS))
+ != (PF_PTRACED|PF_TRACESYS))
return;
- current->exit_code = SIGTRAP;
- current->state = TASK_STOPPED;
- notify_parent(current, SIGCHLD);
+ tsk->exit_code = SIGTRAP;
+ tsk->state = TASK_STOPPED;
+ notify_parent(tsk, SIGCHLD);
schedule();
/*
* this isn't the same as continuing with a signal, but it will do
* for normal use. strace only continues with a signal if the
* stopping signal is not SIGTRAP. -brl
*/
- if (current->exit_code) {
- send_sig(current->exit_code, current, 1);
- current->exit_code = 0;
+ if (tsk->exit_code) {
+ send_sig(tsk->exit_code, tsk, 1);
+ tsk->exit_code = 0;
}
}
-/* $Id: signal.c,v 1.16 2000/01/29 11:31:31 gniibe Exp gniibe $
+/* $Id: signal.c,v 1.21 2000/03/11 14:06:21 gniibe Exp $
*
* linux/arch/sh/kernel/signal.c
*
*
* 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
*
- * SuperH version: Copyright (C) 1999 Niibe Yutaka
+ * SuperH version: Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima
*
*/
unsigned long r6, unsigned long r7,
struct pt_regs regs)
{
- return do_sigaltstack(uss, uoss, regs.sp);
+ return do_sigaltstack(uss, uoss, regs.regs[15]);
}
struct sigframe
{
struct sigcontext sc;
- /* FPU data should come here: SH-3 has no FPU */
unsigned long extramask[_NSIG_WORDS-1];
char retcode[4];
};
void *puc;
struct siginfo info;
struct ucontext uc;
- /* FPU should come here: SH-3 has no FPU */
char retcode[4];
};
+#if defined(__SH4__)
+static inline int restore_sigcontext_fpu(struct sigcontext *sc)
+{
+ current->used_math = 1;
+ return __copy_from_user(&tsk->thread.fpu.hard, &sc->sc_fpregs[0],
+ sizeof(long)*(NUM_FPU_REGS*2+2));
+}
+
+static inline int save_sigcontext_fpu(struct sigcontext *sc)
+{
+ struct task_struct *tsk = current;
+
+ if (!tsk->used_math) {
+ sc->owend_fp = 0;
+ return 0;
+ }
+
+ sc->owend_fp = 1;
+
+ /* This will cause a "finit" to be triggered by the next
+ attempted FPU operation by the 'current' process.
+ */
+ tsk->used_math = 0;
+
+ unlazy_fpu(tsk);
+ return __copy_to_user(&sc->sc_fpregs[0], &tsk->thread.fpu.hard,
+ sizeof(long)*(NUM_FPU_REGS*2+2));
+}
+#endif
static int
restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *r0_p)
COPY(regs[8]); COPY(regs[9]);
COPY(regs[10]); COPY(regs[11]);
COPY(regs[12]); COPY(regs[13]);
- COPY(regs[14]); COPY(sp);
+ COPY(regs[14]); COPY(regs[15]);
COPY(gbr); COPY(mach);
COPY(macl); COPY(pr);
COPY(sr); COPY(pc);
#undef COPY
+#if defined(__SH4__)
+ {
+ int owned_fp;
+ struct task_struct *tsk = current;
+
+ regs->sr |= SR_FD; /* Release FPU */
+ clear_fpu(tsk);
+ current->used_math = 0;
+ __get_user (owned_fp, &context->sc_ownedfp);
+ if (owned_fp)
+ err |= restore_sigcontext_fpu(sc);
+ }
+#endif
+
regs->syscall_nr = -1; /* disable syscall checks */
err |= __get_user(*r0_p, &sc->sc_regs[0]);
-
return err;
}
unsigned long r6, unsigned long r7,
struct pt_regs regs)
{
- struct sigframe *frame = (struct sigframe *)regs.sp;
+ struct sigframe *frame = (struct sigframe *)regs.regs[15];
sigset_t set;
int r0;
unsigned long r6, unsigned long r7,
struct pt_regs regs)
{
- struct rt_sigframe *frame = (struct rt_sigframe *)regs.sp;
+ struct rt_sigframe *frame = (struct rt_sigframe *)regs.regs[15];
sigset_t set;
stack_t st;
int r0;
goto badframe;
/* It is more difficult to avoid calling this function than to
call it and ignore errors. */
- do_sigaltstack(&st, NULL, regs.sp);
+ do_sigaltstack(&st, NULL, regs.regs[15]);
return r0;
COPY(regs[8]); COPY(regs[9]);
COPY(regs[10]); COPY(regs[11]);
COPY(regs[12]); COPY(regs[13]);
- COPY(regs[14]); COPY(sp);
+ COPY(regs[14]); COPY(regs[15]);
COPY(gbr); COPY(mach);
COPY(macl); COPY(pr);
COPY(sr); COPY(pc);
#undef COPY
+#if defined(__SH4__)
+ err |= save_sigcontext_fpu(sc);
+#endif
+
/* non-iBCS2 extensions.. */
err |= __put_user(mask, &sc->oldmask);
int err = 0;
int signal;
- frame = get_sigframe(ka, regs->sp, sizeof(*frame));
+ frame = get_sigframe(ka, regs->regs[15], sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
goto give_sigsegv;
/* Set up registers for signal handler */
- regs->sp = (unsigned long) frame;
+ regs->regs[15] = (unsigned long) frame;
regs->regs[4] = signal; /* Arg for signal handler */
regs->pc = (unsigned long) ka->sa.sa_handler;
int err = 0;
int signal;
- frame = get_sigframe(ka, regs->sp, sizeof(*frame));
+ frame = get_sigframe(ka, regs->regs[15], sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto give_sigsegv;
err |= __put_user(0, &frame->uc.uc_link);
err |= __put_user((void *)current->sas_ss_sp,
&frame->uc.uc_stack.ss_sp);
- err |= __put_user(sas_ss_flags(regs->sp), &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(sas_ss_flags(regs->regs[15]),
+ &frame->uc.uc_stack.ss_flags);
err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
err |= setup_sigcontext(&frame->uc.uc_mcontext,
regs, set->sig[0]);
goto give_sigsegv;
/* Set up registers for signal handler */
- regs->sp = (unsigned long) frame;
+ regs->regs[15] = (unsigned long) frame;
regs->regs[4] = signal; /* Arg for signal handler */
regs->pc = (unsigned long) ka->sa.sa_handler;
-/* $Id: cache.c,v 1.9 2000/02/14 12:45:26 gniibe Exp $
+/* $Id: cache.c,v 1.10 2000/03/07 11:58:34 gniibe Exp $
*
* linux/arch/sh/mm/cache.c
*
#define CACHE_IC_NUM_WAYS 1
#endif
-#define jump_to_p2(__dummy) \
- asm volatile("mova 1f,%0\n\t" \
- "add %1,%0\n\t" \
- "jmp @r0 ! Jump to P2 area\n\t" \
- " nop\n\t" \
- ".balign 4\n" \
- "1:" \
- : "=&z" (__dummy) \
- : "r" (0x20000000))
-
-#define back_to_p1(__dummy) \
- asm volatile("nop;nop;nop;nop;nop;nop\n\t" \
- "mova 9f,%0\n\t" \
- "sub %1,%0\n\t" \
- "jmp @r0 ! Back to P1 area\n\t" \
- " nop\n\t" \
- ".balign 4\n" \
- "9:" \
- : "=&z" (__dummy) \
- : "r" (0x20000000), "0" (__dummy))
-
/* Write back caches to memory (if needed) and invalidates the caches */
void cache_flush_area(unsigned long start, unsigned long end)
{
- unsigned long flags, __dummy;
+ unsigned long flags;
unsigned long addr, data, v, p;
start &= ~(L1_CACHE_BYTES-1);
save_and_cli(flags);
- jump_to_p2(__dummy);
+ jump_to_P2();
for (v = start; v < end; v+=L1_CACHE_BYTES) {
p = __pa(v);
: "m" (__m(v)));
#endif
}
- back_to_p1(__dummy);
+ back_to_P1();
restore_flags(flags);
}
the cache line. */
void cache_purge_area(unsigned long start, unsigned long end)
{
- unsigned long flags, __dummy;
+ unsigned long flags;
unsigned long addr, data, v, p, j;
start &= ~(L1_CACHE_BYTES-1);
save_and_cli(flags);
- jump_to_p2(__dummy);
+ jump_to_P2();
for (v = start; v < end; v+=L1_CACHE_BYTES) {
p = __pa(v);
: "m" (__m(v)));
#endif
}
- back_to_p1(__dummy);
+ back_to_P1();
restore_flags(flags);
}
/* write back the dirty cache, but not invalidate the cache */
void cache_wback_area(unsigned long start, unsigned long end)
{
- unsigned long flags, __dummy;
+ unsigned long flags;
unsigned long v;
start &= ~(L1_CACHE_BYTES-1);
save_and_cli(flags);
- jump_to_p2(__dummy);
+ jump_to_P2();
for (v = start; v < end; v+=L1_CACHE_BYTES) {
#if CACHE_IC_ADDRESS_ARRAY == CACHE_OC_ADDRESS_ARRAY
: "m" (__m(v)));
#endif
}
- back_to_p1(__dummy);
+ back_to_P1();
restore_flags(flags);
}
*/
static void cache_wback_all(void)
{
- unsigned long flags, __dummy;
+ unsigned long flags;
unsigned long addr, data, i, j;
save_and_cli(flags);
- jump_to_p2(__dummy);
+ jump_to_P2();
for (i=0; i<CACHE_OC_NUM_ENTRIES; i++) {
for (j=0; j<CACHE_OC_NUM_WAYS; j++) {
}
}
- back_to_p1(__dummy);
+ back_to_P1();
restore_flags(flags);
}
detect_cpu_and_cache_system(void)
{
#if defined(__sh3__)
- unsigned long __dummy, addr0, addr1, data0, data1, data2, data3;
+ unsigned long addr0, addr1, data0, data1, data2, data3;
- jump_to_p2(__dummy);
+ jump_to_P2();
/* Check if the entry shadows or not.
* When shadowed, it's 128-entry system.
* Otherwise, it's 256-entry system.
/* Invaliate them, in case the cache has been enabled already. */
ctrl_outl(data0&~0x00000001,addr0);
ctrl_outl(data2&~0x00000001,addr1);
- back_to_p1(__dummy);
+ back_to_P1();
if (data0 == data1 && data2 == data3) { /* Shadow */
cache_system_info.way_shift = 11;
void __init cache_init(void)
{
- unsigned long __dummy, ccr;
+ unsigned long ccr;
detect_cpu_and_cache_system();
we only need to flush the half of the caches. */
cache_wback_all();
- jump_to_p2(__dummy);
+ jump_to_P2();
ctrl_outl(CCR_CACHE_INIT, CCR);
- back_to_p1(__dummy);
+ back_to_P1();
}
#if defined(__SH4__)
void flush_icache_page(struct vm_area_struct *vma, struct page *pg)
{
- unsigned long flags, __dummy;
+ unsigned long flags;
unsigned long addr, data, v;
save_and_cli(flags);
- jump_to_p2(__dummy);
+ jump_to_P2();
v = page_address(pg);
data = (v&0xfffffc00); /* Valid=0 */
ctrl_outl(data,addr);
- back_to_p1(__dummy);
+ back_to_P1();
restore_flags(flags);
}
void flush_icache_range(unsigned long start, unsigned long end)
{
- unsigned long flags, __dummy;
+ unsigned long flags;
unsigned long addr, data, v;
start &= ~(L1_CACHE_BYTES-1);
save_and_cli(flags);
- jump_to_p2(__dummy);
+ jump_to_P2();
for (v = start; v < end; v+=L1_CACHE_BYTES) {
/* Write back O Cache */
data = (v&0xfffffc00); /* Valid=0 */
ctrl_outl(data,addr);
}
- back_to_p1(__dummy);
+ back_to_P1();
restore_flags(flags);
}
void flush_cache_all(void)
{
- unsigned long flags,__dummy;
+ unsigned long flags;
/* Write back Operand Cache */
cache_wback_all ();
/* Then, invalidate Instruction Cache and Operand Cache */
save_and_cli(flags);
- jump_to_p2(__dummy);
+ jump_to_P2();
ctrl_outl(CCR_CACHE_INIT, CCR);
- back_to_p1(__dummy);
+ back_to_P1();
restore_flags(flags);
}
void flush_cache_range(struct mm_struct *mm, unsigned long start,
unsigned long end)
{
- unsigned long flags, __dummy;
+ unsigned long flags;
unsigned long addr, data, v;
start &= ~(L1_CACHE_BYTES-1);
save_and_cli(flags);
- jump_to_p2(__dummy);
+ jump_to_P2();
for (v = start; v < end; v+=L1_CACHE_BYTES) {
addr = CACHE_IC_ADDRESS_ARRAY |
(v&CACHE_OC_ENTRY_MASK) | 0x8 /* A-bit */;
ctrl_outl(data,addr);
}
- back_to_p1(__dummy);
+ back_to_P1();
restore_flags(flags);
}
-/* $Id: fault.c,v 1.12 2000/03/01 11:15:27 gniibe Exp $
+/* $Id: fault.c,v 1.13 2000/03/07 12:05:24 gniibe Exp $
*
* linux/arch/sh/mm/fault.c
* Copyright (C) 1999 Niibe Yutaka
{
unsigned long addr, data, asid;
unsigned long saved_asid = MMU_NO_ASID;
-#if defined(__SH4__)
- int i;
-#endif
if (mm->context == NO_CONTEXT)
return;
data = (page & 0xfffe0000) | asid; /* VALID bit is off */
ctrl_outl(data, addr);
#elif defined(__SH4__)
+ jump_to_P2();
addr = MMU_UTLB_ADDRESS_ARRAY | MMU_PAGE_ASSOC_BIT;
data = page | asid; /* VALID bit is off */
ctrl_outl(data, addr);
-
- for (i=0; i<4; i++) {
- addr = MMU_ITLB_ADDRESS_ARRAY | (i<<8);
- data = ctrl_inl(addr);
- data &= ~0x300;
- if (data == (page | asid)) {
- ctrl_outl(data, addr);
- break;
+#if 0 /* Not need when using ASSOC. ??? */
+ {
+ int i;
+ for (i=0; i<4; i++) {
+ addr = MMU_ITLB_ADDRESS_ARRAY | (i<<8);
+ data = ctrl_inl(addr);
+ data &= ~0x300;
+ if (data == (page | asid)) {
+ ctrl_outl(data, addr);
+ break;
+ }
}
}
+#endif
+ back_to_P1();
#endif
if (saved_asid != MMU_NO_ASID)
set_asid(saved_asid);
zones_size[ZONE_DMA] = max_dma - start_pfn;
zones_size[ZONE_NORMAL] = low - max_dma;
}
- free_area_init_node(0, 0, zones_size, __MEMORY_START);
+ free_area_init_node(0, 0, zones_size, __MEMORY_START, 0);
}
}
-# $Id: config.in,v 1.90 2000/03/17 05:18:02 anton Exp $
+# $Id: config.in,v 1.91 2000/03/24 10:00:05 davem Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
bool 'Symmetric multi-processing support (does not work on sun4/sun4c)' CONFIG_SMP
# Global things across all Sun machines.
+define_bool CONFIG_ISA n
+define_bool CONFIG_PCMCIA n
define_bool CONFIG_SBUS y
define_bool CONFIG_SBUSCHAR y
define_bool CONFIG_BUSMOUSE y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
# CONFIG_SMP is not set
+# CONFIG_ISA is not set
+# CONFIG_PCMCIA is not set
CONFIG_SBUS=y
CONFIG_SBUSCHAR=y
CONFIG_BUSMOUSE=y
-/* $Id: sys_sunos.c,v 1.117 2000/03/15 02:43:32 davem Exp $
+/* $Id: sys_sunos.c,v 1.118 2000/03/26 11:28:56 davem Exp $
* sys_sunos.c: SunOS specific syscall compatibility support.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
return def_value;
}
-asmlinkage int sunos_nfs_mount(char *dir_name, int linux_flags, void *data)
+static int sunos_nfs_mount(char *dir_name, int linux_flags, void *data)
{
int server_fd;
char *the_name;
-# $Id: config.in,v 1.105 2000/03/24 00:34:11 davem Exp $
+# $Id: config.in,v 1.106 2000/03/24 10:00:09 davem Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
bool 'Symmetric multi-processing support' CONFIG_SMP
# Global things across all Sun machines.
+define_bool CONFIG_ISA n
+define_bool CONFIG_PCMCIA n
define_bool CONFIG_SBUS y
define_bool CONFIG_SBUSCHAR y
define_bool CONFIG_BUSMOUSE y
define_bool CONFIG_SUN_CONSOLE y
define_bool CONFIG_SUN_AUXIO y
define_bool CONFIG_SUN_IO y
-define_bool CONFIG_ISA n
bool 'PCI support' CONFIG_PCI
source drivers/pci/Config.in
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
# CONFIG_SMP is not set
+# CONFIG_ISA is not set
+# CONFIG_PCMCIA is not set
CONFIG_SBUS=y
CONFIG_SBUSCHAR=y
CONFIG_BUSMOUSE=y
CONFIG_SUN_CONSOLE=y
CONFIG_SUN_AUXIO=y
CONFIG_SUN_IO=y
-# CONFIG_ISA is not set
CONFIG_PCI=y
CONFIG_PCI_NAMES=y
CONFIG_SUN_OPENPROMFS=m
# CONFIG_BLK_DEV_AEC6210 is not set
# CONFIG_AEC6210_TUNING is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD7409 is not set
# CONFIG_AMD7409_OVERRIDE is not set
CONFIG_BLK_DEV_CMD64X=y
-/* $Id: dtlb_base.S,v 1.5 2000/01/31 04:59:12 davem Exp $
+/* $Id: dtlb_base.S,v 1.7 2000/03/26 09:13:48 davem Exp $
* dtlb_base.S: Front end to DTLB miss replacement strategy.
* This is included directly into the trap table.
*
be,pn %xcc, 3f ! Yep, special processing
srax %g4, VPTE_SHIFT, %g6 ! Create VPTE offset
ldxa [%g3 + %g6] ASI_S, %g5 ! Load VPTE
-1: brlz,pt %g5, 2f ! Valid, load into TLB
+1: brlz,pt %g5, 9f ! Valid, load into TLB
and %g5, (_PAGE_PRESENT|_PAGE_READ), %g4 ! Mask readable bits
ba,a,pt %xcc, 4f ! Invalid, branch out
/* DTLB ** ICACHE line 2: Quick kernel TLB misses */
-3: brgez,a,pn %g4, 1b ! Kernel virtual map?
- ldxa [%g3 + %g6] ASI_N, %g5 ! Yep, load k-vpte
- srlx %g4, 40, %g5 ! Else compute phys-kpte
- andcc %g5, 1, %g0 ! I/O area?
- be,pt %xcc, 2f ! Nope, go and load TLB
+3: brlz,pt %g4, 9f ! Kernel virtual map?
xor %g2, %g4, %g5 ! Finish bit twiddles
- ba,pt %xcc, 2f ! Yes, I/O space, back back
- xor %g5, (KERN_IOBITS), %g5 ! After set E, clear CP/CV
+ ldxa [%g3 + %g6] ASI_N, %g5 ! Yep, load k-vpte
+ ba,pt %xcc, 1b ! Continue tlb reload
+ nop
+9: stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Reload TLB
+ retry ! Trap return
+ nop
/* DTLB ** ICACHE line 3: winfixups+real_faults */
4: cmp %g4, (_PAGE_PRESENT|_PAGE_READ) ! Readable page?
mov TLB_TAG_ACCESS, %g4 ! Prepare for fault processing
/* DTLB ** ICACHE line 4: padding */
+ ldxa [%g4] ASI_DMMU, %g5 ! Load faulting VA page
be,pt %xcc, sparc64_realfault_common ! Jump to normal fault handling
- ldxa [%g4] ASI_DMMU, %g5 ! And load faulting VA page
+ mov FAULT_CODE_DTLB, %g4 ! It was read from DTLB
ba,a,pt %xcc, winfix_trampoline ! Call window fixup code
5: or %g5, _PAGE_ACCESSED, %g5 ! Indicate reference
or %g5, %g4, %g5 ! Set valid
stxa %g5, [%g3 + %g6] ASI_S ! Update PTE table (cant trap)
-2: stxa %g5, [%g0] ASI_DTLB_DATA_IN ! Reload TLB
- retry ! Trap return
+ ba,a,pt %xcc, 9b ! Complete tlb miss
#undef TAG_CONTEXT_BITS
#undef VPTE_SHIFT
-/* $Id: dtlb_prot.S,v 1.19 2000/01/31 04:59:12 davem Exp $
+/* $Id: dtlb_prot.S,v 1.20 2000/03/26 09:13:48 davem Exp $
* dtlb_prot.S: DTLB protection trap strategy.
* This is included directly into the trap table.
*
/* PROT ** ICACHE line 3: Real user faults */
1: rdpr %pstate, %g5 ! Move into alternate globals
wrpr %g5, PSTATE_AG|PSTATE_MG, %pstate
- rdpr %tl, %g4 ! Need to do a winfixup?
- cmp %g4, 1 ! Trap level >1?
+ rdpr %tl, %g1 ! Need to do a winfixup?
+ cmp %g1, 1 ! Trap level >1?
mov TLB_TAG_ACCESS, %g4 ! Prepare reload of vaddr
bgu,pn %xcc, winfix_trampoline ! Yes, perform winfixup
ldxa [%g4] ASI_DMMU, %g5 ! Put tagaccess in %g5
ba,pt %xcc, sparc64_realfault_common ! Nope, normal fault
/* PROT ** ICACHE line 4: More real fault processing */
- mov 1, %g4 ! Indicate this was a write
+ mov FAULT_CODE_DTLB | FAULT_CODE_WRITE, %g4
nop
nop
nop
-/* $Id: ebus.c,v 1.46 1999/11/19 05:52:48 davem Exp $
+/* $Id: ebus.c,v 1.47 2000/03/25 05:18:10 davem Exp $
* ebus.c: PCI to EBus bridge device.
*
* Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
struct linux_ebus *ebus;
struct pci_dev *pdev;
struct pcidev_cookie *cookie;
- unsigned short pci_command;
int nd, ebusnd;
int num_ebus = 0;
ebus->self = pdev;
ebus->parent = pbm = cookie->pbm;
- /* Enable BUS Master. */
- pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
- pci_command |= PCI_COMMAND_MASTER;
- pci_write_config_word(pdev, PCI_COMMAND, pci_command);
-
- /* Set reasonable cache line size and latency timer values. */
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
-
- /* NOTE: Cache line size is in 32-bit word units. */
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64/sizeof(u32));
-
ebus_ranges_init(ebus);
ebus_intmap_init(ebus);
-/* $Id: itlb_base.S,v 1.8 2000/01/31 04:59:12 davem Exp $
+/* $Id: itlb_base.S,v 1.9 2000/03/26 09:13:48 davem Exp $
* itlb_base.S: Front end to ITLB miss replacement strategy.
* This is included directly into the trap table.
*
/* ITLB ** ICACHE line 3: Real faults */
rdpr %tpc, %g5 ! And load faulting VA
- clr %g4 ! It was read
+ mov FAULT_CODE_ITLB, %g4 ! It was read from ITLB
sparc64_realfault_common: ! Called by TL0 dtlb_miss too
- sethi %hi(1f), %g7 ! Save state
- ba,pt %xcc, etrap ! ...
-1: or %g7, %lo(1b), %g7 ! ...
- mov %l4, %o2 ! Read/Write/No idea
- srlx %l5, PAGE_SHIFT, %o1 ! Page align faulting VA
- add %sp, STACK_BIAS + REGWIN_SZ, %o0! Compute pt_regs arg
-
-/* ITLB ** ICACHE line 4: Call fault processing code */
+ stb %g4, [%g6 + AOFF_task_thread + AOFF_thread_fault_code]
+ stx %g5, [%g6 + AOFF_task_thread + AOFF_thread_fault_address]
+ ba,pt %xcc, etrap ! Save state
+1: rd %pc, %g7 ! ...
call do_sparc64_fault ! Call fault handler
- sllx %o1, PAGE_SHIFT, %o1 ! Finish page alignment
- ba,a,pt %xcc, rtrap_clr_l6 ! Restore cpu state
- nop
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0! Compute pt_regs arg
+
+/* ITLB ** ICACHE line 4: Finish faults + window fixups */
+ ba,pt %xcc, rtrap_clr_l6 ! Restore cpu state
+ nop
winfix_trampoline:
rdpr %tpc, %g3 ! Prepare winfixup TNPC
or %g3, 0x7c, %g3 ! Compute offset to branch
wrpr %g3, %tnpc ! Write it into TNPC
done ! Do it to it
+ nop
+ nop
#undef TAG_CONTEXT_BITS
#undef VPTE_SHIFT
-/* $Id: pci_common.c,v 1.6 2000/01/06 23:51:49 davem Exp $
+/* $Id: pci_common.c,v 1.7 2000/03/25 05:18:11 davem Exp $
* pci_common.c: PCI controller common support.
*
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
pci_fixup_irq(pbm, pci_bus_b(walk));
}
+#undef DEBUG_BUSMASTERING
+
+static void pdev_setup_busmastering(struct pci_dev *pdev, int is_66mhz)
+{
+ u16 cmd;
+ u8 hdr_type, min_gnt, ltimer;
+
+#ifdef DEBUG_BUSMASTERING
+ printk("PCI: Checking DEV(%s), ", pdev->name);
+#endif
+
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_MASTER;
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+ /* Read it back, if the mastering bit did not
+ * get set, the device does not support bus
+ * mastering so we have nothing to do here.
+ */
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if ((cmd & PCI_COMMAND_MASTER) == 0) {
+#ifdef DEBUG_BUSMASTERING
+ printk("no bus mastering...\n");
+#endif
+ return;
+ }
+
+ /* Set correct cache line size, 64-byte on all
+ * Sparc64 PCI systems. Note that the value is
+ * measured in 32-bit words.
+ */
+#ifdef DEBUG_BUSMASTERING
+ printk("set cachelinesize, ");
+#endif
+ pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE,
+ 64 / sizeof(u32));
+
+ pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr_type);
+ hdr_type &= ~0x80;
+ if (hdr_type != PCI_HEADER_TYPE_NORMAL) {
+#ifdef DEBUG_BUSMASTERING
+ printk("hdr_type=%x, exit\n", hdr_type);
+#endif
+ return;
+ }
+
+ /* If the latency timer is already programmed with a non-zero
+ * value, assume whoever set it (OBP or whoever) knows what
+ * they are doing.
+ */
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, <imer);
+ if (ltimer != 0) {
+#ifdef DEBUG_BUSMASTERING
+ printk("ltimer was %x, exit\n", ltimer);
+#endif
+ return;
+ }
+
+ /* XXX Since I'm tipping off the min grant value to
+ * XXX choose a suitable latency timer value, I also
+ * XXX considered making use of the max latency value
+ * XXX as well. Unfortunately I've seen too many bogusly
+ * XXX low settings for it to the point where it lacks
+ * XXX any usefulness. In one case, an ethernet card
+ * XXX claimed a min grant of 10 and a max latency of 5.
+ * XXX Now, if I had two such cards on the same bus I
+ * XXX could not set the desired burst period (calculated
+ * XXX from min grant) without violating the max latency
+ * XXX bound. Duh...
+ * XXX
+ * XXX I blame dumb PC bios implementors for stuff like
+ * XXX this, most of them don't even try to do something
+ * XXX sensible with latency timer values and just set some
+ * XXX default value (usually 32) into every device.
+ */
+
+ pci_read_config_byte(pdev, PCI_MIN_GNT, &min_gnt);
+
+ if (min_gnt == 0) {
+ /* If no min_gnt setting then use a default
+ * value.
+ */
+ if (is_66mhz)
+ ltimer = 16;
+ else
+ ltimer = 32;
+ } else {
+ int shift_factor;
+
+ if (is_66mhz)
+ shift_factor = 2;
+ else
+ shift_factor = 3;
+
+ /* Use a default value when the min_gnt value
+ * is erroneously high.
+ */
+ if (((unsigned int) min_gnt << shift_factor) > 512 ||
+ ((min_gnt << shift_factor) & 0xff) == 0) {
+ ltimer = 8 << shift_factor;
+ } else {
+ ltimer = min_gnt << shift_factor;
+ }
+ }
+
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, ltimer);
+#ifdef DEBUG_BUSMASTERING
+ printk("set ltimer to %x\n", ltimer);
+#endif
+}
+
+void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct list_head *walk;
+ int all_are_66mhz;
+ u16 status;
+
+ if (pbm->is_66mhz_capable == 0) {
+ all_are_66mhz = 0;
+ goto out;
+ }
+
+ walk = &pbus->devices;
+ all_are_66mhz = 1;
+ for (walk = walk->next; walk != &pbus->devices; walk = walk->next) {
+ struct pci_dev *pdev = pci_dev_b(walk);
+
+ pci_read_config_word(pdev, PCI_STATUS, &status);
+ if (!(status & PCI_STATUS_66MHZ)) {
+ all_are_66mhz = 0;
+ break;
+ }
+ }
+out:
+ pbm->all_devs_66mhz = all_are_66mhz;
+
+ printk("PCI%d(PBM%c): Bus running at %dMHz\n",
+ pbm->parent->index,
+ (pbm == &pbm->parent->pbm_A) ? 'A' : 'B',
+ (all_are_66mhz ? 66 : 33));
+}
+
+void pci_setup_busmastering(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus)
+{
+ struct list_head *walk = &pbus->devices;
+ int is_66mhz;
+
+ is_66mhz = pbm->is_66mhz_capable && pbm->all_devs_66mhz;
+
+ for (walk = walk->next; walk != &pbus->devices; walk = walk->next)
+ pdev_setup_busmastering(pci_dev_b(walk), is_66mhz);
+
+ walk = &pbus->children;
+ for (walk = walk->next; walk != &pbus->children; walk = walk->next)
+ pci_setup_busmastering(pbm, pci_bus_b(walk));
+}
+
/* Generic helper routines for PCI error reporting. */
void pci_scan_for_target_abort(struct pci_controller_info *p,
struct pci_pbm_info *pbm,
-/* $Id: pci_impl.h,v 1.5 2000/02/08 05:11:32 jj Exp $
+/* $Id: pci_impl.h,v 1.6 2000/03/25 05:18:11 davem Exp $
* pci_impl.h: Helper definitions for PCI controller support.
*
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
struct pci_bus *pbus);
extern void pci_fixup_irq(struct pci_pbm_info *pbm,
struct pci_bus *pbus);
+extern void pci_determine_66mhz_disposition(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus);
+extern void pci_setup_busmastering(struct pci_pbm_info *pbm,
+ struct pci_bus *pbus);
/* Error reporting support. */
extern void pci_scan_for_target_abort(struct pci_controller_info *, struct pci_pbm_info *, struct pci_bus *);
-/* $Id: pci_psycho.c,v 1.14 2000/03/10 02:42:15 davem Exp $
+/* $Id: pci_psycho.c,v 1.15 2000/03/25 05:18:11 davem Exp $
* pci_psycho.c: PSYCHO/U2P specific PCI controller support.
*
* Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu)
pbm_renumber(&p->pbm_A, 0xff);
}
+static void __init pbm_config_busmastering(struct pci_pbm_info *pbm)
+{
+ u8 *addr;
+
+ /* Set cache-line size to 64 bytes, this is actually
+ * a nop but I do it for completeness.
+ */
+ addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno,
+ 0, PCI_CACHE_LINE_SIZE);
+ pci_config_write8(addr, 64 / sizeof(u32));
+
+ /* Set PBM latency timer to 64 PCI clocks. */
+ addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno,
+ 0, PCI_LATENCY_TIMER);
+ pci_config_write8(addr, 64);
+}
+
static void __init pbm_scan_bus(struct pci_controller_info *p,
struct pci_pbm_info *pbm)
{
pci_record_assignments(pbm, pbm->pci_bus);
pci_assign_unassigned(pbm, pbm->pci_bus);
pci_fixup_irq(pbm, pbm->pci_bus);
+ pci_determine_66mhz_disposition(pbm, pbm->pci_bus);
+ pci_setup_busmastering(pbm, pbm->pci_bus);
}
static void __init psycho_scan_bus(struct pci_controller_info *p)
{
pbm_bridge_reconfigure(p);
+ pbm_config_busmastering(&p->pbm_B);
+ p->pbm_B.is_66mhz_capable = 0;
+ pbm_config_busmastering(&p->pbm_A);
+ p->pbm_A.is_66mhz_capable = 1;
pbm_scan_bus(p, &p->pbm_B);
pbm_scan_bus(p, &p->pbm_A);
-/* $Id: pci_sabre.c,v 1.15 2000/03/10 02:42:16 davem Exp $
+/* $Id: pci_sabre.c,v 1.16 2000/03/25 05:18:12 davem Exp $
* pci_sabre.c: Sabre specific PCI controller support.
*
* Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu)
struct pci_bus *sabre_bus;
struct list_head *walk;
+ /* The APB bridge speaks to the Sabre host PCI bridge
+ * at 66Mhz, but the front side of APB runs at 33Mhz
+ * for both segments.
+ */
+ p->pbm_A.is_66mhz_capable = 0;
+ p->pbm_B.is_66mhz_capable = 0;
+
/* Unlike for PSYCHO, we can only have one SABRE
* in a system. Having multiple SABREs is thus
* and error, and as a consequence we do not need
sabre_bus = pci_scan_bus(p->pci_first_busno,
p->pci_ops,
&p->pbm_A);
+
+ {
+ unsigned int devfn;
+ u8 *addr;
+
+ devfn = PCI_DEVFN(0, 0);
+ addr = sabre_pci_config_mkaddr(&p->pbm_A, 0,
+ devfn, PCI_LATENCY_TIMER);
+ pci_config_write8(addr, 32);
+ }
+
apb_init(p, sabre_bus);
walk = &sabre_bus->children;
pci_record_assignments(pbm, pbus);
pci_assign_unassigned(pbm, pbus);
pci_fixup_irq(pbm, pbus);
+ pci_determine_66mhz_disposition(pbm, pbus);
+ pci_setup_busmastering(pbm, pbus);
}
sabre_register_error_handlers(p);
-/* $Id: process.c,v 1.104 2000/03/01 02:53:32 davem Exp $
+/* $Id: process.c,v 1.105 2000/03/26 09:13:48 davem Exp $
* arch/sparc64/kernel/process.c
*
* Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
{
unsigned long fp, distance, rval;
- if(!(current->thread.flags & SPARC_FLAG_32BIT)) {
+ if (!(current->thread.flags & SPARC_FLAG_32BIT)) {
csp += STACK_BIAS;
psp += STACK_BIAS;
__get_user(fp, &(((struct reg_window *)psp)->ins[6]));
distance = fp - psp;
rval = (csp - distance);
- if(copy_in_user(rval, psp, distance))
+ if (copy_in_user(rval, psp, distance))
rval = 0;
- else if(current->thread.flags & SPARC_FLAG_32BIT) {
- if(put_user(((u32)csp), &(((struct reg_window32 *)rval)->ins[6])))
+ else if (current->thread.flags & SPARC_FLAG_32BIT) {
+ if (put_user(((u32)csp), &(((struct reg_window32 *)rval)->ins[6])))
rval = 0;
} else {
- if(put_user(((u64)csp - STACK_BIAS),
- &(((struct reg_window *)rval)->ins[6])))
+ if (put_user(((u64)csp - STACK_BIAS),
+ &(((struct reg_window *)rval)->ins[6])))
rval = 0;
else
rval = rval - STACK_BIAS;
{
int i;
- for(i = first_win; i < last_win; i++) {
+ for (i = first_win; i < last_win; i++) {
t->rwbuf_stkptrs[i] = t->rwbuf_stkptrs[i+1];
memcpy(&t->reg_window[i], &t->reg_window[i+1],
sizeof(struct reg_window));
unsigned long window;
flush_user_windows();
- if((window = t->w_saved) != 0) {
+ if ((window = t->w_saved) != 0) {
int winsize = REGWIN_SZ;
int bias = 0;
- if(t->flags & SPARC_FLAG_32BIT)
+ if (t->flags & SPARC_FLAG_32BIT)
winsize = REGWIN32_SZ;
else
bias = STACK_BIAS;
unsigned long sp = (t->rwbuf_stkptrs[window] + bias);
struct reg_window *rwin = &t->reg_window[window];
- if(!copy_to_user((char *)sp, rwin, winsize)) {
+ if (!copy_to_user((char *)sp, rwin, winsize)) {
shift_window_buffer(window, t->w_saved - 1, t);
t->w_saved--;
}
- } while(window--);
+ } while (window--);
}
}
int winsize = REGWIN_SZ;
int bias = 0;
- if(t->flags & SPARC_FLAG_32BIT)
+ if (t->flags & SPARC_FLAG_32BIT)
winsize = REGWIN32_SZ;
else
bias = STACK_BIAS;
+
flush_user_windows();
window = t->w_saved;
- if(window != 0) {
+
+ if (window != 0) {
window -= 1;
do {
unsigned long sp = (t->rwbuf_stkptrs[window] + bias);
struct reg_window *rwin = &t->reg_window[window];
- if(copy_to_user((char *)sp, rwin, winsize))
+ if (copy_to_user((char *)sp, rwin, winsize))
goto barf;
- } while(window--);
+ } while (window--);
}
t->w_saved = 0;
return;
+
barf:
+ t->w_saved = window + 1;
do_exit(SIGILL);
}
t->kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct reg_window));
t->cwp = (regs->tstate + 1) & TSTATE_CWP;
t->fpsaved[0] = 0;
- if(regs->tstate & TSTATE_PRIV) {
+
+ if (regs->tstate & TSTATE_PRIV) {
/* Special case, if we are spawning a kernel thread from
* a userspace task (via KMOD, NFS, or similar) we must
* disable performance counters in the child because the
sizeof(struct reg_window));
t->kregs->u_regs[UREG_G6] = (unsigned long) p;
} else {
- if(t->flags & SPARC_FLAG_32BIT) {
+ if (t->flags & SPARC_FLAG_32BIT) {
sp &= 0x00000000ffffffffUL;
regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
}
unsigned long csp;
csp = clone_stackframe(sp, regs->u_regs[UREG_FP]);
- if(!csp)
+ if (!csp)
return -EFAULT;
t->kregs->u_regs[UREG_FP] = csp;
}
/* Set the second return value for the parent. */
regs->u_regs[UREG_I1] = 0;
-#if 0
- printk("\ncopy_thread: c(%p[mm(%p:%p)]) p(%p[mm(%p:%p)])\n",
- current, current->mm, current->active_mm,
- p, p->mm, p->active_mm);
- printk("copy_thread: c MM_ctx(%016lx) MM_pgd(%016lx)\n",
- (current->mm ? current->mm->context : 0),
- (current->mm ? pgd_val(current->mm->pgd[0]) : 0));
- printk("copy_thread: p MM_ctx(%016lx) MM_pgd(%08x)\n",
- (p->mm ? p->mm->context : 0),
- (p->mm ? pgd_val(p->mm->pgd[0]) : 0));
- printk("copy_thread: c->flags(%x) p->flags(%x) ",
- current->thread.flags, p->thread.flags);
-#endif
+
return 0;
}
-/* $Id: sys32.S,v 1.11 2000/01/11 17:33:29 jj Exp $
+/* $Id: sys32.S,v 1.12 2000/03/24 04:17:37 davem Exp $
* sys32.S: I-cache tricks for 32-bit compatability layer simple
* conversions.
*
* Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
*/
+#include <asm/errno.h>
+
/* NOTE: call as jump breaks return stack, we have to avoid that */
.text
srl %o5, 0, %o5
jmpl %g1 + %lo(sys_mmap), %g0
sllx %o5, 12, %o5
+
+ .align 32
+ .globl sys32_socketcall
+sys32_socketcall: /* %o0=call, %o1=args */
+ cmp %o0, 1
+ bl,pn %xcc, do_einval
+ cmp %o0, 17
+ bg,pn %xcc, do_einval
+ sub %o0, 1, %o0
+ sllx %o0, 5, %o0
+ sethi %hi(__socketcall_table_begin), %g2
+ or %g2, %lo(__socketcall_table_begin), %g2
+ jmpl %g2 + %o0, %g0
+ nop
+
+ /* Each entry is exactly 32 bytes. */
+ .align 32
+__socketcall_table_begin:
+do_sys_socket: /* sys_socket(int, int, int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_socket), %g1
+ ldswa [%o1 + 0x8] %asi, %o2
+ jmpl %g1 + %lo(sys_socket), %g0
+ ldswa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+do_sys_bind: /* sys_bind(int fd, struct sockaddr *, int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_bind), %g1
+ ldswa [%o1 + 0x8] %asi, %o2
+ jmpl %g1 + %lo(sys_bind), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+do_sys_connect: /* sys_connect(int, struct sockaddr *, int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_connect), %g1
+ ldswa [%o1 + 0x8] %asi, %o2
+ jmpl %g1 + %lo(sys_connect), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+do_sys_listen: /* sys_listen(int, int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_listen), %g1
+ jmpl %g1 + %lo(sys_listen), %g0
+ ldswa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+ nop
+do_sys_accept: /* sys_accept(int, struct sockaddr *, int *) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_accept), %g1
+ lduwa [%o1 + 0x8] %asi, %o2
+ jmpl %g1 + %lo(sys_accept), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+do_sys_getsockname: /* sys_getsockname(int, struct sockaddr *, int *) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_getsockname), %g1
+ lduwa [%o1 + 0x8] %asi, %o2
+ jmpl %g1 + %lo(sys_getsockname), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+do_sys_getpeername: /* sys_getpeername(int, struct sockaddr *, int *) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_getpeername), %g1
+ lduwa [%o1 + 0x8] %asi, %o2
+ jmpl %g1 + %lo(sys_getpeername), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+do_sys_socketpair: /* sys_socketpair(int, int, int, int *) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_socketpair), %g1
+ ldswa [%o1 + 0x8] %asi, %o2
+ lduwa [%o1 + 0xc] %asi, %o3
+ jmpl %g1 + %lo(sys_socketpair), %g0
+ ldswa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+do_sys_send: /* sys_send(int, void *, size_t, unsigned int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_send), %g1
+ lduwa [%o1 + 0x8] %asi, %o2
+ lduwa [%o1 + 0xc] %asi, %o3
+ jmpl %g1 + %lo(sys_send), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+do_sys_recv: /* sys_recv(int, void *, size_t, unsigned int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_recv), %g1
+ lduwa [%o1 + 0x8] %asi, %o2
+ lduwa [%o1 + 0xc] %asi, %o3
+ jmpl %g1 + %lo(sys_recv), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+do_sys_sendto: /* sys32_sendto(int, u32, __kernel_size_t32, unsigned int, u32, int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys32_sendto), %g1
+ lduwa [%o1 + 0x8] %asi, %o2
+ lduwa [%o1 + 0xc] %asi, %o3
+ lduwa [%o1 + 0x10] %asi, %o4
+ ldswa [%o1 + 0x14] %asi, %o5
+ jmpl %g1 + %lo(sys32_sendto), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+do_sys_recvfrom: /* sys32_recvfrom(int, u32, __kernel_size_t32, unsigned int, u32, u32) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys32_recvfrom), %g1
+ lduwa [%o1 + 0x8] %asi, %o2
+ lduwa [%o1 + 0xc] %asi, %o3
+ lduwa [%o1 + 0x10] %asi, %o4
+ lduwa [%o1 + 0x14] %asi, %o5
+ jmpl %g1 + %lo(sys32_recvfrom), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+do_sys_shutdown: /* sys_shutdown(int, int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys_shutdown), %g1
+ jmpl %g1 + %lo(sys_shutdown), %g0
+ ldswa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+ nop
+do_sys_setsockopt: /* sys32_setsockopt(int, int, int, char *, int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys32_setsockopt), %g1
+ ldswa [%o1 + 0x8] %asi, %o2
+ lduwa [%o1 + 0xc] %asi, %o3
+ ldswa [%o1 + 0x10] %asi, %o4
+ jmpl %g1 + %lo(sys32_setsockopt), %g0
+ ldswa [%o1 + 0x4] %asi, %o1
+ nop
+do_sys_getsockopt: /* sys32_getsockopt(int, int, int, u32, u32) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys32_getsockopt), %g1
+ ldswa [%o1 + 0x8] %asi, %o2
+ lduwa [%o1 + 0xc] %asi, %o3
+ lduwa [%o1 + 0x10] %asi, %o4
+ jmpl %g1 + %lo(sys32_getsockopt), %g0
+ ldswa [%o1 + 0x4] %asi, %o1
+ nop
+do_sys_sendmsg: /* sys32_sendmsg(int, struct msghdr32 *, unsigned int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys32_sendmsg), %g1
+ lduwa [%o1 + 0x8] %asi, %o2
+ jmpl %g1 + %lo(sys32_sendmsg), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+do_sys_recvmsg: /* sys32_recvmsg(int, struct msghdr32 *, unsigned int) */
+ ldswa [%o1 + 0x0] %asi, %o0
+ sethi %hi(sys32_recvmsg), %g1
+ lduwa [%o1 + 0x8] %asi, %o2
+ jmpl %g1 + %lo(sys32_recvmsg), %g0
+ lduwa [%o1 + 0x4] %asi, %o1
+ nop
+ nop
+ nop
+__socketcall_table_end:
+
+do_einval:
+ retl
+ mov -EINVAL, %o0
+do_efault:
+ retl
+ mov -EFAULT, %o0
+
+ .section __ex_table
+ .align 4
+ .word __socketcall_table_begin, 0, __socketcall_table_end, do_efault
+ .previous
-/* $Id: sys_sparc32.c,v 1.141 2000/03/24 01:31:30 davem Exp $
+/* $Id: sys_sparc32.c,v 1.142 2000/03/24 04:17:38 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
return sys_setsockopt(fd, level, optname, optval, optlen);
}
-/* Argument list sizes for sys_socketcall */
-#define AL(x) ((x) * sizeof(u32))
-static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
- AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
- AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
-#undef AL
-
-extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen);
-extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen);
-extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen);
-extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len);
-extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len);
-extern asmlinkage int sys_send(int fd, void *buff, size_t len, unsigned flags);
-extern asmlinkage int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len,
- unsigned flags, u32 addr, int addr_len);
-extern asmlinkage int sys_recv(int fd, void *ubuf, size_t size, unsigned flags);
-extern asmlinkage int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size,
- unsigned flags, u32 addr, u32 addr_len);
-extern asmlinkage int sys32_getsockopt(int fd, int level, int optname,
- u32 optval, u32 optlen);
-
-extern asmlinkage int sys_socket(int family, int type, int protocol);
-extern asmlinkage int sys_socketpair(int family, int type, int protocol,
- int usockvec[2]);
-extern asmlinkage int sys_shutdown(int fd, int how);
-extern asmlinkage int sys_listen(int fd, int backlog);
-
-asmlinkage int sys32_socketcall(int call, u32 *args)
-{
- u32 a[6];
- u32 a0,a1;
-
- if (call<SYS_SOCKET||call>SYS_RECVMSG)
- return -EINVAL;
- if (copy_from_user(a, args, nargs[call]))
- return -EFAULT;
- a0=a[0];
- a1=a[1];
-
- switch(call)
- {
- case SYS_SOCKET:
- return sys_socket(a0, a1, a[2]);
- case SYS_BIND:
- return sys_bind(a0, (struct sockaddr *)A(a1), a[2]);
- case SYS_CONNECT:
- return sys_connect(a0, (struct sockaddr *)A(a1), a[2]);
- case SYS_LISTEN:
- return sys_listen(a0, a1);
- case SYS_ACCEPT:
- return sys_accept(a0, (struct sockaddr *)A(a1), (int *)A(a[2]));
- case SYS_GETSOCKNAME:
- return sys_getsockname(a0, (struct sockaddr *)A(a1), (int *)A(a[2]));
- case SYS_GETPEERNAME:
- return sys_getpeername(a0, (struct sockaddr *)A(a1), (int *)A(a[2]));
- case SYS_SOCKETPAIR:
- return sys_socketpair(a0, a1, a[2], (int *)A(a[3]));
- case SYS_SEND:
- return sys_send(a0, (void *)A(a1), a[2], a[3]);
- case SYS_SENDTO:
- return sys32_sendto(a0, a1, a[2], a[3], a[4], a[5]);
- case SYS_RECV:
- return sys_recv(a0, (void *)A(a1), a[2], a[3]);
- case SYS_RECVFROM:
- return sys32_recvfrom(a0, a1, a[2], a[3], a[4], a[5]);
- case SYS_SHUTDOWN:
- return sys_shutdown(a0,a1);
- case SYS_SETSOCKOPT:
- return sys32_setsockopt(a0, a1, a[2], (char *)A(a[3]), a[4]);
- case SYS_GETSOCKOPT:
- return sys32_getsockopt(a0, a1, a[2], a[3], a[4]);
- case SYS_SENDMSG:
- return sys32_sendmsg(a0, (struct msghdr32 *)A(a1), a[2]);
- case SYS_RECVMSG:
- return sys32_recvmsg(a0, (struct msghdr32 *)A(a1), a[2]);
- }
- return -EINVAL;
-}
-
extern void check_pending(int signum);
asmlinkage int sys32_sigaction (int sig, struct old_sigaction32 *act, struct old_sigaction32 *oact)
-/* $Id: sys_sunos32.c,v 1.42 2000/03/15 02:43:35 davem Exp $
+/* $Id: sys_sunos32.c,v 1.43 2000/03/26 11:28:53 davem Exp $
* sys_sunos32.c: SunOS binary compatability layer on sparc64.
*
* Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
}
/* XXXXXXXXXXXXXXXXXXXX */
-asmlinkage int sunos_nfs_mount(char *dir_name, int linux_flags, void *data)
+static int sunos_nfs_mount(char *dir_name, int linux_flags, void *data)
{
int server_fd;
char *the_name;
-/* $Id: winfixup.S,v 1.28 1999/07/30 09:35:34 davem Exp $
+/* $Id: winfixup.S,v 1.29 2000/03/26 09:13:48 davem Exp $
*
* winfixup.S: Handle cases where user stack pointer is found to be bogus.
*
fill_fixup:
rdpr %tstate, %g1
andcc %g1, TSTATE_PRIV, %g0
- clr %g4
+ or %g4, FAULT_CODE_WINFIXUP, %g4
be,pt %xcc, window_scheisse_from_user_common
and %g1, TSTATE_CWP, %g1
mov %g6, %o7 ! Get current.
andn %l1, PSTATE_MM, %l1 ! We want to be in RMO
- srlx %g5, PAGE_SHIFT, %o1 ! Fault address
+ stb %g4, [%g6 + AOFF_task_thread + AOFF_thread_fault_code]
+ stx %g5, [%g6 + AOFF_task_thread + AOFF_thread_fault_address]
wrpr %g0, 0x0, %tl ! Out of trap levels.
wrpr %l1, (PSTATE_IE | PSTATE_AG | PSTATE_RMO), %pstate
sethi %uhi(PAGE_OFFSET), %g4 ! Prepare page_offset global reg
mov %o7, %g6
sllx %g4, 32, %g4 ! and finish it...
- clr %o2
/* This is the same as below, except we handle this a bit special
* since we must preserve %l5 and %l6, see comment above.
*/
- sllx %o1, PAGE_SHIFT, %o1
call do_sparc64_fault
add %sp, STACK_BIAS + REGWIN_SZ, %o0
- b,pt %xcc, rtrap
+ ba,pt %xcc, rtrap
nop ! yes, nop is correct
/* Be very careful about usage of the alternate globals here.
andcc %g1, TSTATE_PRIV, %g0
saved
and %g1, TSTATE_CWP, %g1
- be,a,pn %xcc, window_scheisse_from_user_common
- or %g4, 0x4, %g4 ! we know it was a write
+ be,pn %xcc, window_scheisse_from_user_common
+ mov FAULT_CODE_WRITE | FAULT_CODE_DTLB | FAULT_CODE_WINFIXUP, %g4
retry
+
window_scheisse_from_user_common:
+ stb %g4, [%g6 + AOFF_task_thread + AOFF_thread_fault_code]
+ stx %g5, [%g6 + AOFF_task_thread + AOFF_thread_fault_address]
wrpr %g1, %cwp
- sethi %hi(109f), %g7
ba,pt %xcc, etrap
-109: or %g7, %lo(109b), %g7
- srlx %l5, PAGE_SHIFT, %o1
-
- and %l4, 0x4, %o2
- sllx %o1, PAGE_SHIFT, %o1
+ rd %pc, %g7
call do_sparc64_fault
add %sp, STACK_BIAS + REGWIN_SZ, %o0
- ba,pt %xcc, rtrap
- clr %l6
+ ba,a,pt %xcc, rtrap_clr_l6
.globl winfix_mna, fill_fixup_mna, spill_fixup_mna
winfix_mna:
-/* $Id: VIScopy.S,v 1.22 2000/03/16 16:44:38 davem Exp $
+/* $Id: VIScopy.S,v 1.23 2000/03/26 09:13:49 davem Exp $
* VIScopy.S: High speed copy operations utilizing the UltraSparc
* Visual Instruction Set.
*
#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt) \
EXVIS(LDBLK [%src] ASIBLK, %fdest); \
ASI_SETDST_BLK \
+ EXVIS2(STBLK %fsrc, [%dest] ASIBLK); \
add %src, 0x40, %src; \
- add %dest, 0x40, %dest; \
subcc %len, 0x40, %len; \
be,pn %xcc, jmptgt; \
- EXVIS2(STBLK %fsrc, [%dest - 0x40] ASIBLK); \
+ add %dest, 0x40, %dest; \
ASI_SETSRC_BLK
#define LOOP_CHUNK1(src, dest, len, branch_dest) \
-/* $Id: blockops.S,v 1.22 2000/03/15 07:18:55 davem Exp $
+/* $Id: blockops.S,v 1.23 2000/03/26 09:13:50 davem Exp $
* blockops.S: UltraSparc block zero optimized routines.
*
* Copyright (C) 1996, 1998, 1999, 2000 David S. Miller (davem@redhat.com)
sethi %hi(8192), %o2
1: TOUCH(f0, f2, f4, f6, f8, f10, f12, f14)
ldda [%o1] ASI_BLK_P, %f32
+ stda %f48, [%o0] ASI_BLK_P
add %o1, 0x40, %o1
sub %o2, 0x40, %o2
- stda %f48, [%o0] ASI_BLK_P
add %o0, 0x40, %o0
TOUCH(f16, f18, f20, f22, f24, f26, f28, f30)
ldda [%o1] ASI_BLK_P, %f0
+ stda %f48, [%o0] ASI_BLK_P
add %o1, 0x40, %o1
sub %o2, 0x40, %o2
- stda %f48, [%o0] ASI_BLK_P
add %o0, 0x40, %o0
TOUCH(f32, f34, f36, f38, f40, f42, f44, f46)
ldda [%o1] ASI_BLK_P, %f16
- add %o1, 0x40, %o1
- sub %o2, 0x40, %o2
stda %f48, [%o0] ASI_BLK_P
+ sub %o2, 0x40, %o2
+ add %o1, 0x40, %o1
cmp %o2, 0x80
bne,pt %xcc, 1b
add %o0, 0x40, %o0
sethi %hi(8192), %o2
1: TOUCH(f0, f2, f4, f6, f8, f10, f12, f14)
ldda [%o1] ASI_BLK_P, %f32
+ stda %f48, [%o0] ASI_BLK_P
add %o1, 0x40, %o1
sub %o2, 0x40, %o2
- stda %f48, [%o0] ASI_BLK_P
add %o0, 0x40, %o0
TOUCH(f16, f18, f20, f22, f24, f26, f28, f30)
ldda [%o1] ASI_BLK_P, %f0
+ stda %f48, [%o0] ASI_BLK_P
add %o1, 0x40, %o1
sub %o2, 0x40, %o2
- stda %f48, [%o0] ASI_BLK_P
add %o0, 0x40, %o0
TOUCH(f32, f34, f36, f38, f40, f42, f44, f46)
ldda [%o1] ASI_BLK_P, %f16
- add %o1, 0x40, %o1
- sub %o2, 0x40, %o2
stda %f48, [%o0] ASI_BLK_P
+ sub %o2, 0x40, %o2
+ add %o1, 0x40, %o1
cmp %o2, 0x80
bne,pt %xcc, 1b
add %o0, 0x40, %o0
mov 1, %o4
clear_page_common:
+ membar #StoreLoad | #StoreStore | #LoadStore ! LSU Group
fzero %f0 ! FPA Group
mov 32, %o1 ! IEU0
fzero %f2 ! FPA Group
faddd %f0, %f2, %f12 ! FPA Group
fmuld %f0, %f2, %f14 ! FPM
- membar #StoreLoad | #StoreStore | #LoadStore ! LSU Group
1: stda %f0, [%o0 + %g0] ASI_BLK_P ! Store Group
add %o0, 0x40, %o0 ! IEU0
stda %f0, [%o0 + %g0] ASI_BLK_P ! Store Group
-/* $Id: fault.c,v 1.43 2000/03/14 03:59:46 davem Exp $
+/* $Id: fault.c,v 1.44 2000/03/26 09:13:51 davem Exp $
* arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
die_if_kernel("Oops", regs);
}
-/* #define DEBUG_EXCEPTIONS */
-/* #define DEBUG_LOCKUPS */
-
-/* #define INSN_VPTE_LOOKUP */
-
-static inline u32 get_user_insn(unsigned long tpc)
+static unsigned int get_user_insn(unsigned long tpc)
{
- u32 insn;
-#ifndef INSN_VPTE_LOOKUP
pgd_t *pgdp = pgd_offset(current->mm, tpc);
pmd_t *pmdp;
- pte_t *ptep;
+ pte_t *ptep, pte;
+ unsigned long pa;
+ u32 insn = 0;
if(pgd_none(*pgdp))
- return 0;
+ goto out;
pmdp = pmd_offset(pgdp, tpc);
if(pmd_none(*pmdp))
- return 0;
+ goto out;
ptep = pte_offset(pmdp, tpc);
- if(!pte_present(*ptep))
- return 0;
- insn = *(unsigned int *)
- ((unsigned long)__va(pte_pagenr(*ptep) << PAGE_SHIFT) +
- (tpc & ~PAGE_MASK));
-#else
- register unsigned long pte asm("l1");
-
- /* So that we don't pollute TLB, we read the instruction
- * using PHYS bypass. For that, we of course need
- * to know its page table entry. Do this by simulating
- * dtlb_miss handler. -jj */
- pte = ((((long)tpc) >> (PAGE_SHIFT-3)) & ~7);
- asm volatile ("
- rdpr %%pstate, %%l0
- wrpr %%l0, %2, %%pstate
- wrpr %%g0, 1, %%tl
- mov %%l1, %%g6
- ldxa [%%g3 + %%l1] %3, %%g5
- mov %%g5, %%l1
- wrpr %%g0, 0, %%tl
- wrpr %%l0, 0, %%pstate
- " : "=r" (pte) : "0" (pte), "i" (PSTATE_MG|PSTATE_IE), "i" (ASI_S) : "l0");
-
- if ((long)pte >= 0) return 0;
-
- pte = (pte & _PAGE_PADDR) + (tpc & ~PAGE_MASK);
- asm ("lduwa [%1] %2, %0" : "=r" (insn) : "r" (pte), "i" (ASI_PHYS_USE_EC));
-#endif
+ pte = *ptep;
+ if(!pte_present(pte))
+ goto out;
+
+ pa = (pte_pagenr(pte) << PAGE_SHIFT) + (tpc & ~PAGE_MASK);
+ /* Use phys bypass so we don't pollute dtlb/dcache. */
+ __asm__ __volatile__("lduwa [%1] %2, %0"
+ : "=r" (insn)
+ : "r" (pa), "i" (ASI_PHYS_USE_EC));
+
+out:
return insn;
}
-asmlinkage void do_sparc64_fault(struct pt_regs *regs, unsigned long address, int write)
+static void do_fault_siginfo(int code, int sig, unsigned long address)
+{
+ siginfo_t info;
+
+ info.si_code = code;
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_addr = (void *) address;
+ info.si_trapno = 0;
+ force_sig_info(sig, &info, current);
+}
+
+extern int handle_ldf_stq(u32, struct pt_regs *);
+extern int handle_ld_nf(u32, struct pt_regs *);
+
+static void do_kernel_fault(struct pt_regs *regs, int si_code, int fault_code,
+ unsigned int insn, unsigned long address)
+{
+ unsigned long g2;
+ unsigned char asi = ASI_P;
+
+ if (!insn) {
+ if (regs->tstate & TSTATE_PRIV) {
+ if (regs->tpc & 0x3)
+ goto cannot_handle;
+ insn = *(unsigned int *)regs->tpc;
+ } else {
+ insn = get_user_insn(regs->tpc);
+ }
+ }
+
+ /* If user insn could be read (thus insn is zero), that
+ * is fine. We will just gun down the process with a signal
+ * in that case.
+ */
+
+ if (!(fault_code & FAULT_CODE_WRITE) &&
+ (insn & 0xc0800000) == 0xc0800000) {
+ if (insn & 0x2000)
+ asi = (regs->tstate >> 24);
+ else
+ asi = (insn >> 5);
+ if ((asi & 0xf2) == 0x82) {
+ if (insn & 0x1000000) {
+ handle_ldf_stq(insn, regs);
+ } else {
+ /* This was a non-faulting load. Just clear the
+ * destination register(s) and continue with the next
+ * instruction. -jj
+ */
+ handle_ld_nf(insn, regs);
+ }
+ return;
+ }
+ }
+
+ g2 = regs->u_regs[UREG_G2];
+
+ /* Is this in ex_table? */
+ if (regs->tstate & TSTATE_PRIV) {
+ unsigned long fixup;
+
+ if (asi == ASI_P && (insn & 0xc0800000) == 0xc0800000) {
+ if (insn & 0x2000)
+ asi = (regs->tstate >> 24);
+ else
+ asi = (insn >> 5);
+ }
+
+ /* Look in asi.h: All _S asis have LS bit set */
+ if ((asi & 0x1) &&
+ (fixup = search_exception_table (regs->tpc, &g2))) {
+ regs->tpc = fixup;
+ regs->tnpc = regs->tpc + 4;
+ regs->u_regs[UREG_G2] = g2;
+ return;
+ }
+ } else {
+ /* The si_code was set to make clear whether
+ * this was a SEGV_MAPERR or SEGV_ACCERR fault.
+ */
+ do_fault_siginfo(si_code, SIGSEGV, address);
+ return;
+ }
+
+cannot_handle:
+ unhandled_fault (address, current, regs);
+}
+
+asmlinkage void do_sparc64_fault(struct pt_regs *regs)
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
unsigned int insn = 0;
- siginfo_t info;
-#ifdef DEBUG_LOCKUPS
- static unsigned long lastaddr, lastpc;
- static int lastwrite, lockcnt;
-#endif
+ int si_code, fault_code;
+ unsigned long address;
+
+ si_code = SEGV_MAPERR;
+ fault_code = current->thread.fault_code;
+ address = current->thread.fault_address;
+
+ if ((fault_code & FAULT_CODE_ITLB) &&
+ (fault_code & FAULT_CODE_DTLB))
+ BUG();
- info.si_code = SEGV_MAPERR;
/*
* If we're in an interrupt or have no user
* context, we must not take the fault..
*/
if (in_interrupt() || !mm)
- goto do_kernel_fault;
+ goto handle_kernel_fault;
down(&mm->mmap_sem);
-#ifdef DEBUG_LOCKUPS
- if (regs->tpc == lastpc &&
- address == lastaddr &&
- write == lastwrite) {
- lockcnt++;
- if (lockcnt == 100000) {
- unsigned char tmp;
- register unsigned long tmp1 asm("o5");
- register unsigned long tmp2 asm("o4");
-
- printk("do_sparc64_fault[%s:%d]: possible fault loop for %016lx %s\n",
- current->comm, current->pid,
- address, write ? "write" : "read");
- printk("do_sparc64_fault: CHECK[papgd[%016lx],pcac[%016lx]]\n",
- __pa(mm->pgd), pgd_val(mm->pgd[0])<<11UL);
- __asm__ __volatile__(
- "wrpr %%g0, 0x494, %%pstate\n\t"
- "mov %3, %%g4\n\t"
- "mov %%g7, %0\n\t"
- "ldxa [%%g4] %2, %1\n\t"
- "wrpr %%g0, 0x096, %%pstate"
- : "=r" (tmp1), "=r" (tmp2)
- : "i" (ASI_DMMU), "i" (TSB_REG));
- printk("do_sparc64_fault: IS[papgd[%016lx],pcac[%016lx]]\n",
- tmp1, tmp2);
- printk("do_sparc64_fault: CHECK[ctx(%016lx)] IS[ctx(%016lx)]\n",
- mm->context, spitfire_get_secondary_context());
- __asm__ __volatile__("rd %%asi, %0"
- : "=r" (tmp));
- printk("do_sparc64_fault: CHECK[seg(%02x)] IS[seg(%02x)]\n",
- current->thread.current_ds.seg, tmp);
- show_regs(regs);
- __sti();
- while(1)
- barrier();
- }
- } else {
- lastpc = regs->tpc;
- lastaddr = address;
- lastwrite = write;
- lockcnt = 0;
- }
-#endif
vma = find_vma(mm, address);
- if(!vma)
+ if (!vma)
goto bad_area;
-#ifndef INSN_VPTE_LOOKUP
- write &= 0xf;
-#else
- if (write & 0x10) {
- write = 0;
- if((vma->vm_flags & VM_WRITE)) {
- if (regs->tstate & TSTATE_PRIV)
- insn = *(unsigned int *)regs->tpc;
- else
- insn = get_user_insn(regs->tpc);
- if ((insn & 0xc0200000) == 0xc0200000 && (insn & 0x1780000) != 0x1680000)
- write = 1;
+
+ /* Pure DTLB misses do not tell us whether the fault causing
+ * load/store/atomic was a write or not, it only says that there
+ * was no match. So in such a case we (carefully) read the
+ * instruction to try and figure this out. It's an optimization
+ * so it's ok if we can't do this.
+ *
+ * Special hack, window spill/fill knows the exact fault type.
+ */
+ if (((fault_code &
+ (FAULT_CODE_DTLB | FAULT_CODE_WRITE | FAULT_CODE_WINFIXUP)) == FAULT_CODE_DTLB) &&
+ (vma->vm_flags & VM_WRITE) != 0) {
+ unsigned long tpc = regs->tpc;
+
+ if (tpc & 0x3)
+ goto continue_fault;
+
+ if (regs->tstate & TSTATE_PRIV)
+ insn = *(unsigned int *)tpc;
+ else
+ insn = get_user_insn(tpc);
+
+ if ((insn & 0xc0200000) == 0xc0200000 &&
+ (insn & 0x1780000) != 0x1680000) {
+ /* Don't bother updating thread struct value,
+ * because update_mmu_cache only cares which tlb
+ * the access came from.
+ */
+ fault_code |= FAULT_CODE_WRITE;
}
}
-#endif
- if(vma->vm_start <= address)
+continue_fault:
+
+ if (vma->vm_start <= address)
goto good_area;
- if(!(vma->vm_flags & VM_GROWSDOWN))
+ if (!(vma->vm_flags & VM_GROWSDOWN))
goto bad_area;
- if(expand_stack(vma, address))
+ if (expand_stack(vma, address))
goto bad_area;
/*
* Ok, we have a good vm_area for this memory access, so
* we can handle it..
*/
good_area:
- info.si_code = SEGV_ACCERR;
- if(write) {
- if(!(vma->vm_flags & VM_WRITE))
+ si_code = SEGV_ACCERR;
+ if (fault_code & FAULT_CODE_WRITE) {
+ if (!(vma->vm_flags & VM_WRITE))
goto bad_area;
} else {
/* Allow reads even for write-only mappings */
- if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ if (!(vma->vm_flags & (VM_READ | VM_EXEC)))
goto bad_area;
}
{
- int fault = handle_mm_fault(current, vma, address, write);
+ int fault = handle_mm_fault(current, vma,
+ address, (fault_code & FAULT_CODE_WRITE));
if (fault < 0)
goto out_of_memory;
goto do_sigbus;
}
up(&mm->mmap_sem);
- return;
+ goto fault_done;
+
/*
* Something tried to access memory that isn't in our memory map..
* Fix it, but check if it's kernel or user first..
bad_area:
up(&mm->mmap_sem);
-do_kernel_fault:
- {
- unsigned long g2;
- unsigned char asi = ASI_P;
-
- if (!insn) {
- if (regs->tstate & TSTATE_PRIV)
- insn = *(unsigned int *)regs->tpc;
- else
- insn = get_user_insn(regs->tpc);
- }
- if (write != 1 && (insn & 0xc0800000) == 0xc0800000) {
- if (insn & 0x2000)
- asi = (regs->tstate >> 24);
- else
- asi = (insn >> 5);
- if ((asi & 0xf2) == 0x82) {
- /* This was a non-faulting load. Just clear the
- destination register(s) and continue with the next
- instruction. -jj */
- if (insn & 0x1000000) {
- extern int handle_ldf_stq(u32, struct pt_regs *);
-
- handle_ldf_stq(insn, regs);
- } else {
- extern int handle_ld_nf(u32, struct pt_regs *);
-
- handle_ld_nf(insn, regs);
- }
- return;
- }
- }
-
- g2 = regs->u_regs[UREG_G2];
-
- /* Is this in ex_table? */
- if (regs->tstate & TSTATE_PRIV) {
- unsigned long fixup;
+handle_kernel_fault:
+ do_kernel_fault(regs, si_code, fault_code, insn, address);
- if (asi == ASI_P && (insn & 0xc0800000) == 0xc0800000) {
- if (insn & 0x2000)
- asi = (regs->tstate >> 24);
- else
- asi = (insn >> 5);
- }
-
- /* Look in asi.h: All _S asis have LS bit set */
- if ((asi & 0x1) &&
- (fixup = search_exception_table (regs->tpc, &g2))) {
-#ifdef DEBUG_EXCEPTIONS
- printk("Exception: PC<%016lx> faddr<%016lx>\n",
- regs->tpc, address);
- printk("EX_TABLE: insn<%016lx> fixup<%016lx> "
- "g2<%016lx>\n", regs->tpc, fixup, g2);
-#endif
- regs->tpc = fixup;
- regs->tnpc = regs->tpc + 4;
- regs->u_regs[UREG_G2] = g2;
- return;
- }
- } else {
-#if 0
- extern void __show_regs(struct pt_regs *);
- printk("SHIT(%s:%d:cpu(%d)): PC[%016lx] ADDR[%016lx]\n",
- current->comm, current->pid, smp_processor_id(),
- regs->tpc, address);
- __show_regs(regs);
- __sti();
- while(1)
- barrier();
-#endif
- info.si_signo = SIGSEGV;
- info.si_errno = 0;
- /* info.si_code set above to make clear whether
- this was a SEGV_MAPERR or SEGV_ACCERR fault. */
- info.si_addr = (void *)address;
- info.si_trapno = 0;
- force_sig_info (SIGSEGV, &info, current);
- return;
- }
- unhandled_fault (address, current, regs);
- }
- return;
+ goto fault_done;
/*
* We ran out of memory, or some other thing happened to us that made
printk("VM: killing process %s\n", current->comm);
if (!(regs->tstate & TSTATE_PRIV))
do_exit(SIGKILL);
- goto do_kernel_fault;
+ goto handle_kernel_fault;
do_sigbus:
up(&mm->mmap_sem);
* Send a sigbus, regardless of whether we were in kernel
* or user mode.
*/
- info.si_signo = SIGBUS;
- info.si_errno = 0;
- info.si_code = BUS_ADRERR;
- info.si_addr = (void *)address;
- info.si_trapno = 0;
- force_sig_info (SIGBUS, &info, current);
+ do_fault_siginfo(BUS_ADRERR, SIGBUS, address);
/* Kernel mode? Handle exceptions or die */
if (regs->tstate & TSTATE_PRIV)
- goto do_kernel_fault;
+ goto handle_kernel_fault;
+
+fault_done:
+ /* These values are no longer needed, clear them. */
+ current->thread.fault_code = 0;
+ current->thread.fault_address = 0;
}
-/* $Id: ultra.S,v 1.38 2000/03/03 23:48:44 davem Exp $
+/* $Id: ultra.S,v 1.40 2000/03/26 09:13:51 davem Exp $
* ultra.S: Don't expand these all over the place...
*
- * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997, 2000 David S. Miller (davem@redhat.com)
*/
#include <asm/asi.h>
#include <asm/pgtable.h>
+#include <asm/page.h>
#include <asm/spitfire.h>
/* This file is meant to be read efficiently by the CPU, not humans.
srlx %o0, 5, %o0
clr %o1 ! IC_addr
sllx %g1, 36, %g1
+ ldda [%o1] ASI_IC_TAG, %o4
sub %g1, 1, %g2
or %o0, %g1, %o0 ! VALID+phys-addr comparitor
- sllx %g2, 1, %g2
+ sllx %g2, 1, %g2
andn %g2, 0xfe, %g2 ! IC_tag mask
-1: ldda [%o1] ASI_IC_TAG, %o4
- and %o5, %g2, %o5
- cmp %o5, %o0
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+
+1: addx %g0, %g0, %g0
+ ldda [%o1 + %o2] ASI_IC_TAG, %g4
+ addx %g0, %g0, %g0
+ and %o5, %g2, %g3
+ cmp %g3, %o0
+ add %o1, 0x20, %o1
+ ldda [%o1] ASI_IC_TAG, %o4
be,pn %xcc, iflush1
- add %o1, 0x20, %g3
-2: ldda [%o1 + %o2] ASI_IC_TAG, %o4
- and %o5, %g2, %o5
- cmp %o5, %o0
+2: nop
+ and %g5, %g2, %g5
+ cmp %g5, %o0
be,pn %xcc, iflush2
- nop
-3: cmp %g3, %o2
+3: cmp %o1, %o2
bne,pt %xcc, 1b
- mov %g3, %o1
+ addx %g0, %g0, %g0
+ nop
+
+ sethi %uhi(PAGE_OFFSET), %g4
retl
- nop
+ sllx %g4, 32, %g4
-iflush1:stxa %g0, [%o1] ASI_IC_TAG
+iflush1:sub %o1, 0x20, %g3
+ stxa %g0, [%g3] ASI_IC_TAG
flush %g6
ba,a,pt %xcc, 2b
-iflush2:stxa %g0, [%o1 + %o2] ASI_IC_TAG
+iflush2:sub %o1, 0x20, %g3
+ stxa %g0, [%o1 + %o2] ASI_IC_TAG
flush %g6
ba,a,pt %xcc, 3b
+ .align 32
+ .globl __prefill_dtlb
+__prefill_dtlb:
+ rdpr %pstate, %g7
+ wrpr %g7, PSTATE_IE, %pstate
+ mov TLB_TAG_ACCESS, %g1
+ stxa %o0, [%g1] ASI_DMMU
+ stxa %o1, [%g0] ASI_DTLB_DATA_IN
+ flush %g6
+ retl
+ wrpr %g7, %pstate
+
+ .globl __prefill_itlb
+__prefill_itlb:
+ rdpr %pstate, %g7
+ wrpr %g7, PSTATE_IE, %pstate
+ mov TLB_TAG_ACCESS, %g1
+ stxa %o0, [%g1] ASI_IMMU
+ stxa %o1, [%g0] ASI_ITLB_DATA_IN
+ flush %g6
+ retl
+ wrpr %g7, %pstate
+
#ifdef __SMP__
/* These are all called by the slaves of a cross call, at
* trap level 1, with interrupts fully disabled.
-/* $Id: timod.c,v 1.5 1999/11/23 08:55:24 davem Exp $
+/* $Id: timod.c,v 1.6 2000/03/25 03:23:21 davem Exp $
* timod.c: timod emulation.
*
* Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
SOLD("wakeing socket");
sock = ¤t->files->fd[fd]->f_dentry->d_inode->u.socket_i;
wake_up_interruptible(&sock->wait);
- if (sock->fasync_list && !(sock->flags & SO_WAITDATA))
+ if (sock->fasync_list && !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags))
kill_fasync(sock->fasync_list, SIGIO, POLL_IN);
SOLD("done");
}
extern void kbd_reset_kdown(void);
int kbd_read_mask;
-#define IRQ_KEYBOARDRX 15
-
#define VERSION 100
#define KBD_REPORT_ERR
{
unsigned long flags;
+ /* Reset the keyboard state machine. */
+ outb(0, IOMD_KCTRL);
+ outb(8, IOMD_KCTRL);
+
save_flags_cli (flags);
if (request_irq (IRQ_KEYBOARDRX, ps2kbd_rx, 0, "keyboard", NULL) != 0)
panic("Could not allocate keyboard receive IRQ!");
printk (KERN_INFO "PS/2 keyboard driver v%d.%02d\n", VERSION/100, VERSION%100);
return 0;
}
-
#include <asm/byteorder.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
-
-#ifdef CONFIG_ATM_FORE200E_PCA
#include <linux/pci.h>
-#endif
#ifdef CONFIG_ATM_FORE200E_SBA
#include <asm/idprom.h>
tristate 'Atari floppy support' CONFIG_ATARI_FLOPPY
fi
if [ "$CONFIG_MAC" = "y" ]; then
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool 'Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)' CONFIG_BLK_DEV_SWIM_IOP
- fi
+ dep_bool 'Macintosh IIfx/Quadra 900/Quadra 950 floppy support (EXPERIMENTAL)' CONFIG_BLK_DEV_SWIM_IOP $CONFIG_EXPERIMENTAL
fi
if [ "$CONFIG_MCA" = "y" ]; then
tristate 'PS/2 ESDI hard disk support' CONFIG_BLK_DEV_PS2
tristate ' Atari SLM laser printer support' CONFIG_ATARI_SLM
fi
fi
-tristate 'XT hard disk support' CONFIG_BLK_DEV_XD
+dep_tristate 'XT hard disk support' CONFIG_BLK_DEV_XD $CONFIG_ISA
dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARPORT
if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
source drivers/block/paride/Config.in
fi
-
-if [ "$CONFIG_PCI" = "y" ]; then
- tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA
- tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960
-fi
+dep_tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA $CONFIG_PCI
+dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 $CONFIG_PCI
comment 'Additional Block Devices'
tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
-if [ "$CONFIG_NET" = "y" ]; then
- tristate 'Network block device support' CONFIG_BLK_DEV_NBD
-fi
+dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET
+
bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
-if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
- tristate ' Linear (append) mode' CONFIG_MD_LINEAR
- tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED
-# tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
-# tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5
-fi
+dep_tristate ' Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD
+dep_tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED $CONFIG_BLK_DEV_MD
+#dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING $CONFIG_BLK_DEV_MD
+#dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD
#if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then
# bool ' Boot support (linear, striped)' CONFIG_MD_BOOT
#fi
tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
-if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
- bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
-fi
+dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM
endmenu
#endif
#ifdef CONFIG_BLK_DEV_NBD
nbd_init();
+#endif
+#ifdef CONFIG_MDISK
+ mdisk_init();
+#endif
+#ifdef CONFIG_DASD
+ dasd_init();
#endif
return 0;
};
printk(KERN_DEBUG
"%s -- lvm_blk_ioctl -- BLKRAGET\n", lvm_name);
#endif
- if (put_user(lv->lv_read_ahead, (long *)arg))
+ if (put_user(lv_ptr->lv_read_ahead, (long *)arg))
return -EFAULT;
break;
md_setup_args.devices[minor][i] = (kdev_t) 0;
md_setup_args.pers[minor] = level | factor | (fault << FAULT_SHIFT);
md_setup_args.set |= (1 << minor);
- return 0;
+ return 1;
}
#endif
tristate 'Video For Linux' CONFIG_VIDEO_DEV
if [ "$CONFIG_VIDEO_DEV" != "n" ]; then
- dep_tristate ' I2C on parallel port' CONFIG_I2C_PARPORT $CONFIG_PARPORT
+ dep_tristate ' I2C on parallel port' CONFIG_I2C_PARPORT $CONFIG_PARPORT $CONFIG_I2C
comment 'Radio Adapters'
dep_tristate ' ADS Cadet AM/FM Tuner' CONFIG_RADIO_CADET $CONFIG_VIDEO_DEV
dep_tristate ' AIMSlab RadioTrack (aka RadioReveal) support' CONFIG_RADIO_RTRACK $CONFIG_VIDEO_DEV
dep_tristate ' QuickCam Colour Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT
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 'CPiA Parallel Port Lowlevel Support' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT
+ fi
+ if [ "$CONFIG_USB" != "n" ]; then
+ dep_tristate 'CPiA USB Lowlevel Support' CONFIG_VIDEO_CPIA_USB $CONFIG_VIDEO_CPIA $CONFIG_USB
+ fi
+ fi
dep_tristate ' SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV $CONFIG_I2C
dep_tristate ' SAB3036 tuner' CONFIG_TUNER_3036 $CONFIG_VIDEO_DEV $CONFIG_I2C
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
export-objs := busmouse.o console.o i2c-old.o keyboard.o sysrq.o \
misc.o pty.o random.o selection.o serial.o videodev.o \
- tty_io.o bttv.o
+ tty_io.o bttv.o cpia.o
KEYMAP =defkeymap.o
KEYBD =pc_keyb.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o
-obj-$(CONFIG_21825_WATCHDOG) += wdt285.o
+obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o
obj-$(CONFIG_DS1620) += ds1620.o
obj-$(CONFIG_VIDEO_PLANB) += planb.o
obj-$(CONFIG_VIDEO_VINO) += vino.o
obj-$(CONFIG_VIDEO_STRADIS) += stradis.o
+obj-$(CONFIG_VIDEO_CPIA) += cpia.o
+obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
+obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o
obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o
obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o
/* Derived from Applicom driver ac.c for SCO Unix */
/* Ported by David Woodhouse, Axiom (Cambridge) Ltd. */
/* Dave@mvhi.com 30/8/98 */
-/* $Id: ac.c,v 1.16 1999/08/28 15:11:50 dwmw2 Exp $ */
+/* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $ */
/* This module is for Linux 2.1 and 2.2 series kernels. */
/*****************************************************************************/
/* J PAGET 18/02/94 passage V2.4.2 ioctl avec code 2 reset to les interrupt */
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/malloc.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <linux/wait.h>
#include <linux/init.h>
+#include <linux/compatmac.h>
+
#include "applicom.h"
-#ifndef LINUX_VERSION_CODE
-#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < 0x20300
+/* These probably want adding to <linux/compatmac.h> */
+#define init_waitqueue_head(x) do { *(x) = NULL; } while (0);
+#define PCI_BASE_ADDRESS(dev) (dev->base_address[0])
+#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x
+#define __setup(x,y) /* */
+#else
+#define PCI_BASE_ADDRESS(dev) (dev->resource[0].start)
#endif
+/* NOTE: We use for loops with {write,read}b() instead of
+ memcpy_{from,to}io throughout this driver. This is because
+ the board doesn't correctly handle word accesses - only
+ bytes.
+*/
+
+
#undef DEBUG
-#define DEVPRIO PZERO+8
-#define FALSE 0
-#define TRUE ~FALSE
+
#define MAX_BOARD 8 /* maximum of pc board possible */
#define MAX_ISA_BOARD 4
#define LEN_RAM_IO 0x800
#endif
static char *applicom_pci_devnames[] = {
- "PCI board", "PCI2000IBS / PCI2000CAN", "PCI2000PFB"
+ "PCI board",
+ "PCI2000IBS / PCI2000CAN",
+ "PCI2000PFB"
};
MODULE_AUTHOR("David Woodhouse & Applicom International");
struct applicom_board {
unsigned long PhysIO;
unsigned long RamIO;
-#if LINUX_VERSION_CODE > 0x20300
wait_queue_head_t FlagSleepSend;
-#else
- struct wait_queue *FlagSleepSend;
-#endif
long irq;
+ spinlock_t mutex;
} apbs[MAX_BOARD];
static unsigned int irq = 0; /* interrupt number IRQ */
static unsigned int numboards; /* number of installed boards */
static volatile unsigned char Dummy;
-#if LINUX_VERSION_CODE > 0x20300
static DECLARE_WAIT_QUEUE_HEAD(FlagSleepRec);
-#else
-static struct wait_queue *FlagSleepRec;
-#endif
static unsigned int WriteErrorCount; /* number of write error */
static unsigned int ReadErrorCount; /* number of read error */
static unsigned int DeviceErrorCount; /* number of device error */
-static loff_t ac_llseek(struct file *file, loff_t offset, int origin);
-static int ac_open(struct inode *inode, struct file *filp);
-static ssize_t ac_read(struct file *filp, char *buf, size_t count, loff_t * ptr);
-static ssize_t ac_write(struct file *file, const char *buf, size_t count, loff_t * ppos);
-static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
-static int ac_release(struct inode *inode, struct file *file);
-static void ac_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static loff_t ac_llseek(struct file *, loff_t, int);
+static int ac_open(struct inode *, struct file *);
+static ssize_t ac_read (struct file *, char *, size_t, loff_t *);
+static ssize_t ac_write (struct file *, const char *, size_t, loff_t *);
+static int ac_ioctl(struct inode *, struct file *, unsigned int,
+ unsigned long);
+static int ac_release(struct inode *, struct file *);
+static void ac_interrupt(int, void *, struct pt_regs *);
struct file_operations ac_fops = {
llseek:ac_llseek,
&ac_fops
};
-int ac_register_board(unsigned long physloc, unsigned long loc, unsigned char boardno)
+int ac_register_board(unsigned long physloc, unsigned long loc,
+ unsigned char boardno)
{
volatile unsigned char byte_reset_it;
- if ((readb(loc + CONF_END_TEST) != 0x00) || (readb(loc + CONF_END_TEST + 1) != 0x55) || (readb(loc + CONF_END_TEST + 2) != 0xAA) || (readb(loc + CONF_END_TEST + 3) != 0xFF))
+ if((readb(loc + CONF_END_TEST) != 0x00) ||
+ (readb(loc + CONF_END_TEST + 1) != 0x55) ||
+ (readb(loc + CONF_END_TEST + 2) != 0xAA) ||
+ (readb(loc + CONF_END_TEST + 3) != 0xFF))
return 0;
-
if (!boardno)
boardno = readb(loc + NUMCARD_OWNER_TO_PC);
if (!boardno && boardno > MAX_BOARD) {
- printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n", boardno, physloc, MAX_BOARD);
+ printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n",
+ boardno, physloc, MAX_BOARD);
return 0;
}
if (apbs[boardno - 1].RamIO) {
- printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n", boardno, physloc, boardno, apbs[boardno - 1].PhysIO);
+ printk(KERN_WARNING "Board #%d (at 0x%lx) conflicts with previous board #%d (at 0x%lx)\n",
+ boardno, physloc, boardno, apbs[boardno-1].PhysIO);
return 0;
}
apbs[boardno].PhysIO = physloc;
apbs[boardno].RamIO = loc;
-#if LINUX_VERSION_CODE > 0x20300
init_waitqueue_head(&apbs[boardno].FlagSleepSend);
-#else
- apbs[boardno].FlagSleepSend = NULL;
-#endif
+ spin_lock_init(&apbs[boardno].mutex);
byte_reset_it = readb(loc + RAM_IT_TO_PC);
numboards++;
int i;
misc_deregister(&ac_miscdev);
+
for (i = 0; i < MAX_BOARD; i++) {
+
if (!apbs[i].RamIO)
continue;
+
iounmap((void *) apbs[i].RamIO);
+
if (apbs[i].irq)
free_irq(apbs[i].irq, &ac_open);
}
- // printk("Removing Applicom module\n");
}
#endif /* MODULE */
struct pci_dev *dev = NULL;
void *RamIO;
int boardno;
-#if LINUX_VERSION_CODE > 0x20300
-#define PCI_BASE_ADDRESS(dev) (dev->resource[0].start)
-#else
-#define PCI_BASE_ADDRESS(dev) (dev->base_address[0])
-#endif
- printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.16 1999/08/28 15:11:50 dwmw2 Exp $\n");
+ printk(KERN_INFO "Applicom driver: $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $\n");
/* No mem and irq given - check for a PCI card */
- while ((dev = pci_find_device(PCI_VENDOR_ID_APPLICOM, 1, dev))) {
- // mem = dev->base_address[0];
- // irq = dev->irq;
+ while ( (dev = pci_find_class(PCI_CLASS_OTHERS << 16, dev))) {
+ if (dev->vendor != PCI_VENDOR_ID_APPLICOM)
+ continue;
+
+ if (dev->device > MAX_PCI_DEVICE_NUM || dev->device == 0)
+ continue;
+
RamIO = ioremap(PCI_BASE_ADDRESS(dev), LEN_RAM_IO);
if (!RamIO) {
return -EIO;
}
- printk(KERN_INFO "Applicom %s found at mem 0x%lx, irq %d\n", applicom_pci_devnames[dev->device - 1], PCI_BASE_ADDRESS(dev), dev->irq);
+ printk(KERN_INFO "Applicom %s found at mem 0x%lx, irq %d\n",
+ applicom_pci_devnames[dev->device-1], PCI_BASE_ADDRESS(dev),
+ dev->irq);
- if (!(boardno = ac_register_board(PCI_BASE_ADDRESS(dev), (unsigned long) RamIO, 0))) {
+ if (!(boardno = ac_register_board(PCI_BASE_ADDRESS(dev),
+ (unsigned long)RamIO,0))) {
printk(KERN_INFO "ac.o: PCI Applicom device doesn't have correct signature.\n");
iounmap(RamIO);
continue;
RamIO = ioremap(mem, LEN_RAM_IO * MAX_ISA_BOARD);
- if (!RamIO) {
+ if (!RamIO)
printk(KERN_INFO "ac.o: Failed to ioremap ISA memory space at 0x%lx\n", mem);
- }
for (i = 0; i < MAX_ISA_BOARD; i++) {
RamIO = ioremap(mem + (LEN_RAM_IO * i), LEN_RAM_IO);
continue;
}
- if (!(boardno = ac_register_board((unsigned long) mem + (LEN_RAM_IO * i), (unsigned long) RamIO, i + 1))) {
+ if (!(boardno = ac_register_board((unsigned long)mem+ (LEN_RAM_IO*i),
+ (unsigned long)RamIO,i+1))) {
iounmap(RamIO);
continue;
}
- printk("Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO * i), irq);
+ printk(KERN_NOTICE "Applicom ISA card found at mem 0x%lx, irq %d\n", mem + (LEN_RAM_IO*i), irq);
if (!numisa) {
if (request_irq(irq, &ac_interrupt, SA_SHIRQ, "Applicom ISA", &ac_open)) {
- printk("Could not allocate IRQ %d for ISA Applicom device.\n", irq);
+ printk(KERN_WARNING "Could not allocate IRQ %d for ISA Applicom device.\n", irq);
iounmap((void *) RamIO);
apbs[boardno - 1].RamIO = 0;
}
apbs[boardno - 1].irq = irq;
- } else
+ }
+ else
apbs[boardno - 1].irq = 0;
numisa++;
}
if (!numisa)
- printk("ac.o: No valid ISA Applicom boards found at mem 0x%lx\n", mem);
+ printk(KERN_WARNING"ac.o: No valid ISA Applicom boards found at mem 0x%lx\n",mem);
-#if LINUX_VERSION_CODE > 0x20300
+ fin:
init_waitqueue_head(&FlagSleepRec);
-#else
- FlagSleepRec = NULL;
-#endif
+
WriteErrorCount = 0;
ReadErrorCount = 0;
DeviceErrorCount = 0;
- fin:
if (numboards) {
misc_register(&ac_miscdev);
for (i = 0; i < MAX_BOARD; i++) {
for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
- boardname[serial] = 0;
+ boardname[serial] = 0;
- printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d", i + 1, boardname, (int) (readb(apbs[i].RamIO + VERS) >> 4), (int) (readb(apbs[i].RamIO + VERS) & 0xF));
- serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + (readb(apbs[i].RamIO + SERIAL_NUMBER + 2));
+ printk(KERN_INFO "Applicom board %d: %s, PROM V%d.%d",
+ i+1, boardname,
+ (int)(readb(apbs[i].RamIO + VERS) >> 4),
+ (int)(readb(apbs[i].RamIO + VERS) & 0xF));
+
+ serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) +
+ (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) +
+ (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
if (serial != 0)
printk(" S/N %d\n", serial);
unsigned long flags; /* Current priority */
struct st_ram_io st_loc;
struct mailbox tmpmailbox;
+#ifdef DEBUG
+ int c;
+#endif
+ DECLARE_WAITQUEUE(wait, current);
if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
- printk("Hmmm. write() of Applicom card, length %d != expected %d\n", count, sizeof(struct st_ram_io) + sizeof(struct mailbox));
+ static int warncount = 5;
+ if (warncount) {
+ printk(KERN_INFO "Hmmm. write() of Applicom card, length %d != expected %d\n",
+ count, sizeof(struct st_ram_io) + sizeof(struct mailbox));
+ warncount--;
+ }
return -EINVAL;
}
- if (copy_from_user(&st_loc, buf, sizeof(struct st_ram_io))) {
+ if(copy_from_user(&st_loc, buf, sizeof(struct st_ram_io)))
return -EFAULT;
- }
- if (copy_from_user(&tmpmailbox, &buf[sizeof(struct st_ram_io)], sizeof(struct mailbox))) {
+
+ if(copy_from_user(&tmpmailbox, &buf[sizeof(struct st_ram_io)],
+ sizeof(struct mailbox)))
return -EFAULT;
- }
NumCard = st_loc.num_card; /* board number to send */
TicCard = st_loc.tic_des_from_pc; /* tic number to send */
IndexCard = NumCard - 1;
- if ((NumCard < 1) || (NumCard > MAX_BOARD) || !apbs[IndexCard].RamIO) { /* User board number not OK */
- // printk("Write to invalid Applicom board %d\n", NumCard);
- return -EINVAL; /* Return error code user buffer */
- }
-#ifdef DEBUG
- {
- int c;
-
- printk("Write to applicom card #%d. struct st_ram_io follows:", NumCard);
+ if((NumCard < 1) || (NumCard > MAX_BOARD) || !apbs[IndexCard].RamIO)
+ return -EINVAL;
+#ifdef DEBUG
+ printk("Write to applicom card #%d. struct st_ram_io follows:",
+ IndexCard+1);
for (c = 0; c < sizeof(struct st_ram_io);) {
+
printk("\n%5.5X: %2.2X", c, ((unsigned char *) &st_loc)[c]);
for (c++; c % 8 && c < sizeof(struct st_ram_io); c++) {
}
printk("\n");
- }
-
#endif
- save_flags(flags);
- cli(); /* disable interrupt */
+ spin_lock_irqsave(&apbs[IndexCard].mutex, flags);
- if (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) { /* Test octet ready correct */
+ /* Test octet ready correct */
+ if(readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) > 2) {
Dummy = readb(apbs[IndexCard].RamIO + VERS);
- restore_flags(flags);
- printk("APPLICOM driver write error board %d, DataFromPcReady = %d\n", IndexCard, (int) readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY));
+ spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
+ printk(KERN_WARNING "APPLICOM driver write error board %d, DataFromPcReady = %d\n",
+ IndexCard,(int)readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY));
DeviceErrorCount++;
return -EIO;
}
+
+ /* Place ourselves on the wait queue */
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait);
+
+ /* Check whether the card is ready for us */
while (readb(apbs[IndexCard].RamIO + DATA_FROM_PC_READY) != 0) {
Dummy = readb(apbs[IndexCard].RamIO + VERS);
- restore_flags(flags);
- /*
- * FIXME: Race on wakeup. Race on re-entering write
- * in another thread.
- */
- interruptible_sleep_on(&apbs[IndexCard].FlagSleepSend);
- if (signal_pending(current))
+ /* It's busy. Sleep. */
+
+ spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
+ schedule();
+ if (signal_pending(current)) {
+ remove_wait_queue(&apbs[IndexCard].FlagSleepSend,
+ &wait);
return -EINTR;
- save_flags(flags);
- cli();
}
+ spin_lock_irqsave(&apbs[IndexCard].mutex, flags);
+ }
+
+ /* We may not have actually slept */
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&apbs[IndexCard].FlagSleepSend, &wait);
+
writeb(1, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
- // memcpy_toio ((void *)apbs[IndexCard].PtrRamFromPc, (void *)&tmpmailbox, sizeof(struct mailbox));
+ /* Which is best - lock down the pages with rawio and then
+ copy directly, or use bounce buffers? For now we do the latter
+ because it works with 2.2 still */
{
unsigned char *from = (unsigned char *) &tmpmailbox;
unsigned long to = (unsigned long) apbs[IndexCard].RamIO + RAM_FROM_PC;
for (c = 0; c < sizeof(struct mailbox); c++)
writeb(*(from++), to++);
}
+
writeb(0x20, apbs[IndexCard].RamIO + TIC_OWNER_FROM_PC);
writeb(0xff, apbs[IndexCard].RamIO + NUMCARD_OWNER_FROM_PC);
writeb(TicCard, apbs[IndexCard].RamIO + TIC_DES_FROM_PC);
writeb(2, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
Dummy = readb(apbs[IndexCard].RamIO + VERS);
- restore_flags(flags);
+ spin_unlock_irqrestore(&apbs[IndexCard].mutex, flags);
return 0;
}
-static ssize_t ac_read(struct file *filp, char *buf, size_t count, loff_t * ptr)
+static int do_ac_read(int IndexCard, char *buf)
{
- unsigned int NumCard; /* board number 1 -> 8 */
- unsigned int IndexCard; /* index board number 0 -> 7 */
- unsigned long flags;
- unsigned int i;
- unsigned char tmp = 0;
struct st_ram_io st_loc;
struct mailbox tmpmailbox; /* bounce buffer - can't copy to user space with cli() */
+ unsigned long from = (unsigned long)apbs[IndexCard].RamIO + RAM_TO_PC;
+ unsigned char *to = (unsigned char *)&tmpmailbox;
+#ifdef DEBUG
+ int c;
+#endif
-
- if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
- printk("Hmmm. read() of Applicom card, length %d != expected %d\n", count, sizeof(struct st_ram_io) + sizeof(struct mailbox));
- return -EINVAL;
- }
-
- save_flags(flags);
- cli();
-
- i = 0;
-
- while (tmp != 2) {
- for (i = 0; i < MAX_BOARD; i++) {
- if (!apbs[i].RamIO)
- continue;
-
- tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY);
-
- if (tmp == 2)
- break;
-
- if (tmp > 2) { /* Test octet ready correct */
- Dummy = readb(apbs[i].RamIO + VERS);
- restore_flags(flags);
- printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d\n", i, (int) readb(apbs[i].RamIO + DATA_TO_PC_READY));
- DeviceErrorCount++;
- return -EIO;
- }
- Dummy = readb(apbs[i].RamIO + VERS);
-
- }
- if (tmp != 2) {
- /*
- * FIXME: race on wakeup. O_NDELAY not implemented
- * Parallel read threads race.
- */
- restore_flags(flags);
- interruptible_sleep_on(&FlagSleepRec);
- if (signal_pending(current))
- return -EINTR;
- save_flags(flags);
- cli();
- }
- }
-
- IndexCard = i;
- NumCard = i + 1;
st_loc.tic_owner_to_pc = readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC);
st_loc.numcard_owner_to_pc = readb(apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
- // memcpy_fromio(&tmpmailbox, apbs[IndexCard].PtrRamToPc, sizeof(struct mailbox));
{
- unsigned long from = (unsigned long) apbs[IndexCard].RamIO + RAM_TO_PC;
- unsigned char *to = (unsigned char *) &tmpmailbox;
int c;
for (c = 0; c < sizeof(struct mailbox); c++)
}
writeb(1, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
writeb(1, apbs[IndexCard].RamIO + TYP_ACK_FROM_PC);
- writeb(NumCard, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
- writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC), apbs[IndexCard].RamIO + TIC_ACK_FROM_PC);
+ writeb(IndexCard+1, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
+ writeb(readb(apbs[IndexCard].RamIO + TIC_OWNER_TO_PC),
+ apbs[IndexCard].RamIO + TIC_ACK_FROM_PC);
writeb(2, apbs[IndexCard].RamIO + ACK_FROM_PC_READY);
writeb(0, apbs[IndexCard].RamIO + DATA_TO_PC_READY);
writeb(2, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
Dummy = readb(apbs[IndexCard].RamIO + VERS);
- restore_flags(flags);
#ifdef DEBUG
- {
- int c;
-
printk("Read from applicom card #%d. struct st_ram_io follows:", NumCard);
for (c = 0; c < sizeof(struct st_ram_io);) {
}
}
printk("\n");
-
- }
#endif
-
/* Je suis stupide. DW. */
if (copy_to_user(buf, &st_loc, sizeof(struct st_ram_io)))
if (copy_to_user(&buf[sizeof(struct st_ram_io)], &tmpmailbox, sizeof(struct mailbox)))
return -EFAULT;
- return 0;
+ return (sizeof(struct st_ram_io) + sizeof(struct mailbox));
+}
+
+static ssize_t ac_read (struct file *filp, char *buf, size_t count, loff_t *ptr)
+{
+ unsigned long flags;
+ unsigned int i;
+ unsigned char tmp;
+ int ret = 0;
+ DECLARE_WAITQUEUE(wait, current);
+#ifdef DEBUG
+ int loopcount=0;
+#endif
+ /* No need to ratelimit this. Only root can trigger it anyway */
+ if (count != sizeof(struct st_ram_io) + sizeof(struct mailbox)) {
+ printk( KERN_WARNING "Hmmm. read() of Applicom card, length %d != expected %d\n",
+ count,sizeof(struct st_ram_io) + sizeof(struct mailbox));
+ return -EINVAL;
+ }
+
+ while(1) {
+ /* Stick ourself on the wait queue */
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&FlagSleepRec, &wait);
+
+ /* Scan each board, looking for one which has a packet for us */
+ for (i=0; i < MAX_BOARD; i++) {
+ if (!apbs[i].RamIO)
+ continue;
+ spin_lock_irqsave(&apbs[i].mutex, flags);
+
+ tmp = readb(apbs[i].RamIO + DATA_TO_PC_READY);
+
+ if (tmp == 2) {
+ /* Got a packet for us */
+ ret = do_ac_read(i, buf);
+ spin_unlock_irqrestore(&apbs[i].mutex, flags);
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&FlagSleepRec, &wait);
+ return tmp;
+ }
+
+ if (tmp > 2) {
+ /* Got an error */
+ Dummy = readb(apbs[i].RamIO + VERS);
+
+ spin_unlock_irqrestore(&apbs[i].mutex, flags);
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&FlagSleepRec, &wait);
+
+ printk(KERN_WARNING "APPLICOM driver read error board %d, DataToPcReady = %d\n",
+ i,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
+ DeviceErrorCount++;
+ return -EIO;
+ }
+
+ /* Nothing for us. Try the next board */
+ Dummy = readb(apbs[i].RamIO + VERS);
+ spin_unlock_irqrestore(&apbs[i].mutex, flags);
+
+ } /* per board */
+
+ /* OK - No boards had data for us. Sleep now */
+
+ schedule();
+ remove_wait_queue(&FlagSleepRec, &wait);
+
+ if (signal_pending(current))
+ return -EINTR;
+
+#ifdef DEBUG
+ if (loopcount++ > 2) {
+ printk("Looping in ac_read. loopcount %d\n", loopcount);
+ }
+#endif
+ }
}
static void ac_interrupt(int vec, void *dev_instance, struct pt_regs *regs)
unsigned int i;
unsigned int FlagInt;
unsigned int LoopCount;
- // volatile unsigned char ResetIntBoard;
// printk("Applicom interrupt on IRQ %d occurred\n", vec);
LoopCount = 0;
- // for(i=boardno;i<MAX_BOARD;i++) /* loop for not configured board */
- // if (apbs[i].RamIO)
- // ResetIntBoard = *apbs[i].PtrRamItToPc; /* reset interrupt of unused boards */
do {
- FlagInt = FALSE;
+ FlagInt = 0;
for (i = 0; i < MAX_BOARD; i++) {
+
+ /* Skip if this board doesn't exist */
if (!apbs[i].RamIO)
continue;
- if (readb(apbs[i].RamIO + RAM_IT_TO_PC) != 0)
- FlagInt = TRUE;
+ spin_lock(&apbs[i].mutex);
+
+ /* Skip if this board doesn't want attention */
+ if(readb(apbs[i].RamIO + RAM_IT_TO_PC) == 0) {
+ spin_unlock(&apbs[i].mutex);
+ continue;
+ }
+
+ FlagInt = 1;
writeb(0, apbs[i].RamIO + RAM_IT_TO_PC);
if (readb(apbs[i].RamIO + DATA_TO_PC_READY) > 2) {
- printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataToPcReady = %d\n", i + 1, (int) readb(apbs[i].RamIO + DATA_TO_PC_READY));
+ printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataToPcReady = %d\n",
+ i+1,(int)readb(apbs[i].RamIO + DATA_TO_PC_READY));
DeviceErrorCount++;
}
- if ((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) && (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) {
- printk("APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n", i + 1, (int) readb(apbs[i].RamIO + DATA_FROM_PC_READY));
+
+ if((readb(apbs[i].RamIO + DATA_FROM_PC_READY) > 2) &&
+ (readb(apbs[i].RamIO + DATA_FROM_PC_READY) != 6)) {
+
+ printk(KERN_WARNING "APPLICOM driver interrupt err board %d, DataFromPcReady = %d\n",
+ i+1,(int)readb(apbs[i].RamIO + DATA_FROM_PC_READY));
DeviceErrorCount++;
}
+
if (readb(apbs[i].RamIO + DATA_TO_PC_READY) == 2) { /* mailbox sent by the card ? */
+ if (waitqueue_active(&FlagSleepRec)) {
wake_up_interruptible(&FlagSleepRec);
}
+ }
+
if (readb(apbs[i].RamIO + DATA_FROM_PC_READY) == 0) { /* ram i/o free for write by pc ? */
if (waitqueue_active(&apbs[i].FlagSleepSend)) { /* process sleep during read ? */
wake_up_interruptible(&apbs[i].FlagSleepSend);
}
Dummy = readb(apbs[i].RamIO + VERS);
- if (readb(apbs[i].RamIO + RAM_IT_TO_PC))
- i--; /* There's another int waiting on this card */
+ if(readb(apbs[i].RamIO + RAM_IT_TO_PC)) {
+ /* There's another int waiting on this card */
+ spin_unlock(&apbs[i].mutex);
+ i--;
+ } else {
+ spin_unlock(&apbs[i].mutex);
+ }
}
if (FlagInt)
LoopCount = 0;
else
LoopCount++;
- }
- while (LoopCount < 2);
+ } while(LoopCount < 2);
}
+
static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+
{ /* @ ADG ou ATO selon le cas */
int i;
unsigned char IndexCard;
unsigned long pmem;
+ int ret = 0;
volatile unsigned char byte_reset_it;
- struct st_ram_io adgl;
- unsigned char TmpRamIo[sizeof(struct st_ram_io)];
+ struct st_ram_io *adgl;
+ /* In general, the device is only openable by root anyway, so we're not
+ particularly concerned that bogus ioctls can flood the console. */
- if (copy_from_user(&adgl, (void *) arg, sizeof(struct st_ram_io)))
- return -EFAULT;
+ adgl = kmalloc(sizeof(struct st_ram_io), GFP_KERNEL);
+ if (!adgl)
+ return -ENOMEM;
- IndexCard = adgl.num_card - 1;
+ if (copy_from_user(adgl, (void *)arg,sizeof(struct st_ram_io))) {
+ kfree(adgl);
+ return -EFAULT;
+ }
- /*
- * FIXME: user can flood the console using bogus ioctls
- */
+ IndexCard = adgl->num_card-1;
- if (cmd != 0 && cmd != 6 && ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) {
- printk("APPLICOM driver IOCTL, bad board number %d\n", (int) IndexCard + 1);
- printk("apbs[%d].RamIO = %lx\n", IndexCard, apbs[IndexCard].RamIO);
+ if(cmd != 0 && cmd != 6 &&
+ ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) {
+ static int warncount = 10;
+ if (warncount) {
+ printk( KERN_WARNING "APPLICOM driver IOCTL, bad board number %d\n",(int)IndexCard+1);
+ warncount--;
+ }
+ kfree(adgl);
return -EINVAL;
}
- /*
- * FIXME races between ioctls with multiple clients
- */
-
switch (cmd) {
+
case 0:
pmem = apbs[IndexCard].RamIO;
for (i = 0; i < sizeof(struct st_ram_io); i++)
- TmpRamIo[i] = readb(pmem++);
- if (copy_to_user((void *) arg, TmpRamIo, sizeof(struct st_ram_io)))
- return -EFAULT;
+ ((unsigned char *)adgl)[i]=readb(pmem++);
+ if (copy_to_user((void *)arg, adgl, sizeof(struct st_ram_io)))
+ ret = -EFAULT;
break;
case 1:
pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
for (i = 0; i < 4; i++)
- adgl.conf_end_test[i] = readb(pmem++);
+ adgl->conf_end_test[i] = readb(pmem++);
for (i = 0; i < 2; i++)
- adgl.error_code[i] = readb(pmem++);
+ adgl->error_code[i] = readb(pmem++);
for (i = 0; i < 4; i++)
- adgl.parameter_error[i] = readb(pmem++);
+ adgl->parameter_error[i] = readb(pmem++);
pmem = apbs[IndexCard].RamIO + VERS;
- adgl.vers = readb(pmem);
+ adgl->vers = readb(pmem);
pmem = apbs[IndexCard].RamIO + TYPE_CARD;
for (i = 0; i < 20; i++)
- adgl.reserv1[i] = readb(pmem++);
- *(int *) &adgl.reserv1[20] = (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) + (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) + (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2));
-
- if (copy_to_user((void *) arg, &adgl, sizeof(struct st_ram_io)))
- return -EFAULT;
+ adgl->reserv1[i] = readb(pmem++);
+ *(int *)&adgl->reserv1[20] =
+ (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER) << 16) +
+ (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 1) << 8) +
+ (readb(apbs[IndexCard].RamIO + SERIAL_NUMBER + 2) );
+
+ if (copy_to_user((void *)arg, adgl, sizeof(struct st_ram_io)))
+ ret = -EFAULT;
break;
case 2:
pmem = apbs[IndexCard].RamIO + CONF_END_TEST;
for (i = 0; i < 10; i++)
writeb(0xff, pmem++);
- writeb(adgl.data_from_pc_ready, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
+ writeb(adgl->data_from_pc_ready,
+ apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
- /*
- * FIXME: can trash waitqueue that is active.
- */
-#if LINUX_VERSION_CODE > 0x20300
- init_waitqueue_head(&FlagSleepRec);
-#else
- FlagSleepRec = NULL;
-#endif
for (i = 0; i < MAX_BOARD; i++) {
if (apbs[i].RamIO) {
-#if LINUX_VERSION_CODE > 0x20300
- init_waitqueue_head(&apbs[i].FlagSleepSend);
-#else
- apbs[i].FlagSleepSend = NULL;
-#endif
byte_reset_it = readb(apbs[i].RamIO + RAM_IT_TO_PC);
}
}
break;
case 3:
pmem = apbs[IndexCard].RamIO + TIC_DES_FROM_PC;
- writeb(adgl.tic_des_from_pc, pmem);
+ writeb(adgl->tic_des_from_pc, pmem);
break;
case 4:
pmem = apbs[IndexCard].RamIO + TIC_OWNER_TO_PC;
- adgl.tic_owner_to_pc = readb(pmem++);
- adgl.numcard_owner_to_pc = readb(pmem);
- if (copy_to_user((void *) arg, &adgl, sizeof(struct st_ram_io)))
- return -EFAULT;
+ adgl->tic_owner_to_pc = readb(pmem++);
+ adgl->numcard_owner_to_pc = readb(pmem);
+ if (copy_to_user((void *)arg, adgl,sizeof(struct st_ram_io)))
+ ret = -EFAULT;
break;
case 5:
- writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
- writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
- writeb(adgl.num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
+ writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_OWNER_TO_PC);
+ writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_DES_FROM_PC);
+ writeb(adgl->num_card, apbs[IndexCard].RamIO + NUMCARD_ACK_FROM_PC);
writeb(4, apbs[IndexCard].RamIO + DATA_FROM_PC_READY);
writeb(1, apbs[IndexCard].RamIO + RAM_IT_FROM_PC);
break;
case 6:
- printk(KERN_INFO "APPLICOM driver release .... V2.8.0\n");
+ printk(KERN_INFO "APPLICOM driver release .... V2.8.0 ($Revision: 1.30 $)\n");
printk(KERN_INFO "Number of installed boards . %d\n", (int) numboards);
printk(KERN_INFO "Segment of board ........... %X\n", (int) mem);
printk(KERN_INFO "Interrupt IRQ number ....... %d\n", (int) irq);
if (!apbs[i].RamIO)
continue;
-
for (serial = 0; serial < SERIAL_NUMBER - TYPE_CARD; serial++)
boardname[serial] = readb(apbs[i].RamIO + TYPE_CARD + serial);
boardname[serial] = 0;
-
- printk(KERN_INFO "Prom version board %d ....... V%d.%d %s", i + 1, (int) (readb(apbs[IndexCard].RamIO + VERS) >> 4), (int) (readb(apbs[IndexCard].RamIO + VERS) & 0xF), boardname);
+ printk(KERN_INFO "Prom version board %d ....... V%d.%d %s",
+ i+1,
+ (int)(readb(apbs[IndexCard].RamIO + VERS) >> 4),
+ (int)(readb(apbs[IndexCard].RamIO + VERS) & 0xF),
+ boardname);
- serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) + (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) + (readb(apbs[i].RamIO + SERIAL_NUMBER + 2));
+ serial = (readb(apbs[i].RamIO + SERIAL_NUMBER) << 16) +
+ (readb(apbs[i].RamIO + SERIAL_NUMBER + 1) << 8) +
+ (readb(apbs[i].RamIO + SERIAL_NUMBER + 2) );
if (serial != 0)
printk(" S/N %d\n", serial);
if (WriteErrorCount != 0)
printk(KERN_INFO "WriteErrorCount ............ %d\n", WriteErrorCount);
if (waitqueue_active(&FlagSleepRec))
- printk("Process in read pending\n");
+ printk(KERN_INFO "Process in read pending\n");
for (i = 0; i < MAX_BOARD; i++) {
if (apbs[i].RamIO && waitqueue_active(&apbs[i].FlagSleepSend))
- printk("Process in write pending board %d\n", i + 1);
+ printk(KERN_INFO "Process in write pending board %d\n",i+1);
}
break;
default:
- printk("APPLICOM driver ioctl, unknown function code %d\n", cmd);
- return -EINVAL;
+ printk(KERN_INFO "APPLICOM driver ioctl, unknown function code %d\n",cmd) ;
+ ret = -EINVAL;
break;
}
Dummy = readb(apbs[IndexCard].RamIO + VERS);
+ kfree(adgl);
return 0;
}
}
if (ints[0] < 2) {
- printk("applicom numargs: %d\n", ints[0]);
+ printk(KERN_INFO"applicom numargs: %d\n", ints[0]);
return 0;
}
return 1;
}
-#if LINUX_VERSION_CODE > 0x20300
__setup("applicom=", applicom_setup);
-#endif
+
#endif /* MODULE */
+
--- /dev/null
+/*
+ * cpia CPiA driver
+ *
+ * Supports CPiA based Video Camera's.
+ *
+ * (C) Copyright 1999-2000 Peter Pregler,
+ * (C) Copyright 1999-2000 Scott J. Bertin,
+ * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@valinux.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 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.
+ */
+
+/* #define _CPIA_DEBUG_ define for verbose debug output */
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/pagemap.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <linux/wrapper.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include "cpia.h"
+
+#ifdef CONFIG_VIDEO_CPIA_PP
+extern int cpia_pp_init(void);
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB
+extern int cpia_usb_init(void);
+#endif
+
+#ifdef MODULE
+MODULE_AUTHOR("Scott J. Bertin <sbertin@mindspring.com> & Peter Pregler <Peter_Pregler@email.com> & Johannes Erdfelt <jerdfelt@valinux.com>");
+MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras");
+MODULE_SUPPORTED_DEVICE("video");
+#endif
+
+#define ABOUT "V4L-Driver for Vision CPiA based cameras"
+
+#ifndef VID_HARDWARE_CPIA
+#define VID_HARDWARE_CPIA 24 /* FIXME -> from linux/videodev.h */
+#endif
+
+#define CPIA_MODULE_CPIA (0<<5)
+#define CPIA_MODULE_SYSTEM (1<<5)
+#define CPIA_MODULE_VP_CTRL (5<<5)
+#define CPIA_MODULE_CAPTURE (6<<5)
+#define CPIA_MODULE_DEBUG (7<<5)
+
+#define INPUT (DATA_IN << 8)
+#define OUTPUT (DATA_OUT << 8)
+
+#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
+#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
+#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
+#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
+#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
+#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
+#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
+#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
+
+#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
+#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
+#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
+#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
+#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
+#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
+#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
+#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
+#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
+#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
+#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
+#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
+#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
+
+#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
+#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
+#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
+#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
+#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
+#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
+#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
+#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
+#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
+#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
+#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
+#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
+#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
+#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
+#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
+#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
+
+#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
+#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
+#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
+#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
+#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
+#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
+#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
+#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
+#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
+#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
+#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
+#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
+#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
+#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
+
+#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
+#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
+#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
+#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
+#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
+#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
+#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
+
+enum {
+ FRAME_READY, /* Ready to grab into */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+};
+
+#define COMMAND_NONE 0x0000
+#define COMMAND_SETCOMPRESSION 0x0001
+#define COMMAND_SETCOMPRESSIONTARGET 0x0002
+#define COMMAND_SETCOLOURPARAMS 0x0004
+#define COMMAND_SETFORMAT 0x0008
+#define COMMAND_PAUSE 0x0010
+#define COMMAND_RESUME 0x0020
+#define COMMAND_SETYUVTHRESH 0x0040
+#define COMMAND_SETECPTIMING 0x0080
+#define COMMAND_SETCOMPRESSIONPARAMS 0x0100
+#define COMMAND_SETEXPOSURE 0x0200
+#define COMMAND_SETCOLOURBALANCE 0x0400
+#define COMMAND_SETSENSORFPS 0x0800
+#define COMMAND_SETAPCOR 0x1000
+#define COMMAND_SETFLICKERCTRL 0x2000
+#define COMMAND_SETVLOFFSET 0x4000
+
+/* Developer's Guide Table 5 p 3-34
+ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
+static u8 flicker_jumps[2][2][4] =
+{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
+ { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
+};
+
+/* forward declaration of local function */
+static void reset_camera_struct(struct cam_data *cam);
+
+/**********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.29 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
+ * there will be one, but apparentely not yet - jerdfelt
+ *
+ **********************************************************************/
+
+/* 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 = page_address(pte_page(pte)) |
+ (adr & (PAGE_SIZE-1));
+ }
+ }
+ 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);
+ return ret;
+}
+
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr, page;
+
+ /* Round it off to PAGE_SIZE */
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ mem = vmalloc(size);
+ if (!mem)
+ return NULL;
+
+ 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(MAP_NR(__va(page)));
+ adr += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr, page;
+
+ if (!mem)
+ return;
+
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ page = kvirt_to_pa(adr);
+ mem_map_unreserve(MAP_NR(__va(page)));
+ adr += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+ vfree(mem);
+}
+
+/**********************************************************************
+ *
+ * /proc interface
+ *
+ **********************************************************************/
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *cpia_proc_root=NULL;
+
+static int cpia_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ char *out = page;
+ int len, tmp;
+ struct cam_data *cam = data;
+ char tmpstr[20];
+
+ /* IMPORTANT: This output MUST be kept under PAGE_SIZE
+ * or we need to get more sophisticated. */
+
+ out += sprintf(out, "read-only\n-----------------------\n");
+ out += sprintf(out, "V4L Driver version: %d.%d.%d\n",
+ CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+ out += sprintf(out, "CPIA Version: %d.%02d (%d.%d)\n",
+ cam->params.version.firmwareVersion,
+ cam->params.version.firmwareRevision,
+ cam->params.version.vcVersion,
+ cam->params.version.vcRevision);
+ out += sprintf(out, "CPIA PnP-ID: %04x:%04x:%04x\n",
+ cam->params.pnpID.vendor, cam->params.pnpID.product,
+ cam->params.pnpID.deviceRevision);
+ out += sprintf(out, "VP-Version: %d.%d %04x\n",
+ cam->params.vpVersion.vpVersion,
+ cam->params.vpVersion.vpRevision,
+ cam->params.vpVersion.cameraHeadID);
+
+ out += sprintf(out, "system_state: %#04x\n",
+ cam->params.status.systemState);
+ out += sprintf(out, "grab_state: %#04x\n",
+ cam->params.status.grabState);
+ out += sprintf(out, "stream_state: %#04x\n",
+ cam->params.status.streamState);
+ out += sprintf(out, "fatal_error: %#04x\n",
+ cam->params.status.fatalError);
+ out += sprintf(out, "cmd_error: %#04x\n",
+ cam->params.status.cmdError);
+ out += sprintf(out, "debug_flags: %#04x\n",
+ cam->params.status.debugFlags);
+ out += sprintf(out, "vp_status: %#04x\n",
+ cam->params.status.vpStatus);
+ out += sprintf(out, "error_code: %#04x\n",
+ cam->params.status.errorCode);
+ out += sprintf(out, "video_size: %s\n",
+ cam->params.format.videoSize == VIDEOSIZE_CIF ?
+ "CIF " : "QCIF");
+ out += sprintf(out, "sub_sample: %s\n",
+ cam->params.format.subSample == SUBSAMPLE_420 ?
+ "420" : "422");
+ out += sprintf(out, "yuv_order: %s\n",
+ cam->params.format.yuvOrder == YUVORDER_YUYV ?
+ "YUYV" : "UYVY");
+ out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n",
+ cam->params.roi.colStart*8,
+ cam->params.roi.rowStart*4,
+ cam->params.roi.colEnd*8,
+ cam->params.roi.rowEnd*4);
+ out += sprintf(out, "actual_fps: %3d\n", cam->fps);
+ out += sprintf(out, "transfer_rate: %4dkB/s\n",
+ cam->transfer_rate);
+
+ out += sprintf(out, "\nread-write\n");
+ out += sprintf(out, "----------------------- current min"
+ " max default comment\n");
+ out += sprintf(out, "brightness: %8d %8d %8d %8d\n",
+ cam->params.colourParams.brightness, 0, 100, 50);
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2)
+ /* 1-02 firmware limits contrast to 80 */
+ tmp = 80;
+ else
+ tmp = 96;
+
+ out += sprintf(out, "contrast: %8d %8d %8d %8d"
+ " steps of 8\n",
+ cam->params.colourParams.contrast, 0, tmp, 48);
+ out += sprintf(out, "saturation: %8d %8d %8d %8d\n",
+ cam->params.colourParams.saturation, 0, 100, 50);
+ tmp = (25000+5000*cam->params.sensorFps.baserate)/
+ (1<<cam->params.sensorFps.divisor);
+ out += sprintf(out, "sensor_fps: %4d.%03d %8d %8d %8d\n",
+ tmp/1000, tmp%1000, 3, 30, 15);
+ out += sprintf(out, "stream_start_line: %8d %8d %8d %8d\n",
+ 2*cam->params.streamStartLine, 0,
+ cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144,
+ cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120);
+ out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n",
+ cam->params.ecpTiming ? "slow" : "normal", "slow",
+ "normal", "normal");
+
+ if (cam->params.colourBalance.balanceModeIsAuto) {
+ sprintf(tmpstr, "auto");
+ } else {
+ sprintf(tmpstr, "manual");
+ }
+ out += sprintf(out, "color_balance_mode: %8s %8s %8s"
+ " %8s\n", tmpstr, "manual", "auto", "auto");
+ out += sprintf(out, "red_gain: %8d %8d %8d %8d\n",
+ cam->params.colourBalance.redGain, 0, 212, 32);
+ out += sprintf(out, "green_gain: %8d %8d %8d %8d\n",
+ cam->params.colourBalance.greenGain, 0, 212, 6);
+ out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n",
+ cam->params.colourBalance.blueGain, 0, 212, 92);
+
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2)
+ /* 1-02 firmware limits gain to 2 */
+ sprintf(tmpstr, "%8d %8d", 1, 2);
+ else
+ sprintf(tmpstr, "1,2,4,8");
+
+ if (cam->params.exposure.gainMode == 0)
+ out += sprintf(out, "max_gain: unknown %18s"
+ " %8d\n", tmpstr, 2);
+ else
+ out += sprintf(out, "max_gain: %8d %18s %8d\n",
+ 1<<(cam->params.exposure.gainMode-1), tmpstr, 2);
+
+ switch(cam->params.exposure.expMode) {
+ case 1:
+ case 3:
+ sprintf(tmpstr, "manual");
+ break;
+ case 2:
+ sprintf(tmpstr, "auto");
+ break;
+ default:
+ sprintf(tmpstr, "unknown");
+ break;
+ }
+ out += sprintf(out, "exposure_mode: %8s %8s %8s"
+ " %8s\n", tmpstr, "manual", "auto", "auto");
+ out += sprintf(out, "centre_weight: %8s %8s %8s %8s\n",
+ (2-cam->params.exposure.centreWeight) ? "on" : "off",
+ "off", "on", "on");
+ out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n",
+ 1<<cam->params.exposure.gain, 1, 1);
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2)
+ /* 1-02 firmware limits fineExp to 127 */
+ tmp = 255;
+ else
+ tmp = 511;
+
+ out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n",
+ cam->params.exposure.fineExp*2, 0, tmp, 0);
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2)
+ /* 1-02 firmware limits coarseExpHi to 0 */
+ tmp = 255;
+ else
+ tmp = 65535;
+
+ out += sprintf(out, "coarse_exp: %8d %8d %8d"
+ " %8d\n", cam->params.exposure.coarseExpLo+
+ 256*cam->params.exposure.coarseExpHi, 0, tmp, 185);
+ out += sprintf(out, "red_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.redComp, 220, 255, 220);
+ out += sprintf(out, "green1_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.green1Comp, 214, 255, 214);
+ out += sprintf(out, "green2_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.green2Comp, 214, 255, 214);
+ out += sprintf(out, "blue_comp: %8d %8d %8d %8d\n",
+ cam->params.exposure.blueComp, 230, 255, 230);
+
+ out += sprintf(out, "apcor_gain1: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain1, 0, 0xff, 0x1c);
+ out += sprintf(out, "apcor_gain2: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain2, 0, 0xff, 0x1a);
+ out += sprintf(out, "apcor_gain4: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain4, 0, 0xff, 0x2d);
+ out += sprintf(out, "apcor_gain8: %#8x %#8x %#8x %#8x\n",
+ cam->params.apcor.gain8, 0, 0xff, 0x2a);
+ out += sprintf(out, "vl_offset_gain1: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain1, 0, 255, 24);
+ out += sprintf(out, "vl_offset_gain2: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain2, 0, 255, 28);
+ out += sprintf(out, "vl_offset_gain4: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain4, 0, 255, 30);
+ out += sprintf(out, "vl_offset_gain8: %8d %8d %8d %8d\n",
+ cam->params.vlOffset.gain8, 0, 255, 30);
+ out += sprintf(out, "flicker_control: %8s %8s %8s %8s\n",
+ cam->params.flickerControl.flickerMode ? "on" : "off",
+ "off", "on", "off");
+ out += sprintf(out, "mains_frequency: %8d %8d %8d %8d"
+ " only 50/60\n",
+ cam->mainsFreq ? 60 : 50, 50, 60, 50);
+ out += sprintf(out, "allowable_overexposure: %8d %8d %8d %8d\n",
+ cam->params.flickerControl.allowableOverExposure, 0,
+ 255, 0);
+ out += sprintf(out, "compression_mode: ");
+ switch(cam->params.compression.mode) {
+ case CPIA_COMPRESSION_NONE:
+ out += sprintf(out, "%8s", "none");
+ break;
+ case CPIA_COMPRESSION_AUTO:
+ out += sprintf(out, "%8s", "auto");
+ break;
+ case CPIA_COMPRESSION_MANUAL:
+ out += sprintf(out, "%8s", "manual");
+ break;
+ default:
+ out += sprintf(out, "%8s", "unknown");
+ break;
+ }
+ out += sprintf(out, " none,auto,manual auto\n");
+ out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n",
+ cam->params.compression.decimation ==
+ DECIMATION_ENAB ? "on":"off", "off", "off",
+ "off");
+ out += sprintf(out, "compression_target: %9s %9s %9s %9s\n",
+ cam->params.compressionTarget.frTargeting ==
+ CPIA_COMPRESSION_TARGET_FRAMERATE ?
+ "framerate":"quality",
+ "framerate", "quality", "quality");
+ out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n",
+ cam->params.compressionTarget.targetFR, 0, 30, 7);
+ out += sprintf(out, "target_quality: %8d %8d %8d %8d\n",
+ cam->params.compressionTarget.targetQ, 0, 255, 10);
+ out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n",
+ cam->params.yuvThreshold.yThreshold, 0, 31, 15);
+ out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n",
+ cam->params.yuvThreshold.uvThreshold, 0, 31, 15);
+ out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.hysteresis, 0, 255, 3);
+ out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.threshMax, 0, 255, 11);
+ out += sprintf(out, "small_step: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.smallStep, 0, 255, 1);
+ out += sprintf(out, "large_step: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.largeStep, 0, 255, 3);
+ out += sprintf(out, "decimation_hysteresis: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.decimationHysteresis,
+ 0, 255, 2);
+ out += sprintf(out, "fr_diff_step_thresh: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.frDiffStepThresh,
+ 0, 255, 5);
+ out += sprintf(out, "q_diff_step_thresh: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.qDiffStepThresh,
+ 0, 255, 3);
+ out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n",
+ cam->params.compressionParams.decimationThreshMod,
+ 0, 255, 2);
+
+ len = out - page;
+ len -= off;
+ if (len < count) {
+ *eof = 1;
+ if (len <= 0) return 0;
+ } else
+ len = count;
+
+ *start = page + off;
+ return len;
+}
+
+static int cpia_write_proc(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ struct cam_data *cam = data;
+ struct cam_params new_params;
+ int retval, find_colon;
+ int size = count;
+ unsigned long val;
+ u32 command_flags = 0;
+ u8 new_mains;
+
+ if (down_interruptible(&cam->param_lock))
+ return -ERESTARTSYS;
+
+ /*
+ * Skip over leading whitespace
+ */
+ while (count && isspace(*buffer)) {
+ --count;
+ ++buffer;
+ }
+
+ memcpy(&new_params, &cam->params, sizeof(struct cam_params));
+ new_mains = cam->mainsFreq;
+
+#define MATCH(x) \
+ ({ \
+ int _len = strlen(x), _ret, _colon_found; \
+ _ret = (_len <= count && strncmp(buffer, x, _len) == 0); \
+ if (_ret) { \
+ buffer += _len; \
+ count -= _len; \
+ if (find_colon) { \
+ _colon_found = 0; \
+ while (count && (*buffer == ' ' || *buffer == '\t' || \
+ (!_colon_found && *buffer == ':'))) { \
+ if (*buffer == ':') \
+ _colon_found = 1; \
+ --count; \
+ ++buffer; \
+ } \
+ if (!count || !_colon_found) \
+ retval = -EINVAL; \
+ find_colon = 0; \
+ } \
+ } \
+ _ret; \
+ })
+#define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \
+ new_params.version.firmwareRevision == (y))
+#define VALUE \
+ ({ \
+ char *_p; \
+ unsigned long int _ret; \
+ _ret = simple_strtoul(buffer, &_p, 0); \
+ if (_p == buffer) \
+ retval = -EINVAL; \
+ else { \
+ count -= _p - buffer; \
+ buffer = _p; \
+ } \
+ _ret; \
+ })
+
+ retval = 0;
+ while (count && !retval) {
+ find_colon = 1;
+ if (MATCH("brightness")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 100)
+ new_params.colourParams.brightness = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURPARAMS;
+ } else if (MATCH("contrast")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 100) {
+ /* contrast is in steps of 8, so round*/
+ val = ((val + 3) / 8) * 8;
+ /* 1-02 firmware limits contrast to 80*/
+ if (FIRMWARE_VERSION(1,2) && val > 80)
+ val = 80;
+
+ new_params.colourParams.contrast = val;
+ } else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURPARAMS;
+ } else if (MATCH("saturation")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 100)
+ new_params.colourParams.saturation = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURPARAMS;
+ } else if (MATCH("sensor_fps")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ /* find values so that sensorFPS is minimized,
+ * but >= val */
+ if (val > 30)
+ retval = -EINVAL;
+ else if (val > 25) {
+ new_params.sensorFps.divisor = 0;
+ new_params.sensorFps.baserate = 1;
+ } else if (val > 15) {
+ new_params.sensorFps.divisor = 0;
+ new_params.sensorFps.baserate = 0;
+ } else if (val > 12) {
+ new_params.sensorFps.divisor = 1;
+ new_params.sensorFps.baserate = 1;
+ } else if (val > 7) {
+ new_params.sensorFps.divisor = 1;
+ new_params.sensorFps.baserate = 0;
+ } else if (val > 6) {
+ new_params.sensorFps.divisor = 2;
+ new_params.sensorFps.baserate = 1;
+ } else if (val > 3) {
+ new_params.sensorFps.divisor = 2;
+ new_params.sensorFps.baserate = 0;
+ } else {
+ new_params.sensorFps.divisor = 3;
+ /* Either base rate would work here */
+ new_params.sensorFps.baserate = 1;
+ }
+ new_params.flickerControl.coarseJump =
+ flicker_jumps[new_mains]
+ [new_params.sensorFps.baserate]
+ [new_params.sensorFps.divisor];
+ if (new_params.flickerControl.flickerMode)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ }
+ command_flags |= COMMAND_SETSENSORFPS;
+ } else if (MATCH("stream_start_line")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ int max_line = 288;
+
+ if (new_params.format.videoSize == VIDEOSIZE_QCIF)
+ max_line = 144;
+ if (val <= max_line)
+ new_params.streamStartLine = val/2;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("ecp_timing")) {
+ if (!retval && MATCH("normal"))
+ new_params.ecpTiming = 0;
+ else if (!retval && MATCH("slow"))
+ new_params.ecpTiming = 1;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETECPTIMING;
+ } else if (MATCH("color_balance_mode")) {
+ if (!retval && MATCH("manual"))
+ new_params.colourBalance.balanceModeIsAuto = 0;
+ else if (!retval && MATCH("auto"))
+ new_params.colourBalance.balanceModeIsAuto = 1;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ } else if (MATCH("red_gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 212)
+ new_params.colourBalance.redGain = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ } else if (MATCH("green_gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 212)
+ new_params.colourBalance.greenGain = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ } else if (MATCH("blue_gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 212)
+ new_params.colourBalance.blueGain = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOLOURBALANCE;
+ } else if (MATCH("max_gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ /* 1-02 firmware limits gain to 2 */
+ if (FIRMWARE_VERSION(1,2) && val > 2)
+ val = 2;
+ switch(val) {
+ case 1:
+ new_params.exposure.gainMode = 1;
+ break;
+ case 2:
+ new_params.exposure.gainMode = 2;
+ break;
+ case 4:
+ new_params.exposure.gainMode = 3;
+ break;
+ case 8:
+ new_params.exposure.gainMode = 4;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ }
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else if (MATCH("exposure_mode")) {
+ if (!retval && MATCH("auto"))
+ new_params.exposure.expMode = 2;
+ else if (!retval && MATCH("manual")) {
+ if (new_params.exposure.expMode == 2)
+ new_params.exposure.expMode = 3;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else if (MATCH("centre_weight")) {
+ if (!retval && MATCH("on"))
+ new_params.exposure.centreWeight = 1;
+ else if (!retval && MATCH("off"))
+ new_params.exposure.centreWeight = 2;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else if (MATCH("gain")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ switch(val) {
+ case 1:
+ new_params.exposure.gain = 0;
+ new_params.exposure.expMode = 1;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ break;
+ case 2:
+ new_params.exposure.gain = 1;
+ new_params.exposure.expMode = 1;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ break;
+ case 4:
+ new_params.exposure.gain = 2;
+ new_params.exposure.expMode = 1;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ break;
+ case 8:
+ new_params.exposure.gain = 3;
+ new_params.exposure.expMode = 1;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ command_flags |= COMMAND_SETEXPOSURE;
+ if (new_params.exposure.gain >
+ new_params.exposure.gainMode-1)
+ retval = -EINVAL;
+ }
+ } else if (MATCH("fine_exp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val < 256) {
+ /* 1-02 firmware limits fineExp to 127*/
+ if (FIRMWARE_VERSION(1,2) && val > 127)
+ val = 127;
+ new_params.exposure.fineExp = val;
+ new_params.exposure.expMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("coarse_exp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val < 65536) {
+ /* 1-02 firmware limits
+ * coarseExp to 255 */
+ if (FIRMWARE_VERSION(1,2) && val > 255)
+ val = 255;
+ new_params.exposure.coarseExpLo =
+ val & 0xff;
+ new_params.exposure.coarseExpHi =
+ val >> 8;
+ new_params.exposure.expMode = 1;
+ command_flags |= COMMAND_SETEXPOSURE;
+ new_params.flickerControl.flickerMode = 0;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("red_comp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val >= 220 && val <= 255) {
+ new_params.exposure.redComp = val;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("green1_comp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val >= 214 && val <= 255) {
+ new_params.exposure.green1Comp = val;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("green2_comp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val >= 214 && val <= 255) {
+ new_params.exposure.green2Comp = val;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("blue_comp")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val >= 230 && val <= 255) {
+ new_params.exposure.blueComp = val;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("apcor_gain1")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if (val <= 0xff)
+ new_params.apcor.gain1 = val;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("apcor_gain2")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if (val <= 0xff)
+ new_params.apcor.gain2 = val;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("apcor_gain4")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if (val <= 0xff)
+ new_params.apcor.gain4 = val;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("apcor_gain8")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ command_flags |= COMMAND_SETAPCOR;
+ if (val <= 0xff)
+ new_params.apcor.gain8 = val;
+ else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("vl_offset_gain1")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.vlOffset.gain1 = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if (MATCH("vl_offset_gain2")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.vlOffset.gain2 = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if (MATCH("vl_offset_gain4")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.vlOffset.gain4 = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if (MATCH("vl_offset_gain8")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.vlOffset.gain8 = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETVLOFFSET;
+ } else if (MATCH("flicker_control")) {
+ if (!retval && MATCH("on")) {
+ new_params.flickerControl.flickerMode = 1;
+ new_params.exposure.expMode = 2;
+ command_flags |= COMMAND_SETEXPOSURE;
+ } else if (!retval && MATCH("off"))
+ new_params.flickerControl.flickerMode = 0;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else if (MATCH("mains_frequency")) {
+ if (!retval && MATCH("50")) {
+ new_mains = 0;
+ new_params.flickerControl.coarseJump =
+ flicker_jumps[new_mains]
+ [new_params.sensorFps.baserate]
+ [new_params.sensorFps.divisor];
+ if (new_params.flickerControl.flickerMode)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else if (!retval && MATCH("60")) {
+ new_mains = 1;
+ new_params.flickerControl.coarseJump =
+ flicker_jumps[new_mains]
+ [new_params.sensorFps.baserate]
+ [new_params.sensorFps.divisor];
+ if (new_params.flickerControl.flickerMode)
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else
+ retval = -EINVAL;
+ } else if (MATCH("allowable_overexposure")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff) {
+ new_params.flickerControl.
+ allowableOverExposure = val;
+ command_flags |= COMMAND_SETFLICKERCTRL;
+ } else
+ retval = -EINVAL;
+ }
+ } else if (MATCH("compression_mode")) {
+ if (!retval && MATCH("none"))
+ new_params.compression.mode =
+ CPIA_COMPRESSION_NONE;
+ else if (!retval && MATCH("auto"))
+ new_params.compression.mode =
+ CPIA_COMPRESSION_AUTO;
+ else if (!retval && MATCH("manual"))
+ new_params.compression.mode =
+ CPIA_COMPRESSION_MANUAL;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETCOMPRESSION;
+ } else if (MATCH("decimation_enable")) {
+ if (!retval && MATCH("off"))
+ new_params.compression.decimation = 0;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETCOMPRESSION;
+ } else if (MATCH("compression_target")) {
+ if (!retval && MATCH("quality"))
+ new_params.compressionTarget.frTargeting =
+ CPIA_COMPRESSION_TARGET_QUALITY;
+ else if (!retval && MATCH("framerate"))
+ new_params.compressionTarget.frTargeting =
+ CPIA_COMPRESSION_TARGET_FRAMERATE;
+ else
+ retval = -EINVAL;
+
+ command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+ } else if (MATCH("target_framerate")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval)
+ new_params.compressionTarget.targetFR = val;
+ command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+ } else if (MATCH("target_quality")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval)
+ new_params.compressionTarget.targetQ = val;
+
+ command_flags |= COMMAND_SETCOMPRESSIONTARGET;
+ } else if (MATCH("y_threshold")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val < 32)
+ new_params.yuvThreshold.yThreshold = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETYUVTHRESH;
+ } else if (MATCH("uv_threshold")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val < 32)
+ new_params.yuvThreshold.uvThreshold = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETYUVTHRESH;
+ } else if (MATCH("hysteresis")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.hysteresis = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("threshold_max")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.threshMax = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("small_step")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.smallStep = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("large_step")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.largeStep = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("decimation_hysteresis")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.decimationHysteresis = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("fr_diff_step_thresh")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.frDiffStepThresh = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("q_diff_step_thresh")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.qDiffStepThresh = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else if (MATCH("decimation_thresh_mod")) {
+ if (!retval)
+ val = VALUE;
+
+ if (!retval) {
+ if (val <= 0xff)
+ new_params.compressionParams.decimationThreshMod = val;
+ else
+ retval = -EINVAL;
+ }
+ command_flags |= COMMAND_SETCOMPRESSIONPARAMS;
+ } else {
+ DBG("No match found\n");
+ retval = -EINVAL;
+ }
+
+ if (!retval) {
+ while (count && isspace(*buffer) && *buffer != '\n') {
+ --count;
+ ++buffer;
+ }
+ if (count) {
+ if (*buffer != '\n' && *buffer != ';')
+ retval = -EINVAL;
+ else {
+ --count;
+ ++buffer;
+ }
+ }
+ }
+ }
+#undef MATCH
+#undef FIRMWARE_VERSION
+#undef VALUE
+#undef FIND_VALUE
+#undef FIND_END
+ if (!retval) {
+ if (command_flags & COMMAND_SETCOLOURPARAMS) {
+ /* Adjust cam->vp to reflect these changes */
+ cam->vp.brightness =
+ new_params.colourParams.brightness*65535/100;
+ cam->vp.contrast =
+ new_params.colourParams.contrast*65535/100;
+ cam->vp.colour =
+ new_params.colourParams.saturation*65535/100;
+ }
+
+ memcpy(&cam->params, &new_params, sizeof(struct cam_params));
+ cam->mainsFreq = new_mains;
+ cam->cmd_queue |= command_flags;
+ retval = size;
+ } else
+ DBG("error: %d\n", retval);
+
+ up(&cam->param_lock);
+
+ return retval;
+}
+
+static void create_proc_cpia_cam(struct cam_data *cam)
+{
+ char name[7];
+ struct proc_dir_entry *ent;
+
+ if (!cpia_proc_root || !cam)
+ return;
+
+ sprintf(name, "video%d", cam->vdev.minor);
+
+ ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root);
+ if (!ent)
+ return;
+
+ ent->data = cam;
+ ent->read_proc = cpia_read_proc;
+ ent->write_proc = cpia_write_proc;
+ ent->size = 3626;
+ cam->proc_entry = ent;
+}
+
+static void destroy_proc_cpia_cam(struct cam_data *cam)
+{
+ char name[7];
+
+ if (!cam || !cam->proc_entry)
+ return;
+
+ sprintf(name, "video%d", cam->vdev.minor);
+ remove_proc_entry(name, cpia_proc_root);
+ cam->proc_entry = NULL;
+}
+
+static void proc_cpia_create(void)
+{
+ cpia_proc_root = create_proc_entry("cpia", S_IFDIR, 0);
+
+ if (cpia_proc_root)
+ cpia_proc_root->owner = THIS_MODULE;
+ else
+ LOG("Unable to initialise /proc/cpia\n");
+}
+
+static void proc_cpia_destroy(void)
+{
+ remove_proc_entry("cpia", 0);
+}
+#endif /* CONFIG_PROC_FS */
+
+/* ----------------------- debug functions ---------------------- */
+
+#define printstatus(cam) \
+ DBG("%02x %02x %02x %02x %02x %02x %02x %02x\n",\
+ cam->params.status.systemState, cam->params.status.grabState, \
+ cam->params.status.streamState, cam->params.status.fatalError, \
+ cam->params.status.cmdError, cam->params.status.debugFlags, \
+ cam->params.status.vpStatus, cam->params.status.errorCode);
+
+/* ----------------------- v4l helpers -------------------------- */
+
+/* supported frame palettes and depths */
+static inline int valid_mode(u16 palette, u16 depth)
+{
+ return (palette == VIDEO_PALETTE_GREY && depth == 8) ||
+ (palette == VIDEO_PALETTE_RGB555 && depth == 16) ||
+ (palette == VIDEO_PALETTE_RGB565 && depth == 16) ||
+ (palette == VIDEO_PALETTE_RGB24 && depth == 24) ||
+ (palette == VIDEO_PALETTE_RGB32 && depth == 32) ||
+ (palette == VIDEO_PALETTE_YUV422 && depth == 16) ||
+ (palette == VIDEO_PALETTE_YUYV && depth == 16) ||
+ (palette == VIDEO_PALETTE_UYVY && depth == 16);
+}
+
+static int match_videosize( int width, int height )
+{
+ /* return the best match, where 'best' is as always
+ * the largest that is not bigger than what is requested. */
+ if (width>=352 && height>=288)
+ return VIDEOSIZE_352_288; /* CIF */
+
+ if (width>=320 && height>=240)
+ return VIDEOSIZE_320_240; /* SIF */
+
+ if (width>=288 && height>=216)
+ return VIDEOSIZE_288_216;
+
+ if (width>=256 && height>=192)
+ return VIDEOSIZE_256_192;
+
+ if (width>=224 && height>=168)
+ return VIDEOSIZE_224_168;
+
+ if (width>=192 && height>=144)
+ return VIDEOSIZE_192_144;
+
+ if (width>=176 && height>=144)
+ return VIDEOSIZE_176_144; /* QCIF */
+
+ if (width>=160 && height>=120)
+ return VIDEOSIZE_160_120; /* QSIF */
+
+ if (width>=128 && height>=96)
+ return VIDEOSIZE_128_96;
+
+ if (width>=88 && height>=72)
+ return VIDEOSIZE_88_72;
+
+ if (width>=64 && height>=48)
+ return VIDEOSIZE_64_48;
+
+ if (width>=48 && height>=48)
+ return VIDEOSIZE_48_48;
+
+ return -1;
+}
+
+/* these are the capture sizes we support */
+static void set_vw_size(struct cam_data *cam)
+{
+ /* the col/row/start/end values are the result of simple math */
+ /* study the SetROI-command in cpia developers guide p 2-22 */
+ /* streamStartLine is set to the recommended value in the cpia */
+ /* developers guide p 3-37 */
+ switch(cam->video_size) {
+ case VIDEOSIZE_CIF:
+ cam->vw.width = 352;
+ cam->vw.height = 288;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=0;
+ cam->params.roi.colEnd=44;
+ cam->params.roi.rowStart=0;
+ cam->params.roi.rowEnd=72;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_SIF:
+ cam->vw.width = 320;
+ cam->vw.height = 240;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=2;
+ cam->params.roi.colEnd=42;
+ cam->params.roi.rowStart=6;
+ cam->params.roi.rowEnd=66;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_288_216:
+ cam->vw.width = 288;
+ cam->vw.height = 216;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=4;
+ cam->params.roi.colEnd=40;
+ cam->params.roi.rowStart=9;
+ cam->params.roi.rowEnd=63;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_256_192:
+ cam->vw.width = 256;
+ cam->vw.height = 192;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=6;
+ cam->params.roi.colEnd=38;
+ cam->params.roi.rowStart=12;
+ cam->params.roi.rowEnd=60;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_224_168:
+ cam->vw.width = 224;
+ cam->vw.height = 168;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=8;
+ cam->params.roi.colEnd=36;
+ cam->params.roi.rowStart=15;
+ cam->params.roi.rowEnd=57;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_192_144:
+ cam->vw.width = 192;
+ cam->vw.height = 144;
+ cam->params.format.videoSize=VIDEOSIZE_CIF;
+ cam->params.roi.colStart=10;
+ cam->params.roi.colEnd=34;
+ cam->params.roi.rowStart=18;
+ cam->params.roi.rowEnd=54;
+ cam->params.streamStartLine = 120;
+ break;
+ case VIDEOSIZE_QCIF:
+ cam->vw.width = 176;
+ cam->vw.height = 144;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=0;
+ cam->params.roi.colEnd=22;
+ cam->params.roi.rowStart=0;
+ cam->params.roi.rowEnd=36;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_QSIF:
+ cam->vw.width = 160;
+ cam->vw.height = 120;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=1;
+ cam->params.roi.colEnd=21;
+ cam->params.roi.rowStart=3;
+ cam->params.roi.rowEnd=33;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_128_96:
+ cam->vw.width = 128;
+ cam->vw.height = 96;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=3;
+ cam->params.roi.colEnd=19;
+ cam->params.roi.rowStart=6;
+ cam->params.roi.rowEnd=30;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_88_72:
+ cam->vw.width = 88;
+ cam->vw.height = 72;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=5;
+ cam->params.roi.colEnd=16;
+ cam->params.roi.rowStart=9;
+ cam->params.roi.rowEnd=27;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_64_48:
+ cam->vw.width = 64;
+ cam->vw.height = 48;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=7;
+ cam->params.roi.colEnd=15;
+ cam->params.roi.rowStart=12;
+ cam->params.roi.rowEnd=24;
+ cam->params.streamStartLine = 60;
+ break;
+ case VIDEOSIZE_48_48:
+ cam->vw.width = 48;
+ cam->vw.height = 48;
+ cam->params.format.videoSize=VIDEOSIZE_QCIF;
+ cam->params.roi.colStart=8;
+ cam->params.roi.colEnd=14;
+ cam->params.roi.rowStart=6;
+ cam->params.roi.rowEnd=30;
+ cam->params.streamStartLine = 60;
+ break;
+ default:
+ LOG("bad videosize value: %d\n", cam->video_size);
+ }
+
+ return;
+}
+
+static int allocate_frame_buf(struct cam_data *cam)
+{
+ int i;
+
+ cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE);
+ if (!cam->frame_buf)
+ return -ENOBUFS;
+
+ for (i = 0; i < FRAME_NUM; i++)
+ cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE;
+
+ return 0;
+}
+
+static int free_frame_buf(struct cam_data *cam)
+{
+ int i;
+
+ rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE);
+ cam->frame_buf = 0;
+ for (i=0; i < FRAME_NUM; i++)
+ cam->frame[i].data = NULL;
+
+ return 0;
+}
+
+
+static void inline free_frames(struct cpia_frame frame[FRAME_NUM])
+{
+ int i;
+
+ for (i=0; i < FRAME_NUM; i++)
+ frame[i].state = FRAME_UNUSED;
+ return;
+}
+
+/**********************************************************************
+ *
+ * General functions
+ *
+ **********************************************************************/
+/* send an arbitrary command to the camera */
+static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d)
+{
+ int retval, datasize;
+ u8 cmd[8], data[8];
+
+ switch(command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ case CPIA_COMMAND_GetPnPID:
+ case CPIA_COMMAND_GetCameraStatus:
+ case CPIA_COMMAND_GetVPVersion:
+ datasize=8;
+ break;
+ case CPIA_COMMAND_GetColourParams:
+ case CPIA_COMMAND_GetColourBalance:
+ case CPIA_COMMAND_GetExposure:
+ down(&cam->param_lock);
+ datasize=8;
+ break;
+ default:
+ datasize=0;
+ break;
+ }
+
+ cmd[0] = command>>8;
+ cmd[1] = command&0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = datasize;
+ cmd[7] = 0;
+
+ retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+ if (retval)
+ DBG("%x - failed, retval=%d\n", command, retval);
+ else {
+ switch(command) {
+ case CPIA_COMMAND_GetCPIAVersion:
+ cam->params.version.firmwareVersion = data[0];
+ cam->params.version.firmwareRevision = data[1];
+ cam->params.version.vcVersion = data[2];
+ cam->params.version.vcRevision = data[3];
+ break;
+ case CPIA_COMMAND_GetPnPID:
+ cam->params.pnpID.vendor = data[0]+(((u16)data[1])<<8);
+ cam->params.pnpID.product = data[2]+(((u16)data[3])<<8);
+ cam->params.pnpID.deviceRevision =
+ data[4]+(((u16)data[5])<<8);
+ break;
+ case CPIA_COMMAND_GetCameraStatus:
+ cam->params.status.systemState = data[0];
+ cam->params.status.grabState = data[1];
+ cam->params.status.streamState = data[2];
+ cam->params.status.fatalError = data[3];
+ cam->params.status.cmdError = data[4];
+ cam->params.status.debugFlags = data[5];
+ cam->params.status.vpStatus = data[6];
+ cam->params.status.errorCode = data[7];
+ break;
+ case CPIA_COMMAND_GetVPVersion:
+ cam->params.vpVersion.vpVersion = data[0];
+ cam->params.vpVersion.vpRevision = data[1];
+ cam->params.vpVersion.cameraHeadID =
+ data[2]+(((u16)data[3])<<8);
+ break;
+ case CPIA_COMMAND_GetColourParams:
+ cam->params.colourParams.brightness = data[0];
+ cam->params.colourParams.contrast = data[1];
+ cam->params.colourParams.saturation = data[2];
+ up(&cam->param_lock);
+ break;
+ case CPIA_COMMAND_GetColourBalance:
+ cam->params.colourBalance.redGain = data[0];
+ cam->params.colourBalance.greenGain = data[1];
+ cam->params.colourBalance.blueGain = data[2];
+ up(&cam->param_lock);
+ break;
+ case CPIA_COMMAND_GetExposure:
+ cam->params.exposure.gain = data[0];
+ cam->params.exposure.fineExp = data[1];
+ cam->params.exposure.coarseExpLo = data[2];
+ cam->params.exposure.coarseExpHi = data[3];
+ cam->params.exposure.redComp = data[4];
+ cam->params.exposure.green1Comp = data[5];
+ cam->params.exposure.green2Comp = data[6];
+ cam->params.exposure.blueComp = data[7];
+ /* If the *Comp parameters are wacko, generate
+ * a warning, and reset them back to default
+ * values. - rich@annexia.org
+ */
+ if (cam->params.exposure.redComp < 220 ||
+ cam->params.exposure.redComp > 255 ||
+ cam->params.exposure.green1Comp < 214 ||
+ cam->params.exposure.green1Comp > 255 ||
+ cam->params.exposure.green2Comp < 214 ||
+ cam->params.exposure.green2Comp > 255 ||
+ cam->params.exposure.blueComp < 230 ||
+ cam->params.exposure.blueComp > 255)
+ {
+ printk (KERN_WARNING "*_comp parameters have gone AWOL (%d/%d/%d/%d) - reseting them\n",
+ cam->params.exposure.redComp,
+ cam->params.exposure.green1Comp,
+ cam->params.exposure.green2Comp,
+ cam->params.exposure.blueComp);
+ cam->params.exposure.redComp = 220;
+ cam->params.exposure.green1Comp = 214;
+ cam->params.exposure.green2Comp = 214;
+ cam->params.exposure.blueComp = 230;
+ }
+ up(&cam->param_lock);
+ break;
+ default:
+ break;
+ }
+ }
+ return retval;
+}
+
+/* send a command to the camera with an additional data transaction */
+static int do_command_extended(struct cam_data *cam, u16 command,
+ u8 a, u8 b, u8 c, u8 d,
+ u8 e, u8 f, u8 g, u8 h,
+ u8 i, u8 j, u8 k, u8 l)
+{
+ int retval;
+ u8 cmd[8], data[8];
+
+ cmd[0] = command>>8;
+ cmd[1] = command&0xff;
+ cmd[2] = a;
+ cmd[3] = b;
+ cmd[4] = c;
+ cmd[5] = d;
+ cmd[6] = 8;
+ cmd[7] = 0;
+ data[0] = e;
+ data[1] = f;
+ data[2] = g;
+ data[3] = h;
+ data[4] = i;
+ data[5] = j;
+ data[6] = k;
+ data[7] = l;
+
+ retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data);
+ if (retval)
+ LOG("%x - failed\n", command);
+
+ return retval;
+}
+
+/**********************************************************************
+ *
+ * Colorspace conversion
+ *
+ **********************************************************************/
+#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
+
+static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt,
+ int in_uyvy, int mmap_kludge)
+{
+ int y, u, v, r, g, b, y1;
+
+ switch(out_fmt) {
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_RGB24:
+ case VIDEO_PALETTE_RGB32:
+ if (in_uyvy) {
+ u = *yuv++ - 128;
+ y = (*yuv++ - 16) * 76310;
+ v = *yuv++ - 128;
+ y1 = (*yuv - 16) * 76310;
+ } else {
+ y = (*yuv++ - 16) * 76310;
+ u = *yuv++ - 128;
+ y1 = (*yuv++ - 16) * 76310;
+ v = *yuv - 128;
+ }
+ r = 104635 * v;
+ g = -25690 * u + -53294 * v;
+ b = 132278 * u;
+ break;
+ default:
+ y = *yuv++;
+ u = *yuv++;
+ y1 = *yuv++;
+ v = *yuv;
+ /* Just to avoid compiler warnings */
+ r = 0;
+ g = 0;
+ b = 0;
+ break;
+ }
+ switch(out_fmt) {
+ case VIDEO_PALETTE_RGB555:
+ *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3);
+ *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6);
+ *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3);
+ *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6);
+ return 4;
+ case VIDEO_PALETTE_RGB565:
+ *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3);
+ *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5);
+ *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3);
+ *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5);
+ return 4;
+ case VIDEO_PALETTE_RGB24:
+ if (mmap_kludge) {
+ *rgb++ = LIMIT(b+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(r+y);
+ *rgb++ = LIMIT(b+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(r+y1);
+ } else {
+ *rgb++ = LIMIT(r+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(b+y);
+ *rgb++ = LIMIT(r+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(b+y1);
+ }
+ return 6;
+ case VIDEO_PALETTE_RGB32:
+ if (mmap_kludge) {
+ *rgb++ = LIMIT(b+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(r+y);
+ rgb++;
+ *rgb++ = LIMIT(b+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(r+y1);
+ } else {
+ *rgb++ = LIMIT(r+y);
+ *rgb++ = LIMIT(g+y);
+ *rgb++ = LIMIT(b+y);
+ rgb++;
+ *rgb++ = LIMIT(r+y1);
+ *rgb++ = LIMIT(g+y1);
+ *rgb = LIMIT(b+y1);
+ }
+ return 8;
+ case VIDEO_PALETTE_GREY:
+ *rgb++ = y;
+ *rgb = y1;
+ return 2;
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ *rgb++ = y;
+ *rgb++ = u;
+ *rgb++ = y1;
+ *rgb = v;
+ return 4;
+ case VIDEO_PALETTE_UYVY:
+ *rgb++ = u;
+ *rgb++ = y;
+ *rgb++ = v;
+ *rgb = y1;
+ return 4;
+ default:
+ DBG("Empty: %d\n", out_fmt);
+ return 0;
+ }
+}
+
+static int skipcount(int count, int fmt)
+{
+ switch(fmt) {
+ case VIDEO_PALETTE_GREY:
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_UYVY:
+ return 2*count;
+ case VIDEO_PALETTE_RGB24:
+ return 3*count;
+ case VIDEO_PALETTE_RGB32:
+ return 4*count;
+ default:
+ return 0;
+ }
+}
+
+static int parse_picture(struct cam_data *cam, int size)
+{
+ u8 *obuf, *ibuf, *end_obuf;
+ int ll, in_uyvy, compressed, origsize, out_fmt;
+
+ /* make sure params don't change while we are decoding */
+ down(&cam->param_lock);
+
+ obuf = cam->decompressed_frame.data;
+ end_obuf = obuf+CPIA_MAX_FRAME_SIZE;
+ ibuf = cam->raw_image;
+ origsize = size;
+ out_fmt = cam->vp.palette;
+
+ if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) {
+ LOG("header not found\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+
+ if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) {
+ LOG("wrong video size\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+
+ if (ibuf[17] != SUBSAMPLE_422) {
+ LOG("illegal subtype %d\n",ibuf[17]);
+ up(&cam->param_lock);
+ return -1;
+ }
+
+ if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) {
+ LOG("illegal yuvorder %d\n",ibuf[18]);
+ up(&cam->param_lock);
+ return -1;
+ }
+ in_uyvy = ibuf[18] == YUVORDER_UYVY;
+
+#if 0
+ /* FIXME: ROI mismatch occurs when switching capture sizes */
+ if ((ibuf[24] != cam->params.roi.colStart) ||
+ (ibuf[25] != cam->params.roi.colEnd) ||
+ (ibuf[26] != cam->params.roi.rowStart) ||
+ (ibuf[27] != cam->params.roi.rowEnd)) {
+ LOG("ROI mismatch\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+#endif
+
+ if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) {
+ LOG("illegal compression %d\n",ibuf[28]);
+ up(&cam->param_lock);
+ return -1;
+ }
+ compressed = (ibuf[28] == COMPRESSED);
+
+ if (ibuf[29] != NO_DECIMATION) {
+ LOG("decimation not supported\n");
+ up(&cam->param_lock);
+ return -1;
+ }
+
+ cam->params.yuvThreshold.yThreshold = ibuf[30];
+ cam->params.yuvThreshold.uvThreshold = ibuf[31];
+ cam->params.status.systemState = ibuf[32];
+ cam->params.status.grabState = ibuf[33];
+ cam->params.status.streamState = ibuf[34];
+ cam->params.status.fatalError = ibuf[35];
+ cam->params.status.cmdError = ibuf[36];
+ cam->params.status.debugFlags = ibuf[37];
+ cam->params.status.vpStatus = ibuf[38];
+ cam->params.status.errorCode = ibuf[39];
+ cam->fps = ibuf[41];
+ up(&cam->param_lock);
+
+ ibuf += FRAME_HEADER_SIZE;
+ size -= FRAME_HEADER_SIZE;
+ ll = ibuf[0] | (ibuf[1] << 8);
+ ibuf += 2;
+
+ while (size > 0) {
+ size -= (ll+2);
+ if (size < 0) {
+ LOG("Insufficient data in buffer\n");
+ return -1;
+ }
+
+ while (ll > 1) {
+ if (!compressed || (compressed && !(*ibuf & 1))) {
+ obuf += yuvconvert(ibuf, obuf, out_fmt,
+ in_uyvy, cam->mmap_kludge);
+ ibuf += 4;
+ ll -= 4;
+ } else {
+ /*skip compressed interval from previous frame*/
+ int skipsize = skipcount(*ibuf >> 1, out_fmt);
+ obuf += skipsize;
+ if (obuf > end_obuf) {
+ LOG("Insufficient data in buffer\n");
+ return -1;
+ }
+ ++ibuf;
+ ll--;
+ }
+ }
+ if (ll == 1) {
+ if (*ibuf != EOL) {
+ LOG("EOL not found giving up after %d/%d"
+ " bytes\n", origsize-size, origsize);
+ return -1;
+ }
+
+ ibuf++; /* skip over EOL */
+
+ if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) &&
+ (ibuf[2] == EOI) && (ibuf[3] == EOI)) {
+ size -= 4;
+ break;
+ }
+
+ if (size > 1) {
+ ll = ibuf[0] | (ibuf[1] << 8);
+ ibuf += 2; /* skip over line length */
+ }
+ } else {
+ LOG("line length was not 1 but %d after %d/%d bytes\n",
+ ll, origsize-size, origsize);
+ return -1;
+ }
+ }
+
+ cam->decompressed_frame.count = obuf-cam->decompressed_frame.data;
+
+ return cam->decompressed_frame.count;
+}
+
+/* InitStreamCap wrapper to select correct start line */
+static inline int init_stream_cap(struct cam_data *cam)
+{
+ return do_command(cam, CPIA_COMMAND_InitStreamCap,
+ 0, cam->params.streamStartLine, 0, 0);
+}
+
+/* update various camera modes and settings */
+static void dispatch_commands(struct cam_data *cam)
+{
+ down(&cam->param_lock);
+ if (cam->cmd_queue==COMMAND_NONE) {
+ up(&cam->param_lock);
+ return;
+ }
+ DEB_BYTE(cam->cmd_queue);
+ DEB_BYTE(cam->cmd_queue>>8);
+ if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS)
+ do_command(cam, CPIA_COMMAND_SetColourParams,
+ cam->params.colourParams.brightness,
+ cam->params.colourParams.contrast,
+ cam->params.colourParams.saturation, 0);
+
+ if (cam->cmd_queue & COMMAND_SETCOMPRESSION)
+ do_command(cam, CPIA_COMMAND_SetCompression,
+ cam->params.compression.mode,
+ cam->params.compression.decimation, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_SETFORMAT) {
+ do_command(cam, CPIA_COMMAND_SetFormat,
+ cam->params.format.videoSize,
+ cam->params.format.subSample,
+ cam->params.format.yuvOrder, 0);
+ do_command(cam, CPIA_COMMAND_SetROI,
+ cam->params.roi.colStart, cam->params.roi.colEnd,
+ cam->params.roi.rowStart, cam->params.roi.rowEnd);
+ cam->first_frame = 1;
+ }
+
+ if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET)
+ do_command(cam, CPIA_COMMAND_SetCompressionTarget,
+ cam->params.compressionTarget.frTargeting,
+ cam->params.compressionTarget.targetFR,
+ cam->params.compressionTarget.targetQ, 0);
+
+ if (cam->cmd_queue & COMMAND_SETYUVTHRESH)
+ do_command(cam, CPIA_COMMAND_SetYUVThresh,
+ cam->params.yuvThreshold.yThreshold,
+ cam->params.yuvThreshold.uvThreshold, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_SETECPTIMING)
+ do_command(cam, CPIA_COMMAND_SetECPTiming,
+ cam->params.ecpTiming, 0, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS)
+ do_command_extended(cam, CPIA_COMMAND_SetCompressionParams,
+ 0, 0, 0, 0,
+ cam->params.compressionParams.hysteresis,
+ cam->params.compressionParams.threshMax,
+ cam->params.compressionParams.smallStep,
+ cam->params.compressionParams.largeStep,
+ cam->params.compressionParams.decimationHysteresis,
+ cam->params.compressionParams.frDiffStepThresh,
+ cam->params.compressionParams.qDiffStepThresh,
+ cam->params.compressionParams.decimationThreshMod);
+
+ if (cam->cmd_queue & COMMAND_SETEXPOSURE)
+ do_command_extended(cam, CPIA_COMMAND_SetExposure,
+ cam->params.exposure.gainMode,
+ cam->params.exposure.expMode,
+ cam->params.exposure.compMode,
+ cam->params.exposure.centreWeight,
+ cam->params.exposure.gain,
+ cam->params.exposure.fineExp,
+ cam->params.exposure.coarseExpLo,
+ cam->params.exposure.coarseExpHi,
+ cam->params.exposure.redComp,
+ cam->params.exposure.green1Comp,
+ cam->params.exposure.green2Comp,
+ cam->params.exposure.blueComp);
+
+ if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) {
+ if (cam->params.colourBalance.balanceModeIsAuto) {
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 2, 0, 0, 0);
+ } else {
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 1,
+ cam->params.colourBalance.redGain,
+ cam->params.colourBalance.greenGain,
+ cam->params.colourBalance.blueGain);
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+ }
+
+ if (cam->cmd_queue & COMMAND_SETSENSORFPS)
+ do_command(cam, CPIA_COMMAND_SetSensorFPS,
+ cam->params.sensorFps.divisor,
+ cam->params.sensorFps.baserate, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_SETAPCOR)
+ do_command(cam, CPIA_COMMAND_SetApcor,
+ cam->params.apcor.gain1,
+ cam->params.apcor.gain2,
+ cam->params.apcor.gain4,
+ cam->params.apcor.gain8);
+
+ if (cam->cmd_queue & COMMAND_SETFLICKERCTRL)
+ do_command(cam, CPIA_COMMAND_SetFlickerCtrl,
+ cam->params.flickerControl.flickerMode,
+ cam->params.flickerControl.coarseJump,
+ cam->params.flickerControl.allowableOverExposure, 0);
+
+ if (cam->cmd_queue & COMMAND_SETVLOFFSET)
+ do_command(cam, CPIA_COMMAND_SetVLOffset,
+ cam->params.vlOffset.gain1,
+ cam->params.vlOffset.gain2,
+ cam->params.vlOffset.gain4,
+ cam->params.vlOffset.gain8);
+
+ if (cam->cmd_queue & COMMAND_PAUSE)
+ do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
+
+ if (cam->cmd_queue & COMMAND_RESUME)
+ init_stream_cap(cam);
+
+ up(&cam->param_lock);
+ cam->cmd_queue = COMMAND_NONE;
+ return;
+}
+
+/* kernel thread function to read image from camera */
+static void fetch_frame(void *data)
+{
+ int image_size, retry;
+ struct cam_data *cam = (struct cam_data *)data;
+ unsigned long oldjif, rate, diff;
+
+ /* Allow up to two bad images in a row to be read and
+ * ignored before an error is reported */
+ for (retry = 0; retry < 3; ++retry) {
+ if (retry)
+ DBG("retry=%d\n", retry);
+
+ if (!cam->ops)
+ continue;
+
+ /* load first frame always uncompressed */
+ if (cam->first_frame &&
+ cam->params.compression.mode != CPIA_COMPRESSION_NONE)
+ do_command(cam, CPIA_COMMAND_SetCompression,
+ CPIA_COMPRESSION_NONE,
+ NO_DECIMATION, 0, 0);
+
+ /* init camera upload */
+ if (do_command(cam, CPIA_COMMAND_SetGrabMode,
+ CPIA_GRAB_CONTINUOUS, 0, 0, 0))
+ continue;
+
+ if (do_command(cam, CPIA_COMMAND_GrabFrame, 0,
+ cam->params.streamStartLine, 0, 0))
+ continue;
+
+ if (cam->ops->wait_for_stream_ready) {
+ /* loop until image ready */
+ do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0);
+ while (cam->params.status.streamState != STREAM_READY) {
+ if (current->need_resched)
+ schedule();
+
+ current->state = TASK_INTERRUPTIBLE;
+
+ /* sleep for 10 ms, hopefully ;) */
+ schedule_timeout(10*HZ/1000);
+ if (signal_pending(current))
+ return;
+
+ do_command(cam, CPIA_COMMAND_GetCameraStatus,
+ 0, 0, 0, 0);
+ }
+ }
+
+ /* grab image from camera */
+ if (current->need_resched)
+ schedule();
+
+ oldjif = jiffies;
+ image_size = cam->ops->streamRead(cam->lowlevel_data,
+ cam->raw_image, 0);
+ if (image_size <= 0) {
+ DBG("streamRead failed: %d\n", image_size);
+ continue;
+ }
+
+ rate = image_size * HZ / 1024;
+ diff = jiffies-oldjif;
+ cam->transfer_rate = diff==0 ? rate : rate/diff;
+ /* diff==0 ? unlikely but possible */
+
+ /* camera idle now so dispatch queued commands */
+ dispatch_commands(cam);
+
+ /* Update our knowledge of the camera state - FIXME: necessary? */
+ do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+ do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+
+ /* decompress and convert image to by copying it from
+ * raw_image to decompressed_frame
+ */
+ if (current->need_resched)
+ schedule();
+
+ cam->image_size = parse_picture(cam, image_size);
+ if (cam->image_size <= 0)
+ DBG("parse_picture failed %d\n", cam->image_size);
+ else
+ break;
+ }
+
+ if (retry < 3) {
+ /* FIXME: this only works for double buffering */
+ if (cam->frame[cam->curframe].state == FRAME_READY) {
+ memcpy(cam->frame[cam->curframe].data,
+ cam->decompressed_frame.data,
+ cam->decompressed_frame.count);
+ cam->frame[cam->curframe].state = FRAME_DONE;
+ } else
+ cam->decompressed_frame.state = FRAME_DONE;
+
+#if 0
+ if (cam->first_frame &&
+ cam->params.compression.mode != CPIA_COMPRESSION_NONE) {
+ cam->first_frame = 0;
+ cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+ }
+#else
+ if (cam->first_frame) {
+ cam->first_frame = 0;
+ cam->cmd_queue |= COMMAND_SETCOMPRESSION;
+ cam->cmd_queue |= COMMAND_SETEXPOSURE;
+ }
+#endif
+ }
+}
+
+static int capture_frame(struct cam_data *cam, struct video_mmap *vm)
+{
+ int retval = 0;
+
+ if (!cam->frame_buf) {
+ /* we do lazy allocation */
+ if ((retval = allocate_frame_buf(cam)))
+ return retval;
+ }
+
+ /* FIXME: the first frame seems to be captured by the camera
+ without regards to any initial settings, so we throw away
+ that one, the next one is generated with our settings
+ (exposure, color balance, ...)
+ */
+ if (cam->first_frame) {
+ cam->curframe = vm->frame;
+ cam->frame[cam->curframe].state = FRAME_READY;
+ fetch_frame(cam);
+ if (cam->frame[cam->curframe].state != FRAME_DONE)
+ retval = -EIO;
+ }
+ cam->curframe = vm->frame;
+ cam->frame[cam->curframe].state = FRAME_READY;
+ fetch_frame(cam);
+ if (cam->frame[cam->curframe].state != FRAME_DONE)
+ retval=-EIO;
+
+ return retval;
+}
+
+static int goto_high_power(struct cam_data *cam)
+{
+ if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0))
+ return -1;
+ mdelay(100); /* windows driver does it too */
+ if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+ return -1;
+ if (cam->params.status.systemState == HI_POWER_STATE) {
+ DBG("camera now in HIGH power state\n");
+ return 0;
+ }
+ printstatus(cam);
+ return -1;
+}
+
+static int goto_low_power(struct cam_data *cam)
+{
+ if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0))
+ return -1;
+ if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+ return -1;
+ if (cam->params.status.systemState == LO_POWER_STATE) {
+ DBG("camera now in LOW power state\n");
+ return 0;
+ }
+ printstatus(cam);
+ return -1;
+}
+
+static void save_camera_state(struct cam_data *cam)
+{
+ do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
+ do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
+
+ DBG("%d/%d/%d/%d/%d/%d/%d/%d\n",
+ cam->params.exposure.gain,
+ cam->params.exposure.fineExp,
+ cam->params.exposure.coarseExpLo,
+ cam->params.exposure.coarseExpHi,
+ cam->params.exposure.redComp,
+ cam->params.exposure.green1Comp,
+ cam->params.exposure.green2Comp,
+ cam->params.exposure.blueComp);
+ DBG("%d/%d/%d\n",
+ cam->params.colourBalance.redGain,
+ cam->params.colourBalance.greenGain,
+ cam->params.colourBalance.blueGain);
+}
+
+static void set_camera_state(struct cam_data *cam)
+{
+ if(cam->params.colourBalance.balanceModeIsAuto) {
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 2, 0, 0, 0);
+ } else {
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 1,
+ cam->params.colourBalance.redGain,
+ cam->params.colourBalance.greenGain,
+ cam->params.colourBalance.blueGain);
+ do_command(cam, CPIA_COMMAND_SetColourBalance,
+ 3, 0, 0, 0);
+ }
+
+
+ do_command_extended(cam, CPIA_COMMAND_SetExposure,
+ cam->params.exposure.gainMode, 1, 1,
+ cam->params.exposure.centreWeight,
+ cam->params.exposure.gain,
+ cam->params.exposure.fineExp,
+ cam->params.exposure.coarseExpLo,
+ cam->params.exposure.coarseExpHi,
+ cam->params.exposure.redComp,
+ cam->params.exposure.green1Comp,
+ cam->params.exposure.green2Comp,
+ cam->params.exposure.blueComp);
+ do_command_extended(cam, CPIA_COMMAND_SetExposure,
+ 0, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if (!cam->params.exposure.gainMode)
+ cam->params.exposure.gainMode = 2;
+ if (!cam->params.exposure.expMode)
+ cam->params.exposure.expMode = 2;
+ if (!cam->params.exposure.centreWeight)
+ cam->params.exposure.centreWeight = 1;
+
+ cam->cmd_queue = COMMAND_SETCOMPRESSION |
+ COMMAND_SETCOMPRESSIONTARGET |
+ COMMAND_SETCOLOURPARAMS |
+ COMMAND_SETFORMAT |
+ COMMAND_SETYUVTHRESH |
+ COMMAND_SETECPTIMING |
+ COMMAND_SETCOMPRESSIONPARAMS |
+#if 0
+ COMMAND_SETEXPOSURE |
+#endif
+ COMMAND_SETCOLOURBALANCE |
+ COMMAND_SETSENSORFPS |
+ COMMAND_SETAPCOR |
+ COMMAND_SETFLICKERCTRL |
+ COMMAND_SETVLOFFSET;
+ dispatch_commands(cam);
+ save_camera_state(cam);
+
+ return;
+}
+
+static void get_version_information(struct cam_data *cam)
+{
+ /* GetCPIAVersion */
+ do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
+
+ /* GetPnPID */
+ do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
+}
+
+/* initialize camera */
+static int reset_camera(struct cam_data *cam)
+{
+ /* Start the camera in low power mode */
+ if (goto_low_power(cam)) {
+ if (cam->params.status.systemState != WARM_BOOT_STATE)
+ return -ENODEV;
+
+ /* FIXME: this is just dirty trial and error */
+ reset_camera_struct(cam);
+ goto_high_power(cam);
+ do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0);
+ if (goto_low_power(cam))
+ return -NODEV;
+ }
+
+ /* procedure described in developer's guide p3-28 */
+
+ /* Check the firmware version FIXME: should we check PNPID? */
+ cam->params.version.firmwareVersion = 0;
+ get_version_information(cam);
+ if (cam->params.version.firmwareVersion != 1)
+ return -ENODEV;
+
+ /* The fatal error checking should be done after
+ * the camera powers up (developer's guide p 3-38) */
+
+ /* Set streamState before transition to high power to avoid bug
+ * in firmware 1-02 */
+ do_command(cam, CPIA_COMMAND_ModifyCameraStatus, STREAMSTATE, 0,
+ STREAM_NOT_READY, 0);
+
+ /* GotoHiPower */
+ if (goto_high_power(cam))
+ return -ENODEV;
+
+ /* Check the camera status */
+ if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0))
+ return -EIO;
+
+ if (cam->params.status.fatalError) {
+ DBG("fatal_error: %#04x\n",
+ cam->params.status.fatalError);
+ DBG("vp_status: %#04x\n",
+ cam->params.status.vpStatus);
+ if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) {
+ /* Fatal error in camera */
+ return -EIO;
+ } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) {
+ /* Firmware 1-02 may do this for parallel port cameras,
+ * just clear the flags (developer's guide p 3-38) */
+ do_command(cam, CPIA_COMMAND_ModifyCameraStatus,
+ FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0);
+ }
+ }
+
+ /* Check the camera status again */
+ if (cam->params.status.fatalError) {
+ if (cam->params.status.fatalError)
+ return -EIO;
+ }
+
+ /* VPVersion can't be retrieved before the camera is in HiPower,
+ * so get it here instead of in get_version_information. */
+ do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
+
+ /* set camera to a known state */
+ set_camera_state(cam);
+
+ return 0;
+}
+
+/* ------------------------- V4L interface --------------------- */
+static int cpia_open(struct video_device *dev, int flags)
+{
+ int i;
+ struct cam_data *cam = dev->priv;
+
+ if (!cam) {
+ DBG("Internal error, cam_data not found!\n");
+ return -EBUSY;
+ }
+
+ if (cam->open_count > 0) {
+ DBG("Camera already open\n");
+ return -EBUSY;
+ }
+
+ if (!cam->raw_image) {
+ cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE);
+ if (!cam->raw_image)
+ return -ENOMEM;
+ }
+
+ if (!cam->decompressed_frame.data) {
+ cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE);
+ if (!cam->decompressed_frame.data) {
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ /* open cpia */
+ if (cam->ops->open(cam->lowlevel_data)) {
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data = NULL;
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image = NULL;
+ return -ENODEV;
+ }
+
+ /* reset the camera */
+ if ((i = reset_camera(cam)) != 0) {
+ cam->ops->close(cam->lowlevel_data);
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data = NULL;
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image = NULL;
+ return i;
+ }
+
+ /* Set ownership of /proc/cpia/videoX to current user */
+ if(cam->proc_entry)
+ cam->proc_entry->uid = current->uid;
+
+ /* set mark for loading first frame uncompressed */
+ cam->first_frame = 1;
+
+ /* init it to something */
+ cam->mmap_kludge = 0;
+
+ ++cam->open_count;
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+}
+
+static void cpia_close(struct video_device *dev)
+{
+ struct cam_data *cam;
+
+ cam = dev->priv;
+
+ if (cam->ops) {
+ /* Return ownership of /proc/cpia/videoX to root */
+ if(cam->proc_entry)
+ cam->proc_entry->uid = 0;
+
+ /* save camera state for later open (developers guide ch 3.5.3) */
+ save_camera_state(cam);
+
+ /* GotoLoPower */
+ goto_low_power(cam);
+
+ /* Update the camera ststus */
+ do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
+
+ /* cleanup internal state stuff */
+ free_frames(cam->frame);
+
+ /* close cpia */
+ cam->ops->close(cam->lowlevel_data);
+ }
+
+ if (--cam->open_count == 0) {
+ /* clean up capture-buffers */
+ if (cam->raw_image) {
+ rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE);
+ cam->raw_image = NULL;
+ }
+
+ if (cam->decompressed_frame.data) {
+ rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE);
+ cam->decompressed_frame.data = NULL;
+ }
+
+ if (cam->frame_buf)
+ free_frame_buf(cam);
+
+ if (!cam->ops) {
+ video_unregister_device(dev);
+ kfree(cam);
+ }
+ }
+
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+ return;
+}
+
+static long cpia_read(struct video_device *dev, char *buf,
+ unsigned long count, int noblock)
+{
+ struct cam_data *cam = dev->priv;
+
+ /* make this _really_ smp and multithredi-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ if (!buf) {
+ DBG("buf NULL\n");
+ up(&cam->busy_lock);
+ return -EINVAL;
+ }
+
+ if (!count) {
+ DBG("count 0\n");
+ up(&cam->busy_lock);
+ return 0;
+ }
+
+ if (!cam->ops) {
+ DBG("ops NULL\n");
+ up(&cam->busy_lock);
+ return -ENODEV;
+ }
+
+ /* upload frame */
+ cam->decompressed_frame.state = FRAME_READY;
+ cam->mmap_kludge=0;
+ fetch_frame(cam);
+ if (cam->decompressed_frame.state != FRAME_DONE) {
+ DBG("upload failed %d/%d\n", cam->decompressed_frame.count,
+ cam->decompressed_frame.state);
+ up(&cam->busy_lock);
+ return -EIO;
+ }
+ cam->decompressed_frame.state = FRAME_UNUSED;
+
+ /* copy data to user space */
+ if (cam->decompressed_frame.count > count) {
+ DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count,
+ count);
+ up(&cam->busy_lock);
+ return -EFAULT;
+ }
+ if (copy_to_user(buf, cam->decompressed_frame.data,
+ cam->decompressed_frame.count)) {
+ DBG("copy_to_user failed\n");
+ up(&cam->busy_lock);
+ return -EFAULT;
+ }
+
+ up(&cam->busy_lock);
+ return cam->decompressed_frame.count;
+}
+
+static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg)
+{
+ struct cam_data *cam = dev->priv;
+ int retval = 0;
+
+ if (!cam || !cam->ops)
+ return -ENODEV;
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ //DBG("cpia_ioctl: %u\n", ioctlnr);
+
+ switch (ioctlnr) {
+ /* query capabilites */
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+
+ DBG("VIDIOCGCAP\n");
+ strcpy(b.name, "CPiA Camera");
+ b.type = VID_TYPE_CAPTURE;
+ b.channels = 1;
+ b.audios = 0;
+ b.maxwidth = 352; /* VIDEOSIZE_CIF */
+ b.maxheight = 288;
+ b.minwidth = 48; /* VIDEOSIZE_48_48 */
+ b.minheight = 48;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ retval = -EFAULT;
+
+ break;
+ }
+
+ /* get/set video source - we are a camera and nothing else */
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+
+ DBG("VIDIOCGCHAN\n");
+ if (copy_from_user(&v, arg, sizeof(v))) {
+ retval = -EFAULT;
+ break;
+ }
+ if (v.channel != 0) {
+ retval = -EINVAL;
+ break;
+ }
+
+ v.channel = 0;
+ strcpy(v.name, "Camera");
+ v.tuners = 0;
+ v.flags = 0;
+ v.type = VIDEO_TYPE_CAMERA;
+ v.norm = 0;
+
+ if (copy_to_user(arg, &v, sizeof(v)))
+ retval = -EFAULT;
+ break;
+ }
+
+ case VIDIOCSCHAN:
+ {
+ int v;
+
+ DBG("VIDIOCSCHAN\n");
+ if (copy_from_user(&v, arg, sizeof(v)))
+ retval = -EFAULT;
+
+ if (retval == 0 && v != 0)
+ retval = -EINVAL;
+
+ break;
+ }
+
+ /* image properties */
+ case VIDIOCGPICT:
+ DBG("VIDIOCGPICT\n");
+ if (copy_to_user(arg, &cam->vp, sizeof(struct video_picture)))
+ retval = -EFAULT;
+ break;
+
+ case VIDIOCSPICT:
+ {
+ struct video_picture vp;
+
+ DBG("VIDIOCSPICT\n");
+
+ /* copy_from_user */
+ if (copy_from_user(&vp, arg, sizeof(vp))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ /* check validity */
+ DBG("palette: %d\n", vp.palette);
+ DBG("depth: %d\n", vp.depth);
+ if (!valid_mode(vp.palette, vp.depth)) {
+ retval = -EINVAL;
+ break;
+ }
+
+ down(&cam->param_lock);
+ /* brightness, colour, contrast need no check 0-65535 */
+ memcpy( &cam->vp, &vp, sizeof(vp) );
+ /* update cam->params.colourParams */
+ cam->params.colourParams.brightness = vp.brightness*100/65535;
+ cam->params.colourParams.contrast = vp.contrast*100/65535;
+ cam->params.colourParams.saturation = vp.colour*100/65535;
+ /* contrast is in steps of 8, so round */
+ cam->params.colourParams.contrast =
+ ((cam->params.colourParams.contrast + 3) / 8) * 8;
+ if (cam->params.version.firmwareVersion == 1 &&
+ cam->params.version.firmwareRevision == 2 &&
+ cam->params.colourParams.contrast > 80) {
+ /* 1-02 firmware limits contrast to 80 */
+ cam->params.colourParams.contrast = 80;
+ }
+
+ /* queue command to update camera */
+ cam->cmd_queue |= COMMAND_SETCOLOURPARAMS;
+ up(&cam->param_lock);
+ DBG("VIDIOCSPICT: %d / %d // %d / %d / %d / %d\n",
+ vp.depth, vp.palette, vp.brightness, vp.hue, vp.colour,
+ vp.contrast);
+ break;
+ }
+
+ /* get/set capture window */
+ case VIDIOCGWIN:
+ DBG("VIDIOCGWIN\n");
+
+ if (copy_to_user(arg, &cam->vw, sizeof(struct video_window)))
+ retval = -EFAULT;
+ break;
+
+ case VIDIOCSWIN:
+ {
+ /* copy_from_user, check validity, copy to internal structure */
+ struct video_window vw;
+ DBG("VIDIOCSWIN\n");
+ if (copy_from_user(&vw, arg, sizeof(vw))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (vw.clipcount != 0) { /* clipping not supported */
+ retval = -EINVAL;
+ break;
+ }
+ if (vw.clips != NULL) { /* clipping not supported */
+ retval = -EINVAL;
+ break;
+ }
+
+ /* we set the video window to something smaller or equal to what
+ * is requested by the user???
+ */
+ down(&cam->param_lock);
+ if (vw.width != cam->vw.width || vw.height != cam->vw.height) {
+ int video_size = match_videosize(vw.width, vw.height);
+
+ if (video_size < 0) {
+ retval = -EINVAL;
+ up(&cam->param_lock);
+ break;
+ }
+ cam->video_size = video_size;
+ set_vw_size(cam);
+ DBG("%d / %d\n", cam->vw.width, cam->vw.height);
+ cam->cmd_queue |= COMMAND_SETFORMAT;
+ }
+
+ // FIXME needed??? memcpy(&cam->vw, &vw, sizeof(vw));
+ up(&cam->param_lock);
+
+ /* setformat ignored by camera during streaming,
+ * so stop/dispatch/start */
+ if (cam->cmd_queue & COMMAND_SETFORMAT) {
+ DBG("\n");
+ dispatch_commands(cam);
+ }
+ DBG("%d/%d:%d\n", cam->video_size,
+ cam->vw.width, cam->vw.height);
+ break;
+ }
+
+ /* mmap interface */
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf vm;
+ int i;
+
+ DBG("VIDIOCGMBUF\n");
+ memset(&vm, 0, sizeof(vm));
+ vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM;
+ vm.frames = FRAME_NUM;
+ for (i = 0; i < FRAME_NUM; i++)
+ vm.offsets[i] = CPIA_MAX_FRAME_SIZE * i;
+
+ if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+ retval = -EFAULT;
+
+ break;
+ }
+
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap vm;
+ int video_size;
+
+ if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) {
+ retval = -EFAULT;
+ break;
+ }
+#if 1
+ DBG("VIDIOCMCAPTURE: %d / %d / %dx%d\n", vm.format, vm.frame,
+ vm.width, vm.height);
+#endif
+ if (vm.frame<0||vm.frame>FRAME_NUM) {
+ retval = -EINVAL;
+ break;
+ }
+
+ /* set video format */
+ cam->vp.palette = vm.format;
+ switch(vm.format) {
+ case VIDEO_PALETTE_GREY:
+ case VIDEO_PALETTE_RGB555:
+ case VIDEO_PALETTE_RGB565:
+ case VIDEO_PALETTE_YUV422:
+ case VIDEO_PALETTE_YUYV:
+ case VIDEO_PALETTE_UYVY:
+ cam->vp.depth = 16;
+ break;
+ case VIDEO_PALETTE_RGB24:
+ cam->vp.depth = 24;
+ break;
+ case VIDEO_PALETTE_RGB32:
+ cam->vp.depth = 32;
+ break;
+ default:
+ retval = -EINVAL;
+ break;
+ }
+ if (retval)
+ break;
+
+ /* set video size */
+ video_size = match_videosize(vm.width, vm.height);
+ if (cam->video_size < 0) {
+ retval = -EINVAL;
+ break;
+ }
+ if (video_size != cam->video_size) {
+ cam->video_size = video_size;
+ set_vw_size(cam);
+ cam->cmd_queue |= COMMAND_SETFORMAT;
+ dispatch_commands(cam);
+ }
+#if 0
+ DBG("VIDIOCMCAPTURE: %d / %d/%d\n", cam->video_size,
+ cam->vw.width, cam->vw.height);
+#endif
+ /* according to v4l-spec we must start streaming here */
+ cam->mmap_kludge = 1;
+ retval = capture_frame(cam, &vm);
+
+ break;
+ }
+
+ case VIDIOCSYNC:
+ {
+ int frame;
+
+ if (copy_from_user((void *)&frame, arg, sizeof(int))) {
+ retval = -EFAULT;
+ break;
+ }
+ //DBG("VIDIOCSYNC: %d\n", frame);
+
+ if (frame<0 || frame >= FRAME_NUM) {
+ retval = -EINVAL;
+ break;
+ }
+
+ switch (cam->frame[frame].state) {
+ case FRAME_UNUSED:
+ case FRAME_READY:
+ case FRAME_GRABBING:
+ DBG("sync to unused frame %d\n", frame);
+ retval = -EINVAL;
+ break;
+
+ case FRAME_DONE:
+ cam->frame[frame].state = FRAME_UNUSED;
+ //DBG("VIDIOCSYNC: %d synced\n", frame);
+ break;
+ }
+ if (retval == -EINTR) {
+ /* FIXME - xawtv does not handle this nice */
+ retval = 0;
+ }
+ break;
+ }
+
+ /* pointless to implement overlay with this camera */
+ case VIDIOCCAPTURE:
+ retval = -EINVAL;
+ break;
+ case VIDIOCGFBUF:
+ retval = -EINVAL;
+ break;
+ case VIDIOCSFBUF:
+ retval = -EINVAL;
+ break;
+ case VIDIOCKEY:
+ retval = -EINVAL;
+ break;
+
+ /* tuner interface - we have none */
+ case VIDIOCGTUNER:
+ retval = -EINVAL;
+ break;
+ case VIDIOCSTUNER:
+ retval = -EINVAL;
+ break;
+ case VIDIOCGFREQ:
+ retval = -EINVAL;
+ break;
+ case VIDIOCSFREQ:
+ retval = -EINVAL;
+ break;
+
+ /* audio interface - we have none */
+ case VIDIOCGAUDIO:
+ retval = -EINVAL;
+ break;
+ case VIDIOCSAUDIO:
+ retval = -EINVAL;
+ break;
+ default:
+ retval = -ENOIOCTLCMD;
+ break;
+ }
+
+ up(&cam->param_lock);
+ up(&cam->busy_lock);
+ return retval;
+}
+
+/* FIXME */
+static int cpia_mmap(struct video_device *dev, const char *adr,
+ unsigned long size)
+{
+ unsigned long start = (unsigned long)adr;
+ unsigned long page, pos;
+ struct cam_data *cam = dev->priv;
+ int retval;
+
+ if (!cam || !cam->ops)
+ return -ENODEV;
+
+ DBG("cpia_mmap: %ld\n", size);
+
+ if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE)
+ return -EINVAL;
+
+ if (!cam || !cam->ops)
+ return -ENODEV;
+
+ /* make this _really_ smp-safe */
+ if (down_interruptible(&cam->busy_lock))
+ return -EINTR;
+
+ if (!cam->frame_buf) { /* we do lazy allocation */
+ if ((retval = allocate_frame_buf(cam))) {
+ up(&cam->busy_lock);
+ return retval;
+ }
+ }
+
+ pos = (unsigned long)(cam->frame_buf);
+ while (size > 0) {
+ page = kvirt_to_pa(pos);
+ if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+ up(&cam->busy_lock);
+ return -EAGAIN;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ DBG("cpia_mmap: %ld\n", size);
+ up(&cam->busy_lock);
+
+ return 0;
+}
+
+int cpia_video_init(struct video_device *vdev)
+{
+#ifdef CONFIG_PROC_FS
+ create_proc_cpia_cam(vdev->priv);
+#endif
+ return 0;
+}
+
+static struct video_device cpia_template = {
+ "CPiA Camera",
+ VID_TYPE_CAPTURE,
+ VID_HARDWARE_CPIA, /* FIXME */
+ cpia_open, /* open */
+ cpia_close, /* close */
+ cpia_read, /* read */
+ NULL, /* no write */
+ NULL, /* no poll */
+ cpia_ioctl, /* ioctl */
+ cpia_mmap, /* mmap */
+ cpia_video_init, /* initialize */
+ NULL, /* priv */
+ 0, /* busy */
+ -1 /* minor - unset */
+};
+
+/* initialise cam_data structure */
+static void reset_camera_struct(struct cam_data *cam)
+{
+ /* The following parameter values are the defaults from
+ * "Software Developer's Guide for CPiA Cameras". Any changes
+ * to the defaults are noted in comments. */
+ cam->params.colourParams.brightness = 50;
+ cam->params.colourParams.contrast = 48;
+ cam->params.colourParams.saturation = 50;
+ cam->params.exposure.gainMode = 2;
+ cam->params.exposure.expMode = 2; /* AEC */
+ cam->params.exposure.compMode = 1;
+ cam->params.exposure.centreWeight = 1;
+ cam->params.exposure.gain = 0;
+ cam->params.exposure.fineExp = 0;
+ cam->params.exposure.coarseExpLo = 185;
+ cam->params.exposure.coarseExpHi = 0;
+ cam->params.exposure.redComp = 220;
+ cam->params.exposure.green1Comp = 214;
+ cam->params.exposure.green2Comp = 214;
+ cam->params.exposure.blueComp = 230;
+ cam->params.colourBalance.balanceModeIsAuto = 1;
+ cam->params.colourBalance.redGain = 32;
+ cam->params.colourBalance.greenGain = 6;
+ cam->params.colourBalance.blueGain = 92;
+ cam->params.apcor.gain1 = 0x1c;
+ cam->params.apcor.gain2 = 0x1a;
+ cam->params.apcor.gain4 = 0x2d;
+ cam->params.apcor.gain8 = 0x2a;
+ cam->params.flickerControl.flickerMode = 0;
+ cam->params.flickerControl.coarseJump =
+ flicker_jumps[cam->mainsFreq]
+ [cam->params.sensorFps.baserate]
+ [cam->params.sensorFps.divisor];
+ cam->params.vlOffset.gain1 = 24;
+ cam->params.vlOffset.gain2 = 28;
+ cam->params.vlOffset.gain4 = 30;
+ cam->params.vlOffset.gain8 = 30;
+ cam->params.compressionParams.hysteresis = 3;
+ cam->params.compressionParams.threshMax = 11;
+ cam->params.compressionParams.smallStep = 1;
+ cam->params.compressionParams.largeStep = 3;
+ cam->params.compressionParams.decimationHysteresis = 2;
+ cam->params.compressionParams.frDiffStepThresh = 5;
+ cam->params.compressionParams.qDiffStepThresh = 3;
+ cam->params.compressionParams.decimationThreshMod = 2;
+ /* End of default values from Software Developer's Guide */
+
+ cam->transfer_rate = 0;
+
+ /* Set Sensor FPS to 15fps. This seems better than 30fps
+ * for indoor lighting. */
+ cam->params.sensorFps.divisor = 1;
+ cam->params.sensorFps.baserate = 1;
+
+ cam->params.yuvThreshold.yThreshold = 15; /* FIXME? */
+ cam->params.yuvThreshold.uvThreshold = 15; /* FIXME? */
+
+ cam->params.format.subSample = SUBSAMPLE_422;
+ cam->params.format.yuvOrder = YUVORDER_YUYV;
+
+ cam->params.compression.mode = CPIA_COMPRESSION_AUTO;
+ cam->params.compressionTarget.frTargeting =
+ CPIA_COMPRESSION_TARGET_QUALITY;
+ cam->params.compressionTarget.targetFR = 7; /* FIXME? */
+ cam->params.compressionTarget.targetQ = 10; /* FIXME? */
+
+ cam->video_size = VIDEOSIZE_CIF;
+
+ cam->vp.colour = 32768; /* 50% */
+ cam->vp.hue = 32768; /* 50% */
+ cam->vp.brightness = 32768; /* 50% */
+ cam->vp.contrast = 32768; /* 50% */
+ cam->vp.whiteness = 0; /* not used -> grayscale only */
+ cam->vp.depth = 0; /* FIXME: to be set by user? */
+ cam->vp.palette = VIDEO_PALETTE_RGB24; /* FIXME: to be set by user? */
+
+ cam->vw.x = 0;
+ cam->vw.y = 0;
+ set_vw_size(cam);
+ cam->vw.chromakey = 0;
+ /* PP NOTE: my extension to use vw.flags for this, bear it! */
+ cam->vw.flags = 0;
+ cam->vw.clipcount = 0;
+ cam->vw.clips = NULL;
+
+ cam->cmd_queue = COMMAND_NONE;
+ cam->first_frame = 0;
+
+ return;
+}
+
+/* initialize cam_data structure */
+static void init_camera_struct(struct cam_data *cam,
+ struct cpia_camera_ops *ops )
+{
+ int i;
+
+ /* Default everything to 0 */
+ memset(cam, 0, sizeof(struct cam_data));
+
+ cam->ops = ops;
+ init_MUTEX(&cam->param_lock);
+ init_MUTEX(&cam->busy_lock);
+
+ reset_camera_struct(cam);
+
+ cam->proc_entry = NULL;
+
+ memcpy(&cam->vdev, &cpia_template, sizeof(cpia_template));
+ cam->vdev.priv = cam;
+
+ cam->curframe = 0;
+ for (i = 0; i < FRAME_NUM; i++) {
+ cam->frame[i].width = 0;
+ cam->frame[i].height = 0;
+ cam->frame[i].state = FRAME_UNUSED;
+ cam->frame[i].data = NULL;
+ }
+ cam->decompressed_frame.width = 0;
+ cam->decompressed_frame.height = 0;
+ cam->decompressed_frame.state = FRAME_UNUSED;
+ cam->decompressed_frame.data = NULL;
+}
+
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel)
+{
+ struct cam_data *camera;
+
+ /* Need a lock when adding/removing cameras. This doesn't happen
+ * often and doesn't take very long, so grabbing the kernel lock
+ * should be OK. */
+
+ if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) {
+ unlock_kernel();
+ return NULL;
+ }
+
+ init_camera_struct( camera, ops );
+ camera->lowlevel_data = lowlevel;
+
+ /* register v4l device */
+ if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER) == -1) {
+ kfree(camera);
+ unlock_kernel();
+ printk(KERN_DEBUG "video_register_device failed\n");
+ return NULL;
+ }
+
+ /* get version information from camera: open/reset/close */
+
+ /* open cpia */
+ if (camera->ops->open(camera->lowlevel_data))
+ return camera;
+
+ /* reset the camera */
+ if (reset_camera(camera) != 0) {
+ camera->ops->close(camera->lowlevel_data);
+ return camera;
+ }
+
+ /* close cpia */
+ camera->ops->close(camera->lowlevel_data);
+
+/* Eh? Feeling happy? - jerdfelt */
+/*
+ camera->ops->open(camera->lowlevel_data);
+ camera->ops->close(camera->lowlevel_data);
+*/
+
+ printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n",
+ camera->params.version.firmwareVersion,
+ camera->params.version.firmwareRevision,
+ camera->params.version.vcVersion,
+ camera->params.version.vcRevision);
+ printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n",
+ camera->params.pnpID.vendor,
+ camera->params.pnpID.product,
+ camera->params.pnpID.deviceRevision);
+ printk(KERN_INFO " VP-Version: %d.%d %04x\n",
+ camera->params.vpVersion.vpVersion,
+ camera->params.vpVersion.vpRevision,
+ camera->params.vpVersion.cameraHeadID);
+
+ return camera;
+}
+
+void cpia_unregister_camera(struct cam_data *cam)
+{
+ if (!cam->open_count) {
+ DBG("unregistering video\n");
+ video_unregister_device(&cam->vdev);
+ } else {
+ LOG("/dev/video%d removed while open, "
+ "deferring video_unregister_device\n", cam->vdev.minor);
+ DBG("camera open -- setting ops to NULL\n");
+ cam->ops = NULL;
+ }
+
+#ifdef CONFIG_PROC_FS
+ DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor);
+ destroy_proc_cpia_cam(cam);
+#endif
+ if (!cam->open_count) {
+ DBG("freeing camera\n");
+ kfree(cam);
+ }
+}
+
+/****************************************************************************
+ *
+ * Module routines
+ *
+ ***************************************************************************/
+
+#ifdef MODULE
+int init_module(void)
+{
+ printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
+ CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+#ifdef CONFIG_PROC_FS
+ proc_cpia_create();
+#endif
+#ifdef CONFIG_KMOD
+#ifdef CONFIG_VIDEO_CPIA_PP_MODULE
+ request_module("cpia_pp");
+#endif
+#ifdef CONFIG_VIDEO_CPIA_USB_MODULE
+ request_module("cpia_usb");
+#endif
+#endif
+return 0;
+}
+
+void cleanup_module(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_cpia_destroy();
+#endif
+}
+
+#else
+
+int cpia_init(struct video_init *unused)
+{
+ printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT,
+ CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER);
+#ifdef CONFIG_PROC_FS
+ proc_cpia_create();
+#endif
+
+#ifdef CONFIG_VIDEO_CPIA_PP
+ cpia_pp_init();
+#endif
+#ifdef CONFIG_KMOD
+#ifdef CONFIG_VIDEO_CPIA_PP_MODULE
+ request_module("cpia_pp");
+#endif
+
+#ifdef CONFIG_VIDEO_CPIA_USB_MODULE
+ request_module("cpia_usb");
+#endif
+#endif /* CONFIG_KMOD */
+#ifdef CONFIG_VIDEO_CPIA_USB
+ cpia_usb_init();
+#endif
+ return 0;
+}
+
+/* Exported symbols for modules. */
+
+EXPORT_SYMBOL(cpia_register_camera);
+EXPORT_SYMBOL(cpia_unregister_camera);
+
+#endif
--- /dev/null
+#ifndef cpia_h
+#define cpia_h
+
+/*
+ * CPiA Parallel Port Video4Linux driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * (C) Copyright 1999 Bas Huisman,
+ * Peter Pregler,
+ * Scott J. Bertin,
+ * VLSI Vision Ltd.
+ *
+ * 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.
+ */
+
+#define CPIA_MAJ_VER 0
+#define CPIA_MIN_VER 7
+#define CPIA_PATCH_VER 4
+
+#define CPIA_PP_MAJ_VER 0
+#define CPIA_PP_MIN_VER 7
+#define CPIA_PP_PATCH_VER 4
+
+#define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */
+#define CPIA_MAX_FRAME_SIZE ((CPIA_MAX_FRAME_SIZE_UNALIGNED + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) /* align above to PAGE_SIZE */
+
+#ifdef __KERNEL__
+
+#include <asm/uaccess.h>
+#include <linux/videodev.h>
+#include <linux/smp_lock.h>
+
+struct cpia_camera_ops
+{
+ /* open sets privdata to point to structure for this camera.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*open)(void *privdata);
+
+ /* Registers callback function cb to be called with cbdata
+ * when an image is ready. If cb is NULL, only single image grabs
+ * should be used. cb should immediately call streamRead to read
+ * the data or data may be lost. Returns negative value on error,
+ * otherwise 0.
+ */
+ int (*registerCallback)(void *privdata, void (*cb)(void *cbdata),
+ void *cbdata);
+
+ /* transferCmd sends commands to the camera. command MUST point to
+ * an 8 byte buffer in kernel space. data can be NULL if no extra
+ * data is needed. The size of the data is given by the last 2
+ * bytes of comand. data must also point to memory in kernel space.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*transferCmd)(void *privdata, u8 *command, u8 *data);
+
+ /* streamStart initiates stream capture mode.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*streamStart)(void *privdata);
+
+ /* streamStop terminates stream capture mode.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*streamStop)(void *privdata);
+
+ /* streamRead reads a frame from the camera. buffer points to a
+ * buffer large enough to hold a complete frame in kernel space.
+ * noblock indicates if this should be a non blocking read.
+ * Returns the number of bytes read, or negative value on error.
+ */
+ int (*streamRead)(void *privdata, u8 *buffer, int noblock);
+
+ /* close disables the device until open() is called again.
+ * Returns negative value on error, otherwise 0.
+ */
+ int (*close)(void *privdata);
+
+ /* If wait_for_stream_ready is non-zero, wait until the streamState
+ * is STREAM_READY before calling streamRead.
+ */
+ int wait_for_stream_ready;
+};
+
+struct cpia_frame {
+ u8 *data;
+ int count;
+ int width;
+ int height;
+ volatile int state;
+};
+
+struct cam_params {
+ struct {
+ u8 firmwareVersion;
+ u8 firmwareRevision;
+ u8 vcVersion;
+ u8 vcRevision;
+ } version;
+ struct {
+ u16 vendor;
+ u16 product;
+ u16 deviceRevision;
+ } pnpID;
+ struct {
+ u8 vpVersion;
+ u8 vpRevision;
+ u16 cameraHeadID;
+ } vpVersion;
+ struct {
+ u8 systemState;
+ u8 grabState;
+ u8 streamState;
+ u8 fatalError;
+ u8 cmdError;
+ u8 debugFlags;
+ u8 vpStatus;
+ u8 errorCode;
+ } status;
+ struct {
+ u8 brightness;
+ u8 contrast;
+ u8 saturation;
+ } colourParams;
+ struct {
+ u8 gainMode;
+ u8 expMode;
+ u8 compMode;
+ u8 centreWeight;
+ u8 gain;
+ u8 fineExp;
+ u8 coarseExpLo;
+ u8 coarseExpHi;
+ u8 redComp;
+ u8 green1Comp;
+ u8 green2Comp;
+ u8 blueComp;
+ } exposure;
+ struct {
+ u8 balanceModeIsAuto;
+ u8 redGain;
+ u8 greenGain;
+ u8 blueGain;
+ } colourBalance;
+ struct {
+ u8 divisor;
+ u8 baserate;
+ } sensorFps;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } apcor;
+ struct {
+ u8 flickerMode;
+ u8 coarseJump;
+ u8 allowableOverExposure;
+ } flickerControl;
+ struct {
+ u8 gain1;
+ u8 gain2;
+ u8 gain4;
+ u8 gain8;
+ } vlOffset;
+ struct {
+ u8 mode;
+ u8 decimation;
+ } compression;
+ struct {
+ u8 frTargeting;
+ u8 targetFR;
+ u8 targetQ;
+ } compressionTarget;
+ struct {
+ u8 yThreshold;
+ u8 uvThreshold;
+ } yuvThreshold;
+ struct {
+ u8 hysteresis;
+ u8 threshMax;
+ u8 smallStep;
+ u8 largeStep;
+ u8 decimationHysteresis;
+ u8 frDiffStepThresh;
+ u8 qDiffStepThresh;
+ u8 decimationThreshMod;
+ } compressionParams;
+ struct {
+ u8 videoSize; /* CIF/QCIF */
+ u8 subSample;
+ u8 yuvOrder;
+ } format;
+ struct {
+ u8 colStart; /* skip first 8*colStart pixels */
+ u8 colEnd; /* finish at 8*colEnd pixels */
+ u8 rowStart; /* skip first 4*rowStart lines */
+ u8 rowEnd; /* finish at 4*rowEnd lines */
+ } roi;
+ u8 ecpTiming;
+ u8 streamStartLine;
+};
+
+enum v4l_camstates {
+ CPIA_V4L_IDLE = 0,
+ CPIA_V4L_ERROR,
+ CPIA_V4L_COMMAND,
+ CPIA_V4L_GRABBING,
+ CPIA_V4L_STREAMING,
+ CPIA_V4L_STREAMING_PAUSED,
+};
+
+#define FRAME_NUM 2 /* double buffering for now */
+
+struct cam_data {
+ struct cam_data **previous;
+ struct cam_data *next;
+
+ struct semaphore busy_lock; /* guard against SMP multithreading */
+ struct cpia_camera_ops *ops; /* lowlevel driver operations */
+ void *lowlevel_data; /* private data for lowlevel driver */
+ u8 *raw_image; /* buffer for raw image data */
+ struct cpia_frame decompressed_frame;
+ /* buffer to hold decompressed frame */
+ int image_size; /* sizeof last decompressed image */
+ int open_count; /* # of process that have camera open */
+
+ /* camera status */
+ int fps; /* actual fps reported by the camera */
+ int transfer_rate; /* transfer rate from camera in kB/s */
+ u8 mainsFreq; /* for flicker control */
+
+ /* proc interface */
+ struct semaphore param_lock; /* params lock for this camera */
+ struct cam_params params; /* camera settings */
+ struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */
+
+ /* v4l */
+ int video_size; /* VIDEO_SIZE_ */
+ volatile enum v4l_camstates camstate; /* v4l layer status */
+ struct video_device vdev; /* v4l videodev */
+ struct video_picture vp; /* v4l camera settings */
+ struct video_window vw; /* v4l capture area */
+
+ /* mmap interface */
+ int curframe; /* the current frame to grab into */
+ u8 *frame_buf; /* frame buffer data */
+ struct cpia_frame frame[FRAME_NUM];
+ /* FRAME_NUM-buffering, so we need a array */
+
+ int first_frame;
+ int mmap_kludge; /* 'wrong' byte order for mmap */
+ volatile u32 cmd_queue; /* queued commands */
+};
+
+/* cpia_register_camera is called by low level driver for each camera.
+ * A unique camera number is returned, or a negative value on error */
+struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel);
+
+/* cpia_unregister_camera is called by low level driver when a camera
+ * is removed. This must not fail. */
+void cpia_unregister_camera(struct cam_data *cam);
+
+/* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI +
+ * one byte 16bit DMA alignment
+ */
+#define CPIA_MAX_IMAGE_SIZE ((352*288*2)+64+(288*3)+5)
+
+/* constant value's */
+#define MAGIC_0 0x19
+#define MAGIC_1 0x68
+#define DATA_IN 0xC0
+#define DATA_OUT 0x40
+#define VIDEOSIZE_QCIF 0 /* 176x144 */
+#define VIDEOSIZE_CIF 1 /* 352x288 */
+#define VIDEOSIZE_SIF 2 /* 320x240 */
+#define VIDEOSIZE_QSIF 3 /* 160x120 */
+#define VIDEOSIZE_48_48 4 /* where no one has gone before, iconsize! */
+#define VIDEOSIZE_64_48 5
+#define VIDEOSIZE_128_96 6
+#define VIDEOSIZE_160_120 VIDEOSIZE_QSIF
+#define VIDEOSIZE_176_144 VIDEOSIZE_QCIF
+#define VIDEOSIZE_192_144 7
+#define VIDEOSIZE_224_168 8
+#define VIDEOSIZE_256_192 9
+#define VIDEOSIZE_288_216 10
+#define VIDEOSIZE_320_240 VIDEOSIZE_SIF
+#define VIDEOSIZE_352_288 VIDEOSIZE_CIF
+#define VIDEOSIZE_88_72 11 /* quarter CIF */
+#define SUBSAMPLE_420 0
+#define SUBSAMPLE_422 1
+#define YUVORDER_YUYV 0
+#define YUVORDER_UYVY 1
+#define NOT_COMPRESSED 0
+#define COMPRESSED 1
+#define NO_DECIMATION 0
+#define DECIMATION_ENAB 1
+#define EOI 0xff /* End Of Image */
+#define EOL 0xfd /* End Of Line */
+#define FRAME_HEADER_SIZE 64
+
+/* Image grab modes */
+#define CPIA_GRAB_SINGLE 0
+#define CPIA_GRAB_CONTINUOUS 1
+
+/* Compression parameters */
+#define CPIA_COMPRESSION_NONE 0
+#define CPIA_COMPRESSION_AUTO 1
+#define CPIA_COMPRESSION_MANUAL 2
+#define CPIA_COMPRESSION_TARGET_QUALITY 0
+#define CPIA_COMPRESSION_TARGET_FRAMERATE 1
+
+/* Return offsets for GetCameraState */
+#define SYSTEMSTATE 0
+#define GRABSTATE 1
+#define STREAMSTATE 2
+#define FATALERROR 3
+#define CMDERROR 4
+#define DEBUGFLAGS 5
+#define VPSTATUS 6
+#define ERRORCODE 7
+
+/* SystemState */
+#define UNINITIALISED_STATE 0
+#define PASS_THROUGH_STATE 1
+#define LO_POWER_STATE 2
+#define HI_POWER_STATE 3
+#define WARM_BOOT_STATE 4
+
+/* GrabState */
+#define GRAB_IDLE 0
+#define GRAB_ACTIVE 1
+#define GRAB_DONE 2
+
+/* StreamState */
+#define STREAM_NOT_READY 0
+#define STREAM_READY 1
+#define STREAM_OPEN 2
+#define STREAM_PAUSED 3
+#define STREAM_FINISHED 4
+
+/* Fatal Error, CmdError, and DebugFlags */
+#define CPIA_FLAG 1
+#define SYSTEM_FLAG 2
+#define INT_CTRL_FLAG 4
+#define PROCESS_FLAG 8
+#define COM_FLAG 16
+#define VP_CTRL_FLAG 32
+#define CAPTURE_FLAG 64
+#define DEBUG_FLAG 128
+
+/* VPStatus */
+#define VP_STATE_OK 0x00
+
+#define VP_STATE_FAILED_VIDEOINIT 0x01
+#define VP_STATE_FAILED_AECACBINIT 0x02
+#define VP_STATE_AEC_MAX 0x04
+#define VP_STATE_ACB_BMAX 0x08
+
+#define VP_STATE_ACB_RMIN 0x10
+#define VP_STATE_ACB_GMIN 0x20
+#define VP_STATE_ACB_RMAX 0x40
+#define VP_STATE_ACB_GMAX 0x80
+
+/* ErrorCode */
+#define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */
+
+#define ALOG(lineno,fmt,args...) printk(fmt,lineno,##args)
+#define LOG(fmt,args...) ALOG((__LINE__),KERN_INFO __FILE__":"__FUNCTION__"(%d):"fmt,##args)
+
+#ifdef _CPIA_DEBUG_
+#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, lineno, ##args)
+#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):"__FUNCTION__"(%d):"fmt,##args)
+#else
+#define DBG(fmn,args...) do {} while(0)
+#endif
+
+#define DEB_BYTE(p)\
+ DBG("%1d %1d %1d %1d %1d %1d %1d %1d \n",\
+ (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\
+ (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0);
+
+#define ADD_TO_LIST(l, drv) \
+ {\
+ lock_kernel();\
+ (drv)->next = l;\
+ (drv)->previous = &(l);\
+ (l) = drv;\
+ unlock_kernel();\
+ } while(0)
+
+#define REMOVE_FROM_LIST(drv) \
+ {\
+ if ((drv)->previous != NULL) {\
+ lock_kernel();\
+ if ((drv)->next != NULL)\
+ (drv)->next->previous = (drv)->previous;\
+ *((drv)->previous) = (drv)->next;\
+ (drv)->previous = NULL;\
+ (drv)->next = NULL;\
+ unlock_kernel();\
+ }\
+ } while (0)
+
+
+#endif /* __KERNEL__ */
+
+#endif /* cpia_h */
--- /dev/null
+/*
+ * cpia_pp CPiA Parallel Port driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * (C) Copyright 1999 Bas Huisman <bhuism@cs.utwente.nl>
+ * (C) Copyright 1999-2000 Scott J. Bertin <sbertin@mindspring.com>,
+ * (C) Copyright 1999-2000 Peter Pregler <Peter_Pregler@email.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 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/module.h>
+#include <linux/init.h>
+
+#include <linux/kernel.h>
+#include <linux/parport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/smp_lock.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+/* #define _CPIA_DEBUG_ define for verbose debug output */
+#include "cpia.h"
+
+static int cpia_pp_open(void *privdata);
+static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata),
+ void *cbdata);
+static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data);
+static int cpia_pp_streamStart(void *privdata);
+static int cpia_pp_streamStop(void *privdata);
+static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock);
+static int cpia_pp_close(void *privdata);
+
+#define ABOUT "Parallel port driver for Vision CPiA based cameras"
+
+/* IEEE 1284 Compatiblity Mode signal names */
+#define nStrobe PARPORT_CONTROL_STROBE /* inverted */
+#define nAutoFd PARPORT_CONTROL_AUTOFD /* inverted */
+#define nInit PARPORT_CONTROL_INIT
+#define nSelectIn PARPORT_CONTROL_SELECT
+#define IntrEnable PARPORT_CONTROL_INTEN /* normally zero for no IRQ */
+#define DirBit PARPORT_CONTROL_DIRECTION /* 0 = Forward, 1 = Reverse */
+
+#define nFault PARPORT_STATUS_ERROR
+#define Select PARPORT_STATUS_SELECT
+#define PError PARPORT_STATUS_PAPEROUT
+#define nAck PARPORT_STATUS_ACK
+#define Busy PARPORT_STATUS_BUSY /* inverted */
+
+/* some more */
+#define HostClk nStrobe
+#define HostAck nAutoFd
+#define nReverseRequest nInit
+#define Active_1284 nSelectIn
+#define nPeriphRequest nFault
+#define XFlag Select
+#define nAckReverse PError
+#define PeriphClk nAck
+#define PeriphAck Busy
+
+/* these can be used to correct for the inversion on some bits */
+#define STATUS_INVERSION_MASK (Busy)
+#define CONTROL_INVERSION_MASK (nStrobe|nAutoFd|nSelectIn)
+
+#define ECR_empty 0x01
+#define ECR_full 0x02
+#define ECR_serviceIntr 0x04
+#define ECR_dmaEn 0x08
+#define ECR_nErrIntrEn 0x10
+
+#define ECR_mode_mask 0xE0
+#define ECR_SPP_mode 0x00
+#define ECR_PS2_mode 0x20
+#define ECR_FIFO_mode 0x40
+#define ECR_ECP_mode 0x60
+
+#define ECP_FIFO_SIZE 16
+#define DMA_BUFFER_SIZE PAGE_SIZE
+ /* for 16bit DMA make sure DMA_BUFFER_SIZE is 16 bit aligned */
+#define PARPORT_CHUNK_SIZE PAGE_SIZE/* >=2.3.x */
+ /* we read this many bytes at once */
+
+#define GetECRMasked(port,mask) (parport_read_econtrol(port) & (mask))
+#define GetStatus(port) ((parport_read_status(port)^STATUS_INVERSION_MASK)&(0xf8))
+#define SetStatus(port,val) parport_write_status(port,(val)^STATUS_INVERSION_MASK)
+#define GetControl(port) ((parport_read_control(port)^CONTROL_INVERSION_MASK)&(0x3f))
+#define SetControl(port,val) parport_write_control(port,(val)^CONTROL_INVERSION_MASK)
+
+#define GetStatusMasked(port,mask) (GetStatus(port) & (mask))
+#define GetControlMasked(port,mask) (GetControl(port) & (mask))
+#define SetControlMasked(port,mask) SetControl(port,GetControl(port) | (mask));
+#define ClearControlMasked(port,mask) SetControl(port,GetControl(port)&~(mask));
+#define FrobControlBit(port,mask,value) SetControl(port,(GetControl(port)&~(mask))|((value)&(mask)));
+
+#define PACKET_LENGTH 8
+
+/* Magic numbers for defining port-device mappings */
+#define PPCPIA_PARPORT_UNSPEC -4
+#define PPCPIA_PARPORT_AUTO -3
+#define PPCPIA_PARPORT_OFF -2
+#define PPCPIA_PARPORT_NONE -1
+
+#ifdef MODULE
+static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
+static char *parport[PARPORT_MAX] = {NULL,};
+
+MODULE_AUTHOR("B. Huisman <bhuism@cs.utwente.nl> & Peter Pregler <Peter_Pregler@email.com>");
+MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras");
+MODULE_PARM(parport, "1-" __MODULE_STRING(PARPORT_MAX) "s");
+MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp.");
+#else
+static int parport_nr[PARPORT_MAX] __initdata =
+ {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC};
+static int parport_ptr = 0;
+#endif
+
+struct pp_cam_entry {
+ struct pardevice *pdev;
+ struct parport *port;
+ struct tq_struct cb_task;
+ int open_count;
+ wait_queue_head_t wq_stream;
+ /* image state flags */
+ int image_ready; /* we got an interrupt */
+ int image_complete; /* we have seen 4 EOI */
+
+ int streaming; /* we are in streaming mode */
+ int stream_irq;
+};
+
+static struct cpia_camera_ops cpia_pp_ops =
+{
+ cpia_pp_open,
+ cpia_pp_registerCallback,
+ cpia_pp_transferCmd,
+ cpia_pp_streamStart,
+ cpia_pp_streamStop,
+ cpia_pp_streamRead,
+ cpia_pp_close,
+ 1
+};
+
+static struct cam_data *cam_list;
+
+#ifdef _CPIA_DEBUG_
+#define DEB_PORT(port) { \
+u8 controll = GetControl(port); \
+u8 statusss = GetStatus(port); \
+DBG("nsel %c per %c naut %c nstrob %c nak %c busy %c nfaul %c sel %c init %c dir %c\n",\
+((controll & nSelectIn) ? 'U' : 'D'), \
+((statusss & PError) ? 'U' : 'D'), \
+((controll & nAutoFd) ? 'U' : 'D'), \
+((controll & nStrobe) ? 'U' : 'D'), \
+((statusss & nAck) ? 'U' : 'D'), \
+((statusss & Busy) ? 'U' : 'D'), \
+((statusss & nFault) ? 'U' : 'D'), \
+((statusss & Select) ? 'U' : 'D'), \
+((controll & nInit) ? 'U' : 'D'), \
+((controll & DirBit) ? 'R' : 'F') \
+); }
+#else
+#define DEB_PORT(port) {}
+#endif
+
+#define WHILE_OUT_TIMEOUT (HZ/10)
+#define DMA_TIMEOUT 10*HZ
+
+/* FIXME */
+static void cpia_parport_enable_irq( struct parport *port ) {
+ parport_enable_irq(port);
+ mdelay(10);
+ return;
+}
+
+static void cpia_parport_disable_irq( struct parport *port ) {
+ parport_disable_irq(port);
+ mdelay(10);
+ return;
+}
+
+/****************************************************************************
+ *
+ * EndTransferMode
+ *
+ ***************************************************************************/
+static void EndTransferMode(struct pp_cam_entry *cam)
+{
+ parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
+}
+
+/****************************************************************************
+ *
+ * ForwardSetup
+ *
+ ***************************************************************************/
+static int ForwardSetup(struct pp_cam_entry *cam)
+{
+ int retry;
+
+ /* After some commands the camera needs extra time before
+ * it will respond again, so we try up to 3 times */
+ for(retry=0; retry<3; ++retry) {
+ if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) {
+ break;
+ }
+ }
+ if(retry == 3) {
+ DBG("Unable to negotiate ECP mode\n");
+ return -1;
+ }
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * ReverseSetup
+ *
+ ***************************************************************************/
+static int ReverseSetup(struct pp_cam_entry *cam, int extensibility)
+{
+ int retry;
+ int mode = IEEE1284_MODE_ECP;
+ if(extensibility) mode = 8|3|IEEE1284_EXT_LINK;
+
+ /* After some commands the camera needs extra time before
+ * it will respond again, so we try up to 3 times */
+ for(retry=0; retry<3; ++retry) {
+ if(!parport_negotiate(cam->port, mode)) {
+ break;
+ }
+ }
+ if(retry == 3) {
+ if(extensibility)
+ DBG("Unable to negotiate extensibility mode\n");
+ else
+ DBG("Unable to negotiate ECP mode\n");
+ return -1;
+ }
+ if(extensibility) cam->port->ieee1284.mode = IEEE1284_MODE_ECP;
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * WritePacket
+ *
+ ***************************************************************************/
+static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size)
+{
+ int retval=0;
+ int size_written;
+
+ if (packet == NULL) {
+ return -EINVAL;
+ }
+ if (ForwardSetup(cam)) {
+ DBG("Write failed in setup\n");
+ return -EIO;
+ }
+ size_written = parport_write(cam->port, packet, size);
+ if(size_written != size) {
+ DBG("Write failed, wrote %d/%d\n", size_written, size);
+ retval = -EIO;
+ }
+ EndTransferMode(cam);
+ return retval;
+}
+
+/****************************************************************************
+ *
+ * ReadPacket
+ *
+ ***************************************************************************/
+static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size)
+{
+ int retval=0;
+ if (packet == NULL) {
+ return -EINVAL;
+ }
+ if (ReverseSetup(cam, 0)) {
+ return -EIO;
+ }
+ if(parport_read(cam->port, packet, size) != size) {
+ retval = -EIO;
+ }
+ EndTransferMode(cam);
+ return retval;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_streamStart
+ *
+ ***************************************************************************/
+static int cpia_pp_streamStart(void *privdata)
+{
+ struct pp_cam_entry *cam = privdata;
+ DBG("\n");
+ cam->streaming=1;
+ cam->image_ready=0;
+ //if (ReverseSetup(cam,1)) return -EIO;
+ if(cam->stream_irq) cpia_parport_enable_irq(cam->port);
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_streamStop
+ *
+ ***************************************************************************/
+static int cpia_pp_streamStop(void *privdata)
+{
+ struct pp_cam_entry *cam = privdata;
+
+ DBG("\n");
+ cam->streaming=0;
+ cpia_parport_disable_irq(cam->port);
+ //EndTransferMode(cam);
+
+ return 0;
+}
+
+static int cpia_pp_read(struct parport *port, u8 *buffer, int len)
+{
+ int bytes_read, new_bytes;
+ for(bytes_read=0; bytes_read<len; bytes_read += new_bytes) {
+ new_bytes = parport_read(port, buffer+bytes_read,
+ len-bytes_read);
+ if(new_bytes < 0) break;
+ }
+ return bytes_read;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_streamRead
+ *
+ ***************************************************************************/
+static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock)
+{
+ struct pp_cam_entry *cam = privdata;
+ int read_bytes = 0;
+ int i, endseen, block_size, new_bytes;
+
+ if(cam == NULL) {
+ DBG("Internal driver error: cam is NULL\n");
+ return -EINVAL;
+ }
+ if(buffer == NULL) {
+ DBG("Internal driver error: buffer is NULL\n");
+ return -EINVAL;
+ }
+ //if(cam->streaming) DBG("%d / %d\n", cam->image_ready, noblock);
+ if( cam->stream_irq ) {
+ DBG("%d\n", cam->image_ready);
+ cam->image_ready--;
+ }
+ cam->image_complete=0;
+ if (0/*cam->streaming*/) {
+ if(!cam->image_ready) {
+ if(noblock) return -EWOULDBLOCK;
+ interruptible_sleep_on(&cam->wq_stream);
+ if( signal_pending(current) ) return -EINTR;
+ DBG("%d\n", cam->image_ready);
+ }
+ } else {
+ if (ReverseSetup(cam, 1)) {
+ DBG("unable to ReverseSetup\n");
+ return -EIO;
+ }
+ }
+ endseen = 0;
+ block_size = PARPORT_CHUNK_SIZE;
+ while( !cam->image_complete ) {
+ if(current->need_resched) schedule();
+
+ new_bytes = cpia_pp_read(cam->port, buffer, block_size );
+ if( new_bytes <= 0 ) {
+ break;
+ }
+ i=-1;
+ while(++i<new_bytes && endseen<4) {
+ if(*buffer==EOI) {
+ endseen++;
+ } else {
+ endseen=0;
+ }
+ buffer++;
+ }
+ read_bytes += i;
+ if( endseen==4 ) {
+ cam->image_complete=1;
+ break;
+ }
+ if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) {
+ block_size=CPIA_MAX_IMAGE_SIZE-read_bytes;
+ }
+ }
+ EndTransferMode(cam);
+ return cam->image_complete ? read_bytes : -EIO;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_transferCmd
+ *
+ ***************************************************************************/
+static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data)
+{
+ int err;
+ int retval=0;
+ int databytes;
+ struct pp_cam_entry *cam = privdata;
+
+ if(cam == NULL) {
+ DBG("Internal driver error: cam is NULL\n");
+ return -EINVAL;
+ }
+ if(command == NULL) {
+ DBG("Internal driver error: command is NULL\n");
+ return -EINVAL;
+ }
+ databytes = (((int)command[7])<<8) | command[6];
+ if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) {
+ DBG("Error writing command\n");
+ return err;
+ }
+ if(command[0] == DATA_IN) {
+ u8 buffer[8];
+ if(data == NULL) {
+ DBG("Internal driver error: data is NULL\n");
+ return -EINVAL;
+ }
+ if((err = ReadPacket(cam, buffer, 8)) < 0) {
+ return err;
+ DBG("Error reading command result\n");
+ }
+ memcpy(data, buffer, databytes);
+ } else if(command[0] == DATA_OUT) {
+ if(databytes > 0) {
+ if(data == NULL) {
+ DBG("Internal driver error: data is NULL\n");
+ retval = -EINVAL;
+ } else {
+ if((err=WritePacket(cam, data, databytes)) < 0){
+ DBG("Error writing command data\n");
+ return err;
+ }
+ }
+ }
+ } else {
+ DBG("Unexpected first byte of command: %x\n", command[0]);
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_open
+ *
+ ***************************************************************************/
+static int cpia_pp_open(void *privdata)
+{
+ struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata;
+
+ if (cam == NULL)
+ return -EINVAL;
+
+ if(cam->open_count == 0) {
+ if (parport_claim(cam->pdev)) {
+ DBG("failed to claim the port\n");
+ return -EBUSY;
+ }
+ parport_negotiate(cam->port, IEEE1284_MODE_COMPAT);
+ parport_data_forward(cam->port);
+ parport_write_control(cam->port, PARPORT_CONTROL_SELECT);
+ udelay(50);
+ parport_write_control(cam->port,
+ PARPORT_CONTROL_SELECT
+ | PARPORT_CONTROL_INIT);
+ }
+
+ ++cam->open_count;
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_registerCallback
+ *
+ ***************************************************************************/
+static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), void *cbdata)
+{
+ struct pp_cam_entry *cam = privdata;
+ int retval = 0;
+
+ if(cam->port->irq != PARPORT_IRQ_NONE) {
+ cam->cb_task.routine = cb;
+ cam->cb_task.data = cbdata;
+ } else {
+ retval = -1;
+ }
+ return retval;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_close
+ *
+ ***************************************************************************/
+static int cpia_pp_close(void *privdata)
+{
+ struct pp_cam_entry *cam = privdata;
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+ if (--cam->open_count == 0) {
+ parport_release(cam->pdev);
+ }
+ return 0;
+}
+
+/****************************************************************************
+ *
+ * cpia_pp_register
+ *
+ ***************************************************************************/
+static int cpia_pp_register(struct parport *port)
+{
+ struct pardevice *pdev = NULL;
+ struct pp_cam_entry *cam;
+ struct cam_data *cpia;
+
+ if (!(port->modes & PARPORT_MODE_ECP) &&
+ !(port->modes & PARPORT_MODE_TRISTATE)) {
+ LOG("port is not ECP capable\n");
+ return -ENXIO;
+ }
+
+ cam = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL);
+ if (cam == NULL) {
+ LOG("failed to allocate camera structure\n");
+ return -ENOMEM;
+ }
+ memset(cam,0,sizeof(struct pp_cam_entry));
+
+ pdev = parport_register_device(port, "cpia_pp", NULL, NULL,
+ NULL, 0, cam);
+
+ if (!pdev) {
+ LOG("failed to parport_register_device\n");
+ kfree(cam);
+ return -ENXIO;
+ }
+
+ cam->pdev = pdev;
+ cam->port = port;
+ init_waitqueue_head(&cam->wq_stream);
+
+ cam->streaming = 0;
+ cam->stream_irq = 0;
+
+ if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) {
+ LOG("failed to cpia_register_camera\n");
+ parport_unregister_device(pdev);
+ kfree(cam);
+ return -ENXIO;
+ }
+ ADD_TO_LIST(cam_list, cpia);
+
+ return 0;
+}
+
+static void cpia_pp_detach (struct parport *port)
+{
+ struct cam_data *cpia;
+
+ for(cpia = cam_list; cpia != NULL; cpia = cpia->next) {
+ struct pp_cam_entry *cam = cpia->lowlevel_data;
+ if (cam && cam->port->number == port->number) {
+ REMOVE_FROM_LIST(cpia);
+
+ cpia_unregister_camera(cpia);
+
+ if(cam->open_count > 0) {
+ cpia_pp_close(cam);
+ }
+
+ parport_unregister_device(cam->pdev);
+
+ kfree(cam);
+ cpia->lowlevel_data = NULL;
+ break;
+ }
+ }
+}
+
+static void cpia_pp_attach (struct parport *port)
+{
+ unsigned int i;
+
+ switch (parport_nr[0])
+ {
+ case PPCPIA_PARPORT_UNSPEC:
+ case PPCPIA_PARPORT_AUTO:
+ if (port->probe_info[0].class != PARPORT_CLASS_MEDIA ||
+ port->probe_info[0].cmdset == NULL ||
+ strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0)
+ return;
+
+ cpia_pp_register(port);
+
+ break;
+
+ default:
+ for (i = 0; i < PARPORT_MAX; ++i) {
+ if (port->number == parport_nr[i]) {
+ cpia_pp_register(port);
+ break;
+ }
+ }
+ break;
+ }
+}
+
+static struct parport_driver cpia_pp_driver = {
+ "cpia_pp",
+ cpia_pp_attach,
+ cpia_pp_detach,
+ NULL
+};
+
+int cpia_pp_init(void)
+{
+ printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT,
+ CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER);
+
+ if(parport_nr[0] == PPCPIA_PARPORT_OFF) {
+ printk(" disabled\n");
+ return 0;
+ }
+
+ if (parport_register_driver (&cpia_pp_driver)) {
+ LOG ("unable to register with parport\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ if (parport[0]) {
+ /* The user gave some parameters. Let's see what they were. */
+ if (!strncmp(parport[0], "auto", 4)) {
+ parport_nr[0] = PPCPIA_PARPORT_AUTO;
+ } else {
+ int n;
+ for (n = 0; n < PARPORT_MAX && parport[n]; n++) {
+ if (!strncmp(parport[n], "none", 4)) {
+ parport_nr[n] = PPCPIA_PARPORT_NONE;
+ } else {
+ char *ep;
+ unsigned long r = simple_strtoul(parport[n], &ep, 0);
+ if (ep != parport[n]) {
+ parport_nr[n] = r;
+ } else {
+ LOG("bad port specifier `%s'\n", parport[n]);
+ return -ENODEV;
+ }
+ }
+ }
+ }
+ }
+#if defined(CONFIG_KMOD) && defined(CONFIG_PNP_PARPORT_MODULE)
+ if(parport_enumerate() && !parport_enumerate()->probe_info.model) {
+ request_module("parport_probe");
+ }
+#endif
+ return cpia_pp_init();
+}
+
+void cleanup_module(void)
+{
+ parport_unregister_driver (&cpia_pp_driver);
+ return;
+}
+
+#else /* !MODULE */
+
+static int __init cpia_pp_setup(char *str)
+{
+#if 0
+ /* Is this only a 2.2ism? -jerdfelt */
+ if (!str) {
+ if (ints[0] == 0 || ints[1] == 0) {
+ /* disable driver on "cpia_pp=" or "cpia_pp=0" */
+ parport_nr[0] = PPCPIA_PARPORT_OFF;
+ }
+ } else
+#endif
+ if (!strncmp(str, "parport", 7)) {
+ int n = simple_strtoul(str + 7, NULL, 10);
+ if (parport_ptr < PARPORT_MAX) {
+ parport_nr[parport_ptr++] = n;
+ } else {
+ LOG("too many ports, %s ignored.\n", str);
+ }
+ } else if (!strcmp(str, "auto")) {
+ parport_nr[0] = PPCPIA_PARPORT_AUTO;
+ } else if (!strcmp(str, "none")) {
+ parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE;
+ }
+
+ return 0;
+}
+
+__setup("cpia_pp=", cpia_pp_setup);
+
+#endif /* !MODULE */
--- /dev/null
+/*
+ * cpia_usb CPiA USB driver
+ *
+ * Supports CPiA based parallel port Video Camera's.
+ *
+ * Copyright (C) 1999 Jochen Scharrlach <Jochen.Scharrlach@schwaben.de>
+ * Copyright (C) 1999, 2000 Johannes Erdfelt <jerdfelt@valinux.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 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/kernel.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/usb.h>
+
+#include "cpia.h"
+
+#define USB_REQ_CPIA_GRAB_FRAME 0xC1
+#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2
+#define WAIT_FOR_NEXT_FRAME 0
+#define FORCE_FRAME_UPLOAD 1
+
+#define FRAMES_PER_DESC 10
+#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */
+#define CPIA_NUMSBUF 2
+#define STREAM_BUF_SIZE (PAGE_SIZE * 4)
+#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2)
+
+struct cpia_sbuf {
+ char *data;
+ urb_t *urb;
+};
+
+#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100)
+enum framebuf_status {
+ FRAME_EMPTY,
+ FRAME_READING,
+ FRAME_READY,
+ FRAME_ERROR,
+};
+
+struct framebuf {
+ int length;
+ enum framebuf_status status;
+ u8 data[FRAMEBUF_LEN];
+ struct framebuf *next;
+};
+
+struct usb_cpia {
+ /* Device structure */
+ struct usb_device *dev;
+
+ unsigned char iface;
+ wait_queue_head_t wq_stream;
+
+ int cursbuf; /* Current receiving sbuf */
+ struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */
+
+ int streaming;
+ int open;
+ int present;
+ struct framebuf *buffers[3];
+ struct framebuf *curbuff, *workbuff;
+};
+
+static int cpia_usb_open(void *privdata);
+static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
+ void *cbdata);
+static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data);
+static int cpia_usb_streamStart(void *privdata);
+static int cpia_usb_streamStop(void *privdata);
+static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock);
+static int cpia_usb_close(void *privdata);
+
+#define ABOUT "USB driver for Vision CPiA based cameras"
+
+static struct cpia_camera_ops cpia_usb_ops = {
+ cpia_usb_open,
+ cpia_usb_registerCallback,
+ cpia_usb_transferCmd,
+ cpia_usb_streamStart,
+ cpia_usb_streamStop,
+ cpia_usb_streamRead,
+ cpia_usb_close,
+ 0
+};
+
+static struct cam_data *cam_list;
+
+static void cpia_usb_complete(struct urb *urb)
+{
+ int i;
+ char *cdata;
+ struct usb_cpia *ucpia;
+
+ if (!urb || !urb->context)
+ return;
+
+ ucpia = (struct usb_cpia *) urb->context;
+
+ if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open)
+ return;
+
+ if (ucpia->workbuff->status == FRAME_EMPTY) {
+ ucpia->workbuff->status = FRAME_READING;
+ ucpia->workbuff->length = 0;
+ }
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int n = urb->iso_frame_desc[i].actual_length;
+ int st = urb->iso_frame_desc[i].status;
+
+ cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ if (st)
+ printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st);
+
+ if (FRAMEBUF_LEN < ucpia->workbuff->length + n) {
+ printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n);
+ return;
+ }
+
+ if (n) {
+ if ((ucpia->workbuff->length > 0) ||
+ (0x19 == cdata[0] && 0x68 == cdata[1])) {
+ memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n);
+ ucpia->workbuff->length += n;
+ } else
+ DBG("Ignoring packet!\n");
+ } else {
+ if (ucpia->workbuff->length > 4 &&
+ 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] &&
+ 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] &&
+ 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] &&
+ 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) {
+ ucpia->workbuff->status = FRAME_READY;
+ ucpia->curbuff = ucpia->workbuff;
+ ucpia->workbuff = ucpia->workbuff->next;
+ ucpia->workbuff->status = FRAME_EMPTY;
+ ucpia->workbuff->length = 0;
+
+ if (waitqueue_active(&ucpia->wq_stream))
+ wake_up_interruptible(&ucpia->wq_stream);
+ }
+ }
+ }
+}
+
+static int cpia_usb_open(void *privdata)
+{
+ struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+ urb_t *urb;
+ int ret, retval = 0, fx, err;
+
+ if (!ucpia)
+ return -EINVAL;
+
+ ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ if (!ucpia->sbuf[0].data)
+ return -EINVAL;
+
+ ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ if (!ucpia->sbuf[1].data) {
+ retval = -EINVAL;
+ goto error_0;
+ }
+
+ ret = usb_set_interface(ucpia->dev, ucpia->iface, 3);
+ if (ret < 0) {
+ printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret);
+ retval = -EBUSY;
+ goto error_all;
+ }
+
+ ucpia->buffers[0]->status = FRAME_EMPTY;
+ ucpia->buffers[0]->length = 0;
+ ucpia->buffers[1]->status = FRAME_EMPTY;
+ ucpia->buffers[1]->length = 0;
+ ucpia->buffers[2]->status = FRAME_EMPTY;
+ ucpia->buffers[2]->length = 0;
+ ucpia->curbuff = ucpia->buffers[0];
+ ucpia->workbuff = ucpia->buffers[1];
+
+ /* We double buffer the Iso lists */
+ urb = usb_alloc_urb(FRAMES_PER_DESC);
+ if (!urb) {
+ printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n");
+ retval = -ENOMEM;
+ goto error_all;
+ }
+
+ ucpia->sbuf[0].urb = urb;
+ urb->dev = ucpia->dev;
+ urb->context = ucpia;
+ urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
+ urb->transfer_flags = USB_ISO_ASAP;
+ urb->transfer_buffer = ucpia->sbuf[0].data;
+ urb->complete = cpia_usb_complete;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
+ urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+ }
+
+ urb = usb_alloc_urb(FRAMES_PER_DESC);
+ if (!urb) {
+ printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n");
+ retval = -ENOMEM;
+ goto error_all;
+ }
+
+ ucpia->sbuf[1].urb = urb;
+ urb->dev = ucpia->dev;
+ urb->context = ucpia;
+ urb->pipe = usb_rcvisocpipe(ucpia->dev, 1);
+ urb->transfer_flags = USB_ISO_ASAP;
+ urb->transfer_buffer = ucpia->sbuf[1].data;
+ urb->complete = cpia_usb_complete;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+ for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
+ urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
+ urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+ }
+
+ ucpia->sbuf[1].urb->next = ucpia->sbuf[0].urb;
+ ucpia->sbuf[0].urb->next = ucpia->sbuf[1].urb;
+
+ err = usb_submit_urb(ucpia->sbuf[0].urb);
+ if (err)
+ printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n",
+ err);
+ err = usb_submit_urb(ucpia->sbuf[1].urb);
+ if (err)
+ printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n",
+ err);
+
+ ucpia->streaming = 1;
+ ucpia->open = 1;
+
+ return 0;
+
+error_all:
+ kfree (ucpia->sbuf[1].data);
+error_0:
+ kfree (ucpia->sbuf[0].data);
+
+ return retval;
+}
+
+//
+// convenience functions
+//
+
+/****************************************************************************
+ *
+ * WritePacket
+ *
+ ***************************************************************************/
+static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size)
+{
+ if (!packet)
+ return -EINVAL;
+
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ packet[1] + (packet[0] << 8),
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ packet[2] + (packet[3] << 8),
+ packet[4] + (packet[5] << 8), buf, size, HZ);
+}
+
+/****************************************************************************
+ *
+ * ReadPacket
+ *
+ ***************************************************************************/
+static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size)
+{
+ if (!packet || size <= 0)
+ return -EINVAL;
+
+ return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ packet[1] + (packet[0] << 8),
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ packet[2] + (packet[3] << 8),
+ packet[4] + (packet[5] << 8), buf, size, HZ);
+}
+
+static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data)
+{
+ int err = 0;
+ int databytes;
+ struct usb_cpia *ucpia = (struct usb_cpia *)privdata;
+ struct usb_device *udev = ucpia->dev;
+
+ if (!udev) {
+ DBG("Internal driver error: udev is NULL\n");
+ return -EINVAL;
+ }
+
+ if (!command) {
+ DBG("Internal driver error: command is NULL\n");
+ return -EINVAL;
+ }
+
+ databytes = (((int)command[7])<<8) | command[6];
+
+ if (command[0] == DATA_IN) {
+ u8 buffer[8];
+
+ if (!data) {
+ DBG("Internal driver error: data is NULL\n");
+ return -EINVAL;
+ }
+
+ err = ReadPacket(udev, command, buffer, 8);
+ if (err < 0)
+ return err;
+
+ memcpy(data, buffer, databytes);
+ } else if(command[0] == DATA_OUT)
+ WritePacket(udev, command, data, databytes);
+ else {
+ DBG("Unexpected first byte of command: %x\n", command[0]);
+ err = -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata),
+ void *cbdata)
+{
+ return -ENODEV;
+}
+
+static int cpia_usb_streamStart(void *privdata)
+{
+ return -ENODEV;
+}
+
+static int cpia_usb_streamStop(void *privdata)
+{
+ return -ENODEV;
+}
+
+static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock)
+{
+ struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+ struct framebuf *mybuff;
+
+ if (!ucpia || !ucpia->present)
+ return -1;
+
+ if (ucpia->curbuff->status != FRAME_READY)
+ interruptible_sleep_on(&ucpia->wq_stream);
+ else
+ DBG("Frame already waiting!\n");
+
+ mybuff = ucpia->curbuff;
+
+ if (!mybuff)
+ return -1;
+
+ if (mybuff->status != FRAME_READY || mybuff->length < 4) {
+ DBG("Something went wrong!\n");
+ return -1;
+ }
+
+ memcpy(frame, mybuff->data, mybuff->length);
+ mybuff->status = FRAME_EMPTY;
+
+/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */
+/* mybuff->length, frame[0], frame[1], */
+/* frame[mybuff->length-4], frame[mybuff->length-3], */
+/* frame[mybuff->length-2], frame[mybuff->length-1]); */
+
+ return mybuff->length;
+}
+
+static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try)
+{
+ if (!ucpia->streaming)
+ return;
+
+ ucpia->streaming = 0;
+
+ /* Set packet size to 0 */
+ if (try) {
+ int ret;
+
+ ret = usb_set_interface(ucpia->dev, ucpia->iface, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret);
+ return;
+ }
+ }
+
+ /* Unschedule all of the iso td's */
+ if (ucpia->sbuf[1].urb) {
+ usb_unlink_urb(ucpia->sbuf[1].urb);
+ usb_free_urb(ucpia->sbuf[1].urb);
+ ucpia->sbuf[1].urb = NULL;
+ }
+
+ if (ucpia->sbuf[0].urb) {
+ usb_unlink_urb(ucpia->sbuf[0].urb);
+ usb_free_urb(ucpia->sbuf[0].urb);
+ ucpia->sbuf[0].urb = NULL;
+ }
+}
+
+static int cpia_usb_close(void *privdata)
+{
+ struct usb_cpia *ucpia = (struct usb_cpia *) privdata;
+
+ ucpia->open = 0;
+
+ cpia_usb_free_resources(ucpia, 1);
+
+ if (!ucpia->present)
+ kfree(ucpia);
+
+ return 0;
+}
+
+int cpia_usb_init(void)
+{
+ /* return -ENODEV; */
+ return 0;
+}
+
+/* Probing and initializing */
+
+static void *cpia_probe(struct usb_device *udev, unsigned int ifnum)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_cpia *ucpia;
+ struct cam_data *cam;
+ int ret;
+
+ /* A multi-config CPiA camera? */
+ if (udev->descriptor.bNumConfigurations != 1)
+ return NULL;
+
+ interface = &udev->actconfig->interface[ifnum].altsetting[0];
+
+ /* Is it a CPiA? */
+ if (udev->descriptor.idVendor != 0x0553)
+ return NULL;
+ if (udev->descriptor.idProduct != 0x0002)
+ return NULL;
+
+ /* We found a CPiA */
+ printk(KERN_INFO "USB CPiA camera found\n");
+
+ ucpia = kmalloc(sizeof(*ucpia), GFP_KERNEL);
+ if (!ucpia) {
+ printk(KERN_ERR "couldn't kmalloc cpia struct\n");
+ return NULL;
+ }
+
+ memset(ucpia, 0, sizeof(*ucpia));
+
+ ucpia->dev = udev;
+ ucpia->iface = interface->bInterfaceNumber;
+ init_waitqueue_head(&ucpia->wq_stream);
+
+ ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0]));
+ if (!ucpia->buffers[0]) {
+ printk(KERN_ERR "couldn't vmalloc frame buffer 0\n");
+ goto fail_alloc_0;
+ }
+
+ ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1]));
+ if (!ucpia->buffers[1]) {
+ printk(KERN_ERR "couldn't vmalloc frame buffer 1\n");
+ goto fail_alloc_1;
+ }
+
+ ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2]));
+ if (!ucpia->buffers[2]) {
+ printk(KERN_ERR "couldn't vmalloc frame buffer 2\n");
+ goto fail_alloc_2;
+ }
+
+ ucpia->buffers[0]->next = ucpia->buffers[1];
+ ucpia->buffers[1]->next = ucpia->buffers[2];
+ ucpia->buffers[2]->next = ucpia->buffers[0];
+
+ ret = usb_set_interface(udev, ucpia->iface, 0);
+ if (ret < 0) {
+ printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret);
+ /* goto fail_all; */
+ }
+
+ /* Before register_camera, important */
+ ucpia->present = 1;
+
+ cam = cpia_register_camera(&cpia_usb_ops, ucpia);
+ if (!cam) {
+ LOG("failed to cpia_register_camera\n");
+ goto fail_all;
+ }
+
+ ADD_TO_LIST(cam_list, cam);
+
+ return cam;
+
+fail_all:
+ vfree(ucpia->buffers[2]);
+ ucpia->buffers[2] = NULL;
+fail_alloc_2:
+ vfree(ucpia->buffers[1]);
+ ucpia->buffers[1] = NULL;
+fail_alloc_1:
+ vfree(ucpia->buffers[0]);
+ ucpia->buffers[0] = NULL;
+fail_alloc_0:
+
+ return NULL;
+}
+
+static void cpia_disconnect(struct usb_device *dev, void *ptr);
+
+static struct usb_driver cpia_driver = {
+ "cpia",
+ cpia_probe,
+ cpia_disconnect,
+ { NULL, NULL }
+};
+
+/* don't use dev, it may be NULL! (see usb_cpia_cleanup) */
+/* _disconnect from usb_cpia_cleanup is not necessary since usb_deregister */
+/* will do it for us as well as passing a udev structure - jerdfelt */
+static void cpia_disconnect(struct usb_device *udev, void *ptr)
+{
+ struct cam_data *cam = (struct cam_data *) ptr;
+ struct usb_cpia *ucpia = (struct usb_cpia *) cam->lowlevel_data;
+
+ REMOVE_FROM_LIST(cam);
+
+ /* Don't even try to reset the altsetting if we're disconnected */
+ cpia_usb_free_resources(ucpia, 0);
+
+ ucpia->present = 0;
+
+ cpia_unregister_camera(cam);
+
+ ucpia->curbuff->status = FRAME_ERROR;
+
+ if (waitqueue_active(&ucpia->wq_stream))
+ wake_up_interruptible(&ucpia->wq_stream);
+
+ usb_driver_release_interface(&cpia_driver,
+ &udev->actconfig->interface[0]);
+
+ ucpia->curbuff = ucpia->workbuff = NULL;
+
+ if (ucpia->buffers[2]) {
+ vfree(ucpia->buffers[2]);
+ ucpia->buffers[2] = NULL;
+ }
+
+ if (ucpia->buffers[1]) {
+ vfree(ucpia->buffers[1]);
+ ucpia->buffers[1] = NULL;
+ }
+
+ if (ucpia->buffers[0]) {
+ vfree(ucpia->buffers[0]);
+ ucpia->buffers[0] = NULL;
+ }
+
+ if (!ucpia->open)
+ kfree(ucpia);
+}
+
+int usb_cpia_init(void)
+{
+ cam_list = NULL;
+
+ return usb_register(&cpia_driver);
+}
+
+void usb_cpia_cleanup(void)
+{
+/*
+ struct cam_data *cam;
+
+ while ((cam = cam_list) != NULL)
+ cpia_disconnect(NULL, cam);
+*/
+
+ usb_deregister(&cpia_driver);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return usb_cpia_init();
+}
+
+void cleanup_module(void)
+{
+ usb_cpia_cleanup();
+}
+#endif /* !MODULE */
/* U.S. Robotics 56K FAX INT */
{ ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3031), 0, 0,
SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
+ /* Viking 56K FAX INT */
+ { ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x0262), 0, 0,
+ SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT, 1, 115200 },
/* These ID's are taken from M$ documentation */
/* Compaq 14400 Modem */
-/* $Id: sh-sci.c,v 1.32 2000-03-05 13:56:18+09 gniibe Exp $
+/* $Id: sh-sci.c,v 1.36 2000/03/22 13:32:10 gniibe Exp $
*
* linux/drivers/char/sh-sci.c
*
* SuperH on-chip serial module support. (SCI with no FIFO / with FIFO)
* Copyright (C) 1999, 2000 Niibe Yutaka
+ * Copyright (C) 2000 Sugioka Toshinobu
*
* TTY code is based on sx.c (Specialix SX driver) by:
*
#include <asm/uaccess.h>
#include <asm/bitops.h>
-#include "generic_serial.h"
+#include <linux/generic_serial.h>
#include "sh-sci.h"
#ifdef CONFIG_DEBUG_KERNEL_WITH_GDB_STUB
static void sci_enable_rx_interrupts(void *ptr);
static int sci_get_CD(void *ptr);
static void sci_shutdown_port(void *ptr);
-static void sci_set_real_termios(void *ptr);
+static int sci_set_real_termios(void *ptr);
static void sci_hungup(void *ptr);
static void sci_close(void *ptr);
static int sci_chars_in_buffer(void *ptr);
}
if (t > 0) {
- sci_setsignals (port, 1, -1);
+ sci_setsignals (port, 1, -1);
+ if(t >= 256) {
+ ctrl_out((ctrl_in(SCSMR) & ~3) | 1, SCSMR);
+ t >>= 2;
+ }
ctrl_outb(t, SCBRR);
ctrl_outw(0xa400, RFCR); /* Refresh counter clear */
while (ctrl_inw(RFCR) < WAIT_RFCR_COUNTER)
static void sci_set_termios_cflag(struct sci_port *port)
{
unsigned short status;
- unsigned short smr_val=0;
+ unsigned short smr_val;
#if defined(CONFIG_SH_SCIF_SERIAL)
unsigned short fcr_val=6; /* TFRST=1, RFRST=1 */
#endif
fcr_val = 0;
#endif
+ smr_val = ctrl_in(SCSMR) & 3;
if ((port->gs.tty->termios->c_cflag & CSIZE) == CS7)
smr_val |= 0x40;
if (C_PARENB(port->gs.tty))
sci_enable_rx_interrupts(port);
}
-static void sci_set_real_termios(void *ptr)
+static int sci_set_real_termios(void *ptr)
{
struct sci_port *port = ptr;
set_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags);
else
clear_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags);
+
+ return 0;
}
/* ********************************************************************** *
static void sci_rx_interrupt(int irq, void *ptr, struct pt_regs *regs)
{
struct sci_port *port = ptr;
+ unsigned long flags;
if (port->gs.flags & GS_ACTIVE)
- if (!(port->gs.flags & SCI_RX_THROTTLE))
+ if (!(port->gs.flags & SCI_RX_THROTTLE)) {
sci_receive_chars(port);
+ return;
+ }
+ save_and_cli(flags);
+ ctrl_out(ctrl_in(SCSCR) & ~SCI_CTRL_FLAGS_RIE, SCSCR);
+ restore_flags(flags);
}
static void sci_tx_interrupt(int irq, void *ptr, struct pt_regs *regs)
struct sci_port *port = ptr;
if (port->gs.flags & GS_ACTIVE)
- if (port->gs.xmit_cnt) {
- sci_transmit_chars(port);
- }
+ sci_transmit_chars(port);
+ else {
+ unsigned long flags;
+
+ save_and_cli(flags);
+ ctrl_out(ctrl_in(SCSCR) & ~SCI_CTRL_FLAGS_TIE, SCSCR);
+ restore_flags(flags);
+ }
}
static void sci_er_interrupt(int irq, void *ptr, struct pt_regs *regs)
-/* $Id: sh-sci.h,v 1.5 2000-03-05 13:54:32+09 gniibe Exp $
+/* $Id: sh-sci.h,v 1.8 2000/03/08 15:19:39 gniibe Exp $
*
* linux/drivers/char/sh-sci.h
*
#define SC_SR (volatile unsigned char *)0xfffffe88
#define SC_RDR 0xfffffe8a
#define SCSPTR 0xffffff7c
-
-#define SCSCR_INIT 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
-
#elif defined(__SH4__)
-Not yet.
+#define SCSMR (volatile unsigned char *)0xffe00000
+#define SCBRR 0xffe00004
+#define SCSCR (volatile unsigned char *)0xffe00008
+#define SC_TDR 0xffe0000c
+#define SC_SR (volatile unsigned char *)0xffe00010
+#define SC_RDR 0xffe00014
+#define SCSPTR 0xffe0001c
#endif
+#define SCSCR_INIT 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
+
#define SCI_TD_E 0x80
#define SCI_RD_F 0x40
#define SCI_ORER 0x20
#define SCI_CTRL_FLAGS_CKE1 0x02
#define SCI_CTRL_FLAGS_CKE0 0x01
-#define RFCR 0xffffff74
-
#define SCI_ERI_IRQ 23
#define SCI_RXI_IRQ 24
#define SCI_TXI_IRQ 25
#undef SCSPTR /* Is there any register for RTS?? */
#undef SCLSR
-#define RFCR 0xffffff74
-
#define SCSCR_INIT 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
/* 0x33 when external clock is used */
#define SCI_IPR_OFFSET (64+4)
#define SCSPTR 0xFFE80020
#define SCLSR 0xFFE80024
-#define RFCR 0xFF800028
-
#define SCSCR_INIT 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
#define SCI_IPR_OFFSET (32+4)
#endif
#endif
\f
+#if defined(__sh3__)
+#define RFCR 0xffffff74
+#elif defined(__SH4__)
+#define RFCR 0xFF800028
+#endif
+
#define SCI_PRIORITY 3
#define SCI_MINOR_START 64
*/
#if defined(__sh3__)
-#define BPS_2400 191
-#define BPS_4800 95
-#define BPS_9600 47
-#define BPS_19200 23
-#define BPS_38400 11
-#define BPS_115200 3
+#define PCLK 14745600
#elif defined(__SH4__)
-/* Values for SH-4 please! */
-
-#define BPS_115200 8
+#define PCLK 33333333
#endif
+
+#define SCBRR_VALUE(bps) (PCLK/(32*bps)-1)
+#define BPS_2400 SCBRR_VALUE(2400)
+#define BPS_4800 SCBRR_VALUE(4800)
+#define BPS_9600 SCBRR_VALUE(9600)
+#define BPS_19200 SCBRR_VALUE(19200)
+#define BPS_38400 SCBRR_VALUE(38400)
+#define BPS_115200 SCBRR_VALUE(115200)
extern console_8xx_init(void);
extern int rs_8xx_init(void);
#endif /* CONFIG_8xx */
+#ifdef CONFIG_HWC
+extern void hwc_console_init(void);
+#endif
+#ifdef CONFIG_3215
+extern void con3215_init(void);
+#endif /* CONFIG_3215 */
#ifndef MIN
serial167_console_init();
#endif
#endif
+#ifdef CONFIG_3215
+ con3215_init();
+#endif
+#ifdef CONFIG_HWC
+ hwc_console_init();
+#endif
}
static struct tty_driver dev_tty_driver, dev_syscons_driver;
#ifdef CONFIG_VIDEO_BWQCAM
extern int init_bw_qcams(struct video_init *);
#endif
+#ifdef CONFIG_VIDEO_CPIA
+extern int cpia_init(struct video_init *);
+#endif
#ifdef CONFIG_VIDEO_PLANB
extern int init_planbs(struct video_init *);
#endif
#ifdef CONFIG_VIDEO_BWQCAM
{"bw-qcam", init_bw_qcams},
#endif
+#ifdef CONFIG_VIDEO_CPIA
+ {"cpia", cpia_init},
+#endif
#ifdef CONFIG_VIDEO_PLANB
{"planb", init_planbs},
#endif
if(vfl==NULL) {
char modname[20];
+ MOD_INC_USE_COUNT;
sprintf (modname, "char-major-%d-%d", VIDEO_MAJOR, minor);
request_module(modname);
vfl=video_device[minor];
+ MOD_DEC_USE_COUNT;
if (vfl==NULL)
return -ENODEV;
}
pcf_isa_unreg,
};
-static int __init i2c_pcfisa_init(void)
+int __init i2c_pcfisa_init(void)
{
struct i2c_pcf_isa *pisa = &gpi;
IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) ||
IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530) ||
IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD646) ||
+ IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD648) ||
((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) {
unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name);
if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) {
tristate 'PPP (point-to-point protocol) support' CONFIG_PPP
if [ ! "$CONFIG_PPP" = "n" ]; then
+ dep_bool ' PPP multilink support (EXPERIMENTAL)' CONFIG_PPP_MULTILINK $CONFIG_EXPERIMENTAL
dep_tristate ' PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP
dep_tristate ' PPP support for sync tty ports' CONFIG_PPP_SYNC_TTY $CONFIG_PPP
dep_tristate ' PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP
M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
+ifneq ($(ARCH),s390)
O_OBJS += auto_irq.o
+endif
include $(TOPDIR)/Rules.make
+/* drivers/net/eepro100.c: An Intel i82557-559 Ethernet driver for Linux. */
/*
-
- drivers/net/eepro100.c: An Intel i82557-559 Ethernet driver for Linux
-
+ NOTICE: For use with late 2.3 kernels only.
+ May not compile for kernels 2.3.43-47.
Written 1996-1999 by Donald Becker.
- Modified 2000 by Linux Kernel Team
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
This driver is for the Intel EtherExpress Pro100 (Speedo3) design.
It should work with all i82557/558/559 boards.
- To use as a module, use the compile-command at the end of the file.
-
The author may be reached as becker@CESDIS.usra.edu, or C/O
Center of Excellence in Space Data and Information Sciences
Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771
There is a Majordomo mailing list based at
linux-eepro100@cesdis.gsfc.nasa.gov
+ The driver also contains updates by different kernel developers
+ (see incomplete list below).
+ This driver clone is maintained by Andrey V. Savochkin <saw@saw.sw.com.sg>.
+ Please use this email address and linux-kernel mailing list for bug reports.
-
- Version history:
- v1.09j+LK1.0 - Jeff Garzik <jgarzik@mandrakesoft.com>
+ Version history:
+ 1998 Apr - 2000 Feb Andrey V. Savochkin <saw@saw.sw.com.sg>
+ Serious fixes for multicast filter list setting, TX timeout routine;
+ RX ring refilling logic; other stuff
+ 2000 Feb Jeff Garzik <jgarzik@mandrakesoft.com>
Convert to new PCI driver interface
+ 2000 Mar 24 Dragan Stancevic <visitor@valinux.com>
+ Disabled FC and ER, to avoid lockups when when we get FCP interrupts.
+ Dragan Stancevic <visitor@valinux.com> March 24th, 2000.
+*/
+
+static const char *version =
+"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n"
+"eepro100.c: $Revision: 1.28 $ 2000/03/28 Modified by Andrey V. Savochkin <saw@saw.sw.com.sg> and others\n";
+
+/* A few user-configurable values that apply to all boards.
+ First set is undocumented and spelled per Intel recommendations. */
+
+static int congenb = 0; /* Enable congestion control in the DP83840. */
+static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
+static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
+/* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
+static int txdmacount = 128;
+static int rxdmacount = 0;
+
+/* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
+ Lower values use more memory, but are faster. */
+static int rx_copybreak = 200;
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
+static int multicast_filter_limit = 64;
+
+/* 'options' is used to pass a transceiver override or full-duplex flag
+ e.g. "options=16" for FD, "options=32" for 100mbps-only. */
+static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int debug = -1; /* The debug level */
+
+/* A few values that may be tweaked. */
+/* The ring sizes should be a power of two for efficiency. */
+#define TX_RING_SIZE 32
+#define RX_RING_SIZE 32
+/* How much slots multicast filter setup may take.
+ Do not descrease without changing set_rx_mode() implementaion. */
+#define TX_MULTICAST_SIZE 2
+#define TX_MULTICAST_RESERV (TX_MULTICAST_SIZE*2)
+/* Actual number of TX packets queued, must be
+ <= TX_RING_SIZE-TX_MULTICAST_RESERV. */
+#define TX_QUEUE_LIMIT (TX_RING_SIZE-TX_MULTICAST_RESERV)
+/* Hysteresis marking queue as no longer full. */
+#define TX_QUEUE_UNFULL (TX_QUEUE_LIMIT-4)
+
+/* Operational parameters that usually are not changed. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (2*HZ)
+/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
+#define PKT_BUF_SZ 1536
+
+#if !defined(__OPTIMIZE__) || !defined(__KERNEL__)
+#warning You must compile this file with the correct options!
+#warning See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+#include <linux/version.h>
+#include <linux/module.h>
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+MODULE_AUTHOR("Maintainer: Andrey V. Savochkin <saw@saw.sw.com.sg>");
+MODULE_DESCRIPTION("Intel i82557/i82558/i82559 PCI EtherExpressPro driver");
+MODULE_PARM(debug, "i");
+MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
+MODULE_PARM(congenb, "i");
+MODULE_PARM(txfifo, "i");
+MODULE_PARM(rxfifo, "i");
+MODULE_PARM(txdmacount, "i");
+MODULE_PARM(rxdmacount, "i");
+MODULE_PARM(rx_copybreak, "i");
+MODULE_PARM(max_interrupt_work, "i");
+MODULE_PARM(multicast_filter_limit, "i");
+
+#define RUN_AT(x) (jiffies + (x))
+
+/* ACPI power states don't universally work (yet) */
+#ifndef CONFIG_EEPRO100_PM
+#undef pci_set_power_state
+#define pci_set_power_state null_set_power_state
+static inline int null_set_power_state(struct pci_dev *dev, int state)
+{
+ return 0;
+}
+#endif /* CONFIG_EEPRO100_PM */
+
+#ifndef pci_resource_start
+#define pci_resource_start(p, n) (p)->resource[n].start
+#define pci_resource_len(p, n) ((p)->resource[n].end - (p)->resource[n].start)
+#endif
+
+/* Because of changes in this area the driver may not compile for kernels
+ 2.3.43 - 2.3.47. --SAW */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,43)
+#define netif_wake_queue(dev) do { \
+ clear_bit(0, (void*)&dev->tbusy); \
+ mark_bh(NET_BH); \
+ } while(0)
+#define netif_start_queue(dev) clear_bit(0, (void*)&dev->tbusy)
+#define netif_stop_queue(dev) set_bit(0, (void*)&dev->tbusy)
+#define netif_running(dev) dev->start
+#define netdevice_start(dev) dev->start = 1
+#define netdevice_stop(dev) dev->start = 0
+#define netif_set_tx_timeout(dev, tf, tm)
+#define dev_kfree_skb_irq(x) dev_kfree_skb(x)
+#else
+#define netdevice_start(dev)
+#define netdevice_stop(dev)
+#define netif_set_tx_timeout(dev, tf, tm) \
+ do { \
+ (dev)->tx_timeout = (tf); \
+ (dev)->watchdog_timeo = (tm); \
+ } while(0)
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,47)
+#define netif_device_attach(dev) netif_start_queue(dev)
+#define netif_device_detach(dev) netif_stop_queue(dev)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,43)
+typedef u32 dma_addr_t;
+static void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size,
+ dma_addr_t *dma_handle)
+{
+ void *ret;
+ ret = kmalloc(size, GFP_ATOMIC);
+ if (ret != NULL) {
+ *dma_handle = virt_to_bus(ret);
+ }
+ return ret;
+}
+
+void pci_free_consistent(struct pci_dev *hwdev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
+{
+ kfree(vaddr);
+}
+#define PCI_DMA_FROMDEVICE 0
+#define PCI_DMA_TODEVICE 0
+extern inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr,
+ size_t size, int direction)
+{
+ return virt_to_bus(ptr);
+}
+
+extern inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr,
+ size_t size, int direction)
+{
+}
+
+extern inline void pci_dma_sync_single(struct pci_dev *hwdev,
+ dma_addr_t dma_handle,
+ size_t size, int direction)
+{
+}
+#endif
+
+/* The total I/O port extent of the board.
+ The registers beyond 0x18 only exist on the i82558. */
+#define SPEEDO3_TOTAL_SIZE 0x20
+
+int speedo_debug = 1;
+
+/*
Theory of Operation
I. Board Compatibility
is non-trivial, and the larger copy might flush the cache of useful data, so
we pass up the skbuff the packet was received into.
-IIID. Synchronization
-The driver runs as two independent, single-threaded flows of control. One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag. The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'sp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.) After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero. Iff the 'sp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
IV. Notes
Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
*/
+static int speedo_found1(struct pci_dev *pdev, long ioaddr, int irq, int chip_idx, int fnd_cnt, int acpi_idle_state);
-static const char *version =
-"eepro100.c:v1.09j+LK1.0 Feb 13, 2000 Linux Kernel Team http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n";
-
-/* A few user-configurable values that apply to all boards.
- First set is undocumented and spelled per Intel recommendations. */
-
-static int congenb = 0; /* Enable congestion control in the DP83840. */
-static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
-static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
-/* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
-static int txdmacount = 128;
-static int rxdmacount = 0;
-
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
- Lower values use more memory, but are faster. */
-static int rx_copybreak = 200;
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
-
-/* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
-static int multicast_filter_limit = 64;
-
-/* 'options' is used to pass a transceiver override or full-duplex flag
- e.g. "options=16" for FD, "options=32" for 100mbps-only. */
-static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int debug = -1; /* The debug level */
-
-/* A few values that may be tweaked. */
-/* The ring sizes should be a power of two for efficiency. */
-#define TX_RING_SIZE 32 /* Effectively 2 entries fewer. */
-#define RX_RING_SIZE 32
-/* Actual number of TX packets queued, must be <= TX_RING_SIZE-2. */
-#define TX_QUEUE_LIMIT 12
-
-/* Operational parameters that usually are not changed. */
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT (2*HZ)
-/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
-#define PKT_BUF_SZ 1536
-
-#if !defined(__OPTIMIZE__) || !defined(__KERNEL__)
-#warning You must compile this file with the correct options!
-#warning See the last lines of the source file.
-#error You must compile this driver with "-O".
+#ifdef USE_IO
+#define SPEEDO_IOTYPE PCI_USES_MASTER|PCI_USES_IO|PCI_ADDR1
+#define SPEEDO_SIZE 32
+#else
+#define SPEEDO_IOTYPE PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR0
+#define SPEEDO_SIZE 0x1000
#endif
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/pci.h>
-#include <linux/spinlock.h>
-#include <linux/init.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
-
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>
-
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Intel i82557/i82558 PCI EtherExpressPro driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(congenb, "i");
-MODULE_PARM(txfifo, "i");
-MODULE_PARM(rxfifo, "i");
-MODULE_PARM(txdmacount, "i");
-MODULE_PARM(rxdmacount, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(multicast_filter_limit, "i");
-
-#define EEPRO100_MODULE_NAME "eepro100"
-#define PFX EEPRO100_MODULE_NAME ": "
-
-#define RUN_AT(x) (jiffies + (x))
-
-/* ACPI power states don't universally work (yet) */
-#ifndef CONFIG_EEPRO100_PM
-#undef pci_set_power_state
-#define pci_set_power_state null_set_power_state
-static inline int null_set_power_state(struct pci_dev *dev, int state)
-{
- return 0;
-}
-#endif /* CONFIG_EEPRO100_PM */
-
-
-/* compile-time switch to en/disable slow PIO */
-#undef USE_IO
-
-
-int speedo_debug = 1;
-
-
enum pci_flags_bit {
PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
#define outl writel
#endif
-
/* How to wait for the command unit to accept a command.
Typically this takes 0 ticks. */
-static inline void wait_for_cmd_done (long cmd_ioaddr)
+static inline void wait_for_cmd_done(long cmd_ioaddr)
{
- int wait = 100;
- do;
- while (inb (cmd_ioaddr) && --wait >= 0);
+ int wait = 1000;
+ do ;
+ while(inb(cmd_ioaddr) && --wait >= 0);
}
-
/* Offsets to the various registers.
All accesses need not be longword aligned. */
enum speedo_offsets {
SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */
- SCBPointer = 4, /* General purpose pointer. */
- SCBPort = 8, /* Misc. commands and operands. */
- SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
- SCBCtrlMDI = 16, /* MDI interface control. */
- SCBEarlyRx = 20, /* Early receive byte count. */
+ SCBPointer = 4, /* General purpose pointer. */
+ SCBPort = 8, /* Misc. commands and operands. */
+ SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
+ SCBCtrlMDI = 16, /* MDI interface control. */
+ SCBEarlyRx = 20, /* Early receive byte count. */
};
-
-
/* Commands that can be put in a command list entry. */
enum commands {
- CmdNOp = 0,
- CmdIASetup = 0x10000,
- CmdConfigure = 0x20000,
- CmdMulticastList = 0x30000,
- CmdTx = 0x40000,
- CmdTDR = 0x50000,
- CmdDump = 0x60000,
- CmdDiagnose = 0x70000,
+ CmdNOp = 0, CmdIASetup = 0x10000, CmdConfigure = 0x20000,
+ CmdMulticastList = 0x30000, CmdTx = 0x40000, CmdTDR = 0x50000,
+ CmdDump = 0x60000, CmdDiagnose = 0x70000,
CmdSuspend = 0x40000000, /* Suspend after completion. */
CmdIntr = 0x20000000, /* Interrupt after completion. */
CmdTxFlex = 0x00080000, /* Use "Flexible mode" for CmdTx command. */
};
-
-
-/* Do atomically if possible. */
-#if defined(__i386__) || defined(__alpha__) || defined(__ia64__)
-#define clear_suspend(cmd) clear_bit(30, &(cmd)->cmd_status)
-#elif defined(__powerpc__)
-#define clear_suspend(cmd) clear_bit(6, &(cmd)->cmd_status)
+/* Clear CmdSuspend (1<<30) avoiding interference with the card access to the
+ status bits. Previous driver versions used separate 16 bit fields for
+ commands and statuses. --SAW
+ FIXME: it may not work on non-IA32 architectures.
+ */
+#if defined(__LITTLE_ENDIAN)
+#define clear_suspend(cmd) ((__u16 *)&(cmd)->cmd_status)[1] &= ~0x4000
+#elif defined(__BIG_ENDIAN)
+#define clear_suspend(cmd) ((__u16 *)&(cmd)->cmd_status)[1] &= ~0x0040
#else
-#if 0
-# error You are probably in trouble: clear_suspend() MUST be atomic.
-#endif
-# define clear_suspend(cmd) (cmd)->cmd_status &= cpu_to_le32(~CmdSuspend)
+#error Unsupported byteorder
#endif
enum SCBCmdBits {
- SCBMaskCmdDone = 0x8000,
- SCBMaskRxDone = 0x4000,
- SCBMaskCmdIdle = 0x2000,
- SCBMaskRxSuspend = 0x1000,
- SCBMaskEarlyRx = 0x0800,
- SCBMaskFlowCtl = 0x0400,
- SCBTriggerIntr = 0x0200,
- SCBMaskAll = 0x0100,
- /* The rest are Rx and Tx commands. */
- CUStart = 0x0010,
- CUResume = 0x0020,
- CUStatsAddr = 0x0040,
- CUShowStats = 0x0050,
- CUCmdBase = 0x0060, /* CU Base address (set to zero) . */
- CUDumpStats = 0x0070, /* Dump then reset stats counters. */
- RxStart = 0x0001,
- RxResume = 0x0002,
- RxAbort = 0x0004,
- RxAddrLoad = 0x0006,
- RxResumeNoResources = 0x0007,
+ SCBMaskCmdDone=0x8000, SCBMaskRxDone=0x4000, SCBMaskCmdIdle=0x2000,
+ SCBMaskRxSuspend=0x1000, SCBMaskEarlyRx=0x0800, SCBMaskFlowCtl=0x0400,
+ SCBTriggerIntr=0x0200, SCBMaskAll=0x0100,
+ /* The rest are Rx and Tx commands. */
+ CUStart=0x0010, CUResume=0x0020, CUStatsAddr=0x0040, CUShowStats=0x0050,
+ CUCmdBase=0x0060, /* CU Base address (set to zero) . */
+ CUDumpStats=0x0070, /* Dump then reset stats counters. */
+ RxStart=0x0001, RxResume=0x0002, RxAbort=0x0004, RxAddrLoad=0x0006,
+ RxResumeNoResources=0x0007,
};
enum SCBPort_cmds {
- PortReset = 0,
- PortSelfTest = 1,
- PortPartialReset = 2,
- PortDump = 3,
+ PortReset=0, PortSelfTest=1, PortPartialReset=2, PortDump=3,
};
/* The Speedo3 Rx and Tx frame/buffer descriptors. */
-struct descriptor { /* A generic descriptor. */
- s32 cmd_status; /* All command and status fields. */
- u32 link; /* struct descriptor * */
+struct descriptor { /* A generic descriptor. */
+ s32 cmd_status; /* All command and status fields. */
+ u32 link; /* struct descriptor * */
unsigned char params[0];
};
/* The Speedo3 Rx and Tx buffer descriptors. */
-struct RxFD { /* Receive frame descriptor. */
+struct RxFD { /* Receive frame descriptor. */
s32 status;
- u32 link; /* struct RxFD * */
- u32 rx_buf_addr; /* void * */
+ u32 link; /* struct RxFD * */
+ u32 rx_buf_addr; /* void * */
u32 count;
};
/* Selected elements of the Tx/RxFD.status word. */
enum RxFD_bits {
- RxComplete = 0x8000,
- RxOK = 0x2000,
- RxErrCRC = 0x0800,
- RxErrAlign = 0x0400,
- RxErrTooBig = 0x0200,
- RxErrSymbol = 0x0010,
- RxEth2Type = 0x0020,
- RxNoMatch = 0x0004,
- RxNoIAMatch = 0x0002,
- TxUnderrun = 0x1000,
- StatusComplete = 0x8000,
+ RxComplete=0x8000, RxOK=0x2000,
+ RxErrCRC=0x0800, RxErrAlign=0x0400, RxErrTooBig=0x0200, RxErrSymbol=0x0010,
+ RxEth2Type=0x0020, RxNoMatch=0x0004, RxNoIAMatch=0x0002,
+ TxUnderrun=0x1000, StatusComplete=0x8000,
};
-struct TxFD { /* Transmit frame descriptor set. */
+struct TxFD { /* Transmit frame descriptor set. */
s32 status;
- u32 link; /* void * */
- u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */
- s32 count; /* # of TBD (=1), Tx start thresh., etc. */
+ u32 link; /* void * */
+ u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */
+ s32 count; /* # of TBD (=1), Tx start thresh., etc. */
/* This constitutes two "TBD" entries -- we only use one. */
- u32 tx_buf_addr0; /* void *, frame to be transmitted. */
- s32 tx_buf_size0; /* Length of Tx frame. */
- u32 tx_buf_addr1; /* void *, frame to be transmitted. */
- s32 tx_buf_size1; /* Length of Tx frame. */
+#define TX_DESCR_BUF_OFFSET 16
+ u32 tx_buf_addr0; /* void *, frame to be transmitted. */
+ s32 tx_buf_size0; /* Length of Tx frame. */
+ u32 tx_buf_addr1; /* void *, frame to be transmitted. */
+ s32 tx_buf_size1; /* Length of Tx frame. */
+};
+
+/* Multicast filter setting block. --SAW */
+struct speedo_mc_block {
+ struct speedo_mc_block *next;
+ unsigned int tx;
+ dma_addr_t frame_dma;
+ unsigned int len;
+ char fill[16 - sizeof(struct speedo_mc_block *) - sizeof(unsigned int) - sizeof(dma_addr_t) - sizeof(unsigned int)];
+ struct descriptor frame;
};
/* Elements of the dump_statistics block. This block must be lword aligned. */
u32 done_marker;
};
+enum Rx_ring_state_bits {
+ RrNoMem=1, RrPostponed=2, RrNoResources=4, RrOOMReported=8,
+};
+
/* Do not change the position (alignment) of the first few elements!
- The later elements are grouped for cache locality. */
+ The later elements are grouped for cache locality.
+
+ Unfortunately, all the positions have been shifted since there.
+ A new re-alignment is required. 2000/03/06 SAW */
struct speedo_private {
- struct TxFD *tx_ring; /* Commands (usually CmdTxPacket). */
+ struct TxFD *tx_ring; /* Commands (usually CmdTxPacket). */
struct RxFD *rx_ringp[RX_RING_SIZE];/* Rx descriptor, used as ring. */
/* The addresses of a Tx/Rx-in-place packets/buffers. */
struct sk_buff *tx_skbuff[TX_RING_SIZE];
struct sk_buff *rx_skbuff[RX_RING_SIZE];
- dma_addr_t rx_ring_dma[RX_RING_SIZE];
+ /* Mapped addresses of the rings. */
dma_addr_t tx_ring_dma;
- struct descriptor *last_cmd; /* Last command sent. */
- unsigned int cur_tx, dirty_tx; /* The ring entries to be free()ed. */
- spinlock_t lock; /* Group with Tx control cache line. */
- u32 tx_threshold; /* The value for txdesc.count. */
- struct RxFD *last_rxf; /* Last command sent. */
- unsigned int cur_rx, dirty_rx; /* The next free ring entry */
- long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */
+#define TX_RING_ELEM_DMA(sp, n) ((sp)->tx_ring_dma + (n)*sizeof(struct TxFD))
+ dma_addr_t rx_ring_dma[RX_RING_SIZE];
+ struct descriptor *last_cmd; /* Last command sent. */
+ unsigned int cur_tx, dirty_tx; /* The ring entries to be free()ed. */
+ spinlock_t lock; /* Group with Tx control cache line. */
+ u32 tx_threshold; /* The value for txdesc.count. */
+ struct RxFD *last_rxf; /* Last filled RX buffer. */
+ unsigned int cur_rx, dirty_rx; /* The next free ring entry */
+ long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */
const char *product_name;
struct enet_statistics stats;
struct speedo_stats *lstats;
+ dma_addr_t lstats_dma;
int chip_id;
- unsigned char acpi_pwr;
struct pci_dev *pdev;
- struct timer_list timer; /* Media selection timer. */
- int mc_setup_frm_len; /* The length of an allocated.. */
- struct descriptor *mc_setup_frm;/* ..multicast setup frame. */
- int mc_setup_busy; /* Avoid double-use of setup frame. */
- dma_addr_t mc_setup_dma;
- char rx_mode; /* Current PROMISC/ALLMULTI setting. */
- unsigned int tx_full:1; /* The Tx queue is full. */
- unsigned int full_duplex:1; /* Full-duplex operation requested. */
- unsigned int flow_ctrl:1; /* Use 802.3x flow control. */
- unsigned int rx_bug:1; /* Work around receiver hang errata. */
- unsigned int rx_bug10:1; /* Receiver might hang at 10mbps. */
- unsigned int rx_bug100:1; /* Receiver might hang at 100mbps. */
- unsigned char default_port:8; /* Last dev->if_port value. */
- unsigned short phy[2]; /* PHY media interfaces available. */
- unsigned short advertising; /* Current PHY advertised caps. */
- unsigned short partner; /* Link partner caps. */
+ struct timer_list timer; /* Media selection timer. */
+ struct speedo_mc_block *mc_setup_head;/* Multicast setup frame list head. */
+ struct speedo_mc_block *mc_setup_tail;/* Multicast setup frame list tail. */
+ int in_interrupt; /* Word-aligned dev->interrupt */
+ unsigned char acpi_pwr;
+ char rx_mode; /* Current PROMISC/ALLMULTI setting. */
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ unsigned int full_duplex:1; /* Full-duplex operation requested. */
+ unsigned int flow_ctrl:1; /* Use 802.3x flow control. */
+ unsigned int rx_bug:1; /* Work around receiver hang errata. */
+ unsigned char default_port:8; /* Last dev->if_port value. */
+ unsigned char rx_ring_state; /* RX ring status flags. */
+ unsigned short phy[2]; /* PHY media interfaces available. */
+ unsigned short advertising; /* Current PHY advertised caps. */
+ unsigned short partner; /* Link partner caps. */
};
+
/* The parameters for a CmdConfigure operation.
There are so many options that it would be difficult to document each bit.
We mostly use the default or recommended settings. */
const char i82558_config_cmd[22] = {
22, 0x08, 0, 1, 0, 0, 0x22, 0x03, 1, /* 1=Use MII 0=Use AUI */
0, 0x2E, 0, 0x60, 0x08, 0x88,
- 0x68, 0, 0x40, 0xf2, 0xBD, /* 0xBD->0xFD=Force full-duplex */
+ 0x68, 0, 0x40, 0xf2, 0x84, /* Disable FC */
0x31, 0x05, };
/* PHY media interface chips. */
static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
#define EE_READ_CMD (6)
+static int eepro100_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+static void eepro100_remove_one (struct pci_dev *pdev);
+static void eepro100_suspend (struct pci_dev *pdev);
+static void eepro100_resume (struct pci_dev *pdev);
+
static int do_eeprom_cmd(long ioaddr, int cmd, int cmd_len);
static int mdio_read(long ioaddr, int phy_id, int location);
static int mdio_write(long ioaddr, int phy_id, int location, int value);
static void speedo_init_rx_ring(struct net_device *dev);
static void speedo_tx_timeout(struct net_device *dev);
static int speedo_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void speedo_refill_rx_buffers(struct net_device *dev, int force);
static int speedo_rx(struct net_device *dev);
+static void speedo_tx_buffer_gc(struct net_device *dev);
static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
static int speedo_close(struct net_device *dev);
static struct enet_statistics *speedo_get_stats(struct net_device *dev);
static int speedo_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static void set_rx_mode(struct net_device *dev);
+static void speedo_show_state(struct net_device *dev);
\f
0x2000, 0x2100, 0x0400, 0x3100};
#endif
+static int __devinit eepro100_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ unsigned long ioaddr;
+ int irq;
+ int acpi_idle_state = 0, pm;
+ static int cards_found = 0;
+
+ static int did_version = 0; /* Already printed version info. */
+ if (speedo_debug > 0 && did_version++ == 0)
+ printk(version);
+
+ if (!request_region(pci_resource_start(pdev, 1),
+ pci_resource_len(pdev, 1), "eepro100")) {
+ printk (KERN_ERR "eepro100: cannot reserve I/O ports\n");
+ goto err_out_none;
+ }
+ if (!request_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0), "eepro100")) {
+ printk (KERN_ERR "eepro100: cannot reserve MMIO region\n");
+ goto err_out_free_pio_region;
+ }
+
+ irq = pdev->irq;
+#ifdef USE_IO
+ ioaddr = pci_resource_start(pdev, 1);
+ if (speedo_debug > 2)
+ printk("Found Intel i82557 PCI Speedo at I/O %#lx, IRQ %d.\n",
+ ioaddr, irq);
+#else
+ ioaddr = (unsigned long)ioremap(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (!ioaddr) {
+ printk (KERN_ERR "eepro100: cannot remap MMIO region %lx @ %lx\n",
+ pci_resource_len(pdev, 0), pci_resource_start(pdev, 0));
+ goto err_out_free_mmio_region;
+ }
+ if (speedo_debug > 2)
+ printk("Found Intel i82557 PCI Speedo, MMIO at %#lx, IRQ %d.\n",
+ pci_resource_start(pdev, 0), irq);
+#endif
+
+ /* save power state b4 pci_enable_device overwrites it */
+ pm = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pm) {
+ u16 pwr_command;
+ pci_read_config_word(pdev, pm + PCI_PM_CTRL, &pwr_command);
+ acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
+ }
+
+ if (pci_enable_device(pdev)) {
+ printk(KERN_ERR "eepro100: Could not enable PCI device\n");
+ goto err_out_free_mmio_region;
+ }
+
+ pci_set_master(pdev);
+ if (speedo_found1(pdev, ioaddr, irq, 0, cards_found, acpi_idle_state) == 0)
+ cards_found++;
+ else
+ goto err_out_iounmap;
+
+ return 0;
+
+err_out_iounmap: ;
+#ifndef USE_IO
+ iounmap ((void *)ioaddr);
+#endif
+err_out_free_mmio_region:
+ release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+err_out_free_pio_region:
+ release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
+err_out_none:
+ return -ENODEV;
+}
-static int __devinit eepro100_init_one (struct pci_dev *pdev,
- const struct pci_device_id *ent)
+static int speedo_found1(struct pci_dev *pdev,
+ long ioaddr, int irq, int chip_idx, int card_idx, int acpi_idle_state)
{
struct net_device *dev;
struct speedo_private *sp;
- unsigned char *tx_ring;
- dma_addr_t tx_ring_dma;
const char *product;
int i, option;
u16 eeprom[0x100];
- int acpi_idle_state = 0, pm, irq;
- unsigned long ioaddr;
- static int card_idx = -1;
-
- static int did_version = 0; /* Already printed version info. */
-
- if (speedo_debug > 0 && did_version++ == 0)
- printk(version);
-
-#ifdef USE_IO
- ioaddr = pci_resource_start (pdev, 0);
-#else
- ioaddr = pci_resource_start (pdev, 1);
-#endif
- irq = pdev->irq;
-
- card_idx++;
-
- if (!request_region (pci_resource_start (pdev, 1),
- pci_resource_len (pdev, 1),
- EEPRO100_MODULE_NAME)) {
- printk (KERN_ERR PFX "cannot reserve I/O ports\n");
- goto err_out_none;
- }
- if (!request_mem_region (pci_resource_start (pdev, 0),
- pci_resource_len (pdev, 0),
- EEPRO100_MODULE_NAME)) {
- printk (KERN_ERR PFX "cannot reserve MMIO region\n");
- goto err_out_free_pio_region;
- }
-
-#ifndef USE_IO
- ioaddr = (unsigned long) ioremap (pci_resource_start (pdev, 0),
- pci_resource_len (pdev, 0));
- if (!ioaddr) {
- printk (KERN_ERR PFX "cannot remap MMIO region %lx @ %lx\n",
- pci_resource_len (pdev, 0),
- pci_resource_start (pdev, 0));
- goto err_out_free_mmio_region;
- }
-#endif
+ int size;
+ void *tx_ring_space;
+ dma_addr_t tx_ring_dma;
- tx_ring = pci_alloc_consistent(pdev, TX_RING_SIZE * sizeof(struct TxFD)
- + sizeof(struct speedo_stats), &tx_ring_dma);
- if (!tx_ring) {
- printk(KERN_ERR PFX "Could not allocate DMA memory.\n");
- goto err_out_iounmap;
- }
+ size = TX_RING_SIZE * sizeof(struct TxFD) + sizeof(struct speedo_stats);
+ tx_ring_space = pci_alloc_consistent(pdev, size, &tx_ring_dma);
+ if (tx_ring_space == NULL)
+ return -1;
dev = init_etherdev(NULL, sizeof(struct speedo_private));
if (dev == NULL) {
- printk(KERN_ERR PFX "Could not allocate ethernet device.\n");
- goto err_out_free_tx_ring;
- }
- if (dev->priv == NULL) {
- printk(KERN_ERR PFX "Could not allocate ethernet device private info.\n");
- goto err_out_free_netdev;
+ printk(KERN_ERR "eepro100: Could not allocate ethernet device.\n");
+ pci_free_consistent(pdev, size, tx_ring_space, tx_ring_dma);
+ return -1;
}
if (dev->mem_start > 0)
else
option = 0;
- /* save power state b4 pci_enable_device overwrites it */
- pm = pci_find_capability(pdev, PCI_CAP_ID_PM);
- if (pm) {
- u16 pwr_command;
- pci_read_config_word(pdev, pm + PCI_PM_CTRL, &pwr_command);
- acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK;
- }
-
- if (pci_enable_device (pdev)) {
- printk(KERN_ERR PFX "Could not enable PCI device\n");
- goto err_out_free_netdev;
- }
-
- pci_set_master (pdev);
-
/* Read the station address EEPROM before doing the reset.
Nominally his should even be done before accepting the device, but
then we wouldn't have a device name with which to report the error.
if (eeprom[3] & 0x0100)
product = "OEM i82557/i82558 10/100 Ethernet";
else
- product = "Intel PCI EtherExpress Pro100";
+ product = pdev->name;
- printk(KERN_INFO "%s: %s at %#3lx, ", dev->name, product, ioaddr);
+ printk(KERN_INFO "%s: %s, ", dev->name, product);
for (i = 0; i < 5; i++)
printk("%2.2X:", dev->dev_addr[i]);
- printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
+ printk("%2.2X, ", dev->dev_addr[i]);
+#ifdef USE_IO
+ printk("I/O at %#3lx, ", ioaddr);
+#endif
+ printk("IRQ %d.\n", irq);
-#if 1
+#if 1 || defined(kernel_bloat)
/* OK, this is pure kernel bloat. I don't like it when other drivers
waste non-pageable kernel space to emit similar messages, but I need
them for bug reports. */
{
const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
/* The self-test results must be paragraph aligned. */
- volatile s32 *self_test_results = (volatile s32 *)tx_ring;
+ volatile s32 *self_test_results;
int boguscnt = 16000; /* Timeout for set-test. */
if (eeprom[3] & 0x03)
printk(KERN_INFO " Receiver lock-up bug exists -- enabling"
((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
}
- /* Perform a system self-test. Use the tx_ring consistent DMA mapping for it. */
+ /* Perform a system self-test. */
+ self_test_results = (s32*) ((((long) tx_ring_space) + 15) & ~0xf);
self_test_results[0] = 0;
self_test_results[1] = -1;
outl(tx_ring_dma | PortSelfTest, ioaddr + SCBPort);
outl(PortReset, ioaddr + SCBPort);
/* Return the chip to its original power state. */
- pci_set_power_state (pdev, acpi_idle_state);
+ pci_set_power_state(pdev, acpi_idle_state);
pdev->driver_data = dev;
dev->irq = irq;
sp = dev->priv;
-
sp->pdev = pdev;
sp->acpi_pwr = acpi_idle_state;
- sp->tx_ring = (struct TxFD *)tx_ring;
+ sp->tx_ring = tx_ring_space;
sp->tx_ring_dma = tx_ring_dma;
sp->lstats = (struct speedo_stats *)(sp->tx_ring + TX_RING_SIZE);
-
- sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
+ sp->lstats_dma = TX_RING_ELEM_DMA(sp, TX_RING_SIZE);
+ sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
if (card_idx >= 0) {
if (full_duplex[card_idx] >= 0)
sp->full_duplex = full_duplex[card_idx];
}
-
sp->default_port = option >= 0 ? (option & 0x0f) : 0;
sp->phy[0] = eeprom[6];
/* The Speedo-specific entries in the device structure. */
dev->open = &speedo_open;
dev->hard_start_xmit = &speedo_start_xmit;
- dev->tx_timeout = &speedo_tx_timeout;
- dev->watchdog_timeo = TX_TIMEOUT;
+ netif_set_tx_timeout(dev, &speedo_tx_timeout, TX_TIMEOUT);
dev->stop = &speedo_close;
dev->get_stats = &speedo_get_stats;
dev->set_multicast_list = &set_rx_mode;
dev->do_ioctl = &speedo_ioctl;
return 0;
-
-err_out_free_netdev:
- unregister_netdevice (dev);
- kfree (dev);
-err_out_free_tx_ring:
- pci_free_consistent(pdev, TX_RING_SIZE * sizeof(struct TxFD)
- + sizeof(struct speedo_stats),
- tx_ring, tx_ring_dma);
-err_out_iounmap:
-#ifndef USE_IO
- iounmap ((void *)ioaddr);
-err_out_free_mmio_region:
-#endif
- release_mem_region (pci_resource_start (pdev, 0),
- pci_resource_len (pdev, 0));
-err_out_free_pio_region:
- release_region (pci_resource_start (pdev, 1),
- pci_resource_len (pdev, 1));
-err_out_none:
- return -ENODEV;
}
-
-
+\f
/* Serial EEPROM section.
A "bit" grungy, but we work our way through bit-by-bit :->. */
/* EEPROM_Ctrl bits. */
sp->last_cmd = 0;
sp->tx_full = 0;
spin_lock_init(&sp->lock);
+ sp->in_interrupt = 0;
/* .. we can safely take handler calls during init. */
if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ, dev->name, dev)) {
dev->if_port = sp->default_port;
-#if 0
- /* With some transceivers we must retrigger negotiation to reset
- power-up errors. */
+#ifdef oh_no_you_dont_unless_you_honour_the_options_passed_in_to_us
+ /* Retrigger negotiation to reset previous errors. */
if ((sp->phy[0] & 0x8000) == 0) {
int phy_addr = sp->phy[0] & 0x1f ;
/* Use 0x3300 for restarting NWay, other values to force xcvr:
speedo_init_rx_ring(dev);
/* Fire up the hardware. */
+ outw(SCBMaskAll, ioaddr + SCBCmd);
speedo_resume(dev);
+ netdevice_start(dev);
netif_start_queue(dev);
/* Setup the chip and configure the multicast list. */
- sp->mc_setup_frm = NULL;
- sp->mc_setup_frm_len = 0;
- sp->mc_setup_busy = 0;
- sp->rx_mode = -1; /* Invalid -> always reset the mode. */
+ sp->mc_setup_head = NULL;
+ sp->mc_setup_tail = NULL;
sp->flow_ctrl = sp->partner = 0;
+ sp->rx_mode = -1; /* Invalid -> always reset the mode. */
set_rx_mode(dev);
if ((sp->phy[0] & 0x8000) == 0)
sp->advertising = mdio_read(ioaddr, sp->phy[0] & 0x1f, 4);
/* No need to wait for the command unit to accept here. */
if ((sp->phy[0] & 0x8000) == 0)
mdio_read(ioaddr, sp->phy[0] & 0x1f, 0);
+
return 0;
}
struct speedo_private *sp = (struct speedo_private *)dev->priv;
long ioaddr = dev->base_addr;
- outw(SCBMaskAll, ioaddr + SCBCmd);
-
/* Start with a Tx threshold of 256 (0x..20.... 8 byte units). */
sp->tx_threshold = 0x01208000;
wait_for_cmd_done(ioaddr + SCBCmd);
/* Load the statistics block and rx ring addresses. */
- outl(sp->tx_ring_dma + sizeof(struct TxFD) * TX_RING_SIZE, ioaddr + SCBPointer);
+ outl(sp->lstats_dma, ioaddr + SCBPointer);
outb(CUStatsAddr, ioaddr + SCBCmd);
sp->lstats->done_marker = 0;
wait_for_cmd_done(ioaddr + SCBCmd);
- outl(sp->rx_ring_dma[sp->cur_rx % RX_RING_SIZE],
- ioaddr + SCBPointer);
- outb(RxStart, ioaddr + SCBCmd);
- wait_for_cmd_done(ioaddr + SCBCmd);
+ if (sp->rx_ringp[sp->cur_rx % RX_RING_SIZE] == NULL) {
+ if (speedo_debug > 2)
+ printk(KERN_DEBUG "%s: NULL cur_rx in speedo_resume().\n",
+ dev->name);
+ } else {
+ outl(sp->rx_ring_dma[sp->cur_rx % RX_RING_SIZE],
+ ioaddr + SCBPointer);
+ outb(RxStart, ioaddr + SCBCmd);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ }
outb(CUDumpStats, ioaddr + SCBCmd);
/* Fill the first command with our physical address. */
{
- int entry = sp->cur_tx++ % TX_RING_SIZE;
- struct descriptor *cur_cmd = (struct descriptor *)&sp->tx_ring[entry];
+ struct descriptor *ias_cmd;
+ ias_cmd =
+ (struct descriptor *)&sp->tx_ring[sp->cur_tx++ % TX_RING_SIZE];
/* Avoid a bug(?!) here by marking the command already completed. */
- cur_cmd->cmd_status = cpu_to_le32((CmdSuspend | CmdIASetup) | 0xa000);
- cur_cmd->link =
- cpu_to_le32(sp->tx_ring_dma + (sp->cur_tx % TX_RING_SIZE)
- * sizeof(struct TxFD));
- memcpy(cur_cmd->params, dev->dev_addr, 6);
- if (sp->last_cmd)
- clear_suspend(sp->last_cmd);
- sp->last_cmd = cur_cmd;
+ ias_cmd->cmd_status = cpu_to_le32((CmdSuspend | CmdIASetup) | 0xa000);
+ ias_cmd->link =
+ cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->cur_tx % TX_RING_SIZE));
+ memcpy(ias_cmd->params, dev->dev_addr, 6);
+ sp->last_cmd = ias_cmd;
}
/* Start the chip's Tx process and unmask interrupts. */
wait_for_cmd_done(ioaddr + SCBCmd);
- outl(sp->tx_ring_dma
- + (sp->dirty_tx % TX_RING_SIZE) * sizeof(struct TxFD),
+ outl(cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->dirty_tx % TX_RING_SIZE)),
ioaddr + SCBPointer);
- outw(CUStart, ioaddr + SCBCmd);
-
- netif_start_queue (dev);
+ /* We are not ACK-ing FCP and ER in the interrupt handler yet so they should
+ remain masked --Dragan */
+ outw(CUStart | SCBMaskEarlyRx | SCBMaskFlowCtl, ioaddr + SCBCmd);
}
/* Media monitoring and control. */
int partner = mdio_read(ioaddr, phy_num, 5);
if (partner != sp->partner) {
int flow_ctrl = sp->advertising & partner & 0x0400 ? 1 : 0;
+ if (speedo_debug > 2)
+ printk(KERN_DEBUG "%s: Link status change.\n", dev->name);
sp->partner = partner;
if (flow_ctrl != sp->flow_ctrl) {
sp->flow_ctrl = flow_ctrl;
mdio_read(ioaddr, phy_num, 1);
/* If link beat has returned... */
if (mdio_read(ioaddr, phy_num, 1) & 0x0004)
- dev->flags |= IFF_RUNNING;
+ dev->flags |= IFF_RUNNING;
else
dev->flags &= ~IFF_RUNNING;
}
}
-
if (speedo_debug > 3) {
printk(KERN_DEBUG "%s: Media control tick, status %4.4x.\n",
dev->name, inw(ioaddr + SCBStatus));
/* We haven't received a packet in a Long Time. We might have been
bitten by the receiver hang bug. This can be cleared by sending
a set multicast list command. */
+ if (speedo_debug > 2)
+ printk(KERN_DEBUG "%s: Sending a multicast list set command"
+ " from a timer routine.\n", dev->name);
set_rx_mode(dev);
}
/* We must continue to monitor the media. */
/* Print a few items for debugging. */
if (speedo_debug > 0) {
int i;
- printk(KERN_DEBUG "%s: Tx ring dump, Tx queue %d / %d:\n", dev->name,
+ printk(KERN_DEBUG "%s: Tx ring dump, Tx queue %u / %u:\n", dev->name,
sp->cur_tx, sp->dirty_tx);
for (i = 0; i < TX_RING_SIZE; i++)
- printk(KERN_DEBUG "%s: %c%c%d %8.8x.\n", dev->name,
+ printk(KERN_DEBUG "%s: %c%c%2d %8.8x.\n", dev->name,
i == sp->dirty_tx % TX_RING_SIZE ? '*' : ' ',
i == sp->cur_tx % TX_RING_SIZE ? '=' : ' ',
i, sp->tx_ring[i].status);
}
- printk(KERN_DEBUG "%s:Printing Rx ring (next to receive into %d).\n",
- dev->name, sp->cur_rx);
+ printk(KERN_DEBUG "%s: Printing Rx ring"
+ " (next to receive into %u, dirty index %u).\n",
+ dev->name, sp->cur_rx, sp->dirty_rx);
for (i = 0; i < RX_RING_SIZE; i++)
- printk(KERN_DEBUG " Rx ring entry %d %8.8x.\n",
- i, (int)sp->rx_ringp[i]->status);
+ printk(KERN_DEBUG "%s: %c%c%c%2d %8.8x.\n", dev->name,
+ sp->rx_ringp[i] == sp->last_rxf ? 'l' : ' ',
+ i == sp->dirty_rx % RX_RING_SIZE ? '*' : ' ',
+ i == sp->cur_rx % RX_RING_SIZE ? '=' : ' ',
+ i, (sp->rx_ringp[i] != NULL) ?
+ (unsigned)sp->rx_ringp[i]->status : 0);
for (i = 0; i < 16; i++) {
+ /* FIXME: what does it mean? --SAW */
if (i == 6) i = 21;
- printk(KERN_DEBUG " PHY index %d register %d is %4.4x.\n",
- phy_num, i, mdio_read(ioaddr, phy_num, i));
+ printk(KERN_DEBUG "%s: PHY index %d register %d is %4.4x.\n",
+ dev->name, phy_num, i, mdio_read(ioaddr, phy_num, i));
}
}
sp->last_rxf = last_rxf;
}
+static void speedo_purge_tx(struct net_device *dev)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int entry;
+
+ while ((int)(sp->cur_tx - sp->dirty_tx) > 0) {
+ entry = sp->dirty_tx % TX_RING_SIZE;
+ if (sp->tx_skbuff[entry]) {
+ sp->stats.tx_errors++;
+ pci_unmap_single(sp->pdev,
+ le32_to_cpu(sp->tx_ring[entry].tx_buf_addr0),
+ sp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(sp->tx_skbuff[entry]);
+ sp->tx_skbuff[entry] = 0;
+ }
+ sp->dirty_tx++;
+ }
+ while (sp->mc_setup_head != NULL) {
+ struct speedo_mc_block *t;
+ if (speedo_debug > 1)
+ printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name);
+ pci_unmap_single(sp->pdev, sp->mc_setup_head->frame_dma,
+ sp->mc_setup_head->len, PCI_DMA_TODEVICE);
+ t = sp->mc_setup_head->next;
+ kfree(sp->mc_setup_head);
+ sp->mc_setup_head = t;
+ }
+ sp->mc_setup_tail = NULL;
+ sp->tx_full = 0;
+ netif_wake_queue(dev);
+}
+
static void speedo_tx_timeout(struct net_device *dev)
{
struct speedo_private *sp = (struct speedo_private *)dev->priv;
long ioaddr = dev->base_addr;
int status = inw(ioaddr + SCBStatus);
-
- /* Trigger a stats dump to give time before the reset. */
- speedo_get_stats(dev);
+ unsigned long flags;
printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
" %4.4x at %d/%d command %8.8x.\n",
dev->name, status, inw(ioaddr + SCBCmd),
sp->dirty_tx, sp->cur_tx,
sp->tx_ring[sp->dirty_tx % TX_RING_SIZE].status);
+
+ /* Trigger a stats dump to give time before the reset. */
+ speedo_get_stats(dev);
+
speedo_show_state(dev);
+#if 0
if ((status & 0x00C0) != 0x0080
&& (status & 0x003C) == 0x0010) {
/* Only the command unit has stopped. */
printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
dev->name);
- outl(sp->tx_ring_dma
- + (sp->dirty_tx % TX_RING_SIZE) * sizeof(struct TxFD),
+ outl(cpu_to_le32(TX_RING_ELEM_DMA(sp, dirty_tx % TX_RING_SIZE])),
ioaddr + SCBPointer);
outw(CUStart, ioaddr + SCBCmd);
} else {
+#else
+ {
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,43)
+ start_bh_atomic();
+ /* Ensure that timer routine doesn't run! */
+ del_timer(&sp->timer);
+ end_bh_atomic();
+#else /* LINUX_VERSION_CODE */
+#ifdef __SMP__
+ del_timer_sync(&sp->timer);
+#else /* SMP */
+ del_timer(&sp->timer);
+#endif /* SMP */
+#endif /* LINUX_VERSION_CODE */
/* Reset the Tx and Rx units. */
outl(PortReset, ioaddr + SCBPort);
- if (speedo_debug > 0)
- speedo_show_state(dev);
+ /* We may get spurious interrupts here. But I don't think that they
+ may do much harm. 1999/12/09 SAW */
udelay(10);
+ /* Disable interrupts. */
+ outw(SCBMaskAll, ioaddr + SCBCmd);
+ synchronize_irq();
+ speedo_tx_buffer_gc(dev);
+ /* Free as much as possible.
+ It helps to recover from a hang because of out-of-memory.
+ It also simplifies speedo_resume() in case TX ring is full or
+ close-to-be full. */
+ speedo_purge_tx(dev);
+ speedo_refill_rx_buffers(dev, 1);
+ spin_lock_irqsave(&sp->lock, flags);
speedo_resume(dev);
+ sp->rx_mode = -1;
+ dev->trans_start = jiffies;
+ spin_unlock_irqrestore(&sp->lock, flags);
+ set_rx_mode(dev); /* it takes the spinlock itself --SAW */
+ sp->timer.expires = RUN_AT(2*HZ);
+ add_timer(&sp->timer);
}
/* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
if ((sp->phy[0] & 0x8000) == 0) {
int phy_addr = sp->phy[0] & 0x1f;
+ int advertising = mdio_read(ioaddr, phy_addr, 4);
+ int mii_bmcr = mdio_read(ioaddr, phy_addr, 0);
mdio_write(ioaddr, phy_addr, 0, 0x0400);
mdio_write(ioaddr, phy_addr, 1, 0x0000);
mdio_write(ioaddr, phy_addr, 4, 0x0000);
mdio_write(ioaddr, phy_addr, 0, 0x8000);
#ifdef honor_default_port
mdio_write(ioaddr, phy_addr, 0, mii_ctrl[dev->default_port & 7]);
+#else
+ mdio_read(ioaddr, phy_addr, 0);
+ mdio_write(ioaddr, phy_addr, 0, mii_bmcr);
+ mdio_write(ioaddr, phy_addr, 4, advertising);
#endif
}
- sp->stats.tx_errors++;
- dev->trans_start = jiffies;
- netif_start_queue (dev);
+ return;
}
-
static int
speedo_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
long ioaddr = dev->base_addr;
int entry;
- /* Caution: the write order is important here, set the base address
- with the "ownership" bits last. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,43)
+ if (test_bit(0, (void*)&dev->tbusy) != 0) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < TX_TIMEOUT - 2)
+ return 1;
+ if (tickssofar < TX_TIMEOUT) {
+ /* Reap sent packets from the full Tx queue. */
+ unsigned long flags;
+ spin_lock_irqsave(&sp->lock, flags);
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ outw(SCBTriggerIntr, ioaddr + SCBCmd);
+ spin_unlock_irqrestore(&sp->lock, flags);
+ return 1;
+ }
+ speedo_tx_timeout(dev);
+ return 1;
+ }
+#endif
{ /* Prevent interrupts from changing the Tx ring from underneath us. */
unsigned long flags;
spin_lock_irqsave(&sp->lock, flags);
+
+ /* Check if there are enough space. */
+ if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+ printk(KERN_ERR "%s: incorrect tbusy state, fixed.\n", dev->name);
+ netif_stop_queue(dev);
+ sp->tx_full = 1;
+ spin_unlock_irqrestore(&sp->lock, flags);
+ return 1;
+ }
+
/* Calculate the Tx descriptor entry. */
entry = sp->cur_tx++ % TX_RING_SIZE;
sp->tx_skbuff[entry] = skb;
- /* Todo: be a little more clever about setting the interrupt bit. */
sp->tx_ring[entry].status =
cpu_to_le32(CmdSuspend | CmdTx | CmdTxFlex);
+ if (!(entry & ((TX_RING_SIZE>>2)-1)))
+ sp->tx_ring[entry].status |= cpu_to_le32(CmdIntr);
sp->tx_ring[entry].link =
- cpu_to_le32(sp->tx_ring_dma
- + (sp->cur_tx % TX_RING_SIZE)
- * sizeof(struct TxFD));
+ cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->cur_tx % TX_RING_SIZE));
sp->tx_ring[entry].tx_desc_addr =
- cpu_to_le32(sp->tx_ring_dma
- + ((long)&sp->tx_ring[entry].tx_buf_addr0
- - (long)sp->tx_ring));
+ cpu_to_le32(TX_RING_ELEM_DMA(sp, sp->cur_tx % TX_RING_SIZE) +
+ TX_DESCR_BUF_OFFSET);
/* The data region is always in one buffer descriptor. */
sp->tx_ring[entry].count = cpu_to_le32(sp->tx_threshold);
sp->tx_ring[entry].tx_buf_addr0 =
cpu_to_le32(pci_map_single(sp->pdev, skb->data,
skb->len, PCI_DMA_TODEVICE));
sp->tx_ring[entry].tx_buf_size0 = cpu_to_le32(skb->len);
- /* Todo: perhaps leave the interrupt bit set if the Tx queue is more
- than half full. Argument against: we should be receiving packets
- and scavenging the queue. Argument for: if so, it shouldn't
- matter. */
/* Trigger the command unit resume. */
- {
- struct descriptor *last_cmd = sp->last_cmd;
- sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
- last_cmd->cmd_status &= cpu_to_le32(~(CmdSuspend | CmdIntr));
- }
- if (sp->cur_tx - sp->dirty_tx >= TX_QUEUE_LIMIT) {
+ wait_for_cmd_done(ioaddr + SCBCmd);
+ clear_suspend(sp->last_cmd);
+ /* We want the time window between clearing suspend flag on the previous
+ command and resuming CU to be as small as possible.
+ Interrupts in between are very undesired. --SAW */
+ outb(CUResume, ioaddr + SCBCmd);
+ sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
+
+ /* Leave room for set_rx_mode(). If there is no more space than reserved
+ for multicast filter mark the ring as full. */
+ if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+ netif_stop_queue(dev);
sp->tx_full = 1;
- netif_stop_queue (dev);
}
+
spin_unlock_irqrestore(&sp->lock, flags);
}
- wait_for_cmd_done(ioaddr + SCBCmd);
- outw(CUResume, ioaddr + SCBCmd);
dev->trans_start = jiffies;
-
+
return 0;
}
+static void speedo_tx_buffer_gc(struct net_device *dev)
+{
+ unsigned int dirty_tx;
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+
+ dirty_tx = sp->dirty_tx;
+ while ((int)(sp->cur_tx - dirty_tx) > 0) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ int status = le32_to_cpu(sp->tx_ring[entry].status);
+
+ if (speedo_debug > 5)
+ printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
+ entry, status);
+ if ((status & StatusComplete) == 0)
+ break; /* It still hasn't been processed. */
+ if (status & TxUnderrun)
+ if (sp->tx_threshold < 0x01e08000) {
+ if (speedo_debug > 2)
+ printk(KERN_DEBUG "%s: TX underrun, threshold adjusted.\n",
+ dev->name);
+ sp->tx_threshold += 0x00040000;
+ }
+ /* Free the original skb. */
+ if (sp->tx_skbuff[entry]) {
+ sp->stats.tx_packets++; /* Count only user packets. */
+ sp->stats.tx_bytes += sp->tx_skbuff[entry]->len;
+ pci_unmap_single(sp->pdev,
+ le32_to_cpu(sp->tx_ring[entry].tx_buf_addr0),
+ sp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(sp->tx_skbuff[entry]);
+ sp->tx_skbuff[entry] = 0;
+ }
+ dirty_tx++;
+ }
+
+ if (speedo_debug && (int)(sp->cur_tx - dirty_tx) > TX_RING_SIZE) {
+ printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
+ " full=%d.\n",
+ dirty_tx, sp->cur_tx, sp->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+
+ while (sp->mc_setup_head != NULL
+ && (int)(dirty_tx - sp->mc_setup_head->tx - 1) > 0) {
+ struct speedo_mc_block *t;
+ if (speedo_debug > 1)
+ printk(KERN_DEBUG "%s: freeing mc frame.\n", dev->name);
+ pci_unmap_single(sp->pdev, sp->mc_setup_head->frame_dma,
+ sp->mc_setup_head->len, PCI_DMA_TODEVICE);
+ t = sp->mc_setup_head->next;
+ kfree(sp->mc_setup_head);
+ sp->mc_setup_head = t;
+ }
+ if (sp->mc_setup_head == NULL)
+ sp->mc_setup_tail = NULL;
+
+ sp->dirty_tx = dirty_tx;
+}
+
/* The interrupt handler does all of the Rx thread work and cleans up
after the Tx thread. */
static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
ioaddr = dev->base_addr;
sp = (struct speedo_private *)dev->priv;
- spin_lock (&sp->lock);
+#ifndef final_version
+ /* A lock to prevent simultaneous entry on SMP machines. */
+ if (test_and_set_bit(0, (void*)&sp->in_interrupt)) {
+ printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
+ dev->name);
+ sp->in_interrupt = 0; /* Avoid halting machine. */
+ return;
+ }
+#endif
do {
status = inw(ioaddr + SCBStatus);
/* Acknowledge all of the current interrupt sources ASAP. */
+ /* Will change from 0xfc00 to 0xff00 when we start handling
+ FCP and ER interrupts --Dragan */
outw(status & 0xfc00, ioaddr + SCBStatus);
if (speedo_debug > 4)
if ((status & 0xfc00) == 0)
break;
- if (status & 0x4000) /* Packet received. */
+ /* Always check if all rx buffers are allocated. --SAW */
+ speedo_refill_rx_buffers(dev, 0);
+
+ if ((status & 0x5000) || /* Packet received, or Rx error. */
+ (sp->rx_ring_state&(RrNoMem|RrPostponed)) == RrPostponed)
+ /* Need to gather the postponed packet. */
speedo_rx(dev);
if (status & 0x1000) {
- if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */
- outw(RxResumeNoResources, ioaddr + SCBCmd);
- else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */
- /* No idea of what went wrong. Restart the receiver. */
- outl(sp->rx_ring_dma[sp->cur_rx % RX_RING_SIZE],
- ioaddr + SCBPointer);
- outw(RxStart, ioaddr + SCBCmd);
+ spin_lock(&sp->lock);
+ if ((status & 0x003c) == 0x0028) { /* No more Rx buffers. */
+ struct RxFD *rxf;
+ printk(KERN_WARNING "%s: card reports no RX buffers.\n",
+ dev->name);
+ rxf = sp->rx_ringp[sp->cur_rx % RX_RING_SIZE];
+ if (rxf == NULL) {
+ if (speedo_debug > 2)
+ printk(KERN_DEBUG
+ "%s: NULL cur_rx in speedo_interrupt().\n",
+ dev->name);
+ sp->rx_ring_state |= RrNoMem|RrNoResources;
+ } else if (rxf == sp->last_rxf) {
+ if (speedo_debug > 2)
+ printk(KERN_DEBUG
+ "%s: cur_rx is last in speedo_interrupt().\n",
+ dev->name);
+ sp->rx_ring_state |= RrNoMem|RrNoResources;
+ } else
+ outb(RxResumeNoResources, ioaddr + SCBCmd);
+ } else if ((status & 0x003c) == 0x0008) { /* No resources. */
+ struct RxFD *rxf;
+ printk(KERN_WARNING "%s: card reports no resources.\n",
+ dev->name);
+ rxf = sp->rx_ringp[sp->cur_rx % RX_RING_SIZE];
+ if (rxf == NULL) {
+ if (speedo_debug > 2)
+ printk(KERN_DEBUG
+ "%s: NULL cur_rx in speedo_interrupt().\n",
+ dev->name);
+ sp->rx_ring_state |= RrNoMem|RrNoResources;
+ } else if (rxf == sp->last_rxf) {
+ if (speedo_debug > 2)
+ printk(KERN_DEBUG
+ "%s: cur_rx is last in speedo_interrupt().\n",
+ dev->name);
+ sp->rx_ring_state |= RrNoMem|RrNoResources;
+ } else {
+ /* Restart the receiver. */
+ outl(sp->rx_ring_dma[sp->cur_rx % RX_RING_SIZE],
+ ioaddr + SCBPointer);
+ outb(RxStart, ioaddr + SCBCmd);
+ }
}
sp->stats.rx_errors++;
+ spin_unlock(&sp->lock);
+ }
+
+ if ((sp->rx_ring_state&(RrNoMem|RrNoResources)) == RrNoResources) {
+ printk(KERN_WARNING
+ "%s: restart the receiver after a possible hang.\n",
+ dev->name);
+ spin_lock(&sp->lock);
+ /* Restart the receiver.
+ I'm not sure if it's always right to restart the receiver
+ here but I don't know another way to prevent receiver hangs.
+ 1999/12/25 SAW */
+ outl(sp->rx_ring_dma[sp->cur_rx % RX_RING_SIZE],
+ ioaddr + SCBPointer);
+ outb(RxStart, ioaddr + SCBCmd);
+ sp->rx_ring_state &= ~RrNoResources;
+ spin_unlock(&sp->lock);
}
/* User interrupt, Command/Tx unit interrupt or CU not active. */
if (status & 0xA400) {
- unsigned int dirty_tx;
-
- dirty_tx = sp->dirty_tx;
- while (sp->cur_tx - dirty_tx > 0) {
- int entry = dirty_tx % TX_RING_SIZE;
- int status = le32_to_cpu(sp->tx_ring[entry].status);
-
- if (speedo_debug > 5)
- printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
- entry, status);
- if ((status & StatusComplete) == 0)
- break; /* It still hasn't been processed. */
- if (status & TxUnderrun)
- if (sp->tx_threshold < 0x01e08000)
- sp->tx_threshold += 0x00040000;
- /* Free the original skb. */
- if (sp->tx_skbuff[entry]) {
- sp->stats.tx_packets++; /* Count only user packets. */
- sp->stats.tx_bytes += sp->tx_skbuff[entry]->len;
- pci_unmap_single(sp->pdev,
- le32_to_cpu(sp->tx_ring[entry].tx_buf_addr0),
- sp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
- dev_kfree_skb_irq(sp->tx_skbuff[entry]);
- sp->tx_skbuff[entry] = 0;
- } else if ((status & 0x70000) == CmdNOp) {
- if (sp->mc_setup_busy)
- pci_unmap_single(sp->pdev,
- sp->mc_setup_dma,
- sp->mc_setup_frm_len,
- PCI_DMA_TODEVICE);
- sp->mc_setup_busy = 0;
- }
- dirty_tx++;
- }
-
-#ifndef final_version
- if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
- printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
- " full=%d.\n",
- dirty_tx, sp->cur_tx, sp->tx_full);
- dirty_tx += TX_RING_SIZE;
- }
-#endif
-
- sp->dirty_tx = dirty_tx;
+ spin_lock(&sp->lock);
+ speedo_tx_buffer_gc(dev);
if (sp->tx_full
- && sp->cur_tx - dirty_tx < TX_QUEUE_LIMIT - 1) {
- /* The ring is no longer full, clear tbusy. */
+ && (int)(sp->cur_tx - sp->dirty_tx) < TX_QUEUE_UNFULL) {
+ /* The ring is no longer full. */
sp->tx_full = 0;
- netif_wake_queue (dev);
+ netif_wake_queue(dev); /* Attention: under a spinlock. --SAW */
}
+ spin_unlock(&sp->lock);
}
- } while (--boguscnt > 0);
-
- if (boguscnt <= 0) {
- printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n",
- dev->name, status);
- /* Clear all interrupt sources. */
- outl(0xfc00, ioaddr + SCBStatus);
- }
+ if (--boguscnt < 0) {
+ printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n",
+ dev->name, status);
+ /* Clear all interrupt sources. */
+ /* Will change from 0xfc00 to 0xff00 when we start handling
+ FCP and ER interrupts --Dragan */
+ outl(0xfc00, ioaddr + SCBStatus);
+ break;
+ }
+ } while (1);
if (speedo_debug > 3)
printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
dev->name, inw(ioaddr + SCBStatus));
- spin_unlock (&sp->lock);
+ clear_bit(0, (void*)&sp->in_interrupt);
+ return;
+}
+
+static inline struct RxFD *speedo_rx_alloc(struct net_device *dev, int entry)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ struct RxFD *rxf;
+ struct sk_buff *skb;
+ /* Get a fresh skbuff to replace the consumed one. */
+ skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
+ sp->rx_skbuff[entry] = skb;
+ if (skb == NULL) {
+ sp->rx_ringp[entry] = NULL;
+ return NULL;
+ }
+ rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail;
+ sp->rx_ring_dma[entry] =
+ pci_map_single(sp->pdev, rxf,
+ PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
+ skb->dev = dev;
+ skb_reserve(skb, sizeof(struct RxFD));
+ rxf->rx_buf_addr = 0xffffffff;
+ return rxf;
+}
+
+static inline void speedo_rx_link(struct net_device *dev, int entry,
+ struct RxFD *rxf, dma_addr_t rxf_dma)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ rxf->status = cpu_to_le32(0xC0000001); /* '1' for driver use only. */
+ rxf->link = 0; /* None yet. */
+ rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
+ sp->last_rxf->link = cpu_to_le32(rxf_dma);
+ sp->last_rxf->status &= cpu_to_le32(~0xC0000000);
+ sp->last_rxf = rxf;
+}
+
+static int speedo_refill_rx_buf(struct net_device *dev, int force)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ int entry;
+ struct RxFD *rxf;
+
+ entry = sp->dirty_rx % RX_RING_SIZE;
+ if (sp->rx_skbuff[entry] == NULL) {
+ rxf = speedo_rx_alloc(dev, entry);
+ if (rxf == NULL) {
+ unsigned int forw;
+ int forw_entry;
+ if (speedo_debug > 2 || !(sp->rx_ring_state & RrOOMReported)) {
+ printk(KERN_WARNING "%s: can't fill rx buffer (force %d)!\n",
+ dev->name, force);
+ speedo_show_state(dev);
+ sp->rx_ring_state |= RrOOMReported;
+ }
+ if (!force)
+ return -1; /* Better luck next time! */
+ /* Borrow an skb from one of next entries. */
+ for (forw = sp->dirty_rx + 1; forw != sp->cur_rx; forw++)
+ if (sp->rx_skbuff[forw % RX_RING_SIZE] != NULL)
+ break;
+ if (forw == sp->cur_rx)
+ return -1;
+ forw_entry = forw % RX_RING_SIZE;
+ sp->rx_skbuff[entry] = sp->rx_skbuff[forw_entry];
+ sp->rx_skbuff[forw_entry] = NULL;
+ rxf = sp->rx_ringp[forw_entry];
+ sp->rx_ringp[forw_entry] = NULL;
+ sp->rx_ringp[entry] = rxf;
+ }
+ } else {
+ rxf = sp->rx_ringp[entry];
+ }
+ speedo_rx_link(dev, entry, rxf, sp->rx_ring_dma[entry]);
+ sp->dirty_rx++;
+ sp->rx_ring_state &= ~(RrNoMem|RrOOMReported); /* Mark the progress. */
+ return 0;
+}
+
+static void speedo_refill_rx_buffers(struct net_device *dev, int force)
+{
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+
+ /* Refill the RX ring. */
+ while ((int)(sp->cur_rx - sp->dirty_rx) > 0 &&
+ speedo_refill_rx_buf(dev, force) != -1);
}
static int
int entry = sp->cur_rx % RX_RING_SIZE;
int status;
int rx_work_limit = sp->dirty_rx + RX_RING_SIZE - sp->cur_rx;
+ int alloc_ok = 1;
if (speedo_debug > 4)
printk(KERN_DEBUG " In speedo_rx().\n");
if (--rx_work_limit < 0)
break;
+
+ /* Check for a rare out-of-memory case: the current buffer is
+ the last buffer allocated in the RX ring. --SAW */
+ if (sp->last_rxf == sp->rx_ringp[entry]) {
+ /* Postpone the packet. It'll be reaped at an interrupt when this
+ packet is no longer the last packet in the ring. */
+ if (speedo_debug > 2)
+ printk(KERN_DEBUG "%s: RX packet postponed!\n",
+ dev->name);
+ sp->rx_ring_state |= RrPostponed;
+ break;
+ }
+
if (speedo_debug > 4)
printk(KERN_DEBUG " speedo_rx() status %8.8x len %d.\n", status,
pkt_len);
if (status & RxErrTooBig)
printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
"status %8.8x!\n", dev->name, status);
- else if ( ! (status & RxOK)) {
+ else if (! (status & RxOK)) {
/* There was a fatal error. This *should* be impossible. */
sp->stats.rx_errors++;
printk(KERN_ERR "%s: Anomalous event in speedo_rx(), "
- "status %8.8x.\n", dev->name, status);
+ "status %8.8x.\n",
+ dev->name, status);
}
} else {
struct sk_buff *skb;
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
/* 'skb_put()' points to the start of sk_buff data area. */
pci_dma_sync_single(sp->pdev, sp->rx_ring_dma[entry],
- PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
+ PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
#if 1 || USE_IP_CSUM
/* Packet is in one chunk -- we can copy + cksum. */
eth_copy_and_sum(skb, sp->rx_skbuff[entry]->tail, pkt_len, 0);
temp = skb_put(skb, pkt_len);
sp->rx_ringp[entry] = NULL;
pci_unmap_single(sp->pdev, sp->rx_ring_dma[entry],
- PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
+ PKT_BUF_SZ + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
}
skb->protocol = eth_type_trans(skb, dev);
netif_rx(skb);
sp->stats.rx_bytes += pkt_len;
}
entry = (++sp->cur_rx) % RX_RING_SIZE;
+ sp->rx_ring_state &= ~RrPostponed;
+ /* Refill the recently taken buffers.
+ Do it one-by-one to handle traffic bursts better. */
+ if (alloc_ok && speedo_refill_rx_buf(dev, 0) == -1)
+ alloc_ok = 0;
}
- /* Refill the Rx ring buffers. */
- for (; sp->cur_rx - sp->dirty_rx > 0; sp->dirty_rx++) {
- struct RxFD *rxf;
- entry = sp->dirty_rx % RX_RING_SIZE;
- if (sp->rx_skbuff[entry] == NULL) {
- struct sk_buff *skb;
- /* Get a fresh skbuff to replace the consumed one. */
- skb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
- sp->rx_skbuff[entry] = skb;
- if (skb == NULL) {
- sp->rx_ringp[entry] = NULL;
- break; /* Better luck next time! */
- }
- rxf = sp->rx_ringp[entry] = (struct RxFD *)skb->tail;
- sp->rx_ring_dma[entry] =
- pci_map_single(sp->pdev, rxf, PKT_BUF_SZ
- + sizeof(struct RxFD), PCI_DMA_FROMDEVICE);
- skb->dev = dev;
- skb_reserve(skb, sizeof(struct RxFD));
- rxf->rx_buf_addr = 0xffffffff;
- } else {
- rxf = sp->rx_ringp[entry];
- }
- rxf->status = cpu_to_le32(0xC0000001); /* '1' for driver use only. */
- rxf->link = 0; /* None yet. */
- rxf->count = cpu_to_le32(PKT_BUF_SZ << 16);
- sp->last_rxf->link = cpu_to_le32(sp->rx_ring_dma[entry]);
- sp->last_rxf->status &= cpu_to_le32(~0xC0000000);
- sp->last_rxf = rxf;
- }
+ /* Try hard to refill the recently taken buffers. */
+ speedo_refill_rx_buffers(dev, 1);
sp->last_rx_time = jiffies;
+
return 0;
}
struct speedo_private *sp = (struct speedo_private *)dev->priv;
int i;
+ netdevice_stop(dev);
netif_stop_queue(dev);
if (speedo_debug > 1)
/* Shut off the media monitoring timer. */
del_timer(&sp->timer);
- /* Disable interrupts, and stop the chip's Rx process. */
- outw(SCBMaskAll, ioaddr + SCBCmd);
- outw(SCBMaskAll | RxAbort, ioaddr + SCBCmd);
+ /* Shutting down the chip nicely fails to disable flow control. So.. */
+ outl(PortPartialReset, ioaddr + SCBPort);
free_irq(dev->irq, dev);
- /* Free all the skbuffs in the Rx and Tx queues. */
+ /* Print a few items for debugging. */
+ if (speedo_debug > 3)
+ speedo_show_state(dev);
+
+ /* Free all the skbuffs in the Rx and Tx queues. */
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb = sp->rx_skbuff[i];
sp->rx_skbuff[i] = 0;
for (i = 0; i < TX_RING_SIZE; i++) {
struct sk_buff *skb = sp->tx_skbuff[i];
sp->tx_skbuff[i] = 0;
-
/* Clear the Tx descriptors. */
if (skb) {
pci_unmap_single(sp->pdev,
dev_kfree_skb(skb);
}
}
- if (sp->mc_setup_frm) {
- kfree(sp->mc_setup_frm);
- sp->mc_setup_frm_len = 0;
- }
- /* Print a few items for debugging. */
- if (speedo_debug > 3)
- speedo_show_state(dev);
+ /* Free multicast setting blocks. */
+ for (i = 0; sp->mc_setup_head != NULL; i++) {
+ struct speedo_mc_block *t;
+ t = sp->mc_setup_head->next;
+ kfree(sp->mc_setup_head);
+ sp->mc_setup_head = t;
+ }
+ sp->mc_setup_tail = NULL;
+ if (speedo_debug > 0)
+ printk(KERN_DEBUG "%s: %d multicast blocks dropped.\n", dev->name, i);
- /* Alt: acpi_set_pwr_state(pci_bus, pci_devfn, sp->acpi_pwr); */
- pci_set_power_state (sp->pdev, 2);
+ pci_set_power_state(sp->pdev, 2);
MOD_DEC_USE_COUNT;
sp->stats.rx_length_errors += le32_to_cpu(sp->lstats->rx_runt_errs);
sp->lstats->done_marker = 0x0000;
if (netif_running(dev)) {
+ unsigned long flags;
+ /* Take a spinlock to make wait_for_cmd_done and sending the
+ * command atomic. --SAW */
+ spin_lock_irqsave(&sp->lock, flags);
wait_for_cmd_done(ioaddr + SCBCmd);
- outw(CUDumpStats, ioaddr + SCBCmd);
+ outb(CUDumpStats, ioaddr + SCBCmd);
+ spin_unlock_irqrestore(&sp->lock, flags);
}
}
return &sp->stats;
case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
data[0] = phy;
case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
- saved_acpi = pci_set_power_state (sp->pdev, 0);
- data[3] = mdio_read (ioaddr, data[0], data[1]);
- pci_set_power_state (sp->pdev, saved_acpi);
+ /* FIXME: these operations probably need to be serialized with MDIO
+ access from the timer routine and timeout handler. 2000/03/08 SAW */
+ saved_acpi = pci_set_power_state(sp->pdev, 0);
+ data[3] = mdio_read(ioaddr, data[0], data[1]);
+ pci_set_power_state(sp->pdev, saved_acpi);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
if (!capable(CAP_NET_ADMIN))
} else
new_rx_mode = 0;
- if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
- /* The Tx ring is full -- don't add anything! Presumably the new mode
- is in config_cmd_data and will be added anyway. */
+ if (speedo_debug > 3)
+ printk(KERN_DEBUG "%s: set_rx_mode %d -> %d\n", dev->name,
+ sp->rx_mode, new_rx_mode);
+
+ if ((int)(sp->cur_tx - sp->dirty_tx) > TX_RING_SIZE - TX_MULTICAST_SIZE) {
+ /* The Tx ring is full -- don't add anything! Hope the mode will be
+ * set again later. */
sp->rx_mode = -1;
return;
}
sp->tx_skbuff[entry] = 0; /* Redundant. */
sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdConfigure);
sp->tx_ring[entry].link =
- cpu_to_le32(sp->tx_ring_dma + ((entry + 1) % TX_RING_SIZE)
- * sizeof(struct TxFD));
+ cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE));
config_cmd_data = (void *)&sp->tx_ring[entry].tx_desc_addr;
/* Construct a full CmdConfig frame. */
memcpy(config_cmd_data, i82558_config_cmd, sizeof(i82558_config_cmd));
config_cmd_data[4] = rxdmacount;
config_cmd_data[5] = txdmacount + 0x80;
config_cmd_data[15] |= (new_rx_mode & 2) ? 1 : 0;
- config_cmd_data[19] = sp->flow_ctrl ? 0xBD : 0x80;
+ /* 0x80 doesn't disable FC 0x84 does.
+ Disable Flow control since we are not ACK-ing any FC interrupts
+ for now. --Dragan */
+ config_cmd_data[19] = 0x84;
config_cmd_data[19] |= sp->full_duplex ? 0x40 : 0;
config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
if (sp->phy[0] & 0x8000) { /* Use the AUI port instead. */
/* Trigger the command unit resume. */
wait_for_cmd_done(ioaddr + SCBCmd);
clear_suspend(last_cmd);
- outw(CUResume, ioaddr + SCBCmd);
+ outb(CUResume, ioaddr + SCBCmd);
+ if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+ netif_stop_queue(dev);
+ sp->tx_full = 1;
+ }
spin_unlock_irqrestore(&sp->lock, flags);
}
sp->tx_skbuff[entry] = 0;
sp->tx_ring[entry].status = cpu_to_le32(CmdSuspend | CmdMulticastList);
sp->tx_ring[entry].link =
- cpu_to_le32(sp->tx_ring_dma + ((entry + 1) % TX_RING_SIZE)
- * sizeof(struct TxFD));
+ cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE));
sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr;
*setup_params++ = cpu_to_le16(dev->mc_count*6);
wait_for_cmd_done(ioaddr + SCBCmd);
clear_suspend(last_cmd);
/* Immediately trigger the command unit resume. */
- outw(CUResume, ioaddr + SCBCmd);
+ outb(CUResume, ioaddr + SCBCmd);
+
+ if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+ netif_stop_queue(dev);
+ sp->tx_full = 1;
+ }
spin_unlock_irqrestore(&sp->lock, flags);
} else if (new_rx_mode == 0) {
struct dev_mc_list *mclist;
u16 *setup_params, *eaddrs;
- struct descriptor *mc_setup_frm = sp->mc_setup_frm;
+ struct speedo_mc_block *mc_blk;
+ struct descriptor *mc_setup_frm;
int i;
- /* If we are busy, someone might be quickly adding to the MC list.
- Try again later when the list updates stop. */
- if (sp->mc_setup_busy) {
- sp->rx_mode = -1;
+ mc_blk = kmalloc(sizeof(*mc_blk) + 2 + multicast_filter_limit*6,
+ GFP_ATOMIC);
+ if (mc_blk == NULL) {
+ printk(KERN_ERR "%s: Failed to allocate a setup frame.\n",
+ dev->name);
+ sp->rx_mode = -1; /* We failed, try again. */
return;
}
- if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
- || sp->mc_setup_frm == NULL) {
- /* Allocate a full setup frame, 10bytes + <max addrs>. */
- if (sp->mc_setup_frm)
- kfree(sp->mc_setup_frm);
- sp->mc_setup_frm_len = 10 + multicast_filter_limit*6;
- sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC);
- if (sp->mc_setup_frm == NULL) {
- printk(KERN_ERR "%s: Failed to allocate a setup frame.\n",
- dev->name);
- sp->rx_mode = -1; /* We failed, try again. */
- return;
- }
- }
- mc_setup_frm = sp->mc_setup_frm;
+ mc_blk->next = NULL;
+ mc_blk->len = 2 + multicast_filter_limit*6;
+ mc_blk->frame_dma =
+ pci_map_single(sp->pdev, &mc_blk->frame, mc_blk->len,
+ PCI_DMA_TODEVICE);
+ mc_setup_frm = &mc_blk->frame;
+
/* Fill the setup frame. */
if (speedo_debug > 1)
- printk(KERN_DEBUG "%s: Constructing a setup frame at %p, "
- "%d bytes.\n",
- dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
+ printk(KERN_DEBUG "%s: Constructing a setup frame at %p.\n",
+ dev->name, mc_setup_frm);
mc_setup_frm->cmd_status =
cpu_to_le32(CmdSuspend | CmdIntr | CmdMulticastList);
/* Link set below. */
/* Disable interrupts while playing with the Tx Cmd list. */
spin_lock_irqsave(&sp->lock, flags);
+
+ if (sp->mc_setup_tail)
+ sp->mc_setup_tail->next = mc_blk;
+ else
+ sp->mc_setup_head = mc_blk;
+ sp->mc_setup_tail = mc_blk;
+ mc_blk->tx = sp->cur_tx;
+
entry = sp->cur_tx++ % TX_RING_SIZE;
last_cmd = sp->last_cmd;
sp->last_cmd = mc_setup_frm;
- sp->mc_setup_busy = 1;
/* Change the command to a NoOp, pointing to the CmdMulti command. */
sp->tx_skbuff[entry] = 0;
sp->tx_ring[entry].status = cpu_to_le32(CmdNOp);
- sp->mc_setup_dma = pci_map_single(sp->pdev, mc_setup_frm, sp->mc_setup_frm_len, PCI_DMA_TODEVICE);
- sp->tx_ring[entry].link = cpu_to_le32(sp->mc_setup_dma);
+ sp->tx_ring[entry].link = cpu_to_le32(mc_blk->frame_dma);
/* Set the link in the setup frame. */
mc_setup_frm->link =
- cpu_to_le32(sp->tx_ring_dma + ((entry + 1) % TX_RING_SIZE)
- * sizeof(struct TxFD));
+ cpu_to_le32(TX_RING_ELEM_DMA(sp, (entry + 1) % TX_RING_SIZE));
wait_for_cmd_done(ioaddr + SCBCmd);
clear_suspend(last_cmd);
/* Immediately trigger the command unit resume. */
- outw(CUResume, ioaddr + SCBCmd);
+ outb(CUResume, ioaddr + SCBCmd);
+
+ if ((int)(sp->cur_tx - sp->dirty_tx) >= TX_QUEUE_LIMIT) {
+ netif_stop_queue(dev);
+ sp->tx_full = 1;
+ }
spin_unlock_irqrestore(&sp->lock, flags);
+
if (speedo_debug > 5)
printk(" CmdMCSetup frame length %d in entry %d.\n",
dev->mc_count, entry);
sp->rx_mode = new_rx_mode;
}
-
-
-static void eepro100_suspend (struct pci_dev *pdev)
+\f
+static void eepro100_suspend(struct pci_dev *pdev)
{
struct net_device *dev = pdev->driver_data;
long ioaddr = dev->base_addr;
/* XXX call pci_set_power_state ()? */
}
-
-static void eepro100_resume (struct pci_dev *pdev)
+static void eepro100_resume(struct pci_dev *pdev)
{
struct net_device *dev = pdev->driver_data;
- struct speedo_private *np = (struct speedo_private *)dev->priv;
+ struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ long ioaddr = dev->base_addr;
- netif_device_attach(dev);
+ /* I'm absolutely uncertain if this part of code may work.
+ The problems are:
+ - correct hardware reinitialization;
+ - correct driver behavior between different steps of the
+ reinitialization;
+ - serialization with other driver calls.
+ 2000/03/08 SAW */
+ outw(SCBMaskAll, ioaddr + SCBCmd);
speedo_resume(dev);
- np->rx_mode = -1;
- np->flow_ctrl = np->partner = 0;
+ netif_device_attach(dev);
+ sp->rx_mode = -1;
+ sp->flow_ctrl = sp->partner = 0;
set_rx_mode(dev);
}
-
static void __devexit eepro100_remove_one (struct pci_dev *pdev)
{
struct net_device *dev = pdev->driver_data;
struct speedo_private *sp = (struct speedo_private *)dev->priv;
- unregister_netdev (dev);
+ unregister_netdev(dev);
- release_region (pci_resource_start (pdev, 1),
- pci_resource_len (pdev, 1));
- release_mem_region (pci_resource_start (pdev, 0),
- pci_resource_len (pdev, 0));
+ release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
+ release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
#ifndef USE_IO
- iounmap ((char *) dev->base_addr);
+ iounmap((char *)dev->base_addr);
#endif
pci_free_consistent(pdev, TX_RING_SIZE * sizeof(struct TxFD)
- + sizeof(struct speedo_stats),
- sp->tx_ring, sp->tx_ring_dma);
-
- kfree (dev);
+ + sizeof(struct speedo_stats),
+ sp->tx_ring, sp->tx_ring_dma);
+ kfree(dev);
}
-
-
+\f
static struct pci_device_id eepro100_pci_tbl[] __devinitdata = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82557,
PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559ER,
+ PCI_ANY_ID, PCI_ANY_ID, },
{ 0,},
};
-MODULE_DEVICE_TABLE (pci, eepro100_pci_tbl);
-
+MODULE_DEVICE_TABLE(pci, eepro100_pci_tbl);
static struct pci_driver eepro100_driver = {
- name: EEPRO100_MODULE_NAME,
+ name: "eepro100",
id_table: eepro100_pci_tbl,
probe: eepro100_init_one,
remove: eepro100_remove_one,
resume: eepro100_resume,
};
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,48)
+static int pci_module_init(struct pci_driver *pdev)
+{
+ int rc;
+
+ rc = pci_register_driver(pdev);
+ if (rc <= 0) {
+ printk(KERN_INFO "%s: No cards found, driver not installed.\n",
+ pdev->name);
+ pci_unregister_driver(pdev);
+ return -ENODEV;
+ }
+ return 0;
+}
+#endif
static int __init eepro100_init_module(void)
{
+ if (debug >= 0 && speedo_debug != debug)
+ printk(KERN_INFO "eepro100.c: Debug level is %d.\n", debug);
if (debug >= 0)
speedo_debug = debug;
- return pci_module_init (&eepro100_driver);
+ return pci_module_init(&eepro100_driver);
}
-
static void __exit eepro100_cleanup_module(void)
{
- pci_unregister_driver (&eepro100_driver);
+ pci_unregister_driver(&eepro100_driver);
}
-
module_init(eepro100_init_module);
module_exit(eepro100_cleanup_module);
-
+\f
/*
* Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS` `[ -f ./pci-netif.h ] && echo -DHAS_PCI_NETIF`"
- * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * simple-compile-command: "gcc -DMODULE -D__KERNEL__ -O6 -c eepro100.c"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
#include <linux/if_ether.h> /* For the statistics structure. */
#include <linux/if_arp.h> /* For ARPHRD_ETHER */
-#define LOOPBACK_MTU (PAGE_SIZE - 172)
+#define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16)
/*
* The higher levels take care of making this non-reentrant (it's
{
struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
- /*
- * Take this out if the debug says its ok
- */
-
- if (skb == NULL || dev == NULL)
- printk(KERN_DEBUG "loopback fed NULL data - splat\n");
-
/*
* Optimise so buffers with skb->free=1 are not copied but
* instead are lobbed from tx queue to rx queue
/* Initialize the rest of the LOOPBACK device. */
int __init loopback_init(struct net_device *dev)
{
- dev->mtu = LOOPBACK_MTU;
+ dev->mtu = PAGE_SIZE - LOOPBACK_OVERHEAD;
dev->hard_start_xmit = loopback_xmit;
dev->hard_header = eth_header;
dev->hard_header_cache = eth_header_cache;
memset(dev->priv, 0, sizeof(struct net_device_stats));
dev->get_stats = get_stats;
+ if (num_physpages >= ((128*1024*1024)>>PAGE_SHIFT))
+ dev->mtu = 4096*4 - LOOPBACK_OVERHEAD;
+
/*
* Fill in the generic fields of the device structure.
*/
err = -EFAULT;
switch (cmd) {
+ case PPPIOCGCHAN:
+ err = -ENXIO;
+ if (ap == 0)
+ break;
+ err = -EFAULT;
+ if (put_user(ppp_channel_index(&ap->chan), (int *) arg))
+ break;
+ err = 0;
+ break;
+
case PPPIOCGUNIT:
err = -ENXIO;
if (ap == 0)
case PPPIOCSXASYNCMAP:
case PPPIOCGMRU:
case PPPIOCSMRU:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ break;
err = ppp_async_ioctl(&ap->chan, cmd, arg);
break;
case PPPIOCATTACH:
+ case PPPIOCDETACH:
err = ppp_channel_ioctl(&ap->chan, cmd, arg);
break;
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 20000313==
+ * ==FILEVERSION 20000323==
*/
#include <linux/config.h>
#define NP_AT 3 /* Appletalk protocol */
#define NUM_NP 4 /* Number of NPs. */
-#define MPHDRLEN 4 /* multilink protocol header length */
-#define MPHDRLEN_SSN 2 /* ditto with short sequence numbers */
+#define MPHDRLEN 6 /* multilink protocol header length */
+#define MPHDRLEN_SSN 4 /* ditto with short sequence numbers */
#define MIN_FRAG_SIZE 64
/*
add_wait_queue(&pf->rwait, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) {
- ret = -EAGAIN;
skb = skb_dequeue(&pf->rq);
if (skb)
break;
+ ret = 0;
+ if (pf->kind == CHANNEL && PF_TO_CHANNEL(pf)->chan == 0)
+ break;
+ ret = -EAGAIN;
if (file->f_flags & O_NONBLOCK)
break;
ret = -ERESTARTSYS;
spin_lock_bh(&pch->downl);
chan = pch->chan;
err = -ENOTTY;
- if (chan->ops->ioctl)
+ if (chan && chan->ops->ioctl)
err = chan->ops->ioctl(chan, cmd, arg);
spin_unlock_bh(&pch->downl);
}
pch = list_entry(list, struct channel, clist);
spin_lock_bh(&pch->downl);
- if (skb_queue_len(&pch->file.xq) == 0
- && pch->chan->ops->start_xmit(pch->chan, skb))
+ if (pch->chan) {
+ if (pch->chan->ops->start_xmit(pch->chan, skb))
+ skb = 0;
+ } else {
+ /* channel got unregistered */
+ kfree_skb(skb);
+ skb = 0;
+ }
+ if (skb_queue_len(&pch->file.xq) == 0 && skb == 0)
ppp->xmit_pending = 0;
spin_unlock_bh(&pch->downl);
return;
if (frag != 0) {
q = skb_put(frag, fragsize + hdrlen);
/* make the MP header */
+ q[0] = PPP_MP >> 8;
+ q[1] = PPP_MP;
if (ppp->flags & SC_MP_XSHORTSEQ) {
- q[0] = bits + ((ppp->nxseq >> 8) & 0xf);
- q[1] = ppp->nxseq;
- } else {
- q[0] = bits;
- q[1] = ppp->nxseq >> 16;
- q[2] = ppp->nxseq >> 8;
+ q[2] = bits + ((ppp->nxseq >> 8) & 0xf);
q[3] = ppp->nxseq;
+ } else {
+ q[2] = bits;
+ q[3] = ppp->nxseq >> 16;
+ q[4] = ppp->nxseq >> 8;
+ q[5] = ppp->nxseq;
}
/* copy the data in */
kfree_skb(skb);
}
spin_unlock_bh(&pch->downl);
+ pch->had_frag = 1;
}
p += fragsize;
len -= fragsize;
proto = PPP_PROTO(skb);
read_lock_bh(&pch->upl);
- if (pch->ppp == 0 || proto == PPP_LCP || proto == 0x80fb) {
+ if (pch->ppp == 0 || proto >= 0xc000 || proto == PPP_CCPFRAG) {
/* put it on the channel queue */
skb_queue_tail(&pch->file.rq, skb);
/* drop old frames if queue too long */
}
len = slhc_uncompress(ppp->vj, skb->data + 2, skb->len - 2);
if (len <= 0) {
- int i;
printk(KERN_DEBUG "PPP: VJ decompression error\n");
- printk(KERN_DEBUG "PPP: len = %d data =", skb->len);
- for (i = 0; i < 16 && i < skb->len; ++i)
- printk(" %.2x", skb->data[i]);
- printk("\n");
goto err;
}
len += 2;
{
u32 mask, seq, minseq;
struct list_head *l;
- int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? 2: 4;
+ int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
- if (skb->len < mphdrlen + 3)
+ if (skb->len < mphdrlen + 1 || ppp->mrru == 0)
goto err; /* no good, throw it away */
/* Decode sequence number and begin/end bits */
mask = 0xffffff;
}
skb->BEbits = skb->data[2];
- skb_pull(skb, mphdrlen + 2); /* pull off PPP and MP headers*/
+ skb_pull(skb, mphdrlen); /* pull off PPP and MP headers */
/* Expand sequence number to 32 bits */
seq |= pch->lastseq & ~mask;
minseq = seq;
for (l = ppp->channels.next; l != &ppp->channels; l = l->next) {
struct channel *ch = list_entry(l, struct channel, clist);
- if (seq_before(ch->lastseq, seq))
- seq = ch->lastseq;
+ if (seq_before(ch->lastseq, minseq))
+ minseq = ch->lastseq;
}
ppp->minseq = minseq;
next = p->next;
if (seq_before(p->sequence, seq)) {
/* this can't happen, anyway toss the skb */
- printk(KERN_ERR "ppp_mp_reconstruct bad seq %x < %x\n",
+ printk(KERN_ERR "ppp_mp_reconstruct bad seq %u < %u\n",
p->sequence, seq);
__skb_unlink(p, list);
kfree_skb(p);
break;
/* Fragment `seq' is lost, keep going. */
lost = 1;
- seq = seq_before(p->sequence, minseq)?
- p->sequence: minseq;
+ seq = seq_before(minseq, p->sequence)?
+ minseq + 1: p->sequence;
next = p;
continue;
}
/* Got a complete packet yet? */
if (lost == 0 && (p->BEbits & E) && (head->BEbits & B)) {
- if (len > ppp->mrru) {
+ if (len > ppp->mrru + 2) {
++ppp->stats.rx_length_errors;
+ printk(KERN_DEBUG "PPP: reconstructed packet"
+ " is too long (%d)\n", len);
} else if ((skb = dev_alloc_skb(len)) == NULL) {
++ppp->stats.rx_missed_errors;
+ printk(KERN_DEBUG "PPP: no memory for "
+ "reconstructed packet");
} else {
tail = p;
break;
}
+ ppp->nextseq = seq + 1;
}
/*
if (tail != NULL) {
/* If we have discarded any fragments,
signal a receive error. */
- if (head->sequence != ppp->nextseq)
+ if (head->sequence != ppp->nextseq) {
+ if (ppp->debug & 1)
+ printk(KERN_DEBUG " missed pkts %u..%u\n",
+ ppp->nextseq, head->sequence-1);
ppp_receive_error(ppp);
+ }
/* uncompress protocol ID */
if (head->data[0] & 1)
chan->ppp = pch;
init_ppp_file(&pch->file, CHANNEL);
pch->file.hdrlen = chan->hdrlen;
+#ifdef CONFIG_PPP_MULTILINK
+ pch->lastseq = -1;
+#endif /* CONFIG_PPP_MULTILINK */
spin_lock_init(&pch->downl);
pch->upl = RW_LOCK_UNLOCKED;
spin_lock_bh(&all_channels_lock);
}
/*
- * Return the unit number associated with a channel.
+ * Return the index of a channel.
+ */
+int ppp_channel_index(struct ppp_channel *chan)
+{
+ struct channel *pch = chan->ppp;
+
+ return pch->file.index;
+}
+
+/*
+ * Return the PPP unit number to which a channel is connected.
*/
int ppp_unit_number(struct ppp_channel *chan)
{
struct channel *pch = chan->ppp;
+ int unit = -1;
- return pch->ppp->file.index;
+ if (pch != 0) {
+ read_lock_bh(&pch->upl);
+ if (pch->ppp != 0)
+ unit = pch->ppp->file.index;
+ read_unlock_bh(&pch->upl);
+ }
+ return unit;
}
/*
int err = -ENOTTY;
int unit;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
if (pch == 0)
return -EINVAL;
switch (cmd) {
INIT_LIST_HEAD(&ppp->channels);
spin_lock_init(&ppp->rlock);
spin_lock_init(&ppp->wlock);
+#ifdef CONFIG_PPP_MULTILINK
+ ppp->minseq = -1;
+ skb_queue_head_init(&ppp->mrq);
+#endif /* CONFIG_PPP_MULTILINK */
ppp->dev = dev;
dev->init = ppp_net_init;
hdrlen = pch->chan->hdrlen + PPP_HDRLEN;
if (ppp->dev && hdrlen > ppp->dev->hard_header_len)
ppp->dev->hard_header_len = hdrlen;
- list_add(&pch->clist, &ppp->channels);
+ list_add_tail(&pch->clist, &ppp->channels);
++ppp->n_channels;
pch->ppp = ppp;
ret = 0;
EXPORT_SYMBOL(ppp_register_channel);
EXPORT_SYMBOL(ppp_unregister_channel);
+EXPORT_SYMBOL(ppp_channel_index);
EXPORT_SYMBOL(ppp_unit_number);
EXPORT_SYMBOL(ppp_input);
EXPORT_SYMBOL(ppp_input_error);
* PPP driver, written by Michael Callahan and Al Longyear, and
* subsequently hacked by Paul Mackerras.
*
- * ==FILEVERSION 991018==
+ * ==FILEVERSION 20000322==
*/
-/* $Id: ppp_synctty.c,v 1.3 1999/09/02 05:30:10 paulus Exp $ */
-
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ppp_defs.h>
#include <linux/if_ppp.h>
#include <linux/ppp_channel.h>
+#include <linux/init.h>
#include <asm/uaccess.h>
-#define PPP_VERSION "2.4.0"
+#ifndef spin_trylock_bh
+#define spin_trylock_bh(lock) ({ int __r; local_bh_disable(); \
+ __r = spin_trylock(lock); \
+ if (!__r) local_bh_enable(); \
+ __r; })
+#endif
+
+#define PPP_VERSION "2.4.1"
/* Structure for storing local state. */
struct syncppp {
unsigned int flags;
unsigned int rbits;
int mru;
- unsigned long busy;
+ spinlock_t xmit_lock;
+ spinlock_t recv_lock;
+ unsigned long xmit_flags;
u32 xaccm[8];
u32 raccm;
unsigned int bytes_sent;
unsigned int bytes_rcvd;
struct sk_buff *tpkt;
- struct sk_buff_head xq;
unsigned long last_xmit;
struct sk_buff *rpkt;
- struct sk_buff_head rq;
- wait_queue_head_t rwait;
struct ppp_channel chan; /* interface to generic ppp layer */
- int connected;
};
-/* Bit numbers in busy */
-#define XMIT_BUSY 0
-#define RECV_BUSY 1
-#define XMIT_WAKEUP 2
-#define XMIT_FULL 3
+/* Bit numbers in xmit_flags */
+#define XMIT_WAKEUP 0
+#define XMIT_FULL 1
/* Bits in rbits */
#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
/*
* Prototypes.
*/
-static struct sk_buff* ppp_sync_txdequeue(struct syncppp *ap);
+static struct sk_buff* ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *);
static int ppp_sync_send(struct ppp_channel *chan, struct sk_buff *skb);
+static int ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd,
+ unsigned long arg);
static int ppp_sync_push(struct syncppp *ap);
static void ppp_sync_flush_output(struct syncppp *ap);
static void ppp_sync_input(struct syncppp *ap, const unsigned char *buf,
- char *flags, int count);
+ char *flags, int count);
struct ppp_channel_ops sync_ops = {
- ppp_sync_send
+ ppp_sync_send,
+ ppp_sync_ioctl
};
/*
}
}
-/*
- * Routines for locking and unlocking the transmit and receive paths.
- */
-static inline void
-lock_path(struct syncppp *ap, int bit)
-{
- do {
- while (test_bit(bit, &ap->busy))
- mb();
- } while (test_and_set_bit(bit, &ap->busy));
- mb();
-}
-
-static inline int
-trylock_path(struct syncppp *ap, int bit)
-{
- if (test_and_set_bit(bit, &ap->busy))
- return 0;
- mb();
- return 1;
-}
-
-static inline void
-unlock_path(struct syncppp *ap, int bit)
-{
- mb();
- clear_bit(bit, &ap->busy);
-}
-
-#define lock_xmit_path(ap) lock_path(ap, XMIT_BUSY)
-#define trylock_xmit_path(ap) trylock_path(ap, XMIT_BUSY)
-#define unlock_xmit_path(ap) unlock_path(ap, XMIT_BUSY)
-#define lock_recv_path(ap) lock_path(ap, RECV_BUSY)
-#define trylock_recv_path(ap) trylock_path(ap, RECV_BUSY)
-#define unlock_recv_path(ap) unlock_path(ap, RECV_BUSY)
-
-static inline void
-flush_skb_queue(struct sk_buff_head *q)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(q)) != 0)
- kfree_skb(skb);
-}
/*
* Routines implementing the synchronous PPP line discipline.
ppp_sync_open(struct tty_struct *tty)
{
struct syncppp *ap;
+ int err;
ap = kmalloc(sizeof(*ap), GFP_KERNEL);
if (ap == 0)
return -ENOMEM;
- MOD_INC_USE_COUNT;
-
/* initialize the syncppp structure */
memset(ap, 0, sizeof(*ap));
ap->tty = tty;
ap->mru = PPP_MRU;
+ spin_lock_init(&ap->xmit_lock);
+ spin_lock_init(&ap->recv_lock);
ap->xaccm[0] = ~0U;
ap->xaccm[3] = 0x60000000U;
ap->raccm = ~0U;
- skb_queue_head_init(&ap->xq);
- skb_queue_head_init(&ap->rq);
- init_waitqueue_head(&ap->rwait);
+
+ ap->chan.private = ap;
+ ap->chan.ops = &sync_ops;
+ ap->chan.mtu = PPP_MRU;
+ err = ppp_register_channel(&ap->chan);
+ if (err) {
+ kfree(ap);
+ return err;
+ }
tty->disc_data = ap;
+ MOD_INC_USE_COUNT;
return 0;
}
if (ap == 0)
return;
tty->disc_data = 0;
- lock_xmit_path(ap);
- lock_recv_path(ap);
+ ppp_unregister_channel(&ap->chan);
if (ap->rpkt != 0)
kfree_skb(ap->rpkt);
- flush_skb_queue(&ap->rq);
if (ap->tpkt != 0)
kfree_skb(ap->tpkt);
- flush_skb_queue(&ap->xq);
- if (ap->connected)
- ppp_unregister_channel(&ap->chan);
kfree(ap);
MOD_DEC_USE_COUNT;
}
/*
- * Read a PPP frame. pppd can use this to negotiate over the
- * channel before it joins it to a bundle.
+ * Read a PPP frame, for compatibility until pppd is updated.
*/
static ssize_t
ppp_sync_read(struct tty_struct *tty, struct file *file,
unsigned char *buf, size_t count)
{
struct syncppp *ap = tty->disc_data;
- DECLARE_WAITQUEUE(wait, current);
- ssize_t ret;
- struct sk_buff *skb = 0;
- ret = -ENXIO;
if (ap == 0)
- goto out; /* should never happen */
-
- add_wait_queue(&ap->rwait, &wait);
- current->state = TASK_INTERRUPTIBLE;
- for (;;) {
- ret = -EAGAIN;
- skb = skb_dequeue(&ap->rq);
- if (skb)
- break;
- if (file->f_flags & O_NONBLOCK)
- break;
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- break;
- schedule();
- }
- current->state = TASK_RUNNING;
- remove_wait_queue(&ap->rwait, &wait);
-
- if (skb == 0)
- goto out;
-
- ret = -EOVERFLOW;
- if (skb->len > count)
- goto outf;
- ret = -EFAULT;
- if (copy_to_user(buf, skb->data, skb->len))
- goto outf;
- ret = skb->len;
-
- outf:
- kfree_skb(skb);
- out:
- return ret;
+ return -ENXIO;
+ return ppp_channel_read(&ap->chan, file, buf, count);
}
/*
- * Write a ppp frame. pppd can use this to send frames over
- * this particular channel.
+ * Write a ppp frame, for compatibility until pppd is updated.
*/
static ssize_t
ppp_sync_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t count)
{
struct syncppp *ap = tty->disc_data;
- struct sk_buff *skb;
- ssize_t ret;
- ret = -ENXIO;
if (ap == 0)
- goto out; /* should never happen */
-
- ret = -ENOMEM;
- skb = alloc_skb(count + 2, GFP_KERNEL);
- if (skb == 0)
- goto out;
- skb_reserve(skb, 2);
- ret = -EFAULT;
- if (copy_from_user(skb_put(skb, count), buf, count)) {
- kfree_skb(skb);
- goto out;
- }
-
- skb_queue_tail(&ap->xq, skb);
- ppp_sync_push(ap);
-
- ret = count;
-
- out:
- return ret;
+ return -ENXIO;
+ return ppp_channel_write(&ap->chan, buf, count);
}
static int
-ppp_sync_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
+ppp_synctty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct syncppp *ap = tty->disc_data;
int err, val;
- u32 accm[8];
- struct sk_buff *skb;
-
- err = -ENXIO;
- if (ap == 0)
- goto out; /* should never happen */
- err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
- goto out;
err = -EFAULT;
switch (cmd) {
- case PPPIOCGFLAGS:
- val = ap->flags | ap->rbits;
- if (put_user(val, (int *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSFLAGS:
- if (get_user(val, (int *) arg))
- break;
- ap->flags = val & ~SC_RCV_BITS;
- ap->rbits = val & SC_RCV_BITS;
- err = 0;
- break;
-
- case PPPIOCGASYNCMAP:
- if (put_user(ap->xaccm[0], (u32 *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSASYNCMAP:
- if (get_user(ap->xaccm[0], (u32 *) arg))
- break;
- err = 0;
- break;
-
- case PPPIOCGRASYNCMAP:
- if (put_user(ap->raccm, (u32 *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSRASYNCMAP:
- if (get_user(ap->raccm, (u32 *) arg))
- break;
- err = 0;
- break;
-
- case PPPIOCGXASYNCMAP:
- if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
- break;
- err = 0;
- break;
- case PPPIOCSXASYNCMAP:
- if (copy_from_user(accm, (void *) arg, sizeof(accm)))
- break;
- accm[2] &= ~0x40000000U; /* can't escape 0x5e */
- accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
- memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
- err = 0;
- break;
-
- case PPPIOCGMRU:
- if (put_user(ap->mru, (int *) arg))
- break;
- err = 0;
- break;
- case PPPIOCSMRU:
- if (get_user(val, (int *) arg))
- break;
- if (val < PPP_MRU)
- val = PPP_MRU;
- ap->mru = val;
- err = 0;
- break;
-
- case PPPIOCATTACH:
- if (get_user(val, (int *) arg))
- break;
- err = -EALREADY;
- if (ap->connected)
- break;
- ap->chan.private = ap;
- ap->chan.ops = &sync_ops;
- err = ppp_register_channel(&ap->chan);
- if (err != 0)
- break;
- ap->connected = 1;
- break;
- case PPPIOCDETACH:
+ case PPPIOCGUNIT:
err = -ENXIO;
- if (!ap->connected)
+ if (ap == 0)
+ break;
+ err = -EFAULT;
+ if (put_user(ppp_channel_index(&ap->chan), (int *) arg))
break;
- ppp_unregister_channel(&ap->chan);
- ap->connected = 0;
err = 0;
break;
case TCFLSH:
/* flush our buffers and the serial port's buffer */
- if (arg == TCIFLUSH || arg == TCIOFLUSH)
- flush_skb_queue(&ap->rq);
if (arg == TCIOFLUSH || arg == TCOFLUSH)
ppp_sync_flush_output(ap);
err = n_tty_ioctl(tty, file, cmd, arg);
case FIONREAD:
val = 0;
- if ((skb = skb_peek(&ap->rq)) != 0)
- val = skb->len;
if (put_user(val, (int *) arg))
break;
err = 0;
break;
+ /*
+ * Compatibility calls until pppd is updated.
+ */
+ case PPPIOCGFLAGS:
+ case PPPIOCSFLAGS:
+ case PPPIOCGASYNCMAP:
+ case PPPIOCSASYNCMAP:
+ case PPPIOCGRASYNCMAP:
+ case PPPIOCSRASYNCMAP:
+ case PPPIOCGXASYNCMAP:
+ case PPPIOCSXASYNCMAP:
+ case PPPIOCGMRU:
+ case PPPIOCSMRU:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ break;
+ err = ppp_sync_ioctl(&ap->chan, cmd, arg);
+ break;
+
+ case PPPIOCATTACH:
+ case PPPIOCDETACH:
+ err = ppp_channel_ioctl(&ap->chan, cmd, arg);
+ break;
+
default:
err = -ENOIOCTLCMD;
}
- out:
+
return err;
}
struct syncppp *ap = tty->disc_data;
unsigned int mask;
- if (ap == 0)
- return 0; /* should never happen */
- poll_wait(file, &ap->rwait, wait);
mask = POLLOUT | POLLWRNORM;
- if (skb_peek(&ap->rq))
- mask |= POLLIN | POLLRDNORM;
+ /* compatibility for old pppd */
+ if (ap != 0)
+ mask |= ppp_channel_poll(&ap->chan, file, wait);
if (test_bit(TTY_OTHER_CLOSED, &tty->flags) || tty_hung_up_p(file))
mask |= POLLHUP;
return mask;
if (ap == 0)
return;
- trylock_recv_path(ap);
+ spin_lock_bh(&ap->recv_lock);
ppp_sync_input(ap, buf, flags, count);
- unlock_recv_path(ap);
+ spin_unlock_bh(&ap->recv_lock);
if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
&& tty->driver.unthrottle)
tty->driver.unthrottle(tty);
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (ap == 0)
return;
- if (ppp_sync_push(ap) && ap->connected)
+ if (ppp_sync_push(ap))
ppp_output_wakeup(&ap->chan);
}
close: ppp_sync_close,
read: ppp_sync_read,
write: ppp_sync_write,
- ioctl: ppp_sync_ioctl,
+ ioctl: ppp_synctty_ioctl,
poll: ppp_sync_poll,
receive_room: ppp_sync_room,
receive_buf: ppp_sync_receive,
return err;
}
+/*
+ * The following routines provide the PPP channel interface.
+ */
+static int
+ppp_sync_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg)
+{
+ struct syncppp *ap = chan->private;
+ int err, val;
+ u32 accm[8];
+
+ err = -EFAULT;
+ switch (cmd) {
+ case PPPIOCGFLAGS:
+ val = ap->flags | ap->rbits;
+ if (put_user(val, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSFLAGS:
+ if (get_user(val, (int *) arg))
+ break;
+ ap->flags = val & ~SC_RCV_BITS;
+ spin_lock_bh(&ap->recv_lock);
+ ap->rbits = val & SC_RCV_BITS;
+ spin_unlock_bh(&ap->recv_lock);
+ err = 0;
+ break;
+
+ case PPPIOCGASYNCMAP:
+ if (put_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSASYNCMAP:
+ if (get_user(ap->xaccm[0], (u32 *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCGRASYNCMAP:
+ if (put_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSRASYNCMAP:
+ if (get_user(ap->raccm, (u32 *) arg))
+ break;
+ err = 0;
+ break;
+
+ case PPPIOCGXASYNCMAP:
+ if (copy_to_user((void *) arg, ap->xaccm, sizeof(ap->xaccm)))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSXASYNCMAP:
+ if (copy_from_user(accm, (void *) arg, sizeof(accm)))
+ break;
+ accm[2] &= ~0x40000000U; /* can't escape 0x5e */
+ accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
+ memcpy(ap->xaccm, accm, sizeof(ap->xaccm));
+ err = 0;
+ break;
+
+ case PPPIOCGMRU:
+ if (put_user(ap->mru, (int *) arg))
+ break;
+ err = 0;
+ break;
+ case PPPIOCSMRU:
+ if (get_user(val, (int *) arg))
+ break;
+ if (val < PPP_MRU)
+ val = PPP_MRU;
+ ap->mru = val;
+ err = 0;
+ break;
+
+ default:
+ err = -ENOTTY;
+ }
+ return err;
+}
+
/*
* Procedures for encapsulation and framing.
*/
struct sk_buff*
-ppp_sync_txdequeue(struct syncppp *ap)
+ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb)
{
int proto;
unsigned char *data;
int islcp;
- struct sk_buff *skb;
- while ((skb = skb_dequeue(&ap->xq)) != NULL) {
-
- data = skb->data;
- proto = (data[0] << 8) + data[1];
-
- /* LCP packets with codes between 1 (configure-request)
- * and 7 (code-reject) must be sent as though no options
- * have been negotiated.
- */
- islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
-
- /* compress protocol field if option enabled */
- if (data[0] == 0 && (ap->flags & SC_COMP_PROT) && !islcp)
- skb_pull(skb,1);
-
- /* prepend address/control fields if necessary */
- if ((ap->flags & SC_COMP_AC) == 0 || islcp) {
- if (skb_headroom(skb) < 2) {
- struct sk_buff *npkt = dev_alloc_skb(skb->len + 2);
- if (npkt == NULL) {
- kfree_skb(skb);
- continue;
- }
- skb_reserve(npkt,2);
- memcpy(skb_put(npkt,skb->len), skb->data, skb->len);
+ data = skb->data;
+ proto = (data[0] << 8) + data[1];
+
+ /* LCP packets with codes between 1 (configure-request)
+ * and 7 (code-reject) must be sent as though no options
+ * have been negotiated.
+ */
+ islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7;
+
+ /* compress protocol field if option enabled */
+ if (data[0] == 0 && (ap->flags & SC_COMP_PROT) && !islcp)
+ skb_pull(skb,1);
+
+ /* prepend address/control fields if necessary */
+ if ((ap->flags & SC_COMP_AC) == 0 || islcp) {
+ if (skb_headroom(skb) < 2) {
+ struct sk_buff *npkt = dev_alloc_skb(skb->len + 2);
+ if (npkt == NULL) {
kfree_skb(skb);
- skb = npkt;
+ return NULL;
}
- skb_push(skb,2);
- skb->data[0] = PPP_ALLSTATIONS;
- skb->data[1] = PPP_UI;
+ skb_reserve(npkt,2);
+ memcpy(skb_put(npkt,skb->len), skb->data, skb->len);
+ kfree_skb(skb);
+ skb = npkt;
}
-
- ap->last_xmit = jiffies;
- break;
+ skb_push(skb,2);
+ skb->data[0] = PPP_ALLSTATIONS;
+ skb->data[1] = PPP_UI;
}
+ ap->last_xmit = jiffies;
+
if (skb && ap->flags & SC_LOG_OUTPKT)
ppp_print_buffer ("send buffer", skb->data, skb->len);
ppp_sync_push(ap);
- if (test_and_set_bit(XMIT_FULL, &ap->busy))
+ if (test_and_set_bit(XMIT_FULL, &ap->xmit_flags))
return 0; /* already full */
- skb_queue_head(&ap->xq,skb);
+ skb = ppp_sync_txmunge(ap, skb);
+ if (skb != NULL)
+ ap->tpkt = skb;
+ else
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
ppp_sync_push(ap);
return 1;
struct tty_struct *tty = ap->tty;
int tty_stuffed = 0;
- if (!trylock_xmit_path(ap)) {
- set_bit(XMIT_WAKEUP, &ap->busy);
+ set_bit(XMIT_WAKEUP, &ap->xmit_flags);
+ if (!spin_trylock_bh(&ap->xmit_lock))
return 0;
- }
for (;;) {
- if (test_and_clear_bit(XMIT_WAKEUP, &ap->busy))
+ if (test_and_clear_bit(XMIT_WAKEUP, &ap->xmit_flags))
tty_stuffed = 0;
- if (ap->tpkt == 0) {
- if ((ap->tpkt = ppp_sync_txdequeue(ap)) == 0) {
- clear_bit(XMIT_FULL, &ap->busy);
- done = 1;
- }
- }
- if (!tty_stuffed && ap->tpkt != NULL) {
+ if (!tty_stuffed && ap->tpkt != 0) {
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
sent = tty->driver.write(tty, 0, ap->tpkt->data, ap->tpkt->len);
if (sent < 0)
} else {
kfree_skb(ap->tpkt);
ap->tpkt = 0;
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
+ done = 1;
}
continue;
}
/* haven't made any progress */
- unlock_xmit_path(ap);
- if (!(test_bit(XMIT_WAKEUP, &ap->busy)
+ spin_unlock_bh(&ap->xmit_lock);
+ if (!(test_bit(XMIT_WAKEUP, &ap->xmit_flags)
|| (!tty_stuffed && ap->tpkt != 0)))
break;
- if (!trylock_xmit_path(ap))
+ if (!spin_trylock_bh(&ap->xmit_lock))
break;
}
return done;
if (ap->tpkt != 0) {
kfree_skb(ap->tpkt);
ap->tpkt = 0;
- clear_bit(XMIT_FULL, &ap->busy);
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
done = 1;
}
- unlock_xmit_path(ap);
+ spin_unlock_bh(&ap->xmit_lock);
return done;
}
{
int done = 0;
- flush_skb_queue(&ap->xq);
- lock_xmit_path(ap);
+ spin_lock_bh(&ap->xmit_lock);
if (ap->tpkt != NULL) {
kfree_skb(ap->tpkt);
ap->tpkt = 0;
- clear_bit(XMIT_FULL, &ap->busy);
+ clear_bit(XMIT_FULL, &ap->xmit_flags);
done = 1;
}
- unlock_xmit_path(ap);
- if (done && ap->connected)
+ spin_unlock_bh(&ap->xmit_lock);
+ if (done)
ppp_output_wakeup(&ap->chan);
}
} else if (skb->len < 2)
goto err;
- /* pass to generic layer or queue it */
- if (ap->connected) {
- ppp_input(&ap->chan, skb);
- } else {
- skb_queue_tail(&ap->rq, skb);
- /* drop old frames if queue too long */
- while (ap->rq.qlen > PPPSYNC_MAX_RQLEN
- && (skb = skb_dequeue(&ap->rq)) != 0)
- kfree(skb);
- wake_up_interruptible(&ap->rwait);
- }
+ /* pass to generic layer */
+ ppp_input(&ap->chan, skb);
return;
err:
kfree_skb(skb);
- if (ap->connected)
- ppp_input_error(&ap->chan, code);
-}
-
-static inline void
-input_error(struct syncppp *ap, int code)
-{
- if (ap->connected)
- ppp_input_error(&ap->chan, code);
+ ppp_input_error(&ap->chan, code);
}
/* called when the tty driver has data for us.
/* if flag set, then error, ignore frame */
if (flags != 0 && *flags) {
- input_error(ap, *flags);
+ ppp_input_error(&ap->chan, *flags);
return;
}
if ((skb = ap->rpkt) == 0) {
if ((skb = dev_alloc_skb(ap->mru + PPP_HDRLEN + 2)) == 0) {
printk(KERN_ERR "PPPsync: no memory (input pkt)\n");
- input_error(ap, 0);
+ ppp_input_error(&ap->chan, 0);
return;
}
/* Try to get the payload 4-byte aligned */
}
if (count > skb_tailroom(skb)) {
/* packet overflowed MRU */
- input_error(ap, 1);
+ ppp_input_error(&ap->chan, 1);
} else {
sp = skb_put(skb, count);
memcpy(sp, buf, count);
}
}
-#ifdef MODULE
-int
-init_module(void)
-{
- return ppp_sync_init();
-}
-
-void
-cleanup_module(void)
+void __exit
+ppp_sync_cleanup(void)
{
if (tty_register_ldisc(N_SYNC_PPP, NULL) != 0)
printk(KERN_ERR "failed to unregister Sync PPP line discipline\n");
}
-#endif /* MODULE */
+
+module_init(ppp_sync_init);
+module_exit(ppp_sync_cleanup);
pci_set_master(pdev);
-#ifdef __sparc__
- /* Set the proper cache line size value, plus enable
- * write-invalidate and fast back-to-back on Sparc.
- */
- {
- SK_U16 pci_command;
-
- SkPciWriteCfgByte(pAC, PCI_CACHE_LINE_SIZE, 0x10);
-
- SkPciReadCfgWord(pAC, PCI_COMMAND, &pci_command);
- pci_command |= (PCI_COMMAND_INVALIDATE | PCI_COMMAND_FAST_BACK);
- SkPciWriteCfgWord(pAC, PCI_COMMAND, pci_command);
- }
-#endif
-
base_address = pdev->resource[0].start;
#ifdef SK_BIG_ENDIAN
-/* $Id: sunhme.c,v 1.94 2000/03/15 06:47:04 davem Exp $
+/* $Id: sunhme.c,v 1.95 2000/03/25 05:18:15 davem Exp $
* sunhme.c: Sparc HME/BigMac 10/100baseT half/full duplex auto switching,
* auto carrier detecting ethernet driver. Also known as the
* "Happy Meal Ethernet" found on SunSwift SBUS cards.
struct pcidev_cookie *pcp;
struct happy_meal *hp;
unsigned long hpreg_base;
- unsigned short pci_command;
int i, node, qfe_slot = -1;
char prom_name[64];
ether_setup(dev);
- /* If we don't do this, nothing works. */
- pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
- pci_command |= PCI_COMMAND_MASTER;
- pci_write_config_word(pdev, PCI_COMMAND, pci_command);
-
- /* Set the latency timer and cache line size as well,
- * PROM leaves it at zero.
- */
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
-#ifdef __sparc_v9__
- /* NOTE: Cache line size is in 32-bit word units. */
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x10);
-#endif
-
#ifdef MODULE
/* We are home free at this point, link us in to the happy
* module device list.
+2000-03-27 Tim Waugh <twaugh@redhat.com>
+
+ * parport_pc.c (parport_pc_ecp_read_block_pio): Correct operation
+ when peripheral is trying to send data when we stop listening.
+
2000-03-22 Tim Waugh <twaugh@redhat.com>
* init.c (parport_setup): Fix return value.
int cnfga;
const struct parport_pc_private *priv = p->physport->private_data;
- /* Prevent further data transfer. */
- frob_econtrol (p, 0xe0, ECR_TST << 5);
-
/* Adjust for the contents of the FIFO. */
for (residue = priv->fifo_depth; ; residue--) {
if (inb (ECONTROL (p)) & 0x2)
length, flags);
/* Switch to reverse mode if necessary. */
- if (port->ieee1284.phase != IEEE1284_PH_REV_IDLE) {
+ if ((port->ieee1284.phase != IEEE1284_PH_REV_IDLE) &&
+ (port->ieee1284.phase != IEEE1284_PH_REV_DATA)) {
/* Event 38: Set nAutoFd low */
parport_frob_control (port,
PARPORT_CONTROL_AUTOFD,
parport_wait_peripheral (port, PARPORT_STATUS_PAPEROUT, 0);
}
- /* Set up ECP parallel port mode.*/
+ /* Set up ECP FIFO mode.*/
parport_pc_data_reverse (port); /* Must be in PS2 mode */
parport_pc_frob_control (port,
PARPORT_CONTROL_STROBE |
left--;
}
+ port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
+
+ /* Go to forward idle mode to shut the peripheral up. */
+ parport_frob_control (port, PARPORT_CONTROL_INIT, 0);
+ parport_wait_peripheral (port,
+ PARPORT_STATUS_PAPEROUT,
+ PARPORT_STATUS_PAPEROUT);
+ port->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
+
/* Finish up. */
- if (change_mode (port, ECR_PS2) == -EBUSY) {
+ {
int lost = get_fifo_residue (port);
- printk (KERN_DEBUG "%s: DATA LOSS (%d bytes)!\n", port->name,
- lost);
+ if (lost)
+ /* Shouldn't happen with compliant peripherals. */
+ printk (KERN_DEBUG "%s: DATA LOSS (%d bytes)!\n",
+ port->name, lost);
}
- port->ieee1284.phase = IEEE1284_PH_REV_IDLE;
-
return length - left;
}
/*
* Generate devlist.h and classlist.h from the PCI ID file.
*
- * (c) 1999 Martin Mares <mj@suse.cz>
+ * (c) 1999--2000 Martin Mares <mj@suse.cz>
*/
#include <stdio.h>
#include <string.h>
+#define MAX_NAME_SIZE 79
+
static void
pq(FILE *f, const char *c)
{
int
main(void)
{
- char line[1024], *c, vend[8];
+ char line[1024], *c, *bra, vend[8];
int vendors = 0;
int mode = 0;
+ int lino = 0;
+ int vendor_len = 0;
FILE *devf, *clsf;
devf = fopen("devlist.h", "w");
}
while (fgets(line, sizeof(line)-1, stdin)) {
+ lino++;
if ((c = strchr(line, '\n')))
*c = 0;
if (!line[0] || line[0] == '#')
c = line + 5;
while (*c == ' ')
*c++ = 0;
+ if (vendor_len + strlen(c) + 1 > MAX_NAME_SIZE) {
+ /* Too long, try cutting off long description */
+ bra = strchr(c, '[');
+ if (bra && bra > c && bra[-1] == ' ')
+ bra[-1] = 0;
+ if (vendor_len + strlen(c) + 1 > MAX_NAME_SIZE) {
+ fprintf(stderr, "Line %d: Device name too long\n", lino);
+ return 1;
+ }
+ }
fprintf(devf, "\tDEVICE(%s,%s,\"", vend, line+1);
pq(devf, c);
fputs("\")\n", devf);
fputs("ENDVENDOR()\n\n", devf);
vendors++;
strcpy(vend, line);
+ vendor_len = strlen(c);
+ if (vendor_len + 24 > MAX_NAME_SIZE) {
+ fprintf(stderr, "Line %d: Vendor name too long\n", lino);
+ return 1;
+ }
fprintf(devf, "VENDOR(%s,\"", vend);
pq(devf, c);
fputs("\")\n", devf);
mode = 1;
} else {
err:
- fprintf(stderr, "Syntax error in mode %d: %s\n", mode, line);
+ fprintf(stderr, "Line %d: Syntax error in mode %d: %s\n", lino, mode, line);
return 1;
}
}
10db Rohm LSI Systems, Inc.
10dc CERN/ECP/EDU
0001 STAR/RD24 SCI-PCI (PMC)
- 0002 TAR/RD24 SCI-PCI (PMC) [ATT 2C15-3 (FPGA) SCI bridge on PCI 5 Volt card]
+ 0002 TAR/RD24 SCI-PCI (PMC)
0021 HIPPI destination
0022 HIPPI source
10dc ATT2C15-3 FPGA
8089 Kingsberg Spacetec Serial Output Board
809c S5933_HEPC3
811a PCI-IEEE1355-DS-DE Interface
- 8170 S5933 "Matchmaker" PCI Chipset Development Tool
+ 8170 S5933 "Matchmaker" [PCI Chipset Development Tool]
10e9 Alps Electric Co., Ltd.
10ea Intergraphics Systems
1680 IGA-1680
return 0;
}
-static void
+void
pdev_assign_unassigned_resources(struct pci_dev *dev)
{
u32 reg;
bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS
int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
fi
-dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI
+if [ "$CONFIG_X86" = "y" ]; then
+ dep_tristate 'IBM ServeRAID support' CONFIG_SCSI_IPS $CONFIG_SCSI
+fi
dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then
bool ' Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT
fi
+dep_tristate 'DMX3191D SCSI support' CONFIG_SCSI_DMX3191D $CONFIG_SCSI $CONFIG_PCI
dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI
dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support' CONFIG_SCSI_EATA $CONFIG_SCSI
if [ "$CONFIG_SCSI_EATA" != "n" ]; then
"Port CONFIG_SCSI_G_NCR5380_PORT \
Memory CONFIG_SCSI_G_NCR5380_MEM" Port
fi
-if [ "$CONFIG_PCI" = "y" ]; then
- dep_tristate 'Initio 9100U(W) support' CONFIG_SCSI_INITIO $CONFIG_SCSI
- dep_tristate 'Initio INI-A100U2W support' CONFIG_SCSI_INIA100 $CONFIG_SCSI
-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
if [ "$CONFIG_PARPORT" != "n" ]; then
dep_tristate 'IOMEGA parallel port (ppa - older drives)' CONFIG_SCSI_PPA $CONFIG_SCSI $CONFIG_PARPORT
dep_tristate 'IOMEGA parallel port (imm - newer drives)' CONFIG_SCSI_IMM $CONFIG_SCSI $CONFIG_PARPORT
dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI
dep_tristate 'symbios 53c416 SCSI support' CONFIG_SCSI_SYM53C416 $CONFIG_SCSI
dep_tristate 'Simple 53c710 SCSI support (Compaq, NCR machines)' CONFIG_SCSI_SIM710 $CONFIG_SCSI
-if [ "$CONFIG_PCI" = "y" ]; then
- dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI
- if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then
- bool ' always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync
- bool ' allow FAST-SCSI [10MHz]' CONFIG_SCSI_NCR53C7xx_FAST
- bool ' allow DISCONNECT' CONFIG_SCSI_NCR53C7xx_DISCONNECT
- fi
+dep_tristate 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI $CONFIG_PCI
+if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then
+ bool ' always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync
+ bool ' allow FAST-SCSI [10MHz]' CONFIG_SCSI_NCR53C7xx_FAST
+ bool ' allow DISCONNECT' CONFIG_SCSI_NCR53C7xx_DISCONNECT
fi
if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then
dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
dep_tristate 'Qlogic ISP FC SCSI support' CONFIG_SCSI_QLOGIC_FC $CONFIG_SCSI
dep_tristate 'Qlogic QLA 1280 SCSI support' CONFIG_SCSI_QLOGIC_1280 $CONFIG_SCSI
fi
-dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI
+if [ "$CONFIG_X86" = "y" ]; then
+ dep_tristate 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE $CONFIG_SCSI
+fi
if [ "$CONFIG_PCI" = "y" ]; then
dep_tristate 'Tekram DC390(T) and Am53/79C974 SCSI support' CONFIG_SCSI_DC390T $CONFIG_SCSI
if [ "$CONFIG_SCSI_DC390T" != "n" ]; then
bool ' enable elevator sorting' CONFIG_SCSI_U14_34F_LINKED_COMMANDS
int ' maximum number of queued commands' CONFIG_SCSI_U14_34F_MAX_TAGS 8
fi
-dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI
+if [ "$CONFIG_X86" = "y" ]; then
+ dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI
+fi
#
# Note - this is a very special 'host' adapter that simulates the presence of some disks.
# It can come in very handy for troubleshooting. Anyone else is welcome to use it - all
endif
endif
+ifeq ($(CONFIG_SCSI_DMX3191D),y)
+L_OBJS += dmx3191d.o
+else
+ ifeq ($(CONFIG_SCSI_DMX3191D),m)
+ M_OBJS += dmx3191d.o
+ endif
+endif
+
ifeq ($(CONFIG_SCSI_DTC3280),y)
L_OBJS += dtc.o
else
--- /dev/null
+
+/*
+ dmx3191d.c - midlevel driver for the Domex DMX3191D SCSI card.
+ Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
+
+ Based on the generic NCR5380 driver by Drew Eckhardt et al.
+
+ 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 <asm/io.h>
+#include <asm/system.h>
+#include <linux/blk.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/stat.h>
+#include <linux/version.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "constants.h"
+#include "sd.h"
+
+#include "dmx3191d.h"
+
+/* play with these values to tune up your system performances */
+/* default setting from g_NCR5380.c */
+/*
+#define USLEEP
+#define USLEEP_POLL 1
+#define USLEEP_SLEEP 20
+#define USLEEP_WAITLONG 500
+*/
+
+#define AUTOSENSE
+#include "NCR5380.h"
+#include "NCR5380.c"
+
+
+int __init dmx3191d_detect(Scsi_Host_Template *tmpl) {
+ int boards = 0;
+ struct Scsi_Host *instance = NULL;
+ struct pci_dev *pdev = NULL;
+
+ if (!pci_present()) {
+ dmx3191d_printk("PCI support not enabled\n");
+ return 0;
+ }
+
+ tmpl->proc_name = DMX3191D_DRIVER_NAME;
+
+ while ((pdev = pci_find_device(PCI_VENDOR_ID_DOMEX,
+ PCI_DEVICE_ID_DOMEX_DMX3191D, pdev))) {
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+ unsigned long port = pdev->base_address[0] & PCI_IOADDRESS_MASK;
+#else
+ unsigned long port = pdev->resource[0].start;
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) */
+
+ if (check_region(port, DMX3191D_REGION)) {
+ dmx3191d_printk("region 0x%lx-0x%lx already reserved\n",
+ port, port + DMX3191D_REGION);
+ continue;
+ }
+
+ request_region(port, DMX3191D_REGION, DMX3191D_DRIVER_NAME);
+
+ instance = scsi_register(tmpl, sizeof(struct NCR5380_hostdata));
+ instance->io_port = port;
+ instance->irq = pdev->irq;
+ NCR5380_init(instance, FLAG_NO_PSEUDO_DMA | FLAG_DTC3181E);
+
+ if (request_irq(pdev->irq, dmx3191d_do_intr, SA_SHIRQ,
+ DMX3191D_DRIVER_NAME, instance)) {
+ dmx3191d_printk("irq %d not available\n", pdev->irq);
+ /* Steam powered scsi controllers run without an IRQ
+ anyway */
+ instance->irq = IRQ_NONE;
+ }
+
+ boards++;
+ }
+ return boards;
+}
+
+const char * dmx3191d_info(struct Scsi_Host *host) {
+ static const char *info ="Domex DMX3191D";
+
+ return info;
+}
+
+int dmx3191d_release_resources(struct Scsi_Host *instance)
+{
+ release_region(instance->io_port, DMX3191D_REGION);
+ if(instance->irq!=IRQ_NONE)
+ free_irq(instance->irq, instance);
+
+ return 0;
+}
+
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = DMX3191D;
+
+#include "scsi_module.c"
+
+#endif /* MODULE */
+
--- /dev/null
+
+/*
+ dmx3191d.h - defines for the Domex DMX3191D SCSI card.
+ Copyright (C) 2000 by Massimo Piccioni <dafastidio@libero.it>
+
+ 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.
+*/
+
+#ifndef __DMX3191D_H
+#define __DMX3191D_H
+
+#define DMX3191D_DRIVER_NAME "dmx3191d"
+#define DMX3191D_REGION 8
+
+#ifndef PCI_VENDOR_ID_DOMEX
+#define PCI_VENDOR_ID_DOMEX 0x134a
+#define PCI_DEVICE_ID_DOMEX_DMX3191D 0x0001
+#endif
+
+#define dmx3191d_printk( args... ) printk(__FILE__": " ##args)
+
+#ifndef ASM
+int dmx3191d_abort(Scsi_Cmnd *);
+int dmx3191d_detect(Scsi_Host_Template *);
+const char* dmx3191d_info(struct Scsi_Host *);
+int dmx3191d_proc_info(char *, char **, off_t, int, int, int);
+int dmx3191d_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int dmx3191d_release_resources(struct Scsi_Host *);
+int dmx3191d_reset(Scsi_Cmnd *, unsigned int);
+
+
+#if defined(HOSTS_C) || defined(MODULE)
+#define DMX3191D { \
+ proc_info: dmx3191d_proc_info, \
+ name: "Domex DMX3191D", \
+ detect: dmx3191d_detect, \
+ release: dmx3191d_release_resources, \
+ info: dmx3191d_info, \
+ queuecommand: dmx3191d_queue_command, \
+ abort: dmx3191d_abort, \
+ reset: dmx3191d_reset, \
+ bios_param: NULL, \
+ can_queue: 32, \
+ this_id: 7, \
+ sg_tablesize: SG_ALL, \
+ cmd_per_lun: 2, \
+ use_clustering: DISABLE_CLUSTERING \
+}
+#endif /* HOSTS_C || MODULE */
+
+
+#ifndef HOSTS_C
+#define NCR5380_read(reg) inb(port + reg)
+#define NCR5380_write(reg, value) outb(value, port + reg)
+
+#define NCR5380_implementation_fields unsigned int port
+#define NCR5380_local_declare() NCR5380_implementation_fields
+#define NCR5380_setup(instance) port = instance->io_port
+
+#define NCR5380_abort dmx3191d_abort
+#define do_NCR5380_intr dmx3191d_do_intr
+#define NCR5380_intr dmx3191d_intr
+#define NCR5380_proc_info dmx3191d_proc_info
+#define NCR5380_queue_command dmx3191d_queue_command
+#define NCR5380_reset dmx3191d_reset
+
+#endif /* HOSTS_C */
+#endif /* ASM */
+
+#endif /* __DMX3191D_H */
+
#include "t128.h"
#endif
+#ifdef CONFIG_SCSI_DMX3191D
+#include "dmx3191d.h"
+#endif
+
#ifdef CONFIG_SCSI_DTC3280
#include "dtc.h"
#endif
#ifdef CONFIG_SCSI_T128
TRANTOR_T128,
#endif
+#ifdef CONFIG_SCSI_DMX3191D
+ DMX3191D,
+#endif
#ifdef CONFIG_SCSI_DTC3280
DTC3x80,
#endif
return 1;
}
-#ifdef __sparc__
- command |= (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY|
- PCI_COMMAND_INVALIDATE|PCI_COMMAND_SERR);
- pci_write_config_word(pdev, PCI_COMMAND, command);
- pci_read_config_word(pdev, PCI_COMMAND, &command);
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 16);
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
-#endif
#ifdef __alpha__
/* Force ALPHA to use bus I/O and not bus MEM.
This is to avoid having to use HAE_MEM registers,
struct Scsi_Host * host;
Scsi_Device * SDev;
int diskinfo[4];
- struct hd_geometry *loc = (struct hd_geometry *) arg;
SDev = rscsi_disks[DEVICE_NR(dev)].device;
/*
switch (cmd)
{
case HDIO_GETGEO: /* Return BIOS disk parameters */
+ {
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
if(!loc)
return -EINVAL;
put_user(sd[SD_PARTITION(inode->i_rdev)].start_sect, &loc->start))
return -EFAULT;
return 0;
+ }
case BLKGETSIZE: /* Return device size */
if (!arg)
return -EINVAL;
* any later version.
*
*/
- static char * sg_version_str = "Version: 3.1.12 (20000222)";
- static int sg_version_num = 30112; /* 2 digits for each component */
+ static char * sg_version_str = "Version: 3.1.13 (20000323)";
+ static int sg_version_num = 30113; /* 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
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
static int sg_proc_init(void);
+#ifdef MODULE
static void sg_proc_cleanup(void);
#endif
+#endif
#ifndef LINUX_VERSION_CODE
#include <linux/version.h>
hp->cmd_len = (unsigned char)cmd_size;
hp->iovec_count = 0;
hp->mx_sb_len = 0;
+#if 1
+ hp->dxfer_direction = SG_DXFER_UNKNOWN;
+#else
if (input_size > 0)
hp->dxfer_direction = ((old_hdr.reply_len - size_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;
SCpnt->sc_data_direction = SCSI_DATA_READ; break;
case SG_DXFER_TO_DEV:
SCpnt->sc_data_direction = SCSI_DATA_WRITE; break;
+ case SG_DXFER_UNKNOWN:
+ SCpnt->sc_data_direction = SCSI_DATA_UNKNOWN; break;
default:
SCpnt->sc_data_direction = SCSI_DATA_NONE; break;
}
for(k = 0; k < sg_template.dev_max; k++)
if(! sg_dev_arr[k]) break;
- if(k >= sg_template.dev_max) panic ("sg_dev_arr corrupt");
-
- sdp = (Sg_device *)kmalloc(sizeof(Sg_device), GFP_ATOMIC);
+ if(k < sg_template.dev_max)
+ sdp = (Sg_device *)kmalloc(sizeof(Sg_device), GFP_ATOMIC);
+ else
+ sdp = NULL;
if (NULL == sdp) {
scsidp->attached--;
write_unlock_irqrestore(&sg_dev_arr_lock, flags);
Sg_fd * sfp = srp->parentfp;
sg_io_hdr_t * hp = &srp->header;
int dxfer_len = (int)hp->dxfer_len;
+ int dxfer_dir = hp->dxfer_direction;
Sg_scatter_hold * req_schp = &srp->data;
Sg_scatter_hold * rsv_schp = &sfp->reserve;
SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len));
- if ((hp->flags & SG_FLAG_DIRECT_IO) && (dxfer_len > 0) &&
- (hp->dxfer_direction != SG_DXFER_NONE) && (0 == hp->iovec_count) &&
+ 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) &&
(! 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 */
int num_xfer = 0;
int j, k, onum, usglen, ksglen, res, ok;
int iovec_count = (int)hp->iovec_count;
+ int dxfer_dir = hp->dxfer_direction;
unsigned char * p;
unsigned char * up;
int new_interface = ('\0' == hp->interface_id) ? 0 : 1;
- if ((SG_DXFER_TO_DEV == hp->dxfer_direction) ||
- (SG_DXFER_TO_FROM_DEV == hp->dxfer_direction)) {
+ if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_TO_DEV == dxfer_dir) ||
+ (SG_DXFER_TO_FROM_DEV == dxfer_dir)) {
num_xfer = (int)(new_interface ? hp->dxfer_len : hp->flags);
if (schp->bufflen < num_xfer)
num_xfer = schp->bufflen;
int num_xfer = 0;
int j, k, onum, usglen, ksglen, res, ok;
int iovec_count = (int)hp->iovec_count;
+ int dxfer_dir = hp->dxfer_direction;
unsigned char * p;
unsigned char * up;
int new_interface = ('\0' == hp->interface_id) ? 0 : 1;
- if ((SG_DXFER_FROM_DEV == hp->dxfer_direction) ||
- (SG_DXFER_TO_FROM_DEV == hp->dxfer_direction)) {
+ if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_FROM_DEV == dxfer_dir) ||
+ (SG_DXFER_TO_FROM_DEV == dxfer_dir)) {
num_xfer = hp->dxfer_len;
if (schp->bufflen < num_xfer)
num_xfer = schp->bufflen;
return 0;
}
+#ifdef MODULE
static void sg_proc_cleanup()
{
int k;
remove_proc_entry(sg_proc_leaf_names[k], sg_proc_sgp);
remove_proc_entry(sg_proc_sg_dirname, proc_scsi);
}
+#endif
static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset,
int size, int * eof, void * data)
}
read_lock(&sg_dev_arr_lock);
max_dev = sg_last_dev();
- PRINT_PROC("dev_max=%d max_active_device=%d (origin 1)\n",
+ PRINT_PROC("dev_max(currently)=%d max_active_device=%d (origin 1)\n",
sg_template.dev_max, max_dev);
PRINT_PROC(" scsi_dma_free_sectors=%u sg_pool_secs_aval=%d "
"def_reserved_size=%d\n",
}
dev = MINOR(sdp->i_rdev);
- PRINT_PROC(" >>> device=%d(sg%d) ", dev, dev);
- PRINT_PROC("scsi%d chan=%d id=%d lun=%d em=%d sg_tablesize=%d"
+ if ((fp = sdp->headfp)) {
+ PRINT_PROC(" >>> device=%d(sg%d) ", dev, 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);
- fp = sdp->headfp;
+ }
for (k = 1; fp; fp = fp->nextfp, ++k) {
PRINT_PROC(" FD(%d): timeout=%d bufflen=%d "
"(res)sgat=%d low_dma=%d\n",
while (srp) {
hp = &srp->header;
/* stop indenting so far ... */
- PRINT_PROC(srp->res_used ? " reserved_buff>> " :
+ PRINT_PROC(srp->res_used ? " rb>> " :
((SG_INFO_DIRECT_IO_MASK & hp->info) ? " dio>> " : " "));
blen = srp->my_cmdp ? srp->my_cmdp->bufflen : srp->data.bufflen;
usg = srp->my_cmdp ? srp->my_cmdp->use_sg : srp->data.k_use_sg;
PRINT_PROC(srp->done ? "rcv: id=%d" : (srp->my_cmdp ? "act: id=%d" :
"prior: id=%d"), srp->header.pack_id);
- if (! srp->res_used) PRINT_PROC(" blen=%d", blen);
+ PRINT_PROC(" blen=%d", blen);
if (srp->done)
PRINT_PROC(" dur=%d", sg_jif_to_ms(hp->duration));
else
#endif
#endif /* __powerpc__ */
-#ifdef __sparc__
- /*
- ** Fix-ups for sparc.
- */
- if (!cache_line_size)
- suggested_cache_line_size = 16;
-
- driver_setup.pci_fix_up |= 0x7;
-#endif /* __sparc__ */
-
#if defined(__i386__) && !defined(MODULE)
if (!cache_line_size) {
#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
setup_used[i] = 0;
done_setup = 1;
- return 0;
+ return 1;
}
__setup("wd33c93", wd33c93_setup);
static int __initdata dma2 = -1;
#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
-struct pci_dev *ad1816_dev = NULL,
- *mpu_dev = NULL;
+struct pci_dev *ad1816_dev = NULL;
static int activated = 1;
mss_base = ints[4];
mpu_base = ints[5];
mpu_irq = ints[6];
+ return 1;
}
__setup("aedsp16=", setup_aedsp16);
mad_write(MC3_PORT, tmp | 0x04);
hw_config->driver_use_1 = SB_MIDI_ONLY;
- return sb_dsp_detect(hw_config, 0, 0);
+ return sb_dsp_detect(hw_config, 0, 0, NULL);
#else
/* assuming all later Mozart cards are identified as
* either 82C928 or Mozart. If so, following code attempts
typedef struct mixer_def mixer_tab[32][2];
typedef struct mixer_def mixer_ent;
+struct sb_module_options
+{
+ int esstype; /* ESS chip type */
+ int acer; /* Do acer notebook init? */
+ int sm_games; /* Logitech soundman games? */
+};
+
typedef struct sb_devc {
int dev;
int input_opened;
int midi_broken;
void (*midi_input_intr) (int dev, unsigned char data);
- void *midi_irq_cookie; /* IRQ cookie for the midi */
+ void *midi_irq_cookie; /* IRQ cookie for the midi */
+
+ struct sb_module_options sbmo; /* Module options */
+
} sb_devc;
/*
int sb_dsp_reset (sb_devc *devc);
void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value);
unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
-int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio);
+int sb_dsp_detect (struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo);
int sb_dsp_init (struct address_info *hw_config);
void sb_dsp_unload(struct address_info *hw_config, int sbmpu);
int sb_mixer_init(sb_devc *devc);
int sb_audio_open(int dev, int mode);
void sb_audio_close(int dev);
-extern int acer;
extern sb_devc *last_sb;
/* From sb_common.c */
* 13-03-2000 Added some more cards, thanks to Torsten Werner.
* Removed joystick and wavetable code, there are better places for them.
* Code cleanup plus some fixes.
+ * Alessandro Zummo <azummo@ita.flashnet.it>
+ *
+ * 26-03-2000 Fixed acer, esstype and sm_games module options.
+ * Alessandro Zummo <azummo@ita.flashnet.it>
*
*/
extern void *smw_free;
+/*
+ * Note DMA2 of -1 has the right meaning in the SB16 driver as well
+ * as here. It will cause either an error if it is needed or a fallback
+ * to the 8bit channel.
+ */
+
+static int __initdata mpu_io = 0;
+static int __initdata io = -1;
+static int __initdata irq = -1;
+static int __initdata dma = -1;
+static int __initdata dma16 = -1; /* Set this for modules that need it */
+static int __initdata type = 0; /* Can set this to a specific card type */
+static int __initdata esstype = 0; /* ESS chip type */
+static int __initdata acer = 0; /* Do acer notebook init? */
+static int __initdata sm_games = 0; /* Logitech soundman games? */
+
static void __init attach_sb_card(struct address_info *hw_config)
{
if(!sb_dsp_init(hw_config))
static int __init probe_sb(struct address_info *hw_config)
{
+ struct sb_module_options sbmo;
+
if (hw_config->io_base == -1 || hw_config->dma == -1 || hw_config->irq == -1)
{
printk(KERN_ERR "sb_card: I/O, IRQ, and DMA are mandatory\n");
}
#endif
- /* This is useless since is done by sb_dsp_detect - azummo */
+ /* This is useless since it is done by sb_dsp_detect - azummo */
if (check_region(hw_config->io_base, 16))
{
printk(KERN_ERR "sb_card: I/O port 0x%x is already in use\n\n", hw_config->io_base);
return 0;
}
- return sb_dsp_detect(hw_config, 0, 0);
+
+ /* Setup extra module options */
+
+ sbmo.acer = acer;
+ sbmo.sm_games = sm_games;
+ sbmo.esstype = esstype;
+
+ return sb_dsp_detect(hw_config, 0, 0, &sbmo);
}
static void __exit unload_sb(struct address_info *hw_config)
sb_dsp_unload(hw_config, sbmpu);
}
-extern int esstype; /* ESS chip type */
-
static struct address_info cfg;
static struct address_info cfg_mpu;
struct pci_dev *sb_dev = NULL,
*mpu_dev = NULL;
-/*
- * Note DMA2 of -1 has the right meaning in the SB16 driver as well
- * as here. It will cause either an error if it is needed or a fallback
- * to the 8bit channel.
- */
-
-static int __initdata mpu_io = 0;
-static int __initdata io = -1;
-static int __initdata irq = -1;
-static int __initdata dma = -1;
-static int __initdata dma16 = -1; /* Set this for modules that need it */
-static int __initdata type = 0; /* Can set this to a specific card type */
#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
0, 1, 0, 2, 0, 3, 0, 4
};
-/* Do acer notebook init? */
-int acer = 0;
-
-/* soundman games? */
-int sm_games = 0;
-
-extern int esstype;
-
void *smw_free = NULL;
/*
#endif
}
-int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio)
+int sb_dsp_detect(struct address_info *hw_config, int pci, int pciio, struct sb_module_options *sbmo)
{
sb_devc sb_info;
sb_devc *devc = &sb_info;
memset((char *) &sb_info, 0, sizeof(sb_info)); /* Zero everything */
+
+ /* Copy module options in place */
+ if(sbmo) memcpy(&devc->sbmo, sbmo, sizeof(struct sb_module_options));
+
sb_info.my_mididev = -1;
sb_info.my_mixerdev = -1;
sb_info.dev = -1;
printk("Yamaha PCI mode.\n");
}
- if (acer)
+ if (devc->sbmo.acer)
{
cli();
inb(devc->base + 0x09);
unload_uart401(hw_config);
}
-MODULE_PARM(acer, "i");
-MODULE_PARM(sm_games, "i");
-MODULE_PARM(esstype, "i");
-
EXPORT_SYMBOL(sb_dsp_init);
EXPORT_SYMBOL(sb_dsp_detect);
EXPORT_SYMBOL(sb_dsp_unload);
#define ESSTYPE_LIKE20 -1 /* Mimic 2.0 behaviour */
#define ESSTYPE_DETECT 0 /* Mimic 2.0 behaviour */
-int esstype = ESSTYPE_DETECT; /* module parameter in sb_card.c */
-
#define SUBMDL_ES1788 0x10 /* Subtype ES1788 for specific handling */
#define SUBMDL_ES1868 0x11 /* Subtype ES1868 for specific handling */
#define SUBMDL_ES1869 0x12 /* Subtype ES1869 for specific handling */
char *chip = NULL;
int submodel = -1;
- switch (esstype) {
+ switch (devc->sbmo.esstype) {
case ESSTYPE_DETECT:
case ESSTYPE_LIKE20:
break;
submodel = SUBMDL_ES1888;
break;
default:
- printk (KERN_ERR "Invalid esstype=%d specified\n", esstype);
+ printk (KERN_ERR "Invalid esstype=%d specified\n", devc->sbmo.esstype);
return 0;
};
if (submodel != -1) {
devc->submodel = submodel;
- sprintf (modelname, "ES%d", esstype);
+ sprintf (modelname, "ES%d", devc->sbmo.esstype);
chip = modelname;
};
if (chip == NULL && (ess_minor & 0x0f) < 8) {
* If Nothing detected yet, and we want 2.0 behaviour...
* Then let's assume it's ES1688.
*/
- if (chip == NULL && esstype == ESSTYPE_LIKE20) {
+ if (chip == NULL && devc->sbmo.esstype == ESSTYPE_LIKE20) {
chip = "ES1688";
};
printk ( KERN_INFO "ESS chip %s %s%s\n"
, chip
- , ( esstype == ESSTYPE_DETECT || esstype == ESSTYPE_LIKE20
+ , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20
? "detected"
: "specified"
)
- , ( esstype == ESSTYPE_LIKE20
+ , ( devc->sbmo.esstype == ESSTYPE_LIKE20
? " (kernel 2.0 compatible)"
: ""
)
{
char name[32];
int i;
- extern int sm_games;
sprintf(name, "SB_%d", devc->sbmixnum);
- if (sm_games)
+ if (devc->sbmo.sm_games)
devc->levels = load_mixer_volumes(name, smg_default_levels, 1);
else
devc->levels = load_mixer_volumes(name, sb_default_levels, 1);
sb_initialized = 1;
hw_config->name = "AudioTrix SB";
- return sb_dsp_detect(hw_config, 0, 0);
+ return sb_dsp_detect(hw_config, 0, 0, NULL);
}
static void __init attach_trix_sb(struct address_info *hw_config)
return 0;
}
DPRINTK("EXIT after sb_dsp_detect\n");
- return sb_dsp_detect(hw_config, 0, 0);
+ return sb_dsp_detect(hw_config, 0, 0, NULL);
}
tristate 'Support for USB' CONFIG_USB
if [ ! "$CONFIG_USB" = "n" ]; then
+ bool ' USB verbose debug messages' CONFIG_USB_DEBUG
comment 'USB Controllers'
if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
fi
if [ "$CONFIG_USB_UHCI" != "y" ]; then
dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
- if [ "$CONFIG_USB_UHCI_ALT" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' UHCI unlink optimizations (EXPERIMENTAL)' CONFIG_USB_UHCI_ALT_UNLINK_OPTIMIZE
- fi
fi
dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB
comment 'USB Devices'
dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB
dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB
- dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB
+ dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND
dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB
dep_tristate ' USB Serial Converter support' CONFIG_USB_SERIAL $CONFIG_USB
if [ "$CONFIG_USB_SERIAL" != "n" ]; then
fi
bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG
fi
- dep_tristate ' USB CPiA Camera support' CONFIG_USB_CPIA $CONFIG_USB
- dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB
- dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB
+ dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV
+ dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV
dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB
dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT
dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB
- dep_tristate ' USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB
+ dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB $CONFIG_NET
+ dep_tristate ' USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET
dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB
- dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB
+ dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV
fi
-
+
comment 'USB HID'
dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB
if [ "$CONFIG_USB_HID" != "y" ]; then
ifeq ($(CONFIG_USB_SERIAL),y)
SUB_DIRS += serial
- obj-y += serial/serial.o
+ obj-y += serial/usb-serial.o
else
ifeq ($(CONFIG_USB_SERIAL),m)
MOD_IN_SUB_DIRS += serial
obj-$(CONFIG_USB_ACM) += acm.o
obj-$(CONFIG_USB_PRINTER) += printer.o
obj-$(CONFIG_USB_AUDIO) += audio.o
-obj-$(CONFIG_USB_CPIA) += cpia.o
obj-$(CONFIG_USB_IBMCAM) += ibmcam.o
obj-$(CONFIG_USB_DC2XX) += dc2xx.o
obj-$(CONFIG_USB_MDC800) += mdc800.o
/* --------------------------------------------------------------------- */
-static unsigned int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt)
+static unsigned int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int rate)
{
unsigned int i;
- /* first find an exact match */
- for (i = 0; i < nr; i++)
- if (afp[i].format == fmt)
+ /* first find an exact match, taking both format and sample rate into account,
+ but ignore stereo bit */
+ for (i = 0; i < nr; i++) {
+ if (afp[i].format == (fmt & ~AFMT_STEREO) && rate >= afp[i].sratelo && rate <= afp[i].sratehi)
return i;
+ }
+
/* second find a match with the same stereo/mono and 8bit/16bit property */
for (i = 0; i < nr; i++)
if (!AFMT_ISSTEREO(afp[i].format) == !AFMT_ISSTEREO(fmt) &&
- !AFMT_IS16BIT(afp[i].format) == !AFMT_IS16BIT(fmt))
+ !AFMT_IS16BIT(afp[i].format) == !AFMT_IS16BIT(fmt) &&
+ rate >= afp[i].sratelo && rate <= afp[i].sratehi)
return i;
/* third find a match with the same number of channels */
for (i = 0; i < nr; i++)
- if (!AFMT_ISSTEREO(afp[i].format) == !AFMT_ISSTEREO(fmt))
+ if (!AFMT_ISSTEREO(afp[i].format) == !AFMT_ISSTEREO(fmt) &&
+ rate >= afp[i].sratelo && rate <= afp[i].sratehi)
return i;
- /* return anything */
- return 0;
+ /* return failure */
+ return -1;
}
static int set_format_in(struct usb_audiodev *as)
if (u->interface < 0 || u->interface >= config->bNumInterfaces)
return 0;
iface = &config->interface[u->interface];
- fmtnr = find_format(as->fmtin, as->numfmtin, d->format);
+
+ fmtnr = find_format(as->fmtin, as->numfmtin, d->format, d->srate);
+ if (fmtnr < 0) {
+ printk(KERN_ERR "usbaudio: set_format_in(): failed to find desired format/speed combination.\n");
+ return -1;
+ }
+
fmt = as->fmtin + fmtnr;
alts = &iface->altsetting[fmt->altsetting];
u->format = fmt->format;
if (u->interface < 0 || u->interface >= config->bNumInterfaces)
return 0;
iface = &config->interface[u->interface];
- fmtnr = find_format(as->fmtout, as->numfmtout, d->format);
+
+ fmtnr = find_format(as->fmtout, as->numfmtout, d->format, d->srate);
+ if (fmtnr < 0) {
+ printk(KERN_ERR "usbaudio: set_format_out(): failed to find desired format/speed combination.\n");
+ return -1;
+ }
+
fmt = as->fmtout + fmtnr;
u->format = fmt->format;
alts = &iface->altsetting[fmt->altsetting];
struct usb_interface *iface;
unsigned char ifin[USB_MAXINTERFACES], ifout[USB_MAXINTERFACES];
unsigned char *p1;
- unsigned int i, j, numifin = 0, numifout = 0;
+ unsigned int i, j, k, numifin = 0, numifout = 0;
if (!(s = kmalloc(sizeof(struct usb_audio_state), GFP_KERNEL)))
return NULL;
dev->devnum, ctrlif, j);
continue;
}
- if (iface->num_altsetting < 2 ||
- iface->altsetting[0].bNumEndpoints > 0) {
- printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u altsetting 0 not zero bandwidth\n",
- dev->devnum, ctrlif);
+ if (iface->num_altsetting == 0) {
+ printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has no working interface.\n", dev->devnum, ctrlif);
+ continue;
+ }
+ if (iface->num_altsetting == 1) {
+ printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has only 1 altsetting.\n", dev->devnum, ctrlif);
continue;
}
+ if (iface->altsetting[0].bNumEndpoints > 0) {
+ /* Check all endpoints; should they all have a bandwidth of 0 ? */
+ for (k = 0; k < iface->altsetting[0].bNumEndpoints; k++) {
+ if (iface->altsetting[0].endpoint[k].wMaxPacketSize > 0) {
+ printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u endpoint %d does not have 0 bandwidth at alt[0]\n", dev->devnum, ctrlif, k);
+ break;
+ }
+ }
+ if (k < iface->altsetting[0].bNumEndpoints)
+ continue;
+ }
if (iface->altsetting[1].bNumEndpoints < 1) {
printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no endpoint\n",
dev->devnum, ctrlif, j);
p1 = find_csinterface_descriptor(buffer, buflen, p1, OUTPUT_TERMINAL, ctrlif, -1);
}
- ret:
+ret:
if (list_empty(&s->audiolist) && list_empty(&s->mixerlist)) {
kfree(s);
return NULL;
#define MAX_FREQ 16
#define MAX_IFACE 8
#define MAX_FORMAT 8
-#define MAX_ALT 8
+#define MAX_ALT 32 /* Sorry, we need quite a few for the Philips webcams */
struct usb_audio_terminal
{
+++ /dev/null
-/*
- * USB CPiA Video Camera driver
- *
- * Supports CPiA based Video Cameras. Many manufacturers use this chipset.
- *
- * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@valinux.com
- * (C) Copyright 1999 Randy Dunlap
- */
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/list.h>
-#include <linux/malloc.h>
-#include <linux/mm.h>
-#include <linux/smp_lock.h>
-#include <linux/videodev.h>
-#include <linux/vmalloc.h>
-#include <linux/wrapper.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/usb.h>
-
-#include <asm/io.h>
-
-#include "cpia.h"
-
-static int debug = 0;
-MODULE_PARM(debug, "i");
-
-/* Video Size 384 x 288 x 3 bytes for RGB */
-/* 384 because xawtv tries to grab 384 even though we tell it 352 is our max */
-#define MAX_FRAME_SIZE (384 * 288 * 3)
-
-/*******************************/
-/* Memory management functions */
-/*******************************/
-
-#define MDEBUG(x) do { } while(0) /* Debug memory management */
-
-static struct usb_driver cpia_driver;
-
-/* 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 = page_address(pte_page(pte)) | (adr & (PAGE_SIZE-1));
- }
- }
- MDEBUG(printk("uv2kva(%lx-->%lx)", 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)", 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)", 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)", adr, ret));
- return ret;
-}
-
-static void *rvmalloc(unsigned long size)
-{
- void *mem;
- unsigned long adr, page;
-
- /* Round it off to PAGE_SIZE */
- size += (PAGE_SIZE - 1);
- size &= ~(PAGE_SIZE - 1);
-
- mem = vmalloc(size);
- if (!mem)
- return NULL;
-
- 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(MAP_NR(__va(page)));
- adr += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
- }
-
- return mem;
-}
-
-static void rvfree(void *mem, unsigned long size)
-{
- unsigned long adr, page;
-
- if (!mem)
- return;
-
- size += (PAGE_SIZE - 1);
- size &= ~(PAGE_SIZE - 1);
-
- adr=(unsigned long) mem;
- while (size > 0) {
- page = kvirt_to_pa(adr);
- mem_map_unreserve(MAP_NR(__va(page)));
- adr += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
- }
- vfree(mem);
-}
-
-static int usb_cpia_get_version(struct usb_device *dev, void *buf)
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- USB_REQ_CPIA_GET_VERSION,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, 0, buf, 4, HZ);
-}
-
-#ifdef NOTUSED
-static int usb_cpia_get_pnp_id(struct usb_device *dev, void *buf)
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- USB_REQ_CPIA_GET_PNP_ID,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, 0, buf, 6, HZ);
-}
-#endif
-
-#ifdef NOTUSED
-static int usb_cpia_get_camera_status(struct usb_device *dev, void *buf)
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- USB_REQ_CPIA_GET_CAMERA_STATUS,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, 0, buf, 8, HZ);
-}
-#endif
-
-static int usb_cpia_goto_hi_power(struct usb_device *dev)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_GOTO_HI_POWER, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, 0, NULL, 0, HZ);
-}
-
-static int usb_cpia_get_vp_version(struct usb_device *dev, void *buf)
-{
- return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
- USB_REQ_CPIA_GET_VP_VERSION,
- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, 0, buf, 4, HZ);
-}
-
-static int usb_cpia_set_sensor_fps(struct usb_device *dev, int sensorbaserate, int sensorclkdivisor)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_SET_SENSOR_FPS,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- (sensorbaserate << 8) + sensorclkdivisor, 0, NULL, 0, HZ);
-}
-
-#ifdef NOTUSED
-static int usb_cpia_grab_frame(struct usb_device *dev, int streamstartline)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_GRAB_FRAME, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- streamstartline << 8, 0, NULL, 0, HZ);
-}
-#endif
-
-static int usb_cpia_upload_frame(struct usb_device *dev, int forceupload)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_UPLOAD_FRAME, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- forceupload, 0, NULL, 0, HZ);
-}
-
-static int usb_cpia_set_grab_mode(struct usb_device *dev, int continuousgrab)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_SET_GRAB_MODE,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE, continuousgrab,
- 0, NULL, 0, HZ);
-}
-
-static int usb_cpia_set_format(struct usb_device *dev, int size, int subsample, int order)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_SET_FORMAT,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- (subsample << 8) + size, order, NULL, 0, HZ);
-}
-
-static int usb_cpia_set_roi(struct usb_device *dev, int colstart, int colend, int rowstart, int rowend)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_SET_ROI,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- (colend << 8) + colstart, (rowend << 8) + rowstart,
- NULL, 0, HZ);
-}
-
-static int usb_cpia_set_compression(struct usb_device *dev, int compmode, int decimation)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_SET_COMPRESSION,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- (decimation << 8) + compmode, 0, NULL, 0, HZ);
-}
-
-#ifdef NOTUSED
-static int usb_cpia_set_compression_target(struct usb_device *dev, int target, int targetfr, int targetq)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_SET_COMPRESSION_TARGET,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- (targetfr << 8) + target, targetq, NULL, 0, HZ);
-}
-#endif
-
-#ifdef NOTUSED
-static int usb_cpia_initstreamcap(struct usb_device *dev, int skipframes, int streamstartline)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_INIT_STREAM_CAP,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- (streamstartline << 8) + skipframes, 0, NULL, 0, HZ);
-}
-
-static int usb_cpia_finistreamcap(struct usb_device *dev)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_FINI_STREAM_CAP,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, HZ);
-}
-
-static int usb_cpia_startstreamcap(struct usb_device *dev)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_START_STREAM_CAP,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, HZ);
-}
-
-static int usb_cpia_endstreamcap(struct usb_device *dev)
-{
- return usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
- USB_REQ_CPIA_END_STREAM_CAP,
- USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, 0, NULL, 0, HZ);
-}
-#endif
-
-/* How much data is left in the scratch buf? */
-#define scratch_left(x) (cpia->scratchlen - (int)((char *)x - (char *)cpia->scratch))
-
-static void cpia_parse_data(struct usb_cpia *cpia)
-{
- struct cpia_frame *frame, *pframe;
- unsigned char *data = cpia->scratch;
- unsigned long left;
- long copylen = 0;
-
- /* Grab the current frame and the previous frame */
- frame = &cpia->frame[cpia->curframe];
- pframe = &cpia->frame[(cpia->curframe - 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES];
-
- while (1) {
- if (!scratch_left(data))
- goto out;
-
- switch (frame->scanstate) {
- case STATE_SCANNING:
- {
- struct cpia_frame_header *header;
-
- /* We need at least 2 bytes for the magic value */
- if (scratch_left(data) < 2)
- goto out;
-
- header = (struct cpia_frame_header *)data;
-
- if (be16_to_cpup(&header->magic) == CPIA_MAGIC) {
- frame->scanstate = STATE_HEADER;
- break;
- }
-
- /* Woops, lost the header, find the end of the frame */
- if (scratch_left(data) < 4)
- goto out;
-
- /* See if we found the end of the frame */
- while (scratch_left(data) >= 4) {
- if (*((__u32 *)data) == 0xFFFFFFFF) {
- data += 4;
- if (debug >= 1)
- printk(KERN_INFO "cpia: EOF while scanning for magic\n");
- goto error;
- }
- data++;
- }
- break;
- }
- case STATE_HEADER:
- /* We need at least 64 bytes for the header */
- if (scratch_left(data) <
- sizeof(struct cpia_frame_header))
- goto out;
-
- memcpy(&frame->header, data,
- sizeof(struct cpia_frame_header));
-
- /* Skip over the header */
- data += sizeof(struct cpia_frame_header);
-
- frame->hdrwidth = (frame->header.col_end -
- frame->header.col_start) * 8;
- frame->hdrheight = (frame->header.row_end -
- frame->header.row_start) * 4;
- if (debug >= 2) {
- printk(KERN_DEBUG "cpia: frame size %dx%d\n",
- frame->hdrwidth, frame->hdrheight);
- printk(KERN_DEBUG "cpia: frame %scompressed\n",
- frame->header.comp_enable ? "" : "not ");
- }
-
- frame->scanstate = STATE_LINES;
- frame->curline = 0;
- break;
-
- case STATE_LINES:
- {
- unsigned char *f, *end;
- unsigned int len;
- int i;
- int y, u, y1, v, r, g, b;
-
- /* We want at least 2 bytes for the length */
- if (scratch_left(data) < 2)
- goto out;
-
- /* Grab the length */
- len = data[0] + (data[1] << 8);
-
- /* Check to make sure it's nothing outrageous */
- if (len > (frame->hdrwidth * 2) + 1) {
- if (debug >= 1)
- printk(KERN_DEBUG "cpia: bad length, resynching (expected %d, got %d)\n", (frame->hdrwidth * 2) + 1, len);
- goto error;
- }
-
- /* Make sure there's enough data for the entire line */
- if (scratch_left(data + 2) < len)
- goto out;
-
- /* Skip over the length */
- data += 2;
-
- /* Is the end of the line there */
- if (data[len - 1] != 0xFD) {
- if (debug >= 1)
- printk(KERN_DEBUG "cpia: lost synch\n");
- goto error;
- }
-
- /* Start at the beginning */
- end = data + len - 1;
-
- f = frame->data + (frame->width * 3 * frame->curline);
-
- if (frame->header.comp_enable) {
- unsigned char *fp;
-
- /* We use the previous frame as a reference */
- fp = pframe->data +
- (frame->width * 3 * frame->curline);
-
- while (data < end) {
- if (*data & 1) {
- /* Compress RLE data */
- i = *data >> 1;
- memcpy(f, fp, i * 3);
- copylen += (i * 3);
- f += (i * 3);
- fp += (i * 3);
- data++;
- } else {
- /* Raw data */
-
-#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
-
-y = *data++ - 16;
-u = *data++ - 128;
-y1 = *data++ - 16;
-v = *data++ - 128;
-r = 104635 * v;
-g = -25690 * u + -53294 * v;
-b = 132278 * u;
-y *= 76310;
-y1 *= 76310;
-*f++ = LIMIT(b + y); *f++ = LIMIT(g + y); *f++ = LIMIT(r + y);
-*f++ = LIMIT(b + y1); *f++ = LIMIT(g + y1); *f++ = LIMIT(r + y1);
- fp += 6;
- copylen += 6;
- }
- }
- } else {
- /* Raw data */
- while (data < end) {
-y = *data++ - 16;
-u = *data++ - 128;
-y1 = *data++ - 16;
-v = *data++ - 128;
-r = 104635 * v;
-g = -25690 * u + -53294 * v;
-b = 132278 * u;
-y *= 76310;
-y1 *= 76310;
-*f++ = LIMIT(b + y); *f++ = LIMIT(g + y); *f++ = LIMIT(r + y);
-*f++ = LIMIT(b + y1); *f++ = LIMIT(g + y1); *f++ = LIMIT(r + y1);
-copylen += 6;
- }
- }
-
- /* Skip the last byte */
- data++;
-
- if (++frame->curline >= frame->hdrheight)
- goto nextframe;
-
- break;
- } /* end case STATE_LINES */
- } /* end switch (scanstate) */
- } /* end while (1) */
-
-nextframe:
- if (debug >= 1)
- printk(KERN_DEBUG "cpia: marking as success\n");
-
- if (scratch_left(data) >= 4 && *((__u32 *)data) == 0xFFFFFFFF)
- data += 4;
-
- frame->grabstate = FRAME_DONE;
-
- goto wakeup;
-
-error:
- if (debug >= 1)
- printk(KERN_DEBUG "cpia: marking as error\n");
-
- frame->grabstate = FRAME_ERROR;
-
- /* Get a fresh frame since this frame may have been important */
- cpia->compress = 0;
-
- copylen = 0;
-
-wakeup:
- cpia->curframe = -1;
-
- /* This will cause the process to request another frame. */
- if (waitqueue_active(&frame->wq))
- wake_up_interruptible(&frame->wq);
-
-out:
- /* Grab the remaining */
- left = scratch_left(data);
- memmove(cpia->scratch, data, left);
- cpia->scratchlen = left;
-
- /* Update the frame's uncompressed length. */
- frame->scanlength += copylen;
-}
-
-/*
- * Make all of the blocks of data contiguous
- */
-static int cpia_compress_isochronous(struct usb_cpia *cpia, urb_t *urb)
-{
- unsigned char *cdata, *data;
- int i, totlen = 0;
-
- data = cpia->scratch + cpia->scratchlen;
- for (i = 0; i < urb->number_of_packets; i++) {
- int n = urb->iso_frame_desc[i].actual_length;
- int st = urb->iso_frame_desc[i].status;
-
- cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-
- if (st && debug >= 1)
- printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n",
- i, n, st);
-
- if ((cpia->scratchlen + n) > SCRATCH_BUF_SIZE) {
- printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n",cpia->scratchlen, n );
- return totlen;
- }
-
- if (n) {
- memmove(data, cdata, n);
- data += n;
- totlen += n;
- cpia->scratchlen += n;
- }
- }
-
- return totlen;
-}
-
-static void cpia_isoc_irq(struct urb *urb)
-{
- int len;
- struct usb_cpia *cpia = urb->context;
- struct cpia_sbuf *sbuf;
- int i;
-
- if (!cpia->dev)
- return;
-
- if (!cpia->streaming) {
- if (debug >= 1)
- printk(KERN_DEBUG "cpia: oops, not streaming, but interrupt\n");
- return;
- }
-
- sbuf = &cpia->sbuf[cpia->cursbuf];
-
- /* Copy the data received into our scratch buffer */
- len = cpia_compress_isochronous(cpia, urb);
-
- /* If we don't have a frame we're current working on, complain */
- if (cpia->scratchlen) {
- if (cpia->curframe < 0) {
- if (debug >= 1)
- printk(KERN_DEBUG "cpia: received data, but no frame available\n");
- } else
- cpia_parse_data(cpia);
- }
-
- for (i = 0; i < FRAMES_PER_DESC; i++) {
- sbuf->urb->iso_frame_desc[i].status = 0;
- sbuf->urb->iso_frame_desc[i].actual_length = 0;
- }
-
- /* Move to the next sbuf */
- cpia->cursbuf = (cpia->cursbuf + 1) % CPIA_NUMSBUF;
-
- return;
-}
-
-static int cpia_init_isoc(struct usb_cpia *cpia)
-{
- urb_t *urb;
- int fx, err;
-
- cpia->compress = 0;
- cpia->curframe = -1;
- cpia->cursbuf = 0;
- cpia->scratchlen = 0;
-
- /* Alternate interface 3 is is the biggest frame size */
- if (usb_set_interface(cpia->dev, cpia->iface, 3) < 0) {
- printk(KERN_ERR "usb_set_interface error\n");
- return -EBUSY;
- }
-
- /* We double buffer the Iso lists */
- urb = usb_alloc_urb(FRAMES_PER_DESC);
-
- if (!urb) {
- printk(KERN_ERR "cpia_init_isoc: usb_init_isoc ret %d\n",
- 0);
- return -ENOMEM;
- }
- cpia->sbuf[0].urb = urb;
- urb->dev = cpia->dev;
- urb->context = cpia;
- urb->pipe = usb_rcvisocpipe(cpia->dev, 1);
- urb->transfer_flags = USB_ISO_ASAP;
- urb->transfer_buffer = cpia->sbuf[0].data;
- urb->complete = cpia_isoc_irq;
- urb->number_of_packets = FRAMES_PER_DESC;
- urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
- for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
- urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
- urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
- }
- urb = usb_alloc_urb(FRAMES_PER_DESC);
- if (!urb) {
- printk(KERN_ERR "cpia_init_isoc: usb_init_isoc ret %d\n",
- 0);
- return -ENOMEM;
- }
- cpia->sbuf[1].urb = urb;
- urb->dev = cpia->dev;
- urb->context = cpia;
- urb->pipe = usb_rcvisocpipe(cpia->dev, 1);
- urb->transfer_flags = USB_ISO_ASAP;
- urb->transfer_buffer = cpia->sbuf[1].data;
- urb->complete = cpia_isoc_irq;
- urb->number_of_packets = FRAMES_PER_DESC;
- urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
- for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
- urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
- urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
- }
-
- cpia->sbuf[1].urb->next = cpia->sbuf[0].urb;
- cpia->sbuf[0].urb->next = cpia->sbuf[1].urb;
-
- err = usb_submit_urb(cpia->sbuf[0].urb);
- if (err)
- printk(KERN_ERR "cpia_init_isoc: usb_submit_urb(0) ret %d\n",
- err);
- err = usb_submit_urb(cpia->sbuf[1].urb);
- if (err)
- printk(KERN_ERR "cpia_init_isoc: usb_submit_urb(1) ret %d\n",
- err);
-
- cpia->streaming = 1;
-
- return 0;
-}
-
-static void cpia_stop_isoc(struct usb_cpia *cpia)
-{
- if (!cpia->streaming || !cpia->dev)
- return;
-
- /* Turn off continuous grab */
- if (usb_cpia_set_grab_mode(cpia->dev, 0) < 0) {
- printk(KERN_ERR "cpia_set_grab_mode error\n");
- return /* -EBUSY */;
- }
-
- /* Set packet size to 0 */
- if (usb_set_interface(cpia->dev, cpia->iface, 0) < 0) {
- printk(KERN_ERR "usb_set_interface error\n");
- return /* -EINVAL */;
- }
-
- cpia->streaming = 0;
-
- /* Unschedule all of the iso td's */
- if (cpia->sbuf[1].urb) {
- cpia->sbuf[1].urb->next = NULL;
- usb_unlink_urb(cpia->sbuf[1].urb);
- usb_free_urb(cpia->sbuf[1].urb);
- cpia->sbuf[1].urb = NULL;
- }
- if (cpia->sbuf[0].urb) {
- cpia->sbuf[0].urb->next = NULL;
- usb_unlink_urb(cpia->sbuf[0].urb);
- usb_free_urb(cpia->sbuf[0].urb);
- cpia->sbuf[0].urb = NULL;
- }
-}
-
-static int cpia_new_frame(struct usb_cpia *cpia, int framenum)
-{
- struct cpia_frame *frame;
- int width, height;
-
- if (!cpia->dev)
- return -1;
-
- /* If we're not grabbing a frame right now and the other frame is */
- /* ready to be grabbed into, then use it instead */
- if (cpia->curframe == -1) {
- if (cpia->frame[(framenum - 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES].grabstate == FRAME_READY)
- framenum = (framenum - 1 + CPIA_NUMFRAMES) % CPIA_NUMFRAMES;
- } else
- return 0;
-
- frame = &cpia->frame[framenum];
- width = frame->width;
- height = frame->height;
-
- frame->grabstate = FRAME_GRABBING;
- frame->scanstate = STATE_SCANNING;
- frame->scanlength = 0; /* accumulated in cpia_parse_data() */
-
- cpia->curframe = framenum;
-
- /* Make sure it's not too big */
- if (width > 352)
- width = 352;
- width = (width / 8) * 8; /* Multiple of 8 */
-
- if (height > 288)
- height = 288;
- height = (height / 4) * 4; /* Multiple of 4 */
-
- /* Set the ROI they want */
- if (usb_cpia_set_roi(cpia->dev, 0, width / 8, 0, height / 4) < 0)
- return -EBUSY;
-
- if (usb_cpia_set_compression(cpia->dev, cpia->compress ?
- COMP_AUTO : COMP_DISABLED, DONT_DECIMATE) < 0) {
- printk(KERN_ERR "cpia_set_compression error\n");
- return -EBUSY;
- }
-
- /* We want a fresh frame every 30 we get */
- cpia->compress = (cpia->compress + 1) % 30;
-
- /* Grab the frame */
- if (usb_cpia_upload_frame(cpia->dev, WAIT_FOR_NEXT_FRAME) < 0) {
- printk(KERN_ERR "cpia_upload_frame error\n");
- return -EBUSY;
- }
-
- return 0;
-}
-
-/* Video 4 Linux API */
-static int cpia_open(struct video_device *dev, int flags)
-{
- int err = -EBUSY;
- struct usb_cpia *cpia = (struct usb_cpia *)dev;
-
- down(&cpia->lock);
- if (cpia->user)
- goto out_unlock;
-
- cpia->frame[0].grabstate = FRAME_UNUSED;
- cpia->frame[1].grabstate = FRAME_UNUSED;
-
- err = -ENOMEM;
-
- /* Allocate memory for the frame buffers */
- cpia->fbuf = rvmalloc(2 * MAX_FRAME_SIZE);
- if (!cpia->fbuf)
- goto open_err_ret;
-
- cpia->frame[0].data = cpia->fbuf;
- cpia->frame[1].data = cpia->fbuf + MAX_FRAME_SIZE;
-
- cpia->sbuf[0].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
- if (!cpia->sbuf[0].data)
- goto open_err_on0;
-
- cpia->sbuf[1].data = kmalloc (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
- if (!cpia->sbuf[1].data)
- goto open_err_on1;
-
- /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
- * (using read() instead). */
- cpia->frame[0].width = 352;
- cpia->frame[0].height = 288;
- cpia->frame[0].bytes_read = 0;
- cpia->frame[1].width = 352;
- cpia->frame[1].height = 288;
- cpia->frame[1].bytes_read = 0;
-
- err = cpia_init_isoc(cpia);
- if (err)
- goto open_err_on2;
-
- cpia->user++;
- up(&cpia->lock);
-
- MOD_INC_USE_COUNT;
-
- return 0;
-
-open_err_on2:
- kfree (cpia->sbuf[1].data);
-open_err_on1:
- kfree (cpia->sbuf[0].data);
-open_err_on0:
- rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE);
-open_err_ret:
- return err;
-
-out_unlock:
- up(&cpia->lock);
- return err;
-}
-
-static void cpia_close(struct video_device *dev)
-{
- struct usb_cpia *cpia = (struct usb_cpia *)dev;
-
- down(&cpia->lock);
- cpia->user--;
-
- MOD_DEC_USE_COUNT;
-
- cpia_stop_isoc(cpia);
-
- rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE);
-
- kfree(cpia->sbuf[1].data);
- kfree(cpia->sbuf[0].data);
-
- up(&cpia->lock);
-
- if (!cpia->dev) {
- video_unregister_device(&cpia->vdev);
- kfree(cpia);
- }
-}
-
-static int cpia_init_done(struct video_device *dev)
-{
- return 0;
-}
-
-static long cpia_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
-{
- return -EINVAL;
-}
-
-static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
-{
- struct usb_cpia *cpia = (struct usb_cpia *)dev;
-
- if (!cpia->dev)
- return -EIO;
-
- switch (cmd) {
- case VIDIOCGCAP:
- {
- struct video_capability b;
-
- strcpy(b.name, "CPiA USB Camera");
- b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
- b.channels = 1;
- b.audios = 0;
- b.maxwidth = 352; /* CIF */
- b.maxheight = 288; /* " */
- b.minwidth = 8;
- b.minheight = 4;
-
- 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;
- if (v.channel != 0)
- return -EINVAL;
-
- v.flags = 0;
- v.tuners = 0;
- v.type = VIDEO_TYPE_CAMERA;
- strcpy(v.name, "Camera");
-
- if (copy_to_user(arg, &v, sizeof(v)))
- return -EFAULT;
-
- return 0;
- }
- case VIDIOCSCHAN:
- {
- int v;
-
- if (copy_from_user(&v, arg, sizeof(v)))
- return -EFAULT;
-
- if (v != 0)
- return -EINVAL;
-
- return 0;
- }
- case VIDIOCGPICT:
- {
- struct video_picture p;
-
- p.colour = 0x8000; /* Damn British people :) */
- p.hue = 0x8000;
- p.brightness = 180 << 8; /* XXX */
- p.contrast = 192 << 8; /* XXX */
- p.whiteness = 105 << 8; /* XXX */
- p.depth = 24;
- p.palette = VIDEO_PALETTE_RGB24;
-
- 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;
-
- return 0;
- }
- case VIDIOCSWIN:
- {
- struct video_window vw;
-
- if (copy_from_user(&vw, arg, sizeof(vw)))
- return -EFAULT;
- if (vw.flags)
- return -EINVAL;
- if (vw.clipcount)
- return -EINVAL;
- if (vw.height != 288)
- return -EINVAL;
- if (vw.width != 352)
- return -EINVAL;
-
- cpia->compress = 0;
-
- return 0;
- }
- case VIDIOCGWIN:
- {
- struct video_window vw;
-
- vw.x = 0;
- vw.y = 0;
- vw.width = 352;
- vw.height = 288;
- vw.chromakey = 0;
- vw.flags = 30; /* 30 fps */
-
- if (copy_to_user(arg, &vw, sizeof(vw)))
- return -EFAULT;
-
- return 0;
- }
- case VIDIOCGMBUF:
- {
- struct video_mbuf vm;
-
- memset(&vm, 0, sizeof(vm));
- vm.size = MAX_FRAME_SIZE * 2;
- vm.frames = 2;
- vm.offsets[0] = 0;
- vm.offsets[1] = MAX_FRAME_SIZE;
-
- if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
- return -EFAULT;
-
- return 0;
- }
- case VIDIOCMCAPTURE:
- {
- struct video_mmap vm;
-
- if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
- return -EFAULT;
-
- if (debug >= 1)
- printk(KERN_DEBUG "frame: %d, size: %dx%d, format: %d\n",
- vm.frame, vm.width, vm.height, vm.format);
-
- if (vm.format != VIDEO_PALETTE_RGB24)
- return -EINVAL;
-
- if ((vm.frame != 0) && (vm.frame != 1))
- return -EINVAL;
-
- if (cpia->frame[vm.frame].grabstate == FRAME_GRABBING)
- return -EBUSY;
-
- /* Don't compress if the size changed */
- if ((cpia->frame[vm.frame].width != vm.width) ||
- (cpia->frame[vm.frame].height != vm.height))
- cpia->compress = 0;
-
- cpia->frame[vm.frame].width = vm.width;
- cpia->frame[vm.frame].height = vm.height;
-
- /* Mark it as ready */
- cpia->frame[vm.frame].grabstate = FRAME_READY;
-
- return cpia_new_frame(cpia, vm.frame);
- }
- case VIDIOCSYNC:
- {
- int frame;
-
- if (copy_from_user((void *)&frame, arg, sizeof(int)))
- return -EFAULT;
-
- if (debug >= 1)
- printk(KERN_DEBUG "cpia: syncing to frame %d\n", frame);
-
- switch (cpia->frame[frame].grabstate) {
- case FRAME_UNUSED:
- return -EINVAL;
- case FRAME_READY:
- case FRAME_GRABBING:
- case FRAME_ERROR:
-redo:
- if (!cpia->dev)
- return -EIO;
-
- do {
- interruptible_sleep_on(&cpia->frame[frame].wq);
- if (signal_pending(current))
- return -EINTR;
- } while (cpia->frame[frame].grabstate == FRAME_GRABBING);
-
- if (cpia->frame[frame].grabstate == FRAME_ERROR) {
- int ret;
-
- if ((ret = cpia_new_frame(cpia, frame)) < 0)
- return ret;
- goto redo;
- }
- case FRAME_DONE:
- cpia->frame[frame].grabstate = FRAME_UNUSED;
- break;
- }
-
- cpia->frame[frame].grabstate = FRAME_UNUSED;
-
- return 0;
- }
- case VIDIOCGFBUF:
- {
- struct video_buffer vb;
-
- memset(&vb, 0, sizeof(vb));
- vb.base = NULL; /* frame buffer not supported, not used */
-
- if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
- return -EFAULT;
-
- return 0;
- }
- case VIDIOCKEY:
- return 0;
- case VIDIOCCAPTURE:
- return -EINVAL;
- case VIDIOCSFBUF:
- return -EINVAL;
- case VIDIOCGTUNER:
- case VIDIOCSTUNER:
- return -EINVAL;
- case VIDIOCGFREQ:
- case VIDIOCSFREQ:
- return -EINVAL;
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
- return -EINVAL;
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static long cpia_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
-{
- struct usb_cpia *cpia = (struct usb_cpia *)dev;
- int frmx = -1;
- volatile struct cpia_frame *frame;
-
- if (debug >= 1)
- printk(KERN_DEBUG "cpia_read: %ld bytes, noblock=%d\n", count, noblock);
-
- if (!dev || !buf)
- return -EFAULT;
-
- if (!cpia->dev)
- return -EIO;
-
- /* See if a frame is completed, then use it. */
- if (cpia->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */
- frmx = 0;
- else if (cpia->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */
- frmx = 1;
-
- if (noblock && (frmx == -1))
- return -EAGAIN;
-
- /* If no FRAME_DONE, look for a FRAME_GRABBING state. */
- /* See if a frame is in process (grabbing), then use it. */
- if (frmx == -1) {
- if (cpia->frame[0].grabstate == FRAME_GRABBING)
- frmx = 0;
- else if (cpia->frame[1].grabstate == FRAME_GRABBING)
- frmx = 1;
- }
-
- /* If no frame is active, start one. */
- if (frmx == -1)
- cpia_new_frame(cpia, frmx = 0);
-
- frame = &cpia->frame[frmx];
-
-restart:
- if (!cpia->dev)
- return -EIO;
-
- while (frame->grabstate == FRAME_GRABBING) {
- interruptible_sleep_on(&frame->wq);
- if (signal_pending(current))
- return -EINTR;
- }
-
- if (frame->grabstate == FRAME_ERROR) {
- frame->bytes_read = 0;
-printk("cpia_read: errored frame %d\n", cpia->curframe);
- if (cpia_new_frame(cpia, frmx))
- printk(KERN_ERR "cpia_read: cpia_new_frame error\n");
- goto restart;
- }
-
- if (debug >= 1)
- printk(KERN_DEBUG "cpia_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n",
- frmx, frame->bytes_read, frame->scanlength);
-
- /* copy bytes to user space; we allow for partials reads */
- if ((count + frame->bytes_read) > frame->scanlength)
- count = frame->scanlength - frame->bytes_read;
-
- if (copy_to_user(buf, frame->data + frame->bytes_read, count))
- return -EFAULT;
-
- frame->bytes_read += count;
- if (debug >= 1)
- printk(KERN_DEBUG "cpia_read: {copy} count used=%ld, new bytes_read=%ld\n",
- count, frame->bytes_read);
-
- if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
- frame->bytes_read = 0;
-
- /* Mark it as available to be used again. */
- cpia->frame[frmx].grabstate = FRAME_UNUSED;
- if (cpia_new_frame(cpia, frmx ? 0 : 1))
- printk(KERN_ERR "cpia_read: cpia_new_frame returned error\n");
- }
-
- return count;
-}
-
-static int cpia_mmap(struct video_device *dev, const char *adr, unsigned long size)
-{
- struct usb_cpia *cpia = (struct usb_cpia *)dev;
- unsigned long start = (unsigned long)adr;
- unsigned long page, pos;
-
- if (!cpia->dev)
- return -EIO;
-
- if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
- return -EINVAL;
-
- pos = (unsigned long)cpia->fbuf;
- while (size > 0) {
- page = kvirt_to_pa(pos);
- if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
- return -EAGAIN;
-
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- if (size > PAGE_SIZE)
- size -= PAGE_SIZE;
- else
- size = 0;
- }
-
- return 0;
-}
-
-static struct video_device cpia_template = {
- "CPiA USB Camera",
- VID_TYPE_CAPTURE,
- VID_HARDWARE_CPIA,
- cpia_open,
- cpia_close,
- cpia_read,
- cpia_write,
- NULL,
- cpia_ioctl,
- cpia_mmap,
- cpia_init_done,
- NULL,
- 0,
- 0
-};
-
-static int usb_cpia_configure(struct usb_cpia *cpia)
-{
- struct usb_device *dev = cpia->dev;
- unsigned char version[4];
-
- /* Set altsetting 0 */
- if (usb_set_interface(dev, cpia->iface, 0) < 0) {
- printk(KERN_ERR "usb_set_interface error\n");
- return -EBUSY;
- }
-
- if (usb_cpia_get_version(dev, version) < 0) {
- printk(KERN_ERR "cpia_get_version error\n");
- return -EBUSY;
- }
-
- if (debug >= 1)
- printk(KERN_DEBUG "cpia: Firmware v%d.%d, VC Hardware v%d.%d\n",
- version[0], version[1], version[2], version[3]);
-
- memcpy(&cpia->vdev, &cpia_template, sizeof(cpia_template));
-
- init_waitqueue_head(&cpia->frame[0].wq);
- init_waitqueue_head(&cpia->frame[1].wq);
-
- if (video_register_device(&cpia->vdev, VFL_TYPE_GRABBER) == -1) {
- printk(KERN_ERR "video_register_device failed\n");
- return -EBUSY;
- }
-
- if (usb_cpia_goto_hi_power(dev) < 0) {
- printk(KERN_ERR "cpia_goto_hi_power error\n");
- goto error;
- }
-
- if (usb_cpia_get_vp_version(dev, version) < 0) {
- printk(KERN_ERR "cpia_get_vp_version error\n");
- goto error;
- }
-
- if (debug >= 1) {
- printk(KERN_DEBUG "cpia: VP v%d rev %d\n", version[0], version[1]);
- printk(KERN_DEBUG "cpia: Camera Head ID %04X\n", (version[3] << 8) + version[2]);
- }
-
- /* Turn on continuous grab */
- if (usb_cpia_set_grab_mode(dev, 1) < 0) {
- printk(KERN_ERR "cpia_set_grab_mode error\n");
- goto error;
- }
-
- /* Set up the sensor to be 30fps */
- if (usb_cpia_set_sensor_fps(dev, 1, 0) < 0) {
- printk(KERN_ERR "cpia_set_sensor_fps error\n");
- goto error;
- }
-
- /* Set video into CIF mode, and order into YUYV mode */
- if (usb_cpia_set_format(dev, FORMAT_CIF, FORMAT_422,
- FORMAT_YUYV) < 0) {
- printk(KERN_ERR "cpia_set_format error\n");
- goto error;
- }
-
- /* Turn off compression */
- if (usb_cpia_set_compression(dev, COMP_DISABLED, DONT_DECIMATE) < 0) {
- printk(KERN_ERR "cpia_set_compression error\n");
- goto error;
- }
-
- cpia->compress = 0;
-
- return 0;
-
-error:
- video_unregister_device(&cpia->vdev);
- usb_driver_release_interface(&cpia_driver,
- &dev->actconfig->interface[0]);
-
- kfree(cpia);
-
- return -EBUSY;
-}
-
-static void * cpia_probe(struct usb_device *dev, unsigned int ifnum)
-{
- struct usb_interface_descriptor *interface;
- struct usb_cpia *cpia;
-
- /* We don't handle multi-config cameras */
- if (dev->descriptor.bNumConfigurations != 1)
- return NULL;
-
- interface = &dev->actconfig->interface[ifnum].altsetting[0];
-
- /* Is it a CPiA? */
- if (dev->descriptor.idVendor != 0x0553)
- return NULL;
- if (dev->descriptor.idProduct != 0x0002)
- return NULL;
-
- /* Checking vendor/product should be enough, but what the hell */
- if (interface->bInterfaceClass != 0xFF)
- return NULL;
- if (interface->bInterfaceSubClass != 0x00)
- return NULL;
-
- /* We found a CPiA */
- printk(KERN_INFO "USB CPiA camera found\n");
-
- if ((cpia = kmalloc(sizeof(*cpia), GFP_KERNEL)) == NULL) {
- printk(KERN_ERR "couldn't kmalloc cpia struct\n");
- return NULL;
- }
-
- memset(cpia, 0, sizeof(*cpia));
-
- cpia->dev = dev;
- cpia->iface = interface->bInterfaceNumber;
-
- if (!usb_cpia_configure(cpia)) {
- cpia->user=0;
- init_MUTEX(&cpia->lock); /* to 1 == available */
-
- return cpia;
- } else
- return NULL;
-}
-
-static void cpia_disconnect(struct usb_device *dev, void *ptr)
-{
- struct usb_cpia *cpia = (struct usb_cpia *) ptr;
-
- /* We don't want people trying to open up the device */
- if (!cpia->user)
- video_unregister_device(&cpia->vdev);
-
- usb_driver_release_interface(&cpia_driver,
- &cpia->dev->actconfig->interface[0]);
-
- cpia->dev = NULL;
- cpia->frame[0].grabstate = FRAME_ERROR;
- cpia->frame[1].grabstate = FRAME_ERROR;
- cpia->curframe = -1;
-
- /* This will cause the process to request another frame. */
- if (waitqueue_active(&cpia->frame[0].wq))
- wake_up_interruptible(&cpia->frame[0].wq);
-
- if (waitqueue_active(&cpia->frame[1].wq))
- wake_up_interruptible(&cpia->frame[1].wq);
-
- cpia->streaming = 0;
-
- /* Unschedule all of the iso td's */
- if (cpia->sbuf[1].urb) {
- cpia->sbuf[1].urb->next = NULL;
- usb_unlink_urb(cpia->sbuf[1].urb);
- usb_free_urb(cpia->sbuf[1].urb);
- cpia->sbuf[1].urb = NULL;
- }
- if (cpia->sbuf[0].urb) {
- cpia->sbuf[0].urb->next = NULL;
- usb_unlink_urb(cpia->sbuf[0].urb);
- usb_free_urb(cpia->sbuf[0].urb);
- cpia->sbuf[0].urb = NULL;
- }
-
- /* Free the memory */
- if (!cpia->user)
- kfree(cpia);
-}
-
-static struct usb_driver cpia_driver = {
- "cpia",
- cpia_probe,
- cpia_disconnect,
- { NULL, NULL }
-};
-
-int usb_cpia_init(void)
-{
- return usb_register(&cpia_driver);
-}
-
-void usb_cpia_cleanup(void)
-{
- usb_deregister(&cpia_driver);
-}
-
-#ifdef MODULE
-int init_module(void)
-{
- return usb_cpia_init();
-}
-
-void cleanup_module(void)
-{
- usb_cpia_cleanup();
-}
-#endif
+++ /dev/null
-#ifndef __LINUX_CPIA_H
-#define __LINUX_CPIA_H
-
-#include <linux/list.h>
-
-#define USB_REQ_CPIA_GET_VERSION 0x01
-#define USB_REQ_CPIA_GET_PNP_ID 0x02
-#define USB_REQ_CPIA_GET_CAMERA_STATUS 0x03
-#define USB_REQ_CPIA_GOTO_HI_POWER 0x04
-#define USB_REQ_CPIA_GOTO_LO_POWER 0x05
-/* No 0x06 */
-#define USB_REQ_CPIA_GOTO_SUSPEND 0x07
-#define USB_REQ_CPIA_GOTO_PASS_THROUGH 0x08
-/* No 0x09 */
-#define USB_REQ_CPIA_MODIFY_CAMERA_STATUS 0x0A
-
-#define USB_REQ_CPIA_READ_VC_REGS 0x21
-#define USB_REQ_CPIA_WRITE_BC_REG 0x22
-#define USB_REQ_CPIA_READ_MC_PORTS 0x23
-#define USB_REQ_CPIA_WRITE_MC_PORT 0x24
-#define USB_REQ_CPIA_SET_BAUD_RATE 0x25
-#define USB_REQ_CPIA_SET_ECP_TIMING 0x26
-#define USB_REQ_CPIA_READ_IDATA 0x27
-#define USB_REQ_CPIA_WRITE_IDATA 0x28
-#define USB_REQ_CPIA_GENERIC_CALL 0x29
-#define USB_REQ_CPIA_I2CSTART 0x2A
-#define USB_REQ_CPIA_I2CSTOP 0x2B
-#define USB_REQ_CPIA_I2CWRITE 0x2C
-#define USB_REQ_CPIA_I2CREAD 0x2D
-
-#define USB_REQ_CPIA_GET_VP_VERSION 0xA1
-#define USB_REQ_CPIA_SET_COLOUR_PARAMS 0xA3
-#define USB_REQ_CPIA_SET_EXPOSURE 0xA4
-/* No 0xA5 */
-#define USB_REQ_CPIA_SET_COLOUR_BALANCE 0xA6
-#define USB_REQ_CPIA_SET_SENSOR_FPS 0xA7
-#define USB_REQ_CPIA_SET_VP_DEFAULTS 0xA8
-#define USB_REQ_CPIA_SET_APCOR 0xA9
-#define USB_REQ_CPIA_SET_FLICKER_CTRL 0xAA
-#define USB_REQ_CPIA_SET_VL_OFFSET 0xAB
-
-#define USB_REQ_CPIA_GET_COLOUR_PARAMETERS 0xB0
-#define USB_REQ_CPIA_GET_COLOUR_BALANCE 0xB1
-#define USB_REQ_CPIA_GET_EXPOSURE 0xB2
-#define USB_REQ_CPIA_SET_SENSOR_MATRIX 0xB3
-
-#define USB_REQ_CPIA_COLOUR_BARS 0xBD
-#define USB_REQ_CPIA_READ_VP_REGS 0xBE
-#define USB_REQ_CPIA_WRITE_VP_REGS 0xBF
-
-#define USB_REQ_CPIA_GRAB_FRAME 0xC1
-#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2
-#define WAIT_FOR_NEXT_FRAME 0
-#define FORCE_FRAME_UPLOAD 1
-#define USB_REQ_CPIA_SET_GRAB_MODE 0xC3
-#define USB_REQ_CPIA_INIT_STREAM_CAP 0xC4
-#define USB_REQ_CPIA_FINI_STREAM_CAP 0xC5
-#define USB_REQ_CPIA_START_STREAM_CAP 0xC6
-#define USB_REQ_CPIA_END_STREAM_CAP 0xC7
-#define USB_REQ_CPIA_SET_FORMAT 0xC8
-#define FORMAT_QCIF 0
-#define FORMAT_CIF 1
-#define FORMAT_YUYV 0
-#define FORMAT_UYVY 1
-#define FORMAT_420 0
-#define FORMAT_422 1
-#define USB_REQ_CPIA_SET_ROI 0xC9
-#define USB_REQ_CPIA_SET_COMPRESSION 0xCA
-#define COMP_DISABLED 0
-#define COMP_AUTO 1
-#define COMP_MANUAL 2
-#define DONT_DECIMATE 0
-#define DECIMATE 1
-#define USB_REQ_CPIA_SET_COMPRESSION_TARGET 0xCB
-#define TARGET_QUALITY 0
-#define TARGET_FRAMERATE 1
-#define USB_REQ_CPIA_SET_YUV_THRESH 0xCC
-#define USB_REQ_CPIA_SET_COMPRESSION_PARAMS 0xCD
-#define USB_REQ_CPIA_DISCARD_FRAME 0xCE
-
-#define USB_REQ_CPIA_OUTPUT_RS232 0xE1
-#define USB_REQ_CPIA_ABORT_PROCESS 0xE4
-#define USB_REQ_CPIA_SET_DRAM_PAGE 0xE5
-#define USB_REQ_CPIA_START_DRAM_UPLOAD 0xE6
-#define USB_REQ_CPIA_START_DUMMY_STREAM 0xE8
-#define USB_REQ_CPIA_ABORT_STREAM 0xE9
-#define USB_REQ_CPIA_DOWNLOAD_DRAM 0xEA
-/* #define USB_REQ_CPIA_NULL_CMD 0x?? */
-
-#define STREAM_BUF_SIZE (PAGE_SIZE * 4)
-/* #define STREAM_BUF_SIZE (FRAMES_PER_DESC * FRAME_SIZE_PER_DESC) */
-
-#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2)
-
-#define FRAMES_PER_DESC 10
-#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */
-
-enum {
- STATE_SCANNING, /* Scanning for start */
- STATE_HEADER, /* Parsing header */
- STATE_LINES, /* Parsing lines */
-};
-
-#define CPIA_MAGIC 0x1968
-struct cpia_frame_header {
- __u16 magic; /* 0 - 1 */
- __u16 timestamp; /* 2 - 3 */
- __u16 unused; /* 4 - 5 */
- __u16 timestamp1; /* 6 - 7 */
- __u8 unused1[8]; /* 8 - 15 */
- __u8 video_size; /* 16 0 = QCIF, 1 = CIF */
- __u8 sub_sample; /* 17 0 = 4:2:0, 1 = 4:2:2 */
- __u8 yuv_order; /* 18 0 = YUYV, 1 = UYVY */
- __u8 unused2[5]; /* 19 - 23 */
- __u8 col_start; /* 24 */
- __u8 col_end; /* 25 */
- __u8 row_start; /* 26 */
- __u8 row_end; /* 27 */
- __u8 comp_enable; /* 28 0 = non compressed, 1 = compressed */
- __u8 decimation; /* 29 0 = no decimation, 1 = decimation */
- __u8 y_thresh; /* 30 */
- __u8 uv_thresh; /* 31 */
- __u8 system_state; /* 32 */
- __u8 grab_state; /* 33 */
- __u8 stream_state; /* 34 */
- __u8 fatal_error; /* 35 */
- __u8 cmd_error; /* 36 */
- __u8 debug_flags; /* 37 */
- __u8 camera_state_7; /* 38 */
- __u8 camera_state_8; /* 39 */
- __u8 cr_achieved; /* 40 */
- __u8 fr_achieved; /* 41 */
- __u8 unused3[22]; /* 42 - 63 */
-};
-
-struct usb_device;
-
-struct cpia_sbuf {
- char *data;
- urb_t *urb;
-};
-
-enum {
- FRAME_UNUSED, /* Unused (no MCAPTURE) */
- FRAME_READY, /* Ready to start grabbing */
- FRAME_GRABBING, /* In the process of being grabbed into */
- FRAME_DONE, /* Finished grabbing, but not been synced yet */
- FRAME_ERROR, /* Something bad happened while processing */
-};
-
-struct cpia_frame {
- char *data; /* Frame buffer */
-
- struct cpia_frame_header header; /* Header from stream */
-
- int width; /* Width application is expecting */
- int height; /* Height */
-
- int hdrwidth; /* Width the frame actually is */
- int hdrheight; /* Height */
-
- volatile int grabstate; /* State of grabbing */
- int scanstate; /* State of scanning */
-
- int curline; /* Line of frame we're working on */
-
- long scanlength; /* uncompressed, raw data length of frame */
- long bytes_read; /* amount of scanlength that has been read from *data */
-
- wait_queue_head_t wq; /* Processes waiting */
-};
-
-#define CPIA_NUMFRAMES 2
-#define CPIA_NUMSBUF 2
-
-struct usb_cpia {
- struct video_device vdev;
-
- /* Device structure */
- struct usb_device *dev;
-
- unsigned char iface;
-
- struct semaphore lock;
- int user; /* user count for exclusive use */
-
- int streaming; /* Are we streaming Isochronous? */
- int grabbing; /* Are we grabbing? */
-
- int compress; /* Should the next frame be compressed? */
-
- char *fbuf; /* Videodev buffer area */
-
- int curframe;
- struct cpia_frame frame[CPIA_NUMFRAMES]; /* Double buffering */
-
- int cursbuf; /* Current receiving sbuf */
- struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */
-
- /* Scratch space from the Isochronous pipe */
- unsigned char scratch[SCRATCH_BUF_SIZE];
- int scratchlen;
-};
-
-#endif
#include <linux/list.h>
#include <linux/malloc.h>
#include <linux/smp_lock.h>
-#define DEBUG
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
#include <linux/usb.h>
#include <asm/uaccess.h>
* To use the Camera you must support the USB Protocoll of the camera
* to the Kernel Node.
* The Driver uses a misc device Node. Create it with :
- * mknod /dev/mustek c 10 171
+ * mknod /dev/mustek c 180 32
*
* The driver supports only one camera.
*
* version 0.7.1
+ * The mdc800 driver gets assigned the USB Minor 32-47. The Registration
+ * was updated to use these values.
+ * (26/03/2000)
+ *
* The Init und Exit Module Function are updated.
* (01/03/2000)
*
#include <linux/signal.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
-#include <linux/miscdevice.h>
#include <linux/random.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/usb.h>
#define VERSION "0.7.1"
-#define RELEASE_DATE "(01/03/2000)"
+#define RELEASE_DATE "(26/03/2000)"
/* Vendor and Product Information */
#define MDC800_VENDOR_ID 0x055f
#define TO_WRITE_GET_READY 3000
#define TO_DEFAULT_COMMAND 5000
-/* Minor Number of the device (create with mknod /dev/mustek c 10 171) */
-#define MDC800_DEVICE_MINOR 171
+/* Minor Number of the device (create with mknod /dev/mustek c 180 32) */
+#define MDC800_DEVICE_MINOR_BASE 32
/**************************************************************************
Init and Cleanup this driver (Structs and types)
****************************************************************************/
-
-/*
- * USB Driver Struct for this device
- */
-static struct usb_driver mdc800_usb_driver =
-{
- "mdc800",
- mdc800_usb_probe,
- mdc800_usb_disconnect,
- { 0,0 },
- 0,
- 0
-};
-
-
/* File Operations of this drivers */
static struct file_operations mdc800_device_ops =
{
};
+
/*
- * The Misc Device Configuration Struct
+ * USB Driver Struct for this device
*/
-static struct miscdevice mdc800_device =
+static struct usb_driver mdc800_usb_driver =
{
- MDC800_DEVICE_MINOR,
- "USB Mustek MDC800 Camera",
- &mdc800_device_ops
+ "mdc800",
+ mdc800_usb_probe,
+ mdc800_usb_disconnect,
+ { 0,0 },
+ &mdc800_device_ops,
+ MDC800_DEVICE_MINOR_BASE
};
+
/************************************************************************
Init and Cleanup this driver (Main Functions)
*************************************************************************/
/* Register the driver */
if (usb_register (&mdc800_usb_driver) < 0)
goto cleanup_on_fail;
- if (misc_register (&mdc800_device) < 0)
- goto cleanup_on_misc_register_fail;
info ("Mustek Digital Camera Driver " VERSION " (MDC800)");
info (RELEASE_DATE " Henning Zabel <henning@uni-paderborn.de>");
/* Clean driver up, when something fails */
-cleanup_on_misc_register_fail:
- usb_deregister (&mdc800_usb_driver);
-
cleanup_on_fail:
if (mdc800 != 0)
void __exit usb_mdc800_cleanup (void)
{
usb_deregister (&mdc800_usb_driver);
- misc_deregister (&mdc800_device);
usb_free_urb (mdc800->irq_urb);
usb_free_urb (mdc800->download_urb);
*
* Released under GPL v.2 license.
*
- * Version: 1.09
+ * Version: 1.10
*
* Please see the file: linux/Documentation/usb/ov511.txt
* and the website at: http://alpha.dyndns.org/ov511
#define __NO_VERSION__
-/* Handle mangled (versioned) external symbols */
-
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/list.h>
/* Snapshot mode enabled flag */
static int snapshot = 0;
+/* Sensor detection override (global for all attached cameras) */
+static int sensor = 0;
+
MODULE_PARM(autoadjust, "i");
MODULE_PARM(debug, "i");
MODULE_PARM(fix_rgb_offset, "i");
MODULE_PARM(snapshot, "i");
+MODULE_PARM(sensor, "i");
MODULE_AUTHOR("Mark McClelland (and others)");
MODULE_DESCRIPTION("OV511 USB Camera Driver");
vfree(mem);
}
-int ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char value)
+static int ov511_reg_write(struct usb_device *dev,
+ unsigned char reg,
+ unsigned char value)
{
int rc;
2 /* REG_IO */,
USB_TYPE_CLASS | USB_RECIP_DEVICE,
0, (__u16)reg, &value, 1, HZ);
-
+
PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
-
+
+ if (rc < 0)
+ err("ov511_reg_write: error %d", rc);
+
return rc;
}
/* returns: negative is error, pos or zero is data */
-int ov511_reg_read(struct usb_device *dev, unsigned char reg)
+static int ov511_reg_read(struct usb_device *dev, unsigned char reg)
{
int rc;
unsigned char buffer[1];
PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
- if(rc < 0)
+ if(rc < 0) {
+ err("ov511_reg_read: error %d", rc);
return rc;
+ }
else
return buffer[0];
}
-int ov511_i2c_write(struct usb_device *dev, unsigned char reg, unsigned char value)
+static int ov511_i2c_write(struct usb_device *dev,
+ unsigned char reg,
+ unsigned char value)
{
int rc, retries;
for(retries = OV511_I2C_RETRIES;;) {
/* Select camera register */
rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
/* Write "value" to I2C data port of OV511 */
rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value);
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
/* Initiate 3-byte write cycle */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x01);
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
while(rc > 0 && ((rc&1) == 0)); /* Retry until idle */
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
if((rc&2) == 0) /* Ack? */
break;
/* I2C abort */
ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
#endif
- if (--retries < 0) return -1;
+ if (--retries < 0) {
+ err("i2c write retries exhausted");
+ rc = -1;
+ goto error;
+ }
}
return 0;
+
+error:
+ err("ov511_i2c_write: error %d", rc);
+ return rc;
}
/* returns: negative is error, pos or zero is data */
-int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
+static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
{
int rc, value, retries;
for(retries = OV511_I2C_RETRIES;;) {
/* Select camera register */
rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
/* Initiate 2-byte write cycle */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x03);
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
while(rc > 0 && ((rc&1) == 0)); /* Retry until idle */
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
if((rc&2) == 0) /* Ack? */
break;
/* I2C abort */
ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
- if (--retries < 0) return -1;
+ if (--retries < 0) {
+ err("i2c write retries exhausted");
+ rc = -1;
+ goto error;
+ }
}
/* Two byte read cycle */
for(retries = OV511_I2C_RETRIES;;) {
/* Initiate 2-byte read cycle */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL);
while(rc > 0 && ((rc&1) == 0)); /* Retry until idle */
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
if((rc&2) == 0) /* Ack? */
break;
/* I2C abort */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10);
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
- if (--retries < 0) return -1;
+ if (--retries < 0) {
+ err("i2c read retries exhausted");
+ rc = -1;
+ goto error;
+ }
}
value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
/* This is needed to make ov511_i2c_write() work */
rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
- if (rc < 0) return rc;
+ if (rc < 0) goto error;
- return (value);
-}
-
+ return value;
-// This version doesn't always work
-#if 0
- /* returns: negative is error, pos or zero is data */
- int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
- {
- int rc, value;
-
- /* Select camera register */
- rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
- if (rc < 0) return rc;
-
-
- /* Initiate 2-byte write cycle */
- rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x03);
- if (rc < 0) return rc;
-
-
- /* Initiate 2-byte read cycle */
- rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
- if (rc < 0) return rc;
-
- value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
-
- PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
-
- return (value);
- }
-#endif
+error:
+ err("ov511_i2c_read: error %d", rc);
+ return rc;
+}
static int ov511_write_regvals(struct usb_device *dev,
struct ov511_regvals * pRegvals)
{
- int ret;
+ int rc;
+
while(pRegvals->bus != OV511_DONE_BUS) {
if (pRegvals->bus == OV511_REG_BUS) {
- if ((ret = ov511_reg_write(dev, pRegvals->reg,
+ if ((rc = ov511_reg_write(dev, pRegvals->reg,
pRegvals->val)) < 0)
- return ret;
+ goto error;
} else if (pRegvals->bus == OV511_I2C_BUS) {
- if ((ret = ov511_i2c_write(dev, pRegvals->reg,
+ if ((rc = ov511_i2c_write(dev, pRegvals->reg,
pRegvals->val)) < 0)
- return ret;
+ goto error;
} else {
- err("Bad regval array");
+ err("Bad regval array");
+ rc = -1;
+ goto error;
}
pRegvals++;
}
return 0;
+
+error:
+ err("ov511_write_regvals: error %d", rc);
+ return rc;
}
#if 0
{
int i;
int rc;
- for(i=reg1; i<=regn; i++) {
- rc = ov511_i2c_read(dev, i);
-
- PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
+ for(i = reg1; i <= regn; i++) {
+ rc = ov511_i2c_read(dev, i);
+ PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
}
}
{
int i;
int rc;
- for(i=reg1; i<=regn; i++) {
+ for(i = reg1; i <= regn; i++) {
rc = ov511_reg_read(dev, i);
PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
}
}
#endif
-int ov511_reset(struct usb_device *dev, unsigned char reset_type)
+static int ov511_reset(struct usb_device *dev, unsigned char reset_type)
{
int rc;
PDEBUG(3, "Reset: type=0x%X", reset_type);
rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
- if (rc < 0)
- err("reset: command failed");
-
rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0);
+
if (rc < 0)
err("reset: command failed");
return rc;
}
-int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
+/* Temporarily stops OV511 from functioning. Must do this before changing
+ * registers while the camera is streaming */
+static inline int ov511_stop(struct usb_device *dev)
{
- int alt, multiplier, rc;
-
- PDEBUG(3, "set packet size: %d", size);
-
- switch (size) {
- case 992:
- alt = 0;
- multiplier = 31;
- break;
- case 993:
- alt = 1;
- multiplier = 31;
- break;
- case 768:
- alt = 2;
- multiplier = 24;
- break;
- case 769:
- alt = 3;
- multiplier = 24;
- break;
- case 512:
- alt = 4;
- multiplier = 16;
- break;
- case 513:
- alt = 5;
- multiplier = 16;
- break;
- case 257:
- alt = 6;
- multiplier = 8;
- break;
- case 0:
- alt = 7;
- multiplier = 1; // FIXME - is this correct?
- break;
- default:
+ PDEBUG(5, "ov511_stop()");
+ return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d));
+}
+
+/* Restarts OV511 after ov511_stop() is called */
+static inline int ov511_restart(struct usb_device *dev)
+{
+ PDEBUG(5, "ov511_restart()");
+ return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00));
+}
+
+static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
+{
+ int alt, mult;
+
+ if (ov511_stop(ov511->dev) < 0)
+ return -EIO;
+
+ mult = size / 32;
+
+ if (ov511->bridge == BRG_OV511) {
+ if (size == 0) alt = OV511_ALT_SIZE_0;
+ else if (size == 257) alt = OV511_ALT_SIZE_257;
+ else if (size == 512) alt = OV511_ALT_SIZE_512;
+ else if (size == 513) alt = OV511_ALT_SIZE_513;
+ else if (size == 768) alt = OV511_ALT_SIZE_768;
+ else if (size == 769) alt = OV511_ALT_SIZE_769;
+ else if (size == 992) alt = OV511_ALT_SIZE_992;
+ else if (size == 993) alt = OV511_ALT_SIZE_993;
+ else {
+ err("Set packet size: invalid size (%d)", size);
+ return -EINVAL;
+ }
+ }
+ else if (ov511->bridge == BRG_OV511PLUS) {
+ if (size == 0) alt = OV511PLUS_ALT_SIZE_0;
+ else if (size == 33) alt = OV511PLUS_ALT_SIZE_33;
+ else if (size == 129) alt = OV511PLUS_ALT_SIZE_129;
+ else if (size == 257) alt = OV511PLUS_ALT_SIZE_257;
+ else if (size == 385) alt = OV511PLUS_ALT_SIZE_385;
+ else if (size == 513) alt = OV511PLUS_ALT_SIZE_513;
+ else if (size == 769) alt = OV511PLUS_ALT_SIZE_769;
+ else if (size == 961) alt = OV511PLUS_ALT_SIZE_961;
+ else {
err("Set packet size: invalid size (%d)", size);
return -EINVAL;
+ }
+ }
+ else {
+ err("Set packet size: Invalid bridge type");
+ return -EINVAL;
}
- rc = ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE,
- multiplier);
- if (rc < 0) {
- err("Set packet size: Set FIFO size ret %d", rc);
+ PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt);
+
+ if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE,
+ mult) < 0)
return -ENOMEM;
- }
if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) {
err("Set packet size: set interface error");
if (ov511_reset(ov511->dev, OV511_RESET_NOREGS) < 0)
return -ENOMEM;
+ ov511->packet_size = size;
+
+ if (ov511_restart(ov511->dev) < 0)
+ return -EIO;
+
return 0;
}
struct video_picture *p)
{
int ret;
+ struct usb_device *dev = ov511->dev;
- /* Stop the camera */
- if (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
- err("reset: command failed");
+ if (ov511_stop(dev) < 0)
return -EIO;
- }
- if((ret = ov511_i2c_read(ov511->dev, OV7610_REG_COM_B)) < 0)
+ if((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0)
return -EIO;
#if 0
- if(ov511_i2c_write(ov511->dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
+ if(ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
return -EIO;
#endif
+ if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE)
+ if(ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0)
+ return -EIO;
- if(ov511_i2c_write(ov511->dev, OV7610_REG_SAT, p->colour >> 8) < 0)
- return -EIO;
+ if (ov511->sensor == SEN_OV7610) {
+ if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
+ return -EIO;
- if(ov511_i2c_write(ov511->dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
- return -EIO;
+ if(ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
+ return -EIO;
+ }
- if(ov511_i2c_write(ov511->dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
+ if (ov511_restart(dev) < 0)
return -EIO;
- /* Restart the camera */
- if (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x0) < 0) {
- err("reset: command failed");
- return -EIO;
- }
-
return 0;
}
struct video_picture *p)
{
int ret;
+ struct usb_device *dev = ov511->dev;
- /* Stop the camera */
- if (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
- err("reset: command failed");
+ if (ov511_stop(dev) < 0)
return -EIO;
- }
- if((ret = ov511_i2c_read(ov511->dev, OV7610_REG_SAT)) < 0) return -EIO;
+ if((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO;
p->colour = ret << 8;
- if((ret = ov511_i2c_read(ov511->dev, OV7610_REG_CNT)) < 0) return -EIO;
+ if((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO;
p->contrast = ret << 8;
- if((ret = ov511_i2c_read(ov511->dev, OV7610_REG_BRT)) < 0) return -EIO;
+ if((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO;
p->brightness = ret << 8;
p->hue = 0x8000;
p->depth = 3; /* Don't know if this is right */
p->palette = VIDEO_PALETTE_RGB24;
- /* Restart the camera */
- if (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0x0) < 0) {
- err("reset: command failed");
+ if (ov511_restart(dev) < 0)
return -EIO;
- }
return 0;
}
PDEBUG(3, "ov511_mode_init_regs(ov511, w:%d, h:%d, mode:%d, sub:%d)",
width, height, mode, sub_flag);
-// ov511_set_packet_size(ov511, 0);
- if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d) < 0) {
- err("reset: command failed");
+ if (ov511_stop(ov511->dev) < 0)
return -EIO;
- }
if (mode == VIDEO_PALETTE_GREY) {
ov511_reg_write(dev, 0x16, 0x00);
ov511_i2c_write(dev, 0x0e, 0x44);
- ov511_i2c_write(dev, 0x13, 0x21);
+ ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20);
/* For snapshot */
ov511_reg_write(dev, 0x1e, 0x00);
ov511_reg_write(dev, 0x1f, 0x01);
} else {
ov511_reg_write(dev, 0x16, 0x01);
ov511_i2c_write(dev, 0x0e, 0x04);
- ov511_i2c_write(dev, 0x13, 0x01);
+ ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00);
/* For snapshot */
ov511_reg_write(dev, 0x1e, 0x01);
ov511_reg_write(dev, 0x1f, 0x03);
if (width == 640 && height == 480) {
if (sub_flag) {
- ov511_i2c_write(ov511->dev, 0x17, 0x38+(ov511->subx>>2));
- ov511_i2c_write(ov511->dev, 0x18,
+ ov511_i2c_write(dev, 0x17, 0x38+(ov511->subx>>2));
+ ov511_i2c_write(dev, 0x18,
0x3a+((ov511->subx+ov511->subw)>>2));
- ov511_i2c_write(ov511->dev, 0x19, 0x5+(ov511->suby>>1));
- ov511_i2c_write(ov511->dev, 0x1a,
+ ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1));
+ ov511_i2c_write(dev, 0x1a,
0x5+((ov511->suby+ov511->subh)>>1));
- ov511_reg_write(ov511->dev, 0x12, (ov511->subw>>3)-1);
- ov511_reg_write(ov511->dev, 0x13, (ov511->subh>>3)-1);
+ ov511_reg_write(dev, 0x12, (ov511->subw>>3)-1);
+ ov511_reg_write(dev, 0x13, (ov511->subh>>3)-1);
ov511_i2c_write(dev, 0x11, 0x01);
/* Snapshot additions */
- ov511_reg_write(ov511->dev, 0x1a, (ov511->subw>>3)-1);
- ov511_reg_write(ov511->dev, 0x1b, (ov511->subh>>3)-1);
- ov511_reg_write(ov511->dev, 0x1c, 0x00);
- ov511_reg_write(ov511->dev, 0x1d, 0x00);
+ ov511_reg_write(dev, 0x1a, (ov511->subw>>3)-1);
+ ov511_reg_write(dev, 0x1b, (ov511->subh>>3)-1);
+ ov511_reg_write(dev, 0x1c, 0x00);
+ ov511_reg_write(dev, 0x1d, 0x00);
} else {
- ov511_i2c_write(ov511->dev, 0x17, 0x38);
- ov511_i2c_write(ov511->dev, 0x18, 0x3a + (640>>2));
- ov511_i2c_write(ov511->dev, 0x19, 0x5);
- ov511_i2c_write(ov511->dev, 0x1a, 5 + (480>>1));
+ ov511_i2c_write(dev, 0x17, 0x38);
+ ov511_i2c_write(dev, 0x18, 0x3a + (640>>2));
+ ov511_i2c_write(dev, 0x19, 0x5);
+ ov511_i2c_write(dev, 0x1a, 5 + (480>>1));
ov511_reg_write(dev, 0x12, 0x4f);
ov511_reg_write(dev, 0x13, 0x3d);
/* Snapshot additions */
- ov511_reg_write(ov511->dev, 0x1a, 0x4f);
- ov511_reg_write(ov511->dev, 0x1b, 0x3d);
- ov511_reg_write(ov511->dev, 0x1c, 0x00);
- ov511_reg_write(ov511->dev, 0x1d, 0x00);
+ ov511_reg_write(dev, 0x1a, 0x4f);
+ ov511_reg_write(dev, 0x1b, 0x3d);
+ ov511_reg_write(dev, 0x1c, 0x00);
+ ov511_reg_write(dev, 0x1d, 0x00);
if (mode == VIDEO_PALETTE_GREY) {
ov511_i2c_write(dev, 0x11, 4); /* check */
rc = -EINVAL;
}
-// ov511_set_packet_size(ov511, 993);
-
- if (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00) < 0) {
- err("reset: command failed");
+ if (ov511_restart(ov511->dev) < 0)
return -EIO;
- }
return rc;
}
*************************************************************/
static void fixFrameRGBoffset(struct ov511_frame *frame)
{
- int x,y;
- int rowBytes=frame->width*3,w=frame->width;
- unsigned char *rgb=frame->data;
- const int shift=1;//Distance to shift pixels by, vertically
+ int x, y;
+ int rowBytes = frame->width*3, w = frame->width;
+ unsigned char *rgb = frame->data;
+ const int shift = 1; //Distance to shift pixels by, vertically
- if (frame->width<400)
- return;//Don't bother with little images
+ if (frame->width < 400)
+ return; //Don't bother with little images
//Shift red channel up
- for (y=shift;y<frame->height;y++)
+ for (y = shift; y < frame->height; y++)
{
- int lp=(y-shift)*rowBytes;//Previous line offset
- int lc=y*rowBytes;//Current line offset
- for (x=0;x<w;x++)
- rgb[lp+x*3+2]=rgb[lc+x*3+2];//Shift red up
+ int lp = (y-shift)*rowBytes; //Previous line offset
+ int lc = y*rowBytes; //Current line offset
+ for (x = 0; x < w; x++)
+ rgb[lp+x*3+2] = rgb[lc+x*3+2]; //Shift red up
}
+
//Shift blue channel down
- for (y=frame->height-shift-1;y>=0;y--)
+ for (y=frame->height-shift-1; y >= 0; y--)
{
- int ln=(y+shift)*rowBytes;//Next line offset
- int lc=y*rowBytes;//Current line offset
- for (x=0;x<w;x++)
- rgb[ln+x*3+0]=rgb[lc+x*3+0];//Shift blue down
+ int ln = (y+shift)*rowBytes; //Next line offset
+ int lc = y*rowBytes; //Current line offset
+ for (x = 0; x < w; x++)
+ rgb[ln+x*3+0] = rgb[lc+x*3+0]; //Shift blue down
}
}
urb->transfer_buffer = ov511->sbuf[0].data;
urb->complete = ov511_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
- urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+ urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
- urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
- urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+ urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
+ urb->iso_frame_desc[fx].length = ov511->packet_size;
}
urb = usb_alloc_urb(FRAMES_PER_DESC);
urb->transfer_buffer = ov511->sbuf[1].data;
urb->complete = ov511_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
- urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
+ urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
- urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx;
- urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
+ urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
+ urb->iso_frame_desc[fx].length = ov511->packet_size;
}
ov511->sbuf[1].urb->next = ov511->sbuf[0].urb;
PDEBUG(4, "frame [0] @ %p", ov511->frame[0].data);
PDEBUG(4, "frame [1] @ %p", ov511->frame[1].data);
- ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
if (!ov511->sbuf[0].data)
goto open_err_on0;
- ov511->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL);
+ ov511->sbuf[1].data = kmalloc(FRAMES_PER_DESC * MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
if (!ov511->sbuf[1].data)
goto open_err_on1;
initialize: ov511_init_done,
};
-static int ov7610_configure(struct usb_device *dev)
+static int ov7610_configure(struct usb_ov511 *ov511)
{
+ struct usb_device *dev = ov511->dev;
int tries;
+ int rc;
if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
OV7610_I2C_WRITE_ID) < 0)
--tries;
}
- if (tries == 0) {
+ if (tries == 1) {
err("Failed to read OV7610 ID. You might not have an OV7610,");
err("or it may be not responding. Report this to");
err("mmcclelland@delphi.com");
return -1;
}
+ if (sensor == 0) {
+ rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+
+ if (rc < 0) {
+ err("Error detecting sensor type");
+ return -1;
+ }
+ else if((rc & 3) == 3) {
+ printk("ov511: Sensor is an OV7610\n");
+ ov511->sensor = SEN_OV7610;
+ }
+ else if((rc & 3) == 1) {
+ printk("ov511: Sensor is an OV7620AE\n");
+ ov511->sensor = SEN_OV7620AE;
+ }
+ else if((rc & 3) == 0) {
+ printk("ov511: Sensor is an OV7620\n");
+ ov511->sensor = SEN_OV7620;
+ }
+ else {
+ err("Unknown image sensor version: %d", rc & 3);
+ return -1;
+ }
+ }
+ else { /* sensor != 0; user overrode detection */
+ ov511->sensor = sensor;
+ printk("ov511: Sensor set to type %d\n", ov511->sensor);
+ }
+
return 0;
}
{OV511_I2C_BUS, 0x29, 0x03}, /* 91 */
{OV511_I2C_BUS, 0x2a, 0x04},
{OV511_I2C_BUS, 0x2c, 0xfe},
- {OV511_I2C_BUS, 0x2d, 0x93}, /* d7 */
+// {OV511_I2C_BUS, 0x2d, 0x93}, /* d7 */
{OV511_I2C_BUS, 0x30, 0x71},
{OV511_I2C_BUS, 0x31, 0x60},
{OV511_I2C_BUS, 0x32, 0x26},
{OV511_DONE_BUS, 0x0, 0x00},
};
- /* Set altsetting 0 */
- if (usb_set_interface(dev, ov511->iface, 0) < 0) {
- err("usb_set_interface error");
- return -EBUSY;
- }
-
memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
init_waitqueue_head(&ov511->frame[0].wq);
if ((rc = ov511_write_regvals(dev, aRegvalsInit)))
return rc;
- if(ov7610_configure(dev) < 0) {
+ if(ov7610_configure(ov511) < 0) {
err("failed to configure OV7610");
goto error;
}
+ ov511_set_packet_size(ov511, 0);
+
/* Disable compression */
- if (ov511_reg_write(dev, OV511_OMNICE_ENABLE, 0x00) < 0) {
- err("disable compression: command failed");
+ if (ov511_reg_write(dev, OV511_OMNICE_ENABLE, 0x00) < 0)
goto error;
- }
- ov511->compress = 0;
ov511->snap_enabled = snapshot;
/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
if (autoadjust) {
if (ov511_i2c_write(dev, 0x13, 0x01) < 0) goto error;
+ if (ov511_i2c_write(dev, 0x2d, 0x93) < 0) goto error;
}
else {
- if (ov511_i2c_write(dev, 0x13, 0x00) < 0 ) goto error;
+ if (ov511_i2c_write(dev, 0x13, 0x00) < 0) goto error;
+ if (ov511_i2c_write(dev, 0x2d, 0x83) < 0) goto error;
+ ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
}
return 0;
&dev->actconfig->interface[ov511->iface]);
kfree(ov511);
+ ov511 = NULL;
return -EBUSY;
}
interface = &dev->actconfig->interface[ifnum].altsetting[0];
- /* Is it an OV511? */
+ /* Is it an OV511/OV511+? */
if (dev->descriptor.idVendor != 0x05a9)
return NULL;
- if (dev->descriptor.idProduct != 0x0511)
+ if (dev->descriptor.idProduct != 0x0511
+ && dev->descriptor.idProduct != 0xA511)
return NULL;
/* Checking vendor/product should be enough, but what the hell */
if (interface->bInterfaceSubClass != 0x00)
return NULL;
- /* We found one */
- printk(KERN_INFO "ov511: USB OV511-based camera found\n");
-
if ((ov511 = kmalloc(sizeof(*ov511), GFP_KERNEL)) == NULL) {
err("couldn't kmalloc ov511 struct");
return NULL;
ov511->dev = dev;
ov511->iface = interface->bInterfaceNumber;
+ if (dev->descriptor.idProduct == 0x0511) {
+ info("USB OV511 camera found");
+ ov511->bridge = BRG_OV511;
+ }
+ else if (dev->descriptor.idProduct == 0xA511) {
+ info("USB OV511+ camera found");
+ ov511->bridge = BRG_OV511PLUS;
+ }
+
rc = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
if (rc < 0) {
err("Unable to read camera bridge registers");
- return NULL;
+ goto error;
}
switch(ov511->customid = rc) {
case 0: /* This also means that no custom ID was set */
- printk("ov511: Camera is probably a MediaForte MV300\n");
+ printk("ov511: Camera is a generic model (no ID)\n");
break;
case 3:
printk("ov511: Camera is a D-Link DSB-C300\n");
case 36:
printk("ov511: Camera is a Koala-Cam\n");
break;
+ case 38:
+ printk("ov511: Camera is a Lifeview USB Life TV\n");
+ printk("ov511: This device is not supported, exiting...\n");
+ goto error;
+ break;
case 100:
printk("ov511: Camera is a Lifeview RoboCam\n");
break;
err("Specific camera type (%d) not recognized", rc);
err("Please contact mmcclelland@delphi.com to request");
err("support for your camera.");
- return NULL;
}
+// if (ov511->bridge == BRG_OV511PLUS) {
+// err("Sorry, the OV511+ chip is not supported yet");
+// goto error;
+// }
+
if (!ov511_configure(ov511)) {
ov511->user=0;
init_MUTEX(&ov511->lock); /* to 1 == available */
}
else {
err("Failed to configure camera");
- return NULL;
+ goto error;
}
return ov511;
+
+error:
+ if (ov511) {
+ kfree(ov511);
+ ov511 = NULL;
+ }
+
+ return NULL;
}
static void ov511_disconnect(struct usb_device *dev, void *ptr)
#ifndef __LINUX_OV511_H
#define __LINUX_OV511_H
-//#include <linux/list.h>
-
#define OV511_DEBUG /* Turn on debug messages */
#ifdef OV511_DEBUG
#define OV511_REG_SYSTEM_CLOCK_DIVISOR 0x51
#define OV511_REG_SYSTEM_SNAPSHOT 0x52
#define OV511_REG_SYSTEM_INIT 0x53
+#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+ only */
+#define OV511_REG_SYSTEM_LED_CTL 0x55 /* OV511+ only */
#define OV511_REG_SYSTEM_USER_DEFINED 0x5E
#define OV511_REG_SYSTEM_CUSTOM_ID 0x5F
/* OmniCE register numbers */
-#define OV511_OMNICE_PREDICATION_HORIZ_Y 0x70
-#define OV511_OMNICE_PREDICATION_HORIZ_UV 0x71
-#define OV511_OMNICE_PREDICATION_VERT_Y 0x72
-#define OV511_OMNICE_PREDICATION_VERT_UV 0x73
+#define OV511_OMNICE_PREDICTION_HORIZ_Y 0x70
+#define OV511_OMNICE_PREDICTION_HORIZ_UV 0x71
+#define OV511_OMNICE_PREDICTION_VERT_Y 0x72
+#define OV511_OMNICE_PREDICTION_VERT_UV 0x73
#define OV511_OMNICE_QUANTIZATION_HORIZ_Y 0x74
#define OV511_OMNICE_QUANTIZATION_HORIZ_UV 0x75
#define OV511_OMNICE_QUANTIZATION_VERT_Y 0x76
#define OV511_OMNICE_UV_LUT_BEGIN 0xA0
#define OV511_OMNICE_UV_LUT_END 0xBF
-/* Alternate numbers for various max packet sizes */
-#define OV511_ALTERNATE_SIZE_992 0
-#define OV511_ALTERNATE_SIZE_993 1
-#define OV511_ALTERNATE_SIZE_768 2
-#define OV511_ALTERNATE_SIZE_769 3
-#define OV511_ALTERNATE_SIZE_512 4
-#define OV511_ALTERNATE_SIZE_513 5
-#define OV511_ALTERNATE_SIZE_257 6
-#define OV511_ALTERNATE_SIZE_0 7
+/* Alternate numbers for various max packet sizes (OV511 only) */
+#define OV511_ALT_SIZE_992 0
+#define OV511_ALT_SIZE_993 1
+#define OV511_ALT_SIZE_768 2
+#define OV511_ALT_SIZE_769 3
+#define OV511_ALT_SIZE_512 4
+#define OV511_ALT_SIZE_513 5
+#define OV511_ALT_SIZE_257 6
+#define OV511_ALT_SIZE_0 7
+
+/* Alternate numbers for various max packet sizes (OV511+ only) */
+#define OV511PLUS_ALT_SIZE_0 0
+#define OV511PLUS_ALT_SIZE_33 1
+#define OV511PLUS_ALT_SIZE_129 2
+#define OV511PLUS_ALT_SIZE_257 3
+#define OV511PLUS_ALT_SIZE_385 4
+#define OV511PLUS_ALT_SIZE_513 5
+#define OV511PLUS_ALT_SIZE_769 6
+#define OV511PLUS_ALT_SIZE_961 7
/* ov7610 registers */
#define OV7610_REG_GAIN 0x00
#define SCRATCH_BUF_SIZE 384
#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */
-#define FRAME_SIZE_PER_DESC 993 /* FIXME - Shouldn't be hardcoded */
+#define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */
+#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */
// FIXME - should this be 0x81 (endpoint address) or 0x01 (endpoint number)?
#define OV511_ENDPOINT_ADDRESS 0x81 /* Address of isoc endpoint */
unsigned char reg,
unsigned char value);
+/* Bridge types */
+enum {
+ BRG_OV511,
+ BRG_OV511PLUS,
+};
+
+/* Sensor types */
+enum {
+ SEN_UNKNOWN,
+ SEN_OV7610,
+ SEN_OV7620,
+ SEN_OV7620AE,
+};
enum {
STATE_SCANNING, /* Scanning for start */
int hdrheight; /* Height */
int sub_flag; /* Sub-capture mode for this frame? */
- int format; /* Format for this frame */
+ int format; /* Format for this frame */
int segsize; /* How big is each segment from the camera? */
volatile int grabstate; /* State of grabbing */
wait_queue_head_t wq; /* Processes waiting */
int snap_enabled; /* Snapshot mode enabled */
+
+ int bridge; /* Type of bridge (OV511 or OV511+) */
+ int sensor; /* Type of image sensor chip */
+
+ int packet_size; /* Frame size per isoc desc */
};
#endif
#include <linux/usb.h>
-static const char *version = __FILE__ ": v0.3.5 2000/03/21 Written by Petko Manolov (petkan@spct.net)\n";
+static const char *version = __FILE__ ": v0.3.7 2000/03/23 Written by Petko Manolov (petkan@spct.net)\n";
#define PEGASUS_MTU 1500
#define PEGASUS_MAX_MTU 1536
+#define SROM_WRITE 0x01
+#define SROM_READ 0x02
#define PEGASUS_TX_TIMEOUT (HZ*5)
#define ALIGN(x) x __attribute__((aligned(16)))
static struct usb_eth_dev usb_dev_id[] = {
- { "D-Link DSB-650TX", 0x2001, 0x4001, NULL },
+ {"Billionton USB-100", 0x08dd, 0x0986, NULL},
+ {"Corega FEter USB-TX", 0x7aa, 0x0004, NULL},
+ {"MELCO LUA-TX", 0x0411, 0x0001, NULL},
+ { "D-Link DSB-650TX", 0x2001, 0x4002, NULL },
+ { "D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL },
{ "Linksys USB100TX", 0x066b, 0x2203, NULL },
{ "SMC 202 USB Ethernet", 0x0707, 0x0200, NULL },
- { "ADMtek AN986 (Pegasus) USB Ethernet", 0x07a6, 0x0986, NULL },
+ { "ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, NULL },
{ "Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL },
{ NULL, 0, 0, NULL }
};
return 1;
}
-static int pegasus_read_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata)
+static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata, __u8 direction)
{
int i;
- __u8 data[4] = { index, 0, 0, 0x02 };
+ __u8 data[4] = { index, 0, 0, direction };
pegasus_set_registers(dev, 0x20, 4, data);
for (i = 0; i < 100; i++) {
}
}
- warn("read_srom_word() failed");
+ warn("pegasus_rw_srom_word() failed");
return 1;
}
{
int i;
for (i = 0; i < 3; i++)
- if (pegasus_read_srom_word(dev, i, (__u16 *)&id[i * 2]))
+ if (pegasus_rw_srom_word(dev,i,(__u16 *)&id[i * 2],SROM_READ))
return 1;
return 0;
}
(net->flags & IFF_ALLMULTI)) {
pegasus_set_register(pegasus->usb, 0, 0xfa);
pegasus_set_register(pegasus->usb, 2, 0);
+ info("%s set allmulti");
} else {
- dbg("%s: set Rx mode", net->name);
+ info("%s: set Rx mode", net->name);
}
netif_wake_queue(net);
while ( usb_dev_id[i].name ) {
if ( (usb_dev_id[i].vendor == vendor) &&
(usb_dev_id[i].device == product) )
- return 0;
+ return i;
i++;
}
- return 1;
+ return 0;
}
static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
{
struct net_device *net;
struct pegasus *pegasus;
+ int dev_indx;
- if ( check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct) ) {
+ if ( !(dev_indx = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) ) {
return NULL;
}
pegasus->intr_buff, 8, pegasus_irq, pegasus, 250);
- printk(KERN_INFO "%s: ADMtek AN986 Pegasus usb device\n", net->name);
+ printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name);
return pegasus;
}
/* -*- linux-c -*- */
/*
- * Driver for USB Scanners (linux-2.3.42)
+ * Driver for USB Scanners (linux-2.3.99-pre3-7)
*
* Copyright (C) 1999, 2000 David E. Nelson
*
* - Increased the timeout parameter in read_scanner() to 120 Secs.
*
*
+ * 0.4.2 3/23/2000
+ *
+ * - Added Umax 1236U ID. Thanks to Philipp Baer <ph_baer@npw.net>.
+ * - Added Primax, ReadyScan, Visioneer, Colorado, and Genius ID's.
+ * Thanks to Adrian Perez Jorge <adrianpj@easynews.com>.
+ * - Fixed error number reported for non-existant devices. Thanks to
+ * Spyridon Papadimitriou <Spyridon_Papadimitriou@gs91.sp.cs.cmu.edu>.
+ * - Added Acer Prisascan 620U ID's. Thanks to Joao <joey@knoware.nl>.
+ * - Replaced __initcall() with module_init()/module_exit(). Updates
+ * from patch-2.3.48.
+ * - Replaced file_operations structure with new syntax. Updates
+ * from patch-2.3.49.
+ * - Changed #include "usb.h" to #include <linux/usb.h>
+ * - Added #define SCN_IOCTL to exclude development areas
+ * since 2.4.x is about to be released. This mainly affects the
+ * ioctl() stuff. See scanner.h for more details.
+ * - Changed the return value for signal_pending() from -ERESTARTSYS to
+ * -EINTR.
+ *
+ *
* TODO
*
+ * - Performance
* - Select/poll methods
* - More testing
* - Proper registry/assignment for LM9830 ioctl's
if (!p_scn_table[scn_minor]) {
err("open_scanner(%d): invalid scn_minor", scn_minor);
- return -ENOIOCTLCMD;
+ return -ENODEV;
}
scn = p_scn_table[scn_minor];
if (!p_scn_table[scn_minor]) {
err("close_scanner(%d): invalid scn_minor", scn_minor);
- return -ENOIOCTLCMD;
+ return -ENODEV;
}
scn = p_scn_table[scn_minor];
ssize_t bytes_written = 0; /* Overall count of bytes written */
ssize_t ret = 0;
+ kdev_t scn_minor;
+
int this_write; /* Number of bytes to write */
int partial; /* Number of bytes successfully written */
int result = 0;
scn = file->private_data;
+ scn_minor = scn->scn_minor;
+
obuf = scn->obuf;
dev = scn->scn_dev;
while (count > 0) {
if (signal_pending(current)) {
- ret = -ERESTARTSYS;
+ ret = -EINTR;
break;
}
}
result = usb_bulk_msg(dev,usb_sndbulkpipe(dev, scn->bulk_out_ep), obuf, this_write, &partial, 60*HZ);
- dbg("write stats(%d): result:%d this_write:%d partial:%d", scn->scn_minor, result, this_write, partial);
+ dbg("write stats(%d): result:%d this_write:%d partial:%d", scn_minor, result, this_write, partial);
if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */
warn("write_scanner: NAK recieved.");
ret = -ETIME;
break;
} else if (result < 0) { /* We should not get any I/O errors */
- warn("write_scanner(%d): funky result: %d. Please notify the maintainer.", scn->scn_minor, result);
+ warn("write_scanner(%d): funky result: %d. Please notify the maintainer.", scn_minor, result);
ret = -EIO;
break;
}
if (partial) {
unsigned char cnt, cnt_max;
cnt_max = (partial > 24) ? 24 : partial;
- printk(KERN_DEBUG "dump(%d): ", scn->scn_minor);
+ printk(KERN_DEBUG "dump(%d): ", scn_minor);
for (cnt=0; cnt < cnt_max; cnt++) {
printk("%X ", obuf[cnt]);
}
struct scn_usb_data *scn;
struct usb_device *dev;
- ssize_t bytes_read = 0; /* Overall count of bytes_read */
- ssize_t ret = 0;
+ ssize_t bytes_read; /* Overall count of bytes_read */
+ ssize_t ret;
+
+ kdev_t scn_minor;
int partial; /* Number of bytes successfully read */
int this_read; /* Max number of bytes to read */
scn = file->private_data;
+ scn_minor = scn->scn_minor;
+
ibuf = scn->ibuf;
dev = scn->scn_dev;
bytes_read = 0;
+ ret = 0;
while (count) {
if (signal_pending(current)) {
- ret = -ERESTARTSYS;
+ ret = -EINTR;
break;
}
this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, 120*HZ);
- dbg("read stats(%d): result:%d this_read:%d partial:%d", scn->scn_minor, result, this_read, partial);
+ dbg("read stats(%d): result:%d this_read:%d partial:%d", scn_minor, result, this_read, partial);
if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */
- warn("read_scanner(%d): NAK received", scn->scn_minor);
+ warn("read_scanner(%d): NAK received", scn_minor);
ret = -ETIME;
break;
} else if ((result < 0) && (result != USB_ST_DATAUNDERRUN)) {
- warn("read_scanner(%d): funky result:%d. Please notify the maintainer.", scn->scn_minor, (int)result);
+ warn("read_scanner(%d): funky result:%d. Please notify the maintainer.", scn_minor, (int)result);
ret = -EIO;
break;
}
if (partial) {
unsigned char cnt, cnt_max;
cnt_max = (partial > 24) ? 24 : partial;
- printk(KERN_DEBUG "dump(%d): ", scn->scn_minor);
+ printk(KERN_DEBUG "dump(%d): ", scn_minor);
for (cnt=0; cnt < cnt_max; cnt++) {
printk("%X ", ibuf[cnt]);
}
if (dev->descriptor.idVendor == 0x1606) { /* Umax */
if (dev->descriptor.idProduct == 0x0010 || /* Astra 1220U */
- dev->descriptor.idProduct == 0x0030) { /* Astra 2000U */
+ dev->descriptor.idProduct == 0x0030 || /* Astra 2000U */
+ dev->descriptor.idProduct == 0x0002) { /* Astra 1236U */
valid_device = 1;
break;
}
}
if (dev->descriptor.idVendor == 0x04b8) { /* Seiko/Epson Corp. */
- if (dev->descriptor.idProduct == 0x0101 || /* Perfection 636 */
- dev->descriptor.idProduct == 0x0104) { /* Perfection 1200U */
+ if (dev->descriptor.idProduct == 0x0101 || /* Perfection 636U and 636Photo */
+ dev->descriptor.idProduct == 0x0103 || /* Perfection 610 */
+ dev->descriptor.idProduct == 0x0104) { /* Perfection 1200U and 1200Photo */
valid_device = 1;
break;
}
}
}
+ if (dev->descriptor.idVendor == 0x0461) { /* Primax/Colorado */
+ if (dev->descriptor.idProduct == 0x0300 || /* G2-300 #1 */
+ dev->descriptor.idProduct == 0x0380 || /* G2-600 #1 */
+ dev->descriptor.idProduct == 0x0301 || /* G2E-300 */
+ dev->descriptor.idProduct == 0x0381 || /* ReadyScan 636i */
+ dev->descriptor.idProduct == 0x0302 || /* G2-300 #2 */
+ dev->descriptor.idProduct == 0x0382 || /* G2-600 #2 */
+ dev->descriptor.idProduct == 0x0303 || /* G2E-300 */
+ dev->descriptor.idProduct == 0x0383 || /* G2E-600 */
+ dev->descriptor.idProduct == 0x0340 || /* Colorado USB 9600 */
+ dev->descriptor.idProduct == 0x0360 || /* Colorado USB 19200 */
+ dev->descriptor.idProduct == 0x0341 || /* Colorado 600u */
+ dev->descriptor.idProduct == 0x0361) { /* Colorado 1200u */
+ valid_device = 1;
+ break;
+ }
+ }
+
+ if (dev->descriptor.idVendor == 0x04a7) { /* Visioneer */
+ if (dev->descriptor.idProduct == 0x0221 || /* OneTouch 5300 */
+ dev->descriptor.idProduct == 0x0221 || /* OneTouch 7600 */
+ dev->descriptor.idProduct == 0x0231) { /* 6100 */
+ valid_device = 1;
+ break;
+ }
+ }
+
+ if (dev->descriptor.idVendor == 0x0458) { /* Genius */
+ if(dev->descriptor.idProduct == 0x2001) { /* ColorPage-Vivid Pro */
+ valid_device = 1;
+ break;
+ }
+ }
+
+ if (dev->descriptor.idVendor == 0x04a5) { /* Acer */
+ if(dev->descriptor.idProduct == 0x2060) { /* Prisa Acerscan 620U */
+ valid_device = 1;
+ break;
+ }
+ }
+
if (dev->descriptor.idVendor == vendor && /* User specified */
dev->descriptor.idProduct == product) { /* User specified */
valid_device = 1;
break;
}
-
-
+
+
} while (0);
-
+
if (!valid_device)
return NULL; /* We didn't find anything pleasing */
kfree (scn);
}
+#ifdef SCN_IOCTL
static int
ioctl_scanner(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
if (!p_scn_table[scn_minor]) {
err("ioctl_scanner(%d): invalid scn_minor", scn_minor);
- return -ENOIOCTLCMD;
+ return -ENODEV;
}
dev = p_scn_table[scn_minor]->scn_dev;
}
return 0;
}
+#endif /* SCN_IOCTL */
static struct
file_operations usb_scanner_fops = {
read: read_scanner,
write: write_scanner,
+#ifdef SCN_IOCTL
ioctl: ioctl_scanner,
+#endif /* SCN_IOCTL */
open: open_scanner,
release: close_scanner,
};
SCN_BASE_MNR
};
-void __exit usb_scanner_exit(void)
+void __exit
+usb_scanner_exit(void)
{
usb_deregister(&scanner_driver);
}
-int __init usb_scanner_init(void)
+int __init
+usb_scanner_init (void)
{
if (usb_register(&scanner_driver) < 0)
return -1;
#include <linux/malloc.h>
#include <linux/delay.h>
#include <linux/ioctl.h>
+
// #define DEBUG
+
+/* Enable to activate the ioctl interface. This is mainly meant for */
+/* development purposes until an ioctl number is officially registered */
+// #define SCN_IOCTL
+
#include <linux/usb.h>
+// #include "usb.h"
/* WARNING: These DATA_DUMP's can produce a lot of data. Caveat Emptor. */
// #define RD_DATA_DUMP /* Enable to dump data - limited to 24 bytes */
#
-# Makefile for the kernel USB device drivers.
+# Makefile for the USB serial device drivers.
#
-# Subdirs.
-
-SUB_DIRS :=
-MOD_SUB_DIRS := $(SUB_DIRS)
-MOD_IN_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS)
-
-# The target object and module list name.
-
-O_TARGET := serial.o
+O_TARGET := usb-serial.o
M_OBJS := usb-serial.o
-O_OBJS := usb-serial.o
+O_OBJS := usbserial.o visor.o whiteheat.o ftdi_sio.o keyspan_pda.o
MOD_LIST_NAME := USB_SERIAL_MODULES
-# Objects that export symbols.
-
-# Multipart objects.
-
-# Optional parts of multipart objects.
-
-# Object file lists.
-
-obj-y :=
-obj-m :=
-obj-n :=
-obj- :=
-
-# Each configuration option enables a list of files.
-
-obj-$(CONFIG_USB_SERIAL) += usb-serial.o
-
-# Extract lists of the multi-part drivers.
-# The 'int-*' lists are the intermediate files used to build the multi's.
-
-multi-y := $(filter $(list-multi), $(obj-y))
-multi-m := $(filter $(list-multi), $(obj-m))
-int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs)))
-int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs)))
-
-# Files that are both resident and modular: remove from modular.
-
-obj-m := $(filter-out $(obj-y), $(obj-m))
-int-m := $(filter-out $(int-y), $(int-m))
-
-# Take multi-part drivers out of obj-y and put components in.
-
-obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y)
-
-# Translate to Rules.make lists.
-
-O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y)))
-OX_OBJS := $(sort $(filter $(export-objs), $(obj-y)))
-M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m)))
-MX_OBJS := $(sort $(filter $(export-objs), $(obj-m)))
-MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m)))
-MIX_OBJS := $(sort $(filter $(export-objs), $(int-m)))
-
-# The global Rules.make.
-
include $(TOPDIR)/Rules.make
-# Link rules for multi-part drivers.
-
--- /dev/null
+/*
+ * USB FTDI SIO driver
+ *
+ * (C) Copyright (C) 1999, 2000
+ * 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.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (03/26/2000) gkh
+ * Split driver up into device specific pieces.
+ *
+ */
+
+/* Bill Ryder - bryder@sgi.com - wrote the FTDI_SIO implementation */
+/* Thanx to FTDI for so kindly providing details of the protocol required */
+/* to talk to the device */
+
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_SERIAL_FTDI_SIO
+
+#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/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+
+#include "ftdi_sio.h"
+
+#define FTDI_VENDOR_ID 0x0403
+#define FTDI_SIO_SERIAL_CONVERTER_ID 0x8372
+
+/* function prototypes for a FTDI serial converter */
+static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp);
+static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp);
+static int ftdi_sio_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static void ftdi_sio_read_bulk_callback (struct urb *urb);
+static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * old);
+static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
+
+/* All of the device info needed for the FTDI SIO serial converter */
+static __u16 ftdi_vendor_id = FTDI_VENDOR_ID;
+static __u16 ftdi_sio_product_id = FTDI_SIO_SERIAL_CONVERTER_ID;
+struct usb_serial_device_type ftdi_sio_device = {
+ name: "FTDI SIO",
+ idVendor: &ftdi_vendor_id, /* the FTDI vendor ID */
+ idProduct: &ftdi_sio_product_id, /* the FTDI SIO product id */
+ needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */
+ needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */
+ needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */
+ num_interrupt_in: 0,
+ num_bulk_in: 1,
+ num_bulk_out: 1,
+ num_ports: 1,
+ open: ftdi_sio_open,
+ close: ftdi_sio_close,
+ write: ftdi_sio_write,
+ ioctl: ftdi_sio_ioctl,
+ read_bulk_callback: ftdi_sio_read_bulk_callback,
+ set_termios: ftdi_sio_set_termios
+};
+
+/******************************************************************************
+ * FTDI SIO Serial Converter specific driver functions
+ ******************************************************************************/
+
+static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial = port->serial;
+ char buf[1]; /* Needed for the usb_control_msg I think */
+
+ dbg("ftdi_sio_open port %d", port->number);
+
+ if (port->active) {
+ dbg ("port already open");
+ return -EINVAL;
+ }
+ port->active = 1;
+
+ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
+ FTDI_SIO_RESET_SIO,
+ 0, buf, 0, HZ * 5);
+
+ /* FIXME - Should I really purge the buffers? */
+ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
+ FTDI_SIO_RESET_PURGE_RX,
+ 0, buf, 0, HZ * 5);
+
+ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
+ FTDI_SIO_RESET_PURGE_TX,
+ 0, buf, 0, HZ * 5);
+
+
+ /* As per usb_serial_init s/be CS8, B9600, 1 STOP BIT */
+ if ( usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_BAUDRATE_REQUEST,
+ FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
+ ftdi_sio_b9600, 0,
+ buf, 0, HZ * 5) < 0){
+ dbg("Error from baudrate urb");
+ return(-EINVAL);
+ }
+
+ if ( usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_DATA_REQUEST,
+ FTDI_SIO_SET_DATA_REQUEST_TYPE,
+ 8 | FTDI_SIO_SET_DATA_PARITY_NONE |
+ FTDI_SIO_SET_DATA_STOP_BITS_1, 0,
+ buf, 0, HZ * 5) < 0){
+ dbg("Error from cs8/noparity/1 stopbit urb");
+ return(-EINVAL);
+ }
+
+ /* Disable flow control */
+ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+ FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+ 0, 0,
+ buf, 0, HZ * 5) < 0) {
+ dbg("error from flowcontrol urb");
+ return(-EINVAL);
+ }
+
+ /* Turn on RTS and DTR since we are not flow controlling*/
+ /* FIXME - check for correct behaviour clocal vs non clocal */
+ /* FIXME - might be able to do both simultaneously */
+ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST,
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+ (unsigned)FTDI_SIO_SET_DTR_HIGH, 0,
+ buf, 0, HZ * 5) < 0) {
+ dbg("Error from DTR HIGH urb");
+ }
+ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST,
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+ (unsigned)FTDI_SIO_SET_RTS_HIGH, 0,
+ buf, 0, HZ * 5) < 0) {
+ dbg("Error from RTS HIGH urb");
+ }
+
+ /*Start reading from the device*/
+ if (usb_submit_urb(port->read_urb))
+ dbg("usb_submit_urb(read bulk) failed");
+
+ return (0);
+}
+
+
+static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial = port->serial;
+ char buf[1];
+
+ dbg("ftdi_sio_close port %d", port->number);
+
+ /* FIXME - might be able to do both simultaneously */
+ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST,
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+ (unsigned)FTDI_SIO_SET_DTR_LOW, 0,
+ buf, 0, HZ * 5) < 0) {
+ dbg("Error from DTR LOW urb");
+ }
+ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST,
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+ (unsigned)FTDI_SIO_SET_RTS_LOW, 0,
+ buf, 0, HZ * 5) < 0) {
+ dbg("Error from RTS LOW urb");
+ }
+
+ /* FIXME Should I flush the device here? - not doing it for now */
+
+ /* shutdown our bulk reads and writes */
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
+ port->active = 0;
+}
+
+
+
+/* The ftdi_sio requires the first byte to have:
+ B0 1
+ B1 0
+ B2..7 length of message excluding byte 0
+*/
+static int ftdi_sio_write (struct usb_serial_port *port, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct usb_serial *serial = port->serial;
+ const int data_offset = 1;
+
+ dbg("ftdi_sio_serial_write port %d, %d bytes", port->number, count);
+
+ if (count == 0) {
+ dbg("write request of 0 bytes");
+ return 0;
+ }
+
+ /* only do something if we have a bulk out endpoint */
+ if (serial->num_bulk_out) {
+ unsigned char *first_byte = port->write_urb->transfer_buffer;
+
+ if (port->write_urb->status == -EINPROGRESS) {
+ dbg ("already writing");
+ return 0;
+ }
+
+ count += data_offset;
+ count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+ if (count == 0) {
+ return 0;
+ }
+
+ /* Copy in the data to send */
+ if (from_user) {
+ copy_from_user(port->write_urb->transfer_buffer + data_offset ,
+ buf, count - data_offset );
+ }
+ else {
+ memcpy(port->write_urb->transfer_buffer + data_offset,
+ buf, count - data_offset );
+ }
+
+ /* Write the control byte at the front of the packet*/
+ first_byte = port->write_urb->transfer_buffer;
+ *first_byte = 1 | ((count-data_offset) << 2) ;
+
+#ifdef DEBUG
+ dbg("Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]);
+
+ if (count) {
+ int i;
+ printk (KERN_DEBUG __FILE__ ": data written - length = %d, data = ", count);
+ for (i = 0; i < count; ++i) {
+ printk ("0x%02x ", first_byte[i]);
+ if (first_byte[i] > ' ' && first_byte[i] < '~') {
+ printk("%c ", first_byte[i]);
+ } else {
+ printk(" ");
+ }
+ }
+
+
+ printk ("\n");
+ }
+
+#endif
+ /* send the data out the bulk port */
+ port->write_urb->transfer_buffer_length = count;
+
+ if (usb_submit_urb(port->write_urb))
+ dbg("usb_submit_urb(write bulk) failed");
+
+ dbg("write returning: %d", count - data_offset);
+ return (count - data_offset);
+ }
+
+ /* no bulk out, so return 0 bytes written */
+ return 0;
+}
+
+
+static void ftdi_sio_read_bulk_callback (struct urb *urb)
+{ /* ftdi_sio_serial_buld_callback */
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
+ const int data_offset = 2;
+ int i;
+
+ dbg("ftdi_sio_read_bulk_callback");
+
+ if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) {
+ return;
+ }
+
+ if (urb->status) {
+ dbg("nonzero read bulk status received: %d", urb->status);
+ return;
+ }
+
+#ifdef DEBUG
+ if (urb->actual_length > 2) {
+ printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length);
+ for (i = 0; i < urb->actual_length; ++i) {
+ printk ("0x%.2x ", data[i]);
+ if (data[i] > ' ' && data[i] < '~') {
+ printk("%c ", data[i]);
+ } else {
+ printk(" ");
+ }
+ }
+ printk ("\n");
+ }
+#endif
+
+
+ if (urb->actual_length > data_offset) {
+ tty = port->tty;
+ for (i = data_offset ; i < urb->actual_length ; ++i) {
+ tty_insert_flip_char(tty, data[i], 0);
+ }
+ tty_flip_buffer_push(tty);
+ }
+
+ /* Continue trying to always read */
+ if (usb_submit_urb(urb))
+ dbg("failed resubmitting read urb");
+
+ return;
+} /* ftdi_sio_serial_read_bulk_callback */
+
+
+static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+{
+ struct usb_serial *serial = port->serial;
+ unsigned int cflag = port->tty->termios->c_cflag;
+ __u16 urb_value; /* Will hold the new flags */
+ char buf[1]; /* Perhaps I should dynamically alloc this? */
+
+ dbg("ftdi_sio_set_termios port %d", port->number);
+
+ /* FIXME - we should keep the old termios really */
+ /* FIXME -For this cut I don't care if the line is really changing or
+ not - so just do the change regardless */
+
+ /* Set number of data bits, parity, stop bits */
+
+ urb_value = 0;
+ urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
+ FTDI_SIO_SET_DATA_STOP_BITS_1);
+ urb_value |= (cflag & PARENB ?
+ (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD :
+ FTDI_SIO_SET_DATA_PARITY_EVEN) :
+ FTDI_SIO_SET_DATA_PARITY_NONE);
+ if (cflag & CSIZE) {
+ switch (cflag & CSIZE) {
+ case CS5: urb_value |= 5; dbg("Setting CS5"); break;
+ case CS6: urb_value |= 6; dbg("Setting CS6"); break;
+ case CS7: urb_value |= 7; dbg("Setting CS7"); break;
+ case CS8: urb_value |= 8; dbg("Setting CS8"); break;
+ default:
+ dbg("CSIZE was set but not CS5-CS8");
+ }
+ }
+ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_DATA_REQUEST,
+ FTDI_SIO_SET_DATA_REQUEST_TYPE,
+ urb_value , 0,
+ buf, 0, 100) < 0) {
+ dbg("FAILED to set databits/stopbits/parity");
+ }
+
+ /* Now do the baudrate */
+ /* FIXME - should drop lines on B0 */
+ /* FIXME Should also handle CLOCAL here */
+
+ switch(cflag & CBAUD){
+ case B300: urb_value = ftdi_sio_b300; dbg("Set to 300"); break;
+ case B600: urb_value = ftdi_sio_b600; dbg("Set to 600") ; break;
+ case B1200: urb_value = ftdi_sio_b1200; dbg("Set to 1200") ; break;
+ case B2400: urb_value = ftdi_sio_b2400; dbg("Set to 2400") ; break;
+ case B4800: urb_value = ftdi_sio_b4800; dbg("Set to 4800") ; break;
+ case B9600: urb_value = ftdi_sio_b9600; dbg("Set to 9600") ; break;
+ case B19200: urb_value = ftdi_sio_b19200; dbg("Set to 19200") ; break;
+ case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break;
+ case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break;
+ case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break;
+ default: dbg("FTDI_SIO does not support the baudrate requested");
+ /* FIXME - how to return an error for this? */ break;
+ }
+ /* Send the URB */
+ if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_BAUDRATE_REQUEST,
+ FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
+ urb_value, 0,
+ buf, 0, 100) < 0) {
+ dbg("urb failed to set baurdrate");
+ }
+ return;
+}
+
+
+/*FIXME - the beginnings of this implementation - not even hooked into the driver yet */
+static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
+{
+ struct usb_serial *serial = port->serial;
+ __u16 urb_value=0; /* Will hold the new flags */
+ char buf[1];
+ int ret, mask;
+
+ dbg("ftdi_sio_ioctl port %d", port->number);
+
+ /* Based on code from acm.c */
+ switch (cmd) {
+
+ case TIOCMGET:
+ /* Request the status from the device */
+ if ((ret = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_GET_MODEM_STATUS_REQUEST,
+ FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
+ 0, 0,
+ buf, 1, HZ * 5)) < 0 ) {
+ dbg("Get not get modem status of device");
+ return(ret);
+ }
+
+ return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
+ (buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
+ (buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) |
+ (buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0),
+ (unsigned long *) arg);
+ break;
+
+ case TIOCMSET:
+ case TIOCMBIS:
+ case TIOCMBIC:
+ if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
+
+ /* FIXME Need to remember if we have set DTR or RTS since we
+ can't ask the device */
+ /* FIXME - also need to find the meaning of TIOCMBIS/BIC/SET */
+ if (mask & TIOCM_DTR) {
+ switch(cmd) {
+ case TIOCMSET:
+ urb_value = FTDI_SIO_SET_DTR_HIGH | FTDI_SIO_SET_RTS_LOW;
+ break;
+
+ case TIOCMBIS:
+ /* Will leave RTS alone and set DTR */
+ urb_value = FTDI_SIO_SET_DTR_HIGH;
+ break;
+
+ case TIOCMBIC:
+ urb_value = FTDI_SIO_SET_DTR_LOW;
+ break;
+ }
+ }
+
+ if (mask & TIOCM_RTS) {
+ switch(cmd) {
+ case TIOCMSET:
+ urb_value = FTDI_SIO_SET_DTR_LOW | FTDI_SIO_SET_RTS_HIGH;
+ break;
+
+ case TIOCMBIS:
+ /* Will leave DTR and set RTS */
+ urb_value = FTDI_SIO_SET_RTS_HIGH;
+ break;
+
+ case TIOCMBIC:
+ /* Will unset RTS */
+ urb_value = FTDI_SIO_SET_RTS_LOW;
+ break;
+ }
+ }
+
+
+ return(usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST,
+ FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
+ urb_value , 0,
+ buf, 0, HZ * 5));
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+#endif /* CONFIG_USB_SERIAL_FTDI_SIO */
+
+
--- /dev/null
+/*
+ * USB Keyspan PDA Converter driver
+ *
+ * (C) Copyright (C) 1999, 2000
+ * 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.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (03/26/2000) gkh
+ * Split driver up into device specific pieces.
+ *
+ */
+
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
+
+#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/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+#include <linux/usb.h>
+
+struct ezusb_hex_record {
+ __u16 address;
+ __u8 data_size;
+ __u8 data[16];
+};
+
+#include "keyspan_pda_fw.h"
+
+#include "usb-serial.h"
+
+#define KEYSPAN_VENDOR_ID 0x06cd
+#define KEYSPAN_PDA_FAKE_ID 0x0103
+#define KEYSPAN_PDA_ID 0x0104 /* no clue */
+
+/* function prototypes for a Keyspan PDA serial converter */
+static int keyspan_pda_open (struct usb_serial_port *port,
+ struct file *filp);
+static void keyspan_pda_close (struct usb_serial_port *port,
+ struct file *filp);
+static int keyspan_pda_startup (struct usb_serial *serial);
+static void keyspan_pda_rx_throttle (struct usb_serial_port *port);
+static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port);
+static int keyspan_pda_setbaud (struct usb_serial *serial, int baud);
+static int keyspan_pda_write_room (struct usb_serial_port *port);
+static int keyspan_pda_write (struct usb_serial_port *port,
+ int from_user,
+ const unsigned char *buf,
+ int count);
+static void keyspan_pda_write_bulk_callback (struct urb *urb);
+static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port);
+static int keyspan_pda_ioctl (struct usb_serial_port *port,
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg);
+static void keyspan_pda_set_termios (struct usb_serial_port *port,
+ struct termios *old);
+static void keyspan_pda_break_ctl (struct usb_serial_port *port,
+ int break_state);
+static int keyspan_pda_fake_startup (struct usb_serial *serial);
+
+
+/* All of the device info needed for the Keyspan PDA serial converter */
+static __u16 keyspan_vendor_id = KEYSPAN_VENDOR_ID;
+static __u16 keyspan_pda_fake_product_id = KEYSPAN_PDA_FAKE_ID;
+static __u16 keyspan_pda_product_id = KEYSPAN_PDA_ID;
+struct usb_serial_device_type keyspan_pda_fake_device = {
+ name: "Keyspan PDA - (prerenumeration)",
+ idVendor: &keyspan_vendor_id, /* the Keyspan PDA vendor ID */
+ idProduct: &keyspan_pda_fake_product_id, /* the Keyspan PDA initial product id */
+ needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */
+ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */
+ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */
+ num_interrupt_in: NUM_DONT_CARE,
+ num_bulk_in: NUM_DONT_CARE,
+ num_bulk_out: NUM_DONT_CARE,
+ num_ports: 1,
+ startup: keyspan_pda_fake_startup
+};
+struct usb_serial_device_type keyspan_pda_device = {
+ name: "Keyspan PDA",
+ idVendor: &keyspan_vendor_id, /* the Keyspan PDA vendor ID */
+ idProduct: &keyspan_pda_product_id, /* the Keyspan PDA product id */
+ needs_interrupt_in: MUST_HAVE,
+ needs_bulk_in: DONT_CARE,
+ needs_bulk_out: MUST_HAVE,
+ num_interrupt_in: 1,
+ num_bulk_in: 0,
+ num_bulk_out: 1,
+ num_ports: 1,
+ open: keyspan_pda_open,
+ close: keyspan_pda_close,
+ write: keyspan_pda_write,
+ write_room: keyspan_pda_write_room,
+ write_bulk_callback: keyspan_pda_write_bulk_callback,
+ chars_in_buffer: keyspan_pda_chars_in_buffer,
+ throttle: keyspan_pda_rx_throttle,
+ unthrottle: keyspan_pda_rx_unthrottle,
+ startup: keyspan_pda_startup,
+ ioctl: keyspan_pda_ioctl,
+ set_termios: keyspan_pda_set_termios,
+ break_ctl: keyspan_pda_break_ctl,
+};
+
+
+static void keyspan_pda_rx_interrupt (struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
+ int i;
+
+ /* the urb might have been killed. */
+ if (urb->status)
+ return;
+
+ if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
+ return;
+ }
+
+ /* see if the message is data or a status interrupt */
+ switch (data[0]) {
+ case 0:
+ /* rest of message is rx data */
+ if (urb->actual_length) {
+ tty = serial->port[0].tty;
+ for (i = 1; i < urb->actual_length ; ++i) {
+ tty_insert_flip_char(tty, data[i], 0);
+ }
+ tty_flip_buffer_push(tty);
+ }
+ break;
+ case 1:
+ /* status interrupt */
+ dbg(" rx int, d1=%d, d2=%d", data[1], data[2]);
+ switch (data[1]) {
+ case 1: /* modemline change */
+ break;
+ case 2: /* tx unthrottle interrupt */
+ tty = serial->port[0].tty;
+ serial->tx_throttled = 0;
+ wake_up(&serial->write_wait); /* wake up writer */
+ wake_up(&tty->write_wait); /* them too */
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* INT urbs are automatically re-submitted */
+}
+
+
+static void keyspan_pda_rx_throttle (struct usb_serial_port *port)
+{
+ /* stop receiving characters. We just turn off the URB request, and
+ let chars pile up in the device. If we're doing hardware
+ flowcontrol, the device will signal the other end when its buffer
+ fills up. If we're doing XON/XOFF, this would be a good time to
+ send an XOFF, although it might make sense to foist that off
+ upon the device too. */
+
+ dbg("keyspan_pda_rx_throttle port %d", port->number);
+ usb_unlink_urb(port->read_urb);
+}
+
+
+static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port)
+{
+ /* just restart the receive interrupt URB */
+ dbg("keyspan_pda_rx_unthrottle port %d", port->number);
+ if (usb_submit_urb(port->read_urb))
+ dbg(" usb_submit_urb(read urb) failed");
+ return;
+}
+
+
+static int keyspan_pda_setbaud (struct usb_serial *serial, int baud)
+{
+ int rc;
+ int bindex;
+
+ switch(baud) {
+ case 110: bindex = 0; break;
+ case 300: bindex = 1; break;
+ case 1200: bindex = 2; break;
+ case 2400: bindex = 3; break;
+ case 4800: bindex = 4; break;
+ case 9600: bindex = 5; break;
+ case 19200: bindex = 6; break;
+ case 38400: bindex = 7; break;
+ case 57600: bindex = 8; break;
+ case 115200: bindex = 9; break;
+ default: return -EINVAL;
+ }
+
+ /* rather than figure out how to sleep while waiting for this
+ to complete, I just use the "legacy" API. */
+ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ 0, /* set baud */
+ USB_TYPE_VENDOR
+ | USB_RECIP_INTERFACE
+ | USB_DIR_OUT, /* type */
+ bindex, /* value */
+ 0, /* index */
+ NULL, /* &data */
+ 0, /* size */
+ 2*HZ); /* timeout */
+ return(rc);
+}
+
+
+static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state)
+{
+ struct usb_serial *serial = port->serial;
+ int value;
+ if (break_state == -1)
+ value = 1; /* start break */
+ else
+ value = 0; /* clear break */
+ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ 4, /* set break */
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ value, 0, NULL, 0, 2*HZ);
+ /* there is something funky about this.. the TCSBRK that 'cu' performs
+ ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4
+ seconds apart, but it feels like the break sent isn't as long as it
+ is on /dev/ttyS0 */
+}
+
+
+static void keyspan_pda_set_termios (struct usb_serial_port *port,
+ struct termios *old_termios)
+{
+ struct usb_serial *serial = port->serial;
+ unsigned int cflag = port->tty->termios->c_cflag;
+
+ /* cflag specifies lots of stuff: number of stop bits, parity, number
+ of data bits, baud. What can the device actually handle?:
+ CSTOPB (1 stop bit or 2)
+ PARENB (parity)
+ CSIZE (5bit .. 8bit)
+ There is minimal hw support for parity (a PSW bit seems to hold the
+ parity of whatever is in the accumulator). The UART either deals
+ with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data,
+ 1 special, stop). So, with firmware changes, we could do:
+ 8N1: 10 bit
+ 8N2: 11 bit, extra bit always (mark?)
+ 8[EOMS]1: 11 bit, extra bit is parity
+ 7[EOMS]1: 10 bit, b0/b7 is parity
+ 7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?)
+
+ HW flow control is dictated by the tty->termios->c_cflags & CRTSCTS
+ bit.
+
+ For now, just do baud. */
+
+ switch (cflag & CBAUD) {
+ /* we could support more values here, just need to calculate
+ the necessary divisors in the firmware. <asm/termbits.h>
+ has the Bnnn constants. */
+ case B110: keyspan_pda_setbaud(serial, 110); break;
+ case B300: keyspan_pda_setbaud(serial, 300); break;
+ case B1200: keyspan_pda_setbaud(serial, 1200); break;
+ case B2400: keyspan_pda_setbaud(serial, 2400); break;
+ case B4800: keyspan_pda_setbaud(serial, 4800); break;
+ case B9600: keyspan_pda_setbaud(serial, 9600); break;
+ case B19200: keyspan_pda_setbaud(serial, 19200); break;
+ case B38400: keyspan_pda_setbaud(serial, 38400); break;
+ case B57600: keyspan_pda_setbaud(serial, 57600); break;
+ case B115200: keyspan_pda_setbaud(serial, 115200); break;
+ default: dbg("can't handle requested baud rate"); break;
+ }
+}
+
+
+/* modem control pins: DTR and RTS are outputs and can be controlled.
+ DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be
+ read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */
+
+static int keyspan_pda_get_modem_info(struct usb_serial *serial,
+ unsigned char *value)
+{
+ int rc;
+ unsigned char data;
+ rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ 3, /* get pins */
+ USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN,
+ 0, 0, &data, 1, 2*HZ);
+ if (rc > 0)
+ *value = data;
+ return rc;
+}
+
+
+static int keyspan_pda_set_modem_info(struct usb_serial *serial,
+ unsigned char value)
+{
+ int rc;
+ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ 3, /* set pins */
+ USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_OUT,
+ value, 0, NULL, 0, 2*HZ);
+ return rc;
+}
+
+
+static int keyspan_pda_ioctl(struct usb_serial_port *port, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct usb_serial *serial = port->serial;
+ int rc;
+ unsigned int value;
+ unsigned char status, mask;
+
+ switch (cmd) {
+ case TIOCMGET: /* get modem pins state */
+ rc = keyspan_pda_get_modem_info(serial, &status);
+ if (rc < 0)
+ return rc;
+ value =
+ ((status & (1<<7)) ? TIOCM_DTR : 0) |
+ ((status & (1<<6)) ? TIOCM_CAR : 0) |
+ ((status & (1<<5)) ? TIOCM_RNG : 0) |
+ ((status & (1<<4)) ? TIOCM_DSR : 0) |
+ ((status & (1<<3)) ? TIOCM_CTS : 0) |
+ ((status & (1<<2)) ? TIOCM_RTS : 0);
+ if (copy_to_user((unsigned int *)arg, &value, sizeof(int)))
+ return -EFAULT;
+ return 0;
+ case TIOCMSET: /* set a state as returned by MGET */
+ if (copy_from_user(&value, (unsigned int *)arg, sizeof(int)))
+ return -EFAULT;
+ status =
+ ((value & TIOCM_DTR) ? (1<<7) : 0) |
+ ((value & TIOCM_CAR) ? (1<<6) : 0) |
+ ((value & TIOCM_RNG) ? (1<<5) : 0) |
+ ((value & TIOCM_DSR) ? (1<<4) : 0) |
+ ((value & TIOCM_CTS) ? (1<<3) : 0) |
+ ((value & TIOCM_RTS) ? (1<<2) : 0);
+ rc = keyspan_pda_set_modem_info(serial, status);
+ if (rc < 0)
+ return rc;
+ return 0;
+ case TIOCMBIS: /* set bits in bitmask <arg> */
+ case TIOCMBIC: /* clear bits from bitmask <arg> */
+ if (copy_from_user(&value, (unsigned int *)arg, sizeof(int)))
+ return -EFAULT;
+ rc = keyspan_pda_get_modem_info(serial, &status);
+ if (rc < 0)
+ return rc;
+ mask =
+ ((value & TIOCM_RTS) ? (1<<2) : 0) |
+ ((value & TIOCM_DTR) ? (1<<7) : 0);
+ if (cmd == TIOCMBIS)
+ status |= mask;
+ else
+ status &= ~mask;
+ rc = keyspan_pda_set_modem_info(serial, status);
+ if (rc < 0)
+ return rc;
+ return 0;
+ case TIOCMIWAIT:
+ /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
+ /* TODO */
+ case TIOCGICOUNT:
+ /* return count of modemline transitions */
+ return 0; /* TODO */
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static int keyspan_pda_write(struct usb_serial_port *port, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct usb_serial *serial = port->serial;
+ int request_unthrottle = 0;
+ int rc = 0;
+ DECLARE_WAITQUEUE(wait, current);
+
+ /* guess how much room is left in the device's ring buffer, and if we
+ want to send more than that, check first, updating our notion of
+ what is left. If our write will result in no room left, ask the
+ device to give us an interrupt when the room available rises above
+ a threshold, and hold off all writers (eventually, those using
+ select() or poll() too) until we receive that unthrottle interrupt.
+ Block if we can't write anything at all, otherwise write as much as
+ we can. */
+
+ if (count == 0) {
+ dbg(" write request of 0 bytes");
+ return (0);
+ }
+
+ /* we might block because of:
+ the TX urb is in-flight (wait until it completes)
+ the device is full (wait until it says there is room)
+ */
+ while (port->write_urb->status == -EINPROGRESS) {
+ if (0 /* file->f_flags & O_NONBLOCK */) {
+ rc = -EAGAIN;
+ goto err;
+ }
+ interruptible_sleep_on(&serial->write_wait);
+ if (signal_pending(current)) {
+ rc = -ERESTARTSYS;
+ goto err;
+ }
+ }
+
+ /* at this point the URB is in our control, nobody else can submit it
+ again (the only sudden transition was the one from EINPROGRESS to
+ finished) */
+
+ /* the next potential block is that our TX process might be throttled.
+ The transition from throttled->not happens because of an Rx
+ interrupt, and the wake_up occurs during the same interrupt, so we
+ have to be careful to avoid a race that would cause us to sleep
+ forever. */
+
+ add_wait_queue(&serial->write_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (serial->tx_throttled) {
+ /* device can't accomodate any more characters. Sleep until it
+ can. Woken up by an Rx interrupt message, which clears
+ tx_throttled first. */
+ dbg(" tx_throttled, going to sleep");
+ if (signal_pending(current)) {
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&serial->write_wait, &wait);
+ dbg(" woke up because of signal");
+ rc = -ERESTARTSYS;
+ goto err;
+ }
+ schedule();
+ dbg(" woke up");
+ }
+ remove_wait_queue(&serial->write_wait, &wait);
+ set_current_state(TASK_RUNNING);
+
+ count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+ if (count > serial->tx_room) {
+ unsigned char room;
+ /* Looks like we might overrun the Tx buffer. Ask the device
+ how much room it really has */
+ rc = usb_control_msg(serial->dev,
+ usb_rcvctrlpipe(serial->dev, 0),
+ 6, /* write_room */
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+ | USB_DIR_IN,
+ 0, /* value: 0 means "remaining room" */
+ 0, /* index */
+ &room,
+ 1,
+ 2*HZ);
+ if (rc < 0) {
+ dbg(" roomquery failed");
+ return rc; /* failed */
+ }
+ if (rc == 0) {
+ dbg(" roomquery returned 0 bytes");
+ return -EIO; /* device didn't return any data */
+ }
+ dbg(" roomquery says %d", room);
+ serial->tx_room = room;
+ if (count > serial->tx_room) {
+ /* we're about to completely fill the Tx buffer, so
+ we'll be throttled afterwards. */
+ count = serial->tx_room;
+ request_unthrottle = 1;
+ }
+ }
+ serial->tx_room -= count;
+
+ if (count) {
+ /* now transfer data */
+ if (from_user) {
+ copy_from_user(port->write_urb->transfer_buffer, buf, count);
+ }
+ else {
+ memcpy (port->write_urb->transfer_buffer, buf, count);
+ }
+ /* send the data out the bulk port */
+ port->write_urb->transfer_buffer_length = count;
+
+ if (usb_submit_urb(port->write_urb))
+ dbg(" usb_submit_urb(write bulk) failed");
+ }
+ else {
+ /* There wasn't any room left, so we are throttled until
+ the buffer empties a bit */
+ request_unthrottle = 1;
+ }
+
+ if (request_unthrottle) {
+ dbg(" request_unthrottle");
+ /* ask the device to tell us when the tx buffer becomes
+ sufficiently empty */
+ serial->tx_throttled = 1; /* block writers */
+ rc = usb_control_msg(serial->dev,
+ usb_sndctrlpipe(serial->dev, 0),
+ 7, /* request_unthrottle */
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+ | USB_DIR_OUT,
+ 16, /* value: threshold */
+ 0, /* index */
+ NULL,
+ 0,
+ 2*HZ);
+ }
+
+ return (count);
+ err:
+ return (rc);
+}
+
+
+static void keyspan_pda_write_bulk_callback (struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
+
+ if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
+ return;
+ }
+
+ wake_up_interruptible(&serial->write_wait);
+
+ tty = port->tty;
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+
+ wake_up_interruptible(&tty->write_wait);
+}
+
+
+static int keyspan_pda_write_room (struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+
+ /* used by n_tty.c for processing of tabs and such. Giving it our
+ conservative guess is probably good enough, but needs testing by
+ running a console through the device. */
+
+ return (serial->tx_room);
+}
+
+
+static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+
+ /* when throttled, return at least WAKEUP_CHARS to tell select() (via
+ n_tty.c:normal_poll() ) that we're not writeable. */
+ if (serial->tx_throttled)
+ return 256;
+ return 0;
+}
+
+
+static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial = port->serial;
+ unsigned char room;
+ int rc;
+
+ if (port->active) {
+ return -EINVAL;
+ }
+ port->active = 1;
+
+ /* find out how much room is in the Tx ring */
+ rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ 6, /* write_room */
+ USB_TYPE_VENDOR | USB_RECIP_INTERFACE
+ | USB_DIR_IN,
+ 0, /* value */
+ 0, /* index */
+ &room,
+ 1,
+ 2*HZ);
+ if (rc < 0) {
+ dbg(" roomquery failed");
+ return rc; /* failed */
+ }
+ if (rc == 0) {
+ dbg(" roomquery returned 0 bytes");
+ return -EIO; /* device didn't return any data */
+ }
+ serial->tx_room = room;
+ serial->tx_throttled = room ? 0 : 1;
+
+ /* the normal serial device seems to always turn on DTR and RTS here,
+ so do the same */
+ if (port->tty->termios->c_cflag & CBAUD)
+ keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) );
+ else
+ keyspan_pda_set_modem_info(serial, 0);
+
+ /*Start reading from the device*/
+ if (usb_submit_urb(port->read_urb))
+ dbg(" usb_submit_urb(read int) failed");
+
+ return (0);
+}
+
+
+static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial = port->serial;
+
+ /* the normal serial device seems to always shut off DTR and RTS now */
+ if (port->tty->termios->c_cflag & HUPCL)
+ keyspan_pda_set_modem_info(serial, 0);
+
+ /* shutdown our bulk reads and writes */
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
+ port->active = 0;
+}
+
+
+/* download the firmware to a "fake" device (pre-renumeration) */
+static int keyspan_pda_fake_startup (struct usb_serial *serial)
+{
+ int response;
+ const struct ezusb_hex_record *record;
+
+ /* download the firmware here ... */
+ response = ezusb_set_reset(serial, 1);
+
+ record = &keyspan_pda_firmware[0];
+ while(record->address != 0xffff) {
+ response = ezusb_writememory(serial, record->address,
+ (unsigned char *)record->data,
+ record->data_size, 0xa0);
+ if (response < 0) {
+ err("ezusb_writememory failed for Keyspan PDA "
+ "firmware (%d %04X %p %d)",
+ response,
+ record->address, record->data, record->data_size);
+ break;
+ }
+ record++;
+ }
+ /* bring device out of reset. Renumeration will occur in a moment
+ and the new device will bind to the real driver */
+ response = ezusb_set_reset(serial, 0);
+
+ /* we want this device to fail to have a driver assigned to it. */
+ return (1);
+}
+
+
+/* do some startup allocations not currently performed by usb_serial_probe() */
+static int keyspan_pda_startup (struct usb_serial *serial)
+{
+ struct usb_endpoint_descriptor *intin;
+ intin = serial->port[0].interrupt_in_endpoint;
+
+ /* set up the receive interrupt urb */
+ FILL_INT_URB(serial->port[0].read_urb, serial->dev,
+ usb_rcvintpipe(serial->dev, intin->bEndpointAddress),
+ serial->port[0].interrupt_in_buffer,
+ intin->wMaxPacketSize,
+ keyspan_pda_rx_interrupt,
+ serial,
+ intin->bInterval);
+
+ init_waitqueue_head(&serial->write_wait);
+
+ return (0);
+}
+
+#endif /* CONFIG_USB_SERIAL_KEYSPAN_PDA */
+
+
+++ /dev/null
-/*
- * USB Serial Converter driver
- *
- * (C) Copyright (C) 1999, 2000
- * 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 was originally based on the ACM driver by Armin Fuerst (which was
- * based on a driver by Brad Keryan)
- *
- * See Documentation/usb/usb-serial.txt for more information on using this driver
- *
- * (03/19/2000) gkh
- * Fixed oops that could happen when device was removed while a program
- * was talking to the device.
- * Removed the static urbs and now all urbs are created and destroyed
- * dynamically.
- * Reworked the internal interface. Now everything is based on the
- * usb_serial_port structure instead of the larger usb_serial structure.
- * This fixes the bug that a multiport device could not have more than
- * one port open at one time.
- *
- * (03/17/2000) gkh
- * Added config option for debugging messages.
- * Added patch for keyspan pda from Brian Warner.
- *
- * (03/06/2000) gkh
- * Added the keyspan pda code from Brian Warner <warner@lothar.com>
- * Moved a bunch of the port specific stuff into its own structure. This
- * is in anticipation of the true multiport devices (there's a bug if you
- * try to access more than one port of any multiport device right now)
- *
- * (02/21/2000) gkh
- * Made it so that any serial devices only have to specify which functions
- * they want to overload from the generic function calls (great,
- * inheritance in C, in a driver, just what I wanted...)
- * Added support for set_termios and ioctl function calls. No drivers take
- * advantage of this yet.
- * Removed the #ifdef MODULE, now there is no module specific code.
- * Cleaned up a few comments in usb-serial.h that were wrong (thanks again
- * to Miles Lott).
- * Small fix to get_free_serial.
- *
- * (02/14/2000) gkh
- * Removed the Belkin and Peracom functionality from the driver due to
- * the lack of support from the vendor, and me not wanting people to
- * accidenatly buy the device, expecting it to work with Linux.
- * Added read_bulk_callback and write_bulk_callback to the type structure
- * for the needs of the FTDI and WhiteHEAT driver.
- * Changed all reverences to FTDI to FTDI_SIO at the request of Bill
- * Ryder.
- * Changed the output urb size back to the max endpoint size to make
- * the ftdi_sio driver have it easier, and due to the fact that it didn't
- * really increase the speed any.
- *
- * (02/11/2000) gkh
- * Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a
- * patch from Miles Lott (milos@insync.net).
- * Fixed bug with not restoring the minor range that a device grabs, if
- * the startup function fails (thanks Miles for finding this).
- *
- * (02/05/2000) gkh
- * Added initial framework for the Keyspan PDA serial converter so that
- * Brian Warner has a place to put his code.
- * Made the ezusb specific functions generic enough that different
- * devices can use them (whiteheat and keyspan_pda both need them).
- * Split out a whole bunch of structure and other stuff to a seperate
- * usb-serial.h file.
- * Made the Visor connection messages a little more understandable, now
- * that Miles Lott (milos@insync.net) has gotten the Generic channel to
- * work. Also made them always show up in the log file.
- *
- * (01/25/2000) gkh
- * Added initial framework for FTDI serial converter so that Bill Ryder
- * has a place to put his code.
- * Added the vendor specific info from Handspring. Now we can print out
- * informational debug messages as well as understand what is happening.
- *
- * (01/23/2000) gkh
- * Fixed problem of crash when trying to open a port that didn't have a
- * device assigned to it. Made the minor node finding a little smarter,
- * now it looks to find a continous space for the new device.
- *
- * (01/21/2000) gkh
- * Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net)
- * Fixed get_serial_by_minor which was all messed up for multi port
- * devices. Fixed multi port problem for generic devices. Now the number
- * of ports is determined by the number of bulk out endpoints for the
- * generic device.
- *
- * (01/19/2000) gkh
- * Removed lots of cruft that was around from the old (pre urb) driver
- * interface.
- * Made the serial_table dynamic. This should save lots of memory when
- * the number of minor nodes goes up to 256.
- * Added initial support for devices that have more than one port.
- * Added more debugging comments for the Visor, and added a needed
- * set_configuration call.
- *
- * (01/17/2000) gkh
- * Fixed the WhiteHEAT firmware (my processing tool had a bug)
- * and added new debug loader firmware for it.
- * Removed the put_char function as it isn't really needed.
- * Added visor startup commands as found by the Win98 dump.
- *
- * (01/13/2000) gkh
- * Fixed the vendor id for the generic driver to the one I meant it to be.
- *
- * (01/12/2000) gkh
- * Forget the version numbering...that's pretty useless...
- * Made the driver able to be compiled so that the user can select which
- * converter they want to use. This allows people who only want the Visor
- * support to not pay the memory size price of the WhiteHEAT.
- * Fixed bug where the generic driver (idVendor=0000 and idProduct=0000)
- * grabbed the root hub. Not good.
- *
- * version 0.4.0 (01/10/2000) gkh
- * Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT
- * device. Added startup function to allow firmware to be downloaded to
- * a device if it needs to be.
- * Added firmware download logic to the WhiteHEAT device.
- * Started to add #defines to split up the different drivers for potential
- * configuration option.
- *
- * version 0.3.1 (12/30/99) gkh
- * Fixed problems with urb for bulk out.
- * Added initial support for multiple sets of endpoints. This enables
- * the Handspring Visor to be attached successfully. Only the first
- * bulk in / bulk out endpoint pair is being used right now.
- *
- * version 0.3.0 (12/27/99) gkh
- * Added initial support for the Handspring Visor based on a patch from
- * Miles Lott (milos@sneety.insync.net)
- * Cleaned up the code a bunch and converted over to using urbs only.
- *
- * version 0.2.3 (12/21/99) gkh
- * Added initial support for the Connect Tech WhiteHEAT converter.
- * Incremented the number of ports in expectation of getting the
- * WhiteHEAT to work properly (4 ports per connection).
- * Added notification on insertion and removal of what port the
- * device is/was connected to (and what kind of device it was).
- *
- * version 0.2.2 (12/16/99) gkh
- * Changed major number to the new allocated number. We're legal now!
- *
- * version 0.2.1 (12/14/99) gkh
- * Fixed bug that happens when device node is opened when there isn't a
- * device attached to it. Thanks to marek@webdesign.no for noticing this.
- *
- * version 0.2.0 (11/10/99) gkh
- * Split up internals to make it easier to add different types of serial
- * converters to the code.
- * Added a "generic" driver that gets it's vendor and product id
- * from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net)
- * for the idea and sample code (from the usb scanner driver.)
- * Cleared up any licensing questions by releasing it under the GNU GPL.
- *
- * version 0.1.2 (10/25/99) gkh
- * Fixed bug in detecting device.
- *
- * version 0.1.1 (10/05/99) gkh
- * Changed the major number to not conflict with anything else.
- *
- * version 0.1 (09/28/99) gkh
- * Can recognize the two different devices and start up a read from
- * device when asked to. Writes also work. No control signals yet, this
- * all is vendor specific data (i.e. no spec), also no control for
- * different baud rates or other bit settings.
- * Currently we are using the same devid as the acm driver. This needs
- * to change.
- *
- */
-
-#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/malloc.h>
-#include <linux/fcntl.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/tty.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
- #define DEBUG
-#else
- #undef DEBUG
-#endif
-#include <linux/usb.h>
-
-#ifdef CONFIG_USB_SERIAL_WHITEHEAT
-#include "whiteheat.h" /* firmware for the ConnectTech WhiteHEAT device */
-#endif
-
-#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
-struct ezusb_hex_record {
- __u16 address;
- __u8 data_size;
- __u8 data[16];
-};
-#include "keyspan_pda_fw.h"
-#endif
-
-#include "usb-serial.h"
-
-/* parity check flag */
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
-/* local function prototypes */
-static int serial_open (struct tty_struct *tty, struct file * filp);
-static void serial_close (struct tty_struct *tty, struct file * filp);
-static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);
-static int serial_write_room (struct tty_struct *tty);
-static int serial_chars_in_buffer (struct tty_struct *tty);
-static void serial_throttle (struct tty_struct * tty);
-static void serial_unthrottle (struct tty_struct * tty);
-static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
-static void serial_set_termios (struct tty_struct *tty, struct termios * old);
-
-static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum);
-static void usb_serial_disconnect(struct usb_device *dev, void *ptr);
-
-static struct usb_driver usb_serial_driver = {
- name: "serial",
- probe: usb_serial_probe,
- disconnect: usb_serial_disconnect,
-};
-
-static int serial_refcount;
-static struct tty_struct * serial_tty[SERIAL_TTY_MINORS];
-static struct termios * serial_termios[SERIAL_TTY_MINORS];
-static struct termios * serial_termios_locked[SERIAL_TTY_MINORS];
-static struct usb_serial *serial_table[SERIAL_TTY_MINORS] = {NULL, };
-
-
-static inline int serial_paranoia_check (struct usb_serial *serial, const char *function)
-{
- if (!serial) {
- dbg("%s - serial == NULL", function);
- return -1;
- }
- if (serial->magic != USB_SERIAL_MAGIC) {
- dbg("%s - bad magic number for serial", function);
- return -1;
- }
- if (!serial->type) {
- dbg("%s - serial->type == NULL!", function);
- return -1;
- }
-
- return 0;
-}
-
-
-static inline int port_paranoia_check (struct usb_serial_port *port, const char *function)
-{
- if (!port) {
- dbg("%s - port == NULL", function);
- return -1;
- }
- if (port->magic != USB_SERIAL_PORT_MAGIC) {
- dbg("%s - bad magic number for port", function);
- return -1;
- }
- if (!port->serial) {
- dbg("%s - port->serial == NULL", function);
- return -1;
- }
- if (!port->tty) {
- dbg("%s - port->tty == NULL", function);
- return -1;
- }
-
- return 0;
-}
-
-
-static struct usb_serial *get_serial_by_minor (int minor)
-{
- return serial_table[minor];
-}
-
-
-static struct usb_serial *get_free_serial (int num_ports, int *minor)
-{
- struct usb_serial *serial = NULL;
- int i, j;
- int good_spot;
-
- dbg("get_free_serial %d", num_ports);
-
- *minor = 0;
- for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
- if (serial_table[i])
- continue;
-
- good_spot = 1;
- for (j = 1; j <= num_ports-1; ++j)
- if (serial_table[i+j])
- good_spot = 0;
- if (good_spot == 0)
- continue;
-
- if (!(serial = kmalloc(sizeof(struct usb_serial), GFP_KERNEL))) {
- err("Out of memory");
- return NULL;
- }
- memset(serial, 0, sizeof(struct usb_serial));
- serial->magic = USB_SERIAL_MAGIC;
- serial_table[i] = serial;
- *minor = i;
- dbg("minor base = %d", *minor);
- for (i = *minor+1; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)
- serial_table[i] = serial;
- return serial;
- }
- return NULL;
-}
-
-
-static void return_serial (struct usb_serial *serial)
-{
- int i;
-
- dbg("return_serial");
-
- if (serial == NULL)
- return;
-
- for (i = 0; i < serial->num_ports; ++i) {
- serial_table[serial->minor + i] = NULL;
- }
-
- return;
-}
-
-
-#ifdef USES_EZUSB_FUNCTIONS
-/* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
-#define CPUCS_REG 0x7F92
-
-static int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest)
-{
- int result;
- unsigned char *transfer_buffer = kmalloc (length, GFP_KERNEL);
-
-// dbg("ezusb_writememory %x, %d", address, length);
-
- if (!transfer_buffer) {
- err("ezusb_writememory: kmalloc(%d) failed.", length);
- return -ENOMEM;
- }
- memcpy (transfer_buffer, data, length);
- result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), bRequest, 0x40, address, 0, transfer_buffer, length, 300);
- kfree (transfer_buffer);
- return result;
-}
-
-
-static int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit)
-{
- int response;
- dbg("ezusb_set_reset: %d", reset_bit);
- response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0);
- if (response < 0) {
- err("ezusb_set_reset %d failed", reset_bit);
- }
- return response;
-}
-
-#endif /* USES_EZUSB_FUNCTIONS */
-
-
-/*****************************************************************************
- * Driver tty interface functions
- *****************************************************************************/
-static int serial_open (struct tty_struct *tty, struct file * filp)
-{
- struct usb_serial *serial;
- struct usb_serial_port *port;
- int portNumber;
-
- dbg("serial_open");
-
- /* initialize the pointer incase something fails */
- tty->driver_data = NULL;
-
- /* get the serial object associated with this tty pointer */
- serial = get_serial_by_minor (MINOR(tty->device));
-
- if (serial_paranoia_check (serial, "serial_open")) {
- return -ENODEV;
- }
-
- /* set up our port structure */
- portNumber = MINOR(tty->device) - serial->minor;
- port = &serial->port[portNumber];
- port->number = portNumber;
- port->serial = serial;
- port->magic = USB_SERIAL_PORT_MAGIC;
-
- /* make the tty driver remember our port object, and us it */
- tty->driver_data = port;
- port->tty = tty;
-
- /* pass on to the driver specific version of this function if it is available */
- if (serial->type->open) {
- return (serial->type->open(port, filp));
- } else {
- return (generic_open(port, filp));
- }
-}
-
-
-static void serial_close(struct tty_struct *tty, struct file * filp)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial;
-
- dbg("serial_close");
-
- if (port_paranoia_check (port, "serial_close")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "serial_close")) {
- return;
- }
-
- dbg("serial_close port %d", port->number);
-
- if (!port->active) {
- dbg ("port not opened");
- return;
- }
-
- /* pass on to the driver specific version of this function if it is available */
- if (serial->type->close) {
- serial->type->close(port, filp);
- } else {
- generic_close(port, filp);
- }
-}
-
-
-static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial;
-
- dbg("serial_write");
-
- if (port_paranoia_check (port, "serial_write")) {
- return -ENODEV;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "serial_write")) {
- return -ENODEV;
- }
-
- dbg("serial_write port %d, %d byte(s)", port->number, count);
-
- if (!port->active) {
- dbg ("port not opened");
- return -EINVAL;
- }
-
- /* pass on to the driver specific version of this function if it is available */
- if (serial->type->write) {
- return (serial->type->write(port, from_user, buf, count));
- } else {
- return (generic_write(port, from_user, buf, count));
- }
-}
-
-
-static int serial_write_room (struct tty_struct *tty)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial;
-
- dbg("serial_write_room");
-
- if (port_paranoia_check (port, "serial_write")) {
- return -ENODEV;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "serial_write")) {
- return -ENODEV;
- }
-
- dbg("serial_write_room port %d", port->number);
-
- if (!port->active) {
- dbg ("port not open");
- return -EINVAL;
- }
-
- /* pass on to the driver specific version of this function if it is available */
- if (serial->type->write_room) {
- return (serial->type->write_room(port));
- } else {
- return (generic_write_room(port));
- }
-}
-
-
-static int serial_chars_in_buffer (struct tty_struct *tty)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial;
-
- dbg("serial_chars_in_buffer");
-
- if (port_paranoia_check (port, "serial_chars_in_buffer")) {
- return -ENODEV;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "serial_chars_in_buffer")) {
- return -ENODEV;
- }
-
- if (!port->active) {
- dbg ("port not open");
- return -EINVAL;
- }
-
- /* pass on to the driver specific version of this function if it is available */
- if (serial->type->chars_in_buffer) {
- return (serial->type->chars_in_buffer(port));
- } else {
- return (generic_chars_in_buffer(port));
- }
-}
-
-
-static void serial_throttle (struct tty_struct * tty)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial;
-
- dbg("serial_throttle");
-
- if (port_paranoia_check (port, "serial_throttle")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "serial_throttle")) {
- return;
- }
-
- dbg("serial_throttle port %d", port->number);
-
- if (!port->active) {
- dbg ("port not open");
- return;
- }
-
- /* pass on to the driver specific version of this function */
- if (serial->type->throttle) {
- serial->type->throttle(port);
- }
-
- return;
-}
-
-
-static void serial_unthrottle (struct tty_struct * tty)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial;
-
- dbg("serial_unthrottle");
-
- if (port_paranoia_check (port, "serial_unthrottle")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "serial_unthrottle")) {
- return;
- }
-
- dbg("serial_unthrottle port %d", port->number);
-
- if (!port->active) {
- dbg ("port not open");
- return;
- }
-
- /* pass on to the driver specific version of this function */
- if (serial->type->unthrottle) {
- serial->type->unthrottle(port);
- }
-
- return;
-}
-
-
-static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial;
-
- dbg("serial_ioctl");
-
- if (port_paranoia_check (port, "serial_ioctl")) {
- return -ENODEV;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "serial_ioctl")) {
- return -ENODEV;
- }
-
- dbg("serial_ioctl port %d", port->number);
-
- if (!port->active) {
- dbg ("port not open");
- return -ENODEV;
- }
-
- /* pass on to the driver specific version of this function if it is available */
- if (serial->type->ioctl) {
- return (serial->type->ioctl(port, file, cmd, arg));
- } else {
- return -ENOIOCTLCMD;
- }
-}
-
-
-static void serial_set_termios (struct tty_struct *tty, struct termios * old)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial;
-
- dbg("serial_set_termios");
-
- if (port_paranoia_check (port, "serial_set_termios")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "serial_set_termios")) {
- return;
- }
-
- dbg("serial_set_termios port %d", port->number);
-
- if (!port->active) {
- dbg ("port not open");
- return;
- }
-
- /* pass on to the driver specific version of this function if it is available */
- if (serial->type->set_termios) {
- serial->type->set_termios(port, old);
- }
-
- return;
-}
-
-
-static void serial_break (struct tty_struct *tty, int break_state)
-{
- struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
- struct usb_serial *serial;
-
- dbg("serial_break");
-
- if (port_paranoia_check (port, "serial_break")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "serial_break")) {
- return;
- }
-
- dbg("serial_break port %d", port->number);
-
- if (!port->active) {
- dbg ("port not open");
- return;
- }
-
- /* pass on to the driver specific version of this function if it is
- available */
- if (serial->type->break_ctl) {
- serial->type->break_ctl(port, break_state);
- }
-}
-
-
-#ifdef CONFIG_USB_SERIAL_WHITEHEAT
-/*****************************************************************************
- * Connect Tech's White Heat specific driver functions
- *****************************************************************************/
-static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
-{
- dbg("whiteheat_open port %d", port->number);
-
- if (port->active) {
- dbg ("device already open");
- return -EINVAL;
- }
- port->active = 1;
-
- /*Start reading from the device*/
- if (usb_submit_urb(port->read_urb))
- dbg("usb_submit_urb(read bulk) failed");
-
- /* Need to do device specific setup here (control lines, baud rate, etc.) */
- /* FIXME!!! */
-
- return (0);
-}
-
-
-static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
-{
- dbg("whiteheat_close port %d", port->number);
-
- /* Need to change the control lines here */
- /* FIXME */
-
- /* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
- port->active = 0;
-}
-
-
-static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios)
-{
- unsigned int cflag = port->tty->termios->c_cflag;
-
- dbg("whiteheat_set_termios port %d", port->number);
-
- /* check that they really want us to change something */
- if (old_termios) {
- if ((cflag == old_termios->c_cflag) &&
- (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
- dbg("nothing to change...");
- return;
- }
- }
-
- /* do the parsing of the cflag to see what to set the line to */
- /* FIXME!! */
-
- return;
-}
-
-static void whiteheat_throttle (struct usb_serial_port *port)
-{
- dbg("whiteheat_throttle port %d", port->number);
-
- /* Change the control signals */
- /* FIXME!!! */
-
- return;
-}
-
-
-static void whiteheat_unthrottle (struct usb_serial_port *port)
-{
- dbg("whiteheat_unthrottle port %d", port->number);
-
- /* Change the control signals */
- /* FIXME!!! */
-
- return;
-}
-
-
-/* steps to download the firmware to the WhiteHEAT device:
- - hold the reset (by writing to the reset bit of the CPUCS register)
- - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
- - release the reset (by writing to the CPUCS register)
- - download the WH.HEX file for all addresses greater than 0x1b3f using
- VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
- - hold the reset
- - download the WH.HEX file for all addresses less than 0x1b40 using
- VENDOR_REQUEST_ANCHOR_LOAD
- - release the reset
- - device renumerated itself and comes up as new device id with all
- firmware download completed.
-*/
-static int whiteheat_startup (struct usb_serial *serial)
-{
- int response;
- const struct whiteheat_hex_record *record;
-
- dbg("whiteheat_startup");
-
- response = ezusb_set_reset (serial, 1);
-
- record = &whiteheat_loader[0];
- while (record->address != 0xffff) {
- response = ezusb_writememory (serial, record->address,
- (unsigned char *)record->data, record->data_size, 0xa0);
- if (response < 0) {
- err("ezusb_writememory failed for loader (%d %04X %p %d)",
- response, record->address, record->data, record->data_size);
- break;
- }
- ++record;
- }
-
- response = ezusb_set_reset (serial, 0);
-
- record = &whiteheat_firmware[0];
- while (record->address < 0x1b40) {
- ++record;
- }
- while (record->address != 0xffff) {
- response = ezusb_writememory (serial, record->address,
- (unsigned char *)record->data, record->data_size, 0xa0);
- if (response < 0) {
- err("ezusb_writememory failed for first firmware step (%d %04X %p %d)",
- response, record->address, record->data, record->data_size);
- break;
- }
- ++record;
- }
-
- response = ezusb_set_reset (serial, 1);
-
- record = &whiteheat_firmware[0];
- while (record->address < 0x1b40) {
- response = ezusb_writememory (serial, record->address,
- (unsigned char *)record->data, record->data_size, 0xa0);
- if (response < 0) {
- err("ezusb_writememory failed for second firmware step (%d %04X %p %d)",
- response, record->address, record->data, record->data_size);
- break;
- }
- ++record;
- }
-
- response = ezusb_set_reset (serial, 0);
-
- /* we want this device to fail to have a driver assigned to it. */
- return (1);
-}
-#endif /* CONFIG_USB_SERIAL_WHITEHEAT */
-
-
-#ifdef CONFIG_USB_SERIAL_VISOR
-/******************************************************************************
- * Handspring Visor specific driver functions
- ******************************************************************************/
-static int visor_open (struct usb_serial_port *port, struct file *filp)
-{
- dbg("visor_open port %d", port->number);
-
- if (port->active) {
- dbg ("device already open");
- return -EINVAL;
- }
-
- port->active = 1;
-
- /*Start reading from the device*/
- if (usb_submit_urb(port->read_urb))
- dbg("usb_submit_urb(read bulk) failed");
-
- return (0);
-}
-
-
-static void visor_close (struct usb_serial_port *port, struct file * filp)
-{
- struct usb_serial *serial = port->serial;
- unsigned char *transfer_buffer = kmalloc (0x12, GFP_KERNEL);
-
- dbg("visor_close port %d", port->number);
-
- if (!transfer_buffer) {
- err("visor_close: kmalloc(%d) failed.", 0x12);
- } else {
- /* send a shutdown message to the device */
- usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION,
- 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
- }
-
- /* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
- port->active = 0;
-}
-
-
-static void visor_throttle (struct usb_serial_port *port)
-{
- dbg("visor_throttle port %d", port->number);
-
- usb_unlink_urb (port->read_urb);
-
- return;
-}
-
-
-static void visor_unthrottle (struct usb_serial_port *port)
-{
- dbg("visor_unthrottle port %d", port->number);
-
- if (usb_unlink_urb (port->read_urb))
- dbg("usb_submit_urb(read bulk) failed");
-
- return;
-}
-
-
-static int visor_startup (struct usb_serial *serial)
-{
- int response;
- int i;
- unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL);
-
- if (!transfer_buffer) {
- err("visor_startup: kmalloc(%d) failed.", 256);
- return -ENOMEM;
- }
-
- dbg("visor_startup");
-
- dbg("visor_setup: Set config to 1");
- usb_set_configuration (serial->dev, 1);
-
- /* send a get connection info request */
- response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_GET_CONNECTION_INFORMATION,
- 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
- if (response < 0) {
- err("visor_startup: error getting connection information");
- } else {
- struct visor_connection_info *connection_info = (struct visor_connection_info *)transfer_buffer;
- char *string;
- info("%s: Number of ports: %d", serial->type->name, connection_info->num_ports);
- for (i = 0; i < connection_info->num_ports; ++i) {
- switch (connection_info->connections[i].port_function_id) {
- case VISOR_FUNCTION_GENERIC:
- string = "Generic";
- break;
- case VISOR_FUNCTION_DEBUGGER:
- string = "Debugger";
- break;
- case VISOR_FUNCTION_HOTSYNC:
- string = "HotSync";
- break;
- case VISOR_FUNCTION_CONSOLE:
- string = "Console";
- break;
- case VISOR_FUNCTION_REMOTE_FILE_SYS:
- string = "Remote File System";
- break;
- default:
- string = "unknown";
- break;
- }
- info("%s: port %d, is for %s use and is bound to ttyUSB%d", serial->type->name, connection_info->connections[i].port, string, serial->minor + i);
- }
- }
-
- /* ask for the number of bytes available, but ignore the response as it is broken */
- response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE,
- 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300);
- if (response < 0) {
- err("visor_startup: error getting bytes available request");
- }
-
- kfree (transfer_buffer);
-
- /* continue on with initialization */
- return (0);
-}
-
-
-#endif /* CONFIG_USB_SERIAL_VISOR*/
-
-
-#ifdef CONFIG_USB_SERIAL_FTDI_SIO
-/******************************************************************************
- * FTDI SIO Serial Converter specific driver functions
- ******************************************************************************/
-
-/* Bill Ryder - bryder@sgi.com - wrote the FTDI_SIO implementation */
-/* Thanx to FTDI for so kindly providing details of the protocol required */
-/* to talk to the device */
-
-#include "ftdi_sio.h"
-
-static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp)
-{
- struct usb_serial *serial = port->serial;
- char buf[1]; /* Needed for the usb_control_msg I think */
-
- dbg("ftdi_sio_open port %d", port->number);
-
- if (port->active) {
- dbg ("port already open");
- return -EINVAL;
- }
- port->active = 1;
-
- usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
- FTDI_SIO_RESET_SIO,
- 0, buf, 0, HZ * 5);
-
- /* FIXME - Should I really purge the buffers? */
- usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
- FTDI_SIO_RESET_PURGE_RX,
- 0, buf, 0, HZ * 5);
-
- usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
- FTDI_SIO_RESET_PURGE_TX,
- 0, buf, 0, HZ * 5);
-
-
- /* As per usb_serial_init s/be CS8, B9600, 1 STOP BIT */
- if ( usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_BAUDRATE_REQUEST,
- FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
- ftdi_sio_b9600, 0,
- buf, 0, HZ * 5) < 0){
- dbg("Error from baudrate urb");
- return(-EINVAL);
- }
-
- if ( usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_DATA_REQUEST,
- FTDI_SIO_SET_DATA_REQUEST_TYPE,
- 8 | FTDI_SIO_SET_DATA_PARITY_NONE |
- FTDI_SIO_SET_DATA_STOP_BITS_1, 0,
- buf, 0, HZ * 5) < 0){
- dbg("Error from cs8/noparity/1 stopbit urb");
- return(-EINVAL);
- }
-
- /* Disable flow control */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_FLOW_CTRL_REQUEST,
- FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
- 0, 0,
- buf, 0, HZ * 5) < 0) {
- dbg("error from flowcontrol urb");
- return(-EINVAL);
- }
-
- /* Turn on RTS and DTR since we are not flow controlling*/
- /* FIXME - check for correct behaviour clocal vs non clocal */
- /* FIXME - might be able to do both simultaneously */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_DTR_HIGH, 0,
- buf, 0, HZ * 5) < 0) {
- dbg("Error from DTR HIGH urb");
- }
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_RTS_HIGH, 0,
- buf, 0, HZ * 5) < 0) {
- dbg("Error from RTS HIGH urb");
- }
-
- /*Start reading from the device*/
- if (usb_submit_urb(port->read_urb))
- dbg("usb_submit_urb(read bulk) failed");
-
- return (0);
-}
-
-
-static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp)
-{
- struct usb_serial *serial = port->serial;
- char buf[1];
-
- dbg("ftdi_sio_close port %d", port->number);
-
- /* FIXME - might be able to do both simultaneously */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_DTR_LOW, 0,
- buf, 0, HZ * 5) < 0) {
- dbg("Error from DTR LOW urb");
- }
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- (unsigned)FTDI_SIO_SET_RTS_LOW, 0,
- buf, 0, HZ * 5) < 0) {
- dbg("Error from RTS LOW urb");
- }
-
- /* FIXME Should I flush the device here? - not doing it for now */
-
- /* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
- port->active = 0;
-}
-
-
-
-/* The ftdi_sio requires the first byte to have:
- B0 1
- B1 0
- B2..7 length of message excluding byte 0
-*/
-static int ftdi_sio_write (struct usb_serial_port *port, int from_user,
- const unsigned char *buf, int count)
-{
- struct usb_serial *serial = port->serial;
- const int data_offset = 1;
-
- dbg("ftdi_sio_serial_write port %d, %d bytes", port->number, count);
-
- if (count == 0) {
- dbg("write request of 0 bytes");
- return 0;
- }
-
- /* only do something if we have a bulk out endpoint */
- if (serial->num_bulk_out) {
- unsigned char *first_byte = port->write_urb->transfer_buffer;
-
- if (port->write_urb->status == -EINPROGRESS) {
- dbg ("already writing");
- return 0;
- }
-
- count += data_offset;
- count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
- if (count == 0) {
- return 0;
- }
-
- /* Copy in the data to send */
- if (from_user) {
- copy_from_user(port->write_urb->transfer_buffer + data_offset ,
- buf, count - data_offset );
- }
- else {
- memcpy(port->write_urb->transfer_buffer + data_offset,
- buf, count - data_offset );
- }
-
- /* Write the control byte at the front of the packet*/
- first_byte = port->write_urb->transfer_buffer;
- *first_byte = 1 | ((count-data_offset) << 2) ;
-
-#ifdef DEBUG
- dbg("Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]);
-
- if (count) {
- int i;
- printk (KERN_DEBUG __FILE__ ": data written - length = %d, data = ", count);
- for (i = 0; i < count; ++i) {
- printk ("0x%02x ", first_byte[i]);
- if (first_byte[i] > ' ' && first_byte[i] < '~') {
- printk("%c ", first_byte[i]);
- } else {
- printk(" ");
- }
- }
-
-
- printk ("\n");
- }
-
-#endif
- /* send the data out the bulk port */
- port->write_urb->transfer_buffer_length = count;
-
- if (usb_submit_urb(port->write_urb))
- dbg("usb_submit_urb(write bulk) failed");
-
- dbg("write returning: %d", count - data_offset);
- return (count - data_offset);
- }
-
- /* no bulk out, so return 0 bytes written */
- return 0;
-}
-
-
-static void ftdi_sio_read_bulk_callback (struct urb *urb)
-{ /* ftdi_sio_serial_buld_callback */
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial;
- struct tty_struct *tty;
- unsigned char *data = urb->transfer_buffer;
- const int data_offset = 2;
- int i;
-
- dbg("ftdi_sio_read_bulk_callback");
-
- if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) {
- return;
- }
-
- if (urb->status) {
- dbg("nonzero read bulk status received: %d", urb->status);
- return;
- }
-
-#ifdef DEBUG
- if (urb->actual_length > 2) {
- printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length);
- for (i = 0; i < urb->actual_length; ++i) {
- printk ("0x%.2x ", data[i]);
- if (data[i] > ' ' && data[i] < '~') {
- printk("%c ", data[i]);
- } else {
- printk(" ");
- }
- }
- printk ("\n");
- }
-#endif
-
-
- if (urb->actual_length > data_offset) {
- tty = port->tty;
- for (i = data_offset ; i < urb->actual_length ; ++i) {
- tty_insert_flip_char(tty, data[i], 0);
- }
- tty_flip_buffer_push(tty);
- }
-
- /* Continue trying to always read */
- if (usb_submit_urb(urb))
- dbg("failed resubmitting read urb");
-
- return;
-} /* ftdi_sio_serial_read_bulk_callback */
-
-
-static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *old_termios)
-{
- struct usb_serial *serial = port->serial;
- unsigned int cflag = port->tty->termios->c_cflag;
- __u16 urb_value; /* Will hold the new flags */
- char buf[1]; /* Perhaps I should dynamically alloc this? */
-
- dbg("ftdi_sio_set_termios port %d", port->number);
-
- /* FIXME - we should keep the old termios really */
- /* FIXME -For this cut I don't care if the line is really changing or
- not - so just do the change regardless */
-
- /* Set number of data bits, parity, stop bits */
-
- urb_value = 0;
- urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
- FTDI_SIO_SET_DATA_STOP_BITS_1);
- urb_value |= (cflag & PARENB ?
- (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD :
- FTDI_SIO_SET_DATA_PARITY_EVEN) :
- FTDI_SIO_SET_DATA_PARITY_NONE);
- if (cflag & CSIZE) {
- switch (cflag & CSIZE) {
- case CS5: urb_value |= 5; dbg("Setting CS5"); break;
- case CS6: urb_value |= 6; dbg("Setting CS6"); break;
- case CS7: urb_value |= 7; dbg("Setting CS7"); break;
- case CS8: urb_value |= 8; dbg("Setting CS8"); break;
- default:
- dbg("CSIZE was set but not CS5-CS8");
- }
- }
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_DATA_REQUEST,
- FTDI_SIO_SET_DATA_REQUEST_TYPE,
- urb_value , 0,
- buf, 0, 100) < 0) {
- dbg("FAILED to set databits/stopbits/parity");
- }
-
- /* Now do the baudrate */
- /* FIXME - should drop lines on B0 */
- /* FIXME Should also handle CLOCAL here */
-
- switch(cflag & CBAUD){
- case B300: urb_value = ftdi_sio_b300; dbg("Set to 300"); break;
- case B600: urb_value = ftdi_sio_b600; dbg("Set to 600") ; break;
- case B1200: urb_value = ftdi_sio_b1200; dbg("Set to 1200") ; break;
- case B2400: urb_value = ftdi_sio_b2400; dbg("Set to 2400") ; break;
- case B4800: urb_value = ftdi_sio_b4800; dbg("Set to 4800") ; break;
- case B9600: urb_value = ftdi_sio_b9600; dbg("Set to 9600") ; break;
- case B19200: urb_value = ftdi_sio_b19200; dbg("Set to 19200") ; break;
- case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break;
- case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break;
- case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break;
- default: dbg("FTDI_SIO does not support the baudrate requested");
- /* FIXME - how to return an error for this? */ break;
- }
- /* Send the URB */
- if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_BAUDRATE_REQUEST,
- FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
- urb_value, 0,
- buf, 0, 100) < 0) {
- dbg("urb failed to set baurdrate");
- }
- return;
-}
-
-
-/*FIXME - the beginnings of this implementation - not even hooked into the driver yet */
-static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg)
-{
- struct usb_serial *serial = port->serial;
- __u16 urb_value=0; /* Will hold the new flags */
- char buf[1];
- int ret, mask;
-
- dbg("ftdi_sio_ioctl port %d", port->number);
-
- /* Based on code from acm.c */
- switch (cmd) {
-
- case TIOCMGET:
- /* Request the status from the device */
- if ((ret = usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_GET_MODEM_STATUS_REQUEST,
- FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
- 0, 0,
- buf, 1, HZ * 5)) < 0 ) {
- dbg("Get not get modem status of device");
- return(ret);
- }
-
- return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
- (buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
- (buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) |
- (buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0),
- (unsigned long *) arg);
- break;
-
- case TIOCMSET:
- case TIOCMBIS:
- case TIOCMBIC:
- if ((ret = get_user(mask, (unsigned long *) arg))) return ret;
-
- /* FIXME Need to remember if we have set DTR or RTS since we
- can't ask the device */
- /* FIXME - also need to find the meaning of TIOCMBIS/BIC/SET */
- if (mask & TIOCM_DTR) {
- switch(cmd) {
- case TIOCMSET:
- urb_value = FTDI_SIO_SET_DTR_HIGH | FTDI_SIO_SET_RTS_LOW;
- break;
-
- case TIOCMBIS:
- /* Will leave RTS alone and set DTR */
- urb_value = FTDI_SIO_SET_DTR_HIGH;
- break;
-
- case TIOCMBIC:
- urb_value = FTDI_SIO_SET_DTR_LOW;
- break;
- }
- }
-
- if (mask & TIOCM_RTS) {
- switch(cmd) {
- case TIOCMSET:
- urb_value = FTDI_SIO_SET_DTR_LOW | FTDI_SIO_SET_RTS_HIGH;
- break;
-
- case TIOCMBIS:
- /* Will leave DTR and set RTS */
- urb_value = FTDI_SIO_SET_RTS_HIGH;
- break;
-
- case TIOCMBIC:
- /* Will unset RTS */
- urb_value = FTDI_SIO_SET_RTS_LOW;
- break;
- }
- }
-
-
- return(usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- FTDI_SIO_SET_MODEM_CTRL_REQUEST,
- FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
- urb_value , 0,
- buf, 0, HZ * 5));
- }
-
- return -ENOIOCTLCMD;
-}
-
-#endif /* CONFIG_USB_SERIAL_FTDI_SIO */
-
-
-#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
-/*****************************************************************************
- * Keyspan PDA specific driver functions
- *****************************************************************************/
-
-static void keyspan_pda_rx_interrupt (struct urb *urb)
-{
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial;
- struct tty_struct *tty;
- unsigned char *data = urb->transfer_buffer;
- int i;
-
- /* the urb might have been killed. */
- if (urb->status)
- return;
-
- if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
- return;
- }
-
- /* see if the message is data or a status interrupt */
- switch (data[0]) {
- case 0:
- /* rest of message is rx data */
- if (urb->actual_length) {
- tty = serial->port[0].tty;
- for (i = 1; i < urb->actual_length ; ++i) {
- tty_insert_flip_char(tty, data[i], 0);
- }
- tty_flip_buffer_push(tty);
- }
- break;
- case 1:
- /* status interrupt */
- dbg(" rx int, d1=%d, d2=%d", data[1], data[2]);
- switch (data[1]) {
- case 1: /* modemline change */
- break;
- case 2: /* tx unthrottle interrupt */
- tty = serial->port[0].tty;
- serial->tx_throttled = 0;
- wake_up(&serial->write_wait); /* wake up writer */
- wake_up(&tty->write_wait); /* them too */
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
-
- /* INT urbs are automatically re-submitted */
-}
-
-
-static void keyspan_pda_rx_throttle (struct usb_serial_port *port)
-{
- /* stop receiving characters. We just turn off the URB request, and
- let chars pile up in the device. If we're doing hardware
- flowcontrol, the device will signal the other end when its buffer
- fills up. If we're doing XON/XOFF, this would be a good time to
- send an XOFF, although it might make sense to foist that off
- upon the device too. */
-
- dbg("keyspan_pda_rx_throttle port %d", port->number);
- usb_unlink_urb(port->read_urb);
-}
-
-
-static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port)
-{
- /* just restart the receive interrupt URB */
- dbg("keyspan_pda_rx_unthrottle port %d", port->number);
- if (usb_submit_urb(port->read_urb))
- dbg(" usb_submit_urb(read urb) failed");
- return;
-}
-
-
-static int keyspan_pda_setbaud (struct usb_serial *serial, int baud)
-{
- int rc;
- int bindex;
-
- switch(baud) {
- case 110: bindex = 0; break;
- case 300: bindex = 1; break;
- case 1200: bindex = 2; break;
- case 2400: bindex = 3; break;
- case 4800: bindex = 4; break;
- case 9600: bindex = 5; break;
- case 19200: bindex = 6; break;
- case 38400: bindex = 7; break;
- case 57600: bindex = 8; break;
- case 115200: bindex = 9; break;
- default: return -EINVAL;
- }
-
- /* rather than figure out how to sleep while waiting for this
- to complete, I just use the "legacy" API. */
- rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- 0, /* set baud */
- USB_TYPE_VENDOR
- | USB_RECIP_INTERFACE
- | USB_DIR_OUT, /* type */
- bindex, /* value */
- 0, /* index */
- NULL, /* &data */
- 0, /* size */
- 2*HZ); /* timeout */
- return(rc);
-}
-
-
-static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state)
-{
- struct usb_serial *serial = port->serial;
- int value;
- if (break_state == -1)
- value = 1; /* start break */
- else
- value = 0; /* clear break */
- usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- 4, /* set break */
- USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
- value, 0, NULL, 0, 2*HZ);
- /* there is something funky about this.. the TCSBRK that 'cu' performs
- ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4
- seconds apart, but it feels like the break sent isn't as long as it
- is on /dev/ttyS0 */
-}
-
-
-static void keyspan_pda_set_termios (struct usb_serial_port *port,
- struct termios *old_termios)
-{
- struct usb_serial *serial = port->serial;
- unsigned int cflag = port->tty->termios->c_cflag;
-
- /* cflag specifies lots of stuff: number of stop bits, parity, number
- of data bits, baud. What can the device actually handle?:
- CSTOPB (1 stop bit or 2)
- PARENB (parity)
- CSIZE (5bit .. 8bit)
- There is minimal hw support for parity (a PSW bit seems to hold the
- parity of whatever is in the accumulator). The UART either deals
- with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data,
- 1 special, stop). So, with firmware changes, we could do:
- 8N1: 10 bit
- 8N2: 11 bit, extra bit always (mark?)
- 8[EOMS]1: 11 bit, extra bit is parity
- 7[EOMS]1: 10 bit, b0/b7 is parity
- 7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?)
-
- HW flow control is dictated by the tty->termios->c_cflags & CRTSCTS
- bit.
-
- For now, just do baud. */
-
- switch (cflag & CBAUD) {
- /* we could support more values here, just need to calculate
- the necessary divisors in the firmware. <asm/termbits.h>
- has the Bnnn constants. */
- case B110: keyspan_pda_setbaud(serial, 110); break;
- case B300: keyspan_pda_setbaud(serial, 300); break;
- case B1200: keyspan_pda_setbaud(serial, 1200); break;
- case B2400: keyspan_pda_setbaud(serial, 2400); break;
- case B4800: keyspan_pda_setbaud(serial, 4800); break;
- case B9600: keyspan_pda_setbaud(serial, 9600); break;
- case B19200: keyspan_pda_setbaud(serial, 19200); break;
- case B38400: keyspan_pda_setbaud(serial, 38400); break;
- case B57600: keyspan_pda_setbaud(serial, 57600); break;
- case B115200: keyspan_pda_setbaud(serial, 115200); break;
- default: dbg("can't handle requested baud rate"); break;
- }
-}
-
-
-/* modem control pins: DTR and RTS are outputs and can be controlled.
- DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be
- read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */
-
-static int keyspan_pda_get_modem_info(struct usb_serial *serial,
- unsigned char *value)
-{
- int rc;
- unsigned char data;
- rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
- 3, /* get pins */
- USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN,
- 0, 0, &data, 1, 2*HZ);
- if (rc > 0)
- *value = data;
- return rc;
-}
-
-
-static int keyspan_pda_set_modem_info(struct usb_serial *serial,
- unsigned char value)
-{
- int rc;
- rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- 3, /* set pins */
- USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_OUT,
- value, 0, NULL, 0, 2*HZ);
- return rc;
-}
-
-
-static int keyspan_pda_ioctl(struct usb_serial_port *port, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct usb_serial *serial = port->serial;
- int rc;
- unsigned int value;
- unsigned char status, mask;
-
- switch (cmd) {
- case TIOCMGET: /* get modem pins state */
- rc = keyspan_pda_get_modem_info(serial, &status);
- if (rc < 0)
- return rc;
- value =
- ((status & (1<<7)) ? TIOCM_DTR : 0) |
- ((status & (1<<6)) ? TIOCM_CAR : 0) |
- ((status & (1<<5)) ? TIOCM_RNG : 0) |
- ((status & (1<<4)) ? TIOCM_DSR : 0) |
- ((status & (1<<3)) ? TIOCM_CTS : 0) |
- ((status & (1<<2)) ? TIOCM_RTS : 0);
- if (copy_to_user((unsigned int *)arg, &value, sizeof(int)))
- return -EFAULT;
- return 0;
- case TIOCMSET: /* set a state as returned by MGET */
- if (copy_from_user(&value, (unsigned int *)arg, sizeof(int)))
- return -EFAULT;
- status =
- ((value & TIOCM_DTR) ? (1<<7) : 0) |
- ((value & TIOCM_CAR) ? (1<<6) : 0) |
- ((value & TIOCM_RNG) ? (1<<5) : 0) |
- ((value & TIOCM_DSR) ? (1<<4) : 0) |
- ((value & TIOCM_CTS) ? (1<<3) : 0) |
- ((value & TIOCM_RTS) ? (1<<2) : 0);
- rc = keyspan_pda_set_modem_info(serial, status);
- if (rc < 0)
- return rc;
- return 0;
- case TIOCMBIS: /* set bits in bitmask <arg> */
- case TIOCMBIC: /* clear bits from bitmask <arg> */
- if (copy_from_user(&value, (unsigned int *)arg, sizeof(int)))
- return -EFAULT;
- rc = keyspan_pda_get_modem_info(serial, &status);
- if (rc < 0)
- return rc;
- mask =
- ((value & TIOCM_RTS) ? (1<<2) : 0) |
- ((value & TIOCM_DTR) ? (1<<7) : 0);
- if (cmd == TIOCMBIS)
- status |= mask;
- else
- status &= ~mask;
- rc = keyspan_pda_set_modem_info(serial, status);
- if (rc < 0)
- return rc;
- return 0;
- case TIOCMIWAIT:
- /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
- /* TODO */
- case TIOCGICOUNT:
- /* return count of modemline transitions */
- return 0; /* TODO */
- }
-
- return -ENOIOCTLCMD;
-}
-
-static int keyspan_pda_write(struct usb_serial_port *port, int from_user,
- const unsigned char *buf, int count)
-{
- struct usb_serial *serial = port->serial;
- int request_unthrottle = 0;
- int rc = 0;
- DECLARE_WAITQUEUE(wait, current);
-
- /* guess how much room is left in the device's ring buffer, and if we
- want to send more than that, check first, updating our notion of
- what is left. If our write will result in no room left, ask the
- device to give us an interrupt when the room available rises above
- a threshold, and hold off all writers (eventually, those using
- select() or poll() too) until we receive that unthrottle interrupt.
- Block if we can't write anything at all, otherwise write as much as
- we can. */
-
- if (count == 0) {
- dbg(" write request of 0 bytes");
- return (0);
- }
-
- /* we might block because of:
- the TX urb is in-flight (wait until it completes)
- the device is full (wait until it says there is room)
- */
- while (port->write_urb->status == -EINPROGRESS) {
- if (0 /* file->f_flags & O_NONBLOCK */) {
- rc = -EAGAIN;
- goto err;
- }
- interruptible_sleep_on(&serial->write_wait);
- if (signal_pending(current)) {
- rc = -ERESTARTSYS;
- goto err;
- }
- }
-
- /* at this point the URB is in our control, nobody else can submit it
- again (the only sudden transition was the one from EINPROGRESS to
- finished) */
-
- /* the next potential block is that our TX process might be throttled.
- The transition from throttled->not happens because of an Rx
- interrupt, and the wake_up occurs during the same interrupt, so we
- have to be careful to avoid a race that would cause us to sleep
- forever. */
-
- add_wait_queue(&serial->write_wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- while (serial->tx_throttled) {
- /* device can't accomodate any more characters. Sleep until it
- can. Woken up by an Rx interrupt message, which clears
- tx_throttled first. */
- dbg(" tx_throttled, going to sleep");
- if (signal_pending(current)) {
- current->state = TASK_RUNNING;
- remove_wait_queue(&serial->write_wait, &wait);
- dbg(" woke up because of signal");
- rc = -ERESTARTSYS;
- goto err;
- }
- schedule();
- dbg(" woke up");
- }
- remove_wait_queue(&serial->write_wait, &wait);
- set_current_state(TASK_RUNNING);
-
- count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
- if (count > serial->tx_room) {
- unsigned char room;
- /* Looks like we might overrun the Tx buffer. Ask the device
- how much room it really has */
- rc = usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev, 0),
- 6, /* write_room */
- USB_TYPE_VENDOR | USB_RECIP_INTERFACE
- | USB_DIR_IN,
- 0, /* value: 0 means "remaining room" */
- 0, /* index */
- &room,
- 1,
- 2*HZ);
- if (rc < 0) {
- dbg(" roomquery failed");
- return rc; /* failed */
- }
- if (rc == 0) {
- dbg(" roomquery returned 0 bytes");
- return -EIO; /* device didn't return any data */
- }
- dbg(" roomquery says %d", room);
- serial->tx_room = room;
- if (count > serial->tx_room) {
- /* we're about to completely fill the Tx buffer, so
- we'll be throttled afterwards. */
- count = serial->tx_room;
- request_unthrottle = 1;
- }
- }
- serial->tx_room -= count;
-
- if (count) {
- /* now transfer data */
- if (from_user) {
- copy_from_user(port->write_urb->transfer_buffer, buf, count);
- }
- else {
- memcpy (port->write_urb->transfer_buffer, buf, count);
- }
- /* send the data out the bulk port */
- port->write_urb->transfer_buffer_length = count;
-
- if (usb_submit_urb(port->write_urb))
- dbg(" usb_submit_urb(write bulk) failed");
- }
- else {
- /* There wasn't any room left, so we are throttled until
- the buffer empties a bit */
- request_unthrottle = 1;
- }
-
- if (request_unthrottle) {
- dbg(" request_unthrottle");
- /* ask the device to tell us when the tx buffer becomes
- sufficiently empty */
- serial->tx_throttled = 1; /* block writers */
- rc = usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev, 0),
- 7, /* request_unthrottle */
- USB_TYPE_VENDOR | USB_RECIP_INTERFACE
- | USB_DIR_OUT,
- 16, /* value: threshold */
- 0, /* index */
- NULL,
- 0,
- 2*HZ);
- }
-
- return (count);
- err:
- return (rc);
-}
-
-
-static void keyspan_pda_write_bulk_callback (struct urb *urb)
-{
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial;
- struct tty_struct *tty;
-
- if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) {
- return;
- }
-
- wake_up_interruptible(&serial->write_wait);
-
- tty = port->tty;
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
-
- wake_up_interruptible(&tty->write_wait);
-}
-
-
-static int keyspan_pda_write_room (struct usb_serial_port *port)
-{
- struct usb_serial *serial = port->serial;
-
- /* used by n_tty.c for processing of tabs and such. Giving it our
- conservative guess is probably good enough, but needs testing by
- running a console through the device. */
-
- return (serial->tx_room);
-}
-
-
-static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port)
-{
- struct usb_serial *serial = port->serial;
-
- /* when throttled, return at least WAKEUP_CHARS to tell select() (via
- n_tty.c:normal_poll() ) that we're not writeable. */
- if (serial->tx_throttled)
- return 256;
- return 0;
-}
-
-
-static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp)
-{
- struct usb_serial *serial = port->serial;
- unsigned char room;
- int rc;
-
- if (port->active) {
- return -EINVAL;
- }
- port->active = 1;
-
- /* find out how much room is in the Tx ring */
- rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
- 6, /* write_room */
- USB_TYPE_VENDOR | USB_RECIP_INTERFACE
- | USB_DIR_IN,
- 0, /* value */
- 0, /* index */
- &room,
- 1,
- 2*HZ);
- if (rc < 0) {
- dbg(" roomquery failed");
- return rc; /* failed */
- }
- if (rc == 0) {
- dbg(" roomquery returned 0 bytes");
- return -EIO; /* device didn't return any data */
- }
- serial->tx_room = room;
- serial->tx_throttled = room ? 0 : 1;
-
- /* the normal serial device seems to always turn on DTR and RTS here,
- so do the same */
- if (port->tty->termios->c_cflag & CBAUD)
- keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) );
- else
- keyspan_pda_set_modem_info(serial, 0);
-
- /*Start reading from the device*/
- if (usb_submit_urb(port->read_urb))
- dbg(" usb_submit_urb(read int) failed");
-
- return (0);
-}
-
-
-static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp)
-{
- struct usb_serial *serial = port->serial;
-
- /* the normal serial device seems to always shut off DTR and RTS now */
- if (port->tty->termios->c_cflag & HUPCL)
- keyspan_pda_set_modem_info(serial, 0);
-
- /* shutdown our bulk reads and writes */
- usb_unlink_urb (port->write_urb);
- usb_unlink_urb (port->read_urb);
- port->active = 0;
-}
-
-
-/* download the firmware to a "fake" device (pre-renumeration) */
-static int keyspan_pda_fake_startup (struct usb_serial *serial)
-{
- int response;
- const struct ezusb_hex_record *record;
-
- /* download the firmware here ... */
- response = ezusb_set_reset(serial, 1);
-
- record = &keyspan_pda_firmware[0];
- while(record->address != 0xffff) {
- response = ezusb_writememory(serial, record->address,
- (unsigned char *)record->data,
- record->data_size, 0xa0);
- if (response < 0) {
- err("ezusb_writememory failed for Keyspan PDA "
- "firmware (%d %04X %p %d)",
- response,
- record->address, record->data, record->data_size);
- break;
- }
- record++;
- }
- /* bring device out of reset. Renumeration will occur in a moment
- and the new device will bind to the real driver */
- response = ezusb_set_reset(serial, 0);
-
- /* we want this device to fail to have a driver assigned to it. */
- return (1);
-}
-
-
-/* do some startup allocations not currently performed by usb_serial_probe() */
-static int keyspan_pda_startup (struct usb_serial *serial)
-{
- struct usb_endpoint_descriptor *intin;
- intin = serial->port[0].interrupt_in_endpoint;
-
- /* set up the receive interrupt urb */
- FILL_INT_URB(serial->port[0].read_urb, serial->dev,
- usb_rcvintpipe(serial->dev, intin->bEndpointAddress),
- serial->port[0].interrupt_in_buffer,
- intin->wMaxPacketSize,
- keyspan_pda_rx_interrupt,
- serial,
- intin->bInterval);
-
- init_waitqueue_head(&serial->write_wait);
-
- return (0);
-}
-
-#endif /* CONFIG_USB_SERIAL_KEYSPAN_PDA */
-
-
-/*****************************************************************************
- * generic devices specific driver functions
- *****************************************************************************/
-static int generic_open (struct usb_serial_port *port, struct file *filp)
-{
- struct usb_serial *serial = port->serial;
-
- dbg("generic_open port %d", port->number);
-
- if (port->active) {
- dbg ("device already open");
- return -EINVAL;
- }
- port->active = 1;
-
- /* if we have a bulk interrupt, start reading from it */
- if (serial->num_bulk_in) {
- /*Start reading from the device*/
- if (usb_submit_urb(port->read_urb))
- dbg("usb_submit_urb(read bulk) failed");
- }
-
- return (0);
-}
-
-
-static void generic_close (struct usb_serial_port *port, struct file * filp)
-{
- struct usb_serial *serial = port->serial;
-
- dbg("generic_close port %d", port->number);
-
- /* shutdown any bulk reads that might be going on */
- if (serial->num_bulk_out) {
- usb_unlink_urb (port->write_urb);
- }
- if (serial->num_bulk_in) {
- usb_unlink_urb (port->read_urb);
- }
-
- port->active = 0;
-}
-
-
-static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
-{
- struct usb_serial *serial = port->serial;
-
- dbg("generic_serial_write port %d", port->number);
-
- if (count == 0) {
- dbg("write request of 0 bytes");
- return (0);
- }
-
- /* only do something if we have a bulk out endpoint */
- if (serial->num_bulk_out) {
- if (port->write_urb->status == -EINPROGRESS) {
- dbg ("already writing");
- return (0);
- }
-
- count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
-
- if (from_user) {
- copy_from_user(port->write_urb->transfer_buffer, buf, count);
- }
- else {
- memcpy (port->write_urb->transfer_buffer, buf, count);
- }
-
- /* send the data out the bulk port */
- port->write_urb->transfer_buffer_length = count;
-
- if (usb_submit_urb(port->write_urb))
- dbg("usb_submit_urb(write bulk) failed");
-
- return (count);
- }
-
- /* no bulk out, so return 0 bytes written */
- return (0);
-}
-
-
-static int generic_write_room (struct usb_serial_port *port)
-{
- struct usb_serial *serial = port->serial;
- int room;
-
- dbg("generic_write_room port %d", port->number);
-
- if (serial->num_bulk_out) {
- if (port->write_urb->status == -EINPROGRESS)
- room = 0;
- else
- room = port->bulk_out_size;
- dbg("generic_write_room returns %d", room);
- return (room);
- }
-
- return (0);
-}
-
-
-static int generic_chars_in_buffer (struct usb_serial_port *port)
-{
- struct usb_serial *serial = port->serial;
-
- dbg("generic_chars_in_buffer port %d", port->number);
-
- if (serial->num_bulk_out) {
- if (port->write_urb->status == -EINPROGRESS) {
- return (port->bulk_out_size);
- }
- }
-
- return (0);
-}
-
-
-static void generic_read_bulk_callback (struct urb *urb)
-{
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial;
- struct tty_struct *tty;
- unsigned char *data = urb->transfer_buffer;
- int i;
-
- dbg("generic_read_bulk_callback");
-
- if (port_paranoia_check (port, "generic_read_bulk_callback")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "generic_read_bulk_callback")) {
- return;
- }
-
- if (urb->status) {
- dbg("nonzero read bulk status received: %d", urb->status);
- return;
- }
-
-#ifdef DEBUG
- if (urb->actual_length) {
- printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length);
- for (i = 0; i < urb->actual_length; ++i) {
- printk ("%.2x ", data[i]);
- }
- printk ("\n");
- }
-#endif
-
- tty = port->tty;
- if (urb->actual_length) {
- for (i = 0; i < urb->actual_length ; ++i) {
- tty_insert_flip_char(tty, data[i], 0);
- }
- tty_flip_buffer_push(tty);
- }
-
- /* Continue trying to always read */
- if (usb_submit_urb(urb))
- dbg("failed resubmitting read urb");
-
- return;
-}
-
-
-static void generic_write_bulk_callback (struct urb *urb)
-{
- struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
- struct usb_serial *serial;
- struct tty_struct *tty;
-
- dbg("generic_write_bulk_callback");
-
- if (port_paranoia_check (port, "generic_write_bulk_callback")) {
- return;
- }
-
- serial = port->serial;
- if (serial_paranoia_check (serial, "generic_write_bulk_callback")) {
- return;
- }
-
- if (urb->status) {
- dbg("nonzero write bulk status received: %d", urb->status);
- return;
- }
-
- tty = port->tty;
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
-
- wake_up_interruptible(&tty->write_wait);
-
- return;
-}
-
-
-static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
-{
- struct usb_serial *serial = NULL;
- struct usb_serial_port *port;
- struct usb_interface_descriptor *interface;
- struct usb_endpoint_descriptor *endpoint;
- struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
- struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
- struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
- struct usb_serial_device_type *type;
- int device_num;
- int minor;
- int buffer_size;
- int i;
- char interrupt_pipe;
- char bulk_in_pipe;
- char bulk_out_pipe;
- int num_interrupt_in = 0;
- int num_bulk_in = 0;
- int num_bulk_out = 0;
- int num_ports;
-
- /* loop through our list of known serial converters, and see if this device matches */
- device_num = 0;
- while (usb_serial_devices[device_num] != NULL) {
- type = usb_serial_devices[device_num];
- dbg ("Looking at %s Vendor id=%.4x Product id=%.4x", type->name, *(type->idVendor), *(type->idProduct));
-
- /* look at the device descriptor */
- if ((dev->descriptor.idVendor == *(type->idVendor)) &&
- (dev->descriptor.idProduct == *(type->idProduct))) {
-
- dbg("descriptor matches...looking at the endpoints");
-
- /* descriptor matches, let's try to find the endpoints needed */
- interrupt_pipe = bulk_in_pipe = bulk_out_pipe = HAS_NOT;
-
- /* check out the endpoints */
- interface = &dev->actconfig->interface[ifnum].altsetting[0];
- for (i = 0; i < interface->bNumEndpoints; ++i) {
- endpoint = &interface->endpoint[i];
-
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x02)) {
- /* we found a bulk in endpoint */
- dbg("found bulk in");
- bulk_in_pipe = HAS;
- bulk_in_endpoint[num_bulk_in] = endpoint;
- ++num_bulk_in;
- }
-
- if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
- ((endpoint->bmAttributes & 3) == 0x02)) {
- /* we found a bulk out endpoint */
- dbg("found bulk out");
- bulk_out_pipe = HAS;
- bulk_out_endpoint[num_bulk_out] = endpoint;
- ++num_bulk_out;
- }
-
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x03)) {
- /* we found a interrupt in endpoint */
- dbg("found interrupt in");
- interrupt_pipe = HAS;
- interrupt_in_endpoint[num_interrupt_in] = endpoint;
- ++num_interrupt_in;
- }
-
- }
-
- /* verify that we found all of the endpoints that we need */
- if ((interrupt_pipe & type->needs_interrupt_in) &&
- (bulk_in_pipe & type->needs_bulk_in) &&
- (bulk_out_pipe & type->needs_bulk_out)) {
- /* found all that we need */
- info("%s converter detected", type->name);
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
- if (type == &generic_device)
- num_ports = num_bulk_out;
- else
-#endif
- num_ports = type->num_ports;
-
- serial = get_free_serial (num_ports, &minor);
- if (serial == NULL) {
- err("No more free serial devices");
- return NULL;
- }
-
- serial->dev = dev;
- serial->type = type;
- serial->minor = minor;
- serial->num_ports = num_ports;
- serial->num_bulk_in = num_bulk_in;
- serial->num_bulk_out = num_bulk_out;
- serial->num_interrupt_in = num_interrupt_in;
-
- /* collect interrupt_in endpoints now, because
- the keyspan_pda startup function needs
- to know about them */
- for (i = 0; i < num_interrupt_in; ++i) {
- port = &serial->port[i];
- buffer_size = interrupt_in_endpoint[i]->wMaxPacketSize;
- port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
- if (!port->interrupt_in_buffer) {
- err("Couldn't allocate interrupt_in_buffer");
- goto probe_error;
- }
- port->interrupt_in_endpoint = interrupt_in_endpoint[i];
- }
-
- /* if this device type has a startup function, call it */
- if (type->startup) {
- if (type->startup (serial)) {
- return_serial (serial);
- return NULL;
- }
- }
-
- /* set up the endpoint information */
- for (i = 0; i < num_bulk_in; ++i) {
- port = &serial->port[i];
- port->read_urb = usb_alloc_urb (0);
- if (!port->read_urb) {
- err("No free urbs available");
- goto probe_error;
- }
- buffer_size = bulk_in_endpoint[i]->wMaxPacketSize;
- port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
- if (!port->bulk_in_buffer) {
- err("Couldn't allocate bulk_in_buffer");
- goto probe_error;
- }
- if (serial->type->read_bulk_callback) {
- FILL_BULK_URB(port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
- port->bulk_in_buffer, buffer_size, serial->type->read_bulk_callback, port);
- } else {
- FILL_BULK_URB(port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
- port->bulk_in_buffer, buffer_size, generic_read_bulk_callback, port);
- }
- }
-
- for (i = 0; i < num_bulk_out; ++i) {
- port = &serial->port[i];
- port->write_urb = usb_alloc_urb(0);
- if (!port->write_urb) {
- err("No free urbs available");
- goto probe_error;
- }
- port->bulk_out_size = bulk_out_endpoint[i]->wMaxPacketSize;
- port->bulk_out_buffer = kmalloc (port->bulk_out_size, GFP_KERNEL);
- if (!port->bulk_out_buffer) {
- err("Couldn't allocate bulk_out_buffer");
- goto probe_error;
- }
- if (serial->type->write_bulk_callback) {
- FILL_BULK_URB(port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
- port->bulk_out_buffer, port->bulk_out_size, serial->type->write_bulk_callback, port);
- } else {
- FILL_BULK_URB(port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
- port->bulk_out_buffer, port->bulk_out_size, generic_write_bulk_callback, port);
- }
- }
-
-#if 0 /* use this code when WhiteHEAT is up and running */
- for (i = 0; i < num_interrupt_in; ++i) {
- port = &serial->port[i];
- port->control_urb = usb_alloc_urb(0);
- if (!port->control_urb) {
- err("No free urbs available");
- goto probe_error;
- }
- buffer_size = interrupt_in_endpoint[i]->wMaxPacketSize;
- port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
- if (!port->interrupt_in_buffer) {
- err("Couldn't allocate interrupt_in_buffer");
- goto probe_error;
- }
- FILL_INT_URB(port->control_urb, dev, usb_rcvintpipe (dev, interrupt_in_endpoint[i]->bEndpointAddress),
- port->interrupt_in_buffer, buffer_size, serial_control_irq,
- port, interrupt_in_endpoint[i]->bInterval);
- }
-#endif
-
- for (i = 0; i < serial->num_ports; ++i) {
- info("%s converter now attached to ttyUSB%d", type->name, serial->minor + i);
- }
-
- MOD_INC_USE_COUNT;
-
- return serial;
- } else {
- info("descriptors matched, but endpoints did not");
- }
- }
-
- /* look at the next type in our list */
- ++device_num;
- }
-
-probe_error:
- if (serial) {
- for (i = 0; i < num_bulk_in; ++i) {
- port = &serial->port[i];
- if (port->read_urb)
- usb_free_urb (port->read_urb);
- if (serial->port[i].bulk_in_buffer[i])
- kfree (serial->port[i].bulk_in_buffer);
- }
- for (i = 0; i < num_bulk_out; ++i) {
- port = &serial->port[i];
- if (port->write_urb)
- usb_free_urb (port->write_urb);
- if (serial->port[i].bulk_out_buffer)
- kfree (serial->port[i].bulk_out_buffer);
- }
- for (i = 0; i < num_interrupt_in; ++i) {
- port = &serial->port[i];
- if (port->control_urb)
- usb_free_urb (port->control_urb);
- if (serial->port[i].interrupt_in_buffer)
- kfree (serial->port[i].interrupt_in_buffer);
- }
-
- /* return the minor range that this device had */
- return_serial (serial);
-
- /* free up any memory that we allocated */
- kfree (serial);
- }
- return NULL;
-}
-
-
-static void usb_serial_disconnect(struct usb_device *dev, void *ptr)
-{
- struct usb_serial *serial = (struct usb_serial *) ptr;
- struct usb_serial_port *port;
- int i;
-
- if (serial) {
- for (i = 0; i < serial->num_ports; ++i)
- serial->port[i].active = 0;
-
- for (i = 0; i < serial->num_bulk_in; ++i) {
- port = &serial->port[i];
- if (port->read_urb) {
- usb_unlink_urb (port->read_urb);
- usb_free_urb (port->read_urb);
- }
- if (port->bulk_in_buffer)
- kfree (port->bulk_in_buffer);
- }
- for (i = 0; i < serial->num_bulk_out; ++i) {
- port = &serial->port[i];
- if (port->write_urb) {
- usb_unlink_urb (port->write_urb);
- usb_free_urb (port->write_urb);
- }
- if (port->bulk_out_buffer)
- kfree (port->bulk_out_buffer);
- }
- for (i = 0; i < serial->num_interrupt_in; ++i) {
- port = &serial->port[i];
- if (port->control_urb) {
- usb_unlink_urb (port->control_urb);
- usb_free_urb (port->control_urb);
- }
- if (port->interrupt_in_buffer)
- kfree (port->interrupt_in_buffer);
- }
-
- for (i = 0; i < serial->num_ports; ++i) {
- info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->minor + i);
- }
-
- /* return the minor range that this device had */
- return_serial (serial);
-
- /* free up any memory that we allocated */
- kfree (serial);
-
- } else {
- info("device disconnected");
- }
-
- MOD_DEC_USE_COUNT;
-}
-
-
-static struct tty_driver serial_tty_driver = {
- magic: TTY_DRIVER_MAGIC,
- driver_name: "usb",
- name: "ttyUSB%d",
- major: SERIAL_TTY_MAJOR,
- minor_start: 0,
- num: SERIAL_TTY_MINORS,
- type: TTY_DRIVER_TYPE_SERIAL,
- subtype: SERIAL_TYPE_NORMAL,
- flags: TTY_DRIVER_REAL_RAW,
- refcount: &serial_refcount,
- table: serial_tty,
- proc_entry: NULL,
- other: NULL,
- termios: serial_termios,
- termios_locked: serial_termios_locked,
-
- open: serial_open,
- close: serial_close,
- write: serial_write,
- put_char: NULL,
- flush_chars: NULL,
- write_room: serial_write_room,
- ioctl: serial_ioctl,
- set_termios: serial_set_termios,
- set_ldisc: NULL,
- throttle: serial_throttle,
- unthrottle: serial_unthrottle,
- stop: NULL,
- start: NULL,
- hangup: NULL,
- break_ctl: serial_break,
- wait_until_sent: NULL,
- send_xchar: NULL,
- read_proc: NULL,
- chars_in_buffer: serial_chars_in_buffer,
- flush_buffer: NULL
-};
-
-
-int usb_serial_init(void)
-{
- int i;
-
- /* Initalize our global data */
- for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
- serial_table[i] = NULL;
- }
-
- /* register the tty driver */
- serial_tty_driver.init_termios = tty_std_termios;
- serial_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- if (tty_register_driver (&serial_tty_driver)) {
- err("failed to register tty driver");
- return -EPERM;
- }
-
- /* register the USB driver */
- if (usb_register(&usb_serial_driver) < 0) {
- tty_unregister_driver(&serial_tty_driver);
- return -1;
- }
-
- info("support registered");
- return 0;
-}
-
-
-void usb_serial_exit(void)
-{
- tty_unregister_driver(&serial_tty_driver);
- usb_deregister(&usb_serial_driver);
-}
-
-
-module_init(usb_serial_init);
-module_exit(usb_serial_exit);
-
-
#ifndef __LINUX_USB_SERIAL_H
#define __LINUX_USB_SERIAL_H
-#include <linux/config.h>
-
-/* Module information */
-MODULE_AUTHOR("Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/");
-MODULE_DESCRIPTION("USB Serial Driver");
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
-static __u16 vendor = 0x05f9;
-static __u16 product = 0xffff;
-MODULE_PARM(vendor, "i");
-MODULE_PARM_DESC(vendor, "User specified USB idVendor");
-
-MODULE_PARM(product, "i");
-MODULE_PARM_DESC(product, "User specified USB idProduct");
-#endif
-
-
-/* USB Serial devices vendor ids and device ids that this driver supports */
-#define CONNECT_TECH_VENDOR_ID 0x0710
-#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001
-#define CONNECT_TECH_WHITE_HEAT_ID 0x8001
-#define HANDSPRING_VENDOR_ID 0x082d
-#define HANDSPRING_VISOR_ID 0x0100
-#define FTDI_VENDOR_ID 0x0403
-#define FTDI_SIO_SERIAL_CONVERTER_ID 0x8372
-#define KEYSPAN_VENDOR_ID 0x06cd
-#define KEYSPAN_PDA_FAKE_ID 0x0103
-#define KEYSPAN_PDA_ID 0x0104 /* no clue */
#define SERIAL_TTY_MAJOR 188 /* Nice legal number now */
#define SERIAL_TTY_MINORS 16 /* Actually we are allowed 255, but this is good for now */
-
-#define MAX_NUM_PORTS 8 /* The maximum number of ports one device can grab at once */
+#define MAX_NUM_PORTS 8 /* The maximum number of ports one device can grab at once */
#define USB_SERIAL_MAGIC 0x6702 /* magic number for usb_serial struct */
#define USB_SERIAL_PORT_MAGIC 0x7301 /* magic number for usb_serial_port struct */
+/* parity check flag */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
struct usb_serial_port {
int magic;
};
-/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */
-/* need to always compile these in, as some of the other devices use these functions as their own. */
-/* if a driver does not provide a function pointer, the generic function will be called. */
-static int generic_open (struct usb_serial_port *port, struct file *filp);
-static void generic_close (struct usb_serial_port *port, struct file *filp);
-static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
-static int generic_write_room (struct usb_serial_port *port);
-static int generic_chars_in_buffer (struct usb_serial_port *port);
-static void generic_read_bulk_callback (struct urb *urb);
-static void generic_write_bulk_callback (struct urb *urb);
-
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
-/* All of the device info needed for the Generic Serial Converter */
-static struct usb_serial_device_type generic_device = {
- name: "Generic",
- idVendor: &vendor, /* use the user specified vendor id */
- idProduct: &product, /* use the user specified product id */
- needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */
- needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */
- needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */
- num_interrupt_in: NUM_DONT_CARE,
- num_bulk_in: NUM_DONT_CARE,
- num_bulk_out: NUM_DONT_CARE,
- num_ports: 1,
-};
-#endif
-
-#ifdef CONFIG_USB_SERIAL_WHITEHEAT
-/* function prototypes for the Connect Tech WhiteHEAT serial converter */
-static int whiteheat_open (struct usb_serial_port *port, struct file *filp);
-static void whiteheat_close (struct usb_serial_port *port, struct file *filp);
-static void whiteheat_set_termios (struct usb_serial_port *port, struct termios * old);
-static void whiteheat_throttle (struct usb_serial_port *port);
-static void whiteheat_unthrottle (struct usb_serial_port *port);
-static int whiteheat_startup (struct usb_serial *serial);
-
-/* All of the device info needed for the Connect Tech WhiteHEAT */
-static __u16 connecttech_vendor_id = CONNECT_TECH_VENDOR_ID;
-static __u16 connecttech_whiteheat_fake_product_id = CONNECT_TECH_FAKE_WHITE_HEAT_ID;
-static __u16 connecttech_whiteheat_product_id = CONNECT_TECH_WHITE_HEAT_ID;
-static struct usb_serial_device_type whiteheat_fake_device = {
- name: "Connect Tech - WhiteHEAT - (prerenumeration)",
- idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */
- idProduct: &connecttech_whiteheat_fake_product_id, /* the White Heat initial product id */
- needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */
- needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */
- needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */
- num_interrupt_in: NUM_DONT_CARE,
- num_bulk_in: NUM_DONT_CARE,
- num_bulk_out: NUM_DONT_CARE,
- num_ports: 1,
- startup: whiteheat_startup
-};
-static struct usb_serial_device_type whiteheat_device = {
- name: "Connect Tech - WhiteHEAT",
- idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */
- idProduct: &connecttech_whiteheat_product_id, /* the White Heat real product id */
- needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */
- needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */
- needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */
- num_interrupt_in: NUM_DONT_CARE,
- num_bulk_in: NUM_DONT_CARE,
- num_bulk_out: NUM_DONT_CARE,
- num_ports: 4,
- open: whiteheat_open,
- close: whiteheat_close,
- throttle: whiteheat_throttle,
- unthrottle: whiteheat_unthrottle,
- set_termios: whiteheat_set_termios,
-};
-#endif
-
-
-#ifdef CONFIG_USB_SERIAL_VISOR
-
-/****************************************************************************
- * Handspring Visor Vendor specific request codes (bRequest values)
- * A big thank you to Handspring for providing the following information.
- * If anyone wants the original file where these values and structures came
- * from, send email to <greg@kroah.com>.
- ****************************************************************************/
-
-/****************************************************************************
- * VISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
- * are available to be transfered to the host for the specified endpoint.
- * Currently this is not used, and always returns 0x0001
- ****************************************************************************/
-#define VISOR_REQUEST_BYTES_AVAILABLE 0x01
-
-/****************************************************************************
- * VISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
- * is now closing the pipe. An empty packet is sent in response.
- ****************************************************************************/
-#define VISOR_CLOSE_NOTIFICATION 0x02
-
-/****************************************************************************
- * VISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
- * get the endpoints used by the connection.
- ****************************************************************************/
-#define VISOR_GET_CONNECTION_INFORMATION 0x03
-
-
-/****************************************************************************
- * VISOR_GET_CONNECTION_INFORMATION returns data in the following format
- ****************************************************************************/
-struct visor_connection_info {
- __u16 num_ports;
- struct {
- __u8 port_function_id;
- __u8 port;
- } connections[2];
-};
-
-
-/* struct visor_connection_info.connection[x].port defines: */
-#define VISOR_ENDPOINT_1 0x01
-#define VISOR_ENDPOINT_2 0x02
-
-/* struct visor_connection_info.connection[x].port_function_id defines: */
-#define VISOR_FUNCTION_GENERIC 0x00
-#define VISOR_FUNCTION_DEBUGGER 0x01
-#define VISOR_FUNCTION_HOTSYNC 0x02
-#define VISOR_FUNCTION_CONSOLE 0x03
-#define VISOR_FUNCTION_REMOTE_FILE_SYS 0x04
-
-
-/* function prototypes for a handspring visor */
-static int visor_open (struct usb_serial_port *port, struct file *filp);
-static void visor_close (struct usb_serial_port *port, struct file *filp);
-static void visor_throttle (struct usb_serial_port *port);
-static void visor_unthrottle (struct usb_serial_port *port);
-static int visor_startup (struct usb_serial *serial);
-
-/* All of the device info needed for the Handspring Visor */
-static __u16 handspring_vendor_id = HANDSPRING_VENDOR_ID;
-static __u16 handspring_product_id = HANDSPRING_VISOR_ID;
-static struct usb_serial_device_type handspring_device = {
- name: "Handspring Visor",
- idVendor: &handspring_vendor_id, /* the Handspring vendor ID */
- idProduct: &handspring_product_id, /* the Handspring Visor product id */
- needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */
- needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */
- needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */
- num_interrupt_in: 0,
- num_bulk_in: 2,
- num_bulk_out: 2,
- num_ports: 2,
- open: visor_open,
- close: visor_close,
- throttle: visor_throttle,
- unthrottle: visor_unthrottle,
- startup: visor_startup,
-};
-#endif
-
-
-#ifdef CONFIG_USB_SERIAL_FTDI_SIO
-/* function prototypes for a FTDI serial converter */
-static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp);
-static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp);
-static int ftdi_sio_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
-static void ftdi_sio_read_bulk_callback (struct urb *urb);
-static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * old);
-static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg);
-
-/* All of the device info needed for the FTDI SIO serial converter */
-static __u16 ftdi_vendor_id = FTDI_VENDOR_ID;
-static __u16 ftdi_sio_product_id = FTDI_SIO_SERIAL_CONVERTER_ID;
-static struct usb_serial_device_type ftdi_sio_device = {
- name: "FTDI SIO",
- idVendor: &ftdi_vendor_id, /* the FTDI vendor ID */
- idProduct: &ftdi_sio_product_id, /* the FTDI SIO product id */
- needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */
- needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */
- needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */
- num_interrupt_in: 0,
- num_bulk_in: 1,
- num_bulk_out: 1,
- num_ports: 1,
- open: ftdi_sio_open,
- close: ftdi_sio_close,
- write: ftdi_sio_write,
- ioctl: ftdi_sio_ioctl,
- read_bulk_callback: ftdi_sio_read_bulk_callback,
- set_termios: ftdi_sio_set_termios
-};
-#endif
-
-
-#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
-/* function prototypes for a Keyspan PDA serial converter */
-static int keyspan_pda_open (struct usb_serial_port *port,
- struct file *filp);
-static void keyspan_pda_close (struct usb_serial_port *port,
- struct file *filp);
-static int keyspan_pda_startup (struct usb_serial *serial);
-static void keyspan_pda_rx_throttle (struct usb_serial_port *port);
-static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port);
-static int keyspan_pda_setbaud (struct usb_serial *serial, int baud);
-static int keyspan_pda_write_room (struct usb_serial_port *port);
-static int keyspan_pda_write (struct usb_serial_port *port,
- int from_user,
- const unsigned char *buf,
- int count);
-static void keyspan_pda_write_bulk_callback (struct urb *urb);
-static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port);
-static int keyspan_pda_ioctl (struct usb_serial_port *port,
- struct file *file,
- unsigned int cmd,
- unsigned long arg);
-static void keyspan_pda_set_termios (struct usb_serial_port *port,
- struct termios *old);
-static void keyspan_pda_break_ctl (struct usb_serial_port *port,
- int break_state);
-static int keyspan_pda_fake_startup (struct usb_serial *serial);
-
-/* All of the device info needed for the Keyspan PDA serial converter */
-static __u16 keyspan_vendor_id = KEYSPAN_VENDOR_ID;
-static __u16 keyspan_pda_fake_product_id = KEYSPAN_PDA_FAKE_ID;
-static __u16 keyspan_pda_product_id = KEYSPAN_PDA_ID;
-static struct usb_serial_device_type keyspan_pda_fake_device = {
- name: "Keyspan PDA - (prerenumeration)",
- idVendor: &keyspan_vendor_id, /* the Keyspan PDA vendor ID */
- idProduct: &keyspan_pda_fake_product_id, /* the Keyspan PDA initial product id */
- needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */
- needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */
- needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */
- num_interrupt_in: NUM_DONT_CARE,
- num_bulk_in: NUM_DONT_CARE,
- num_bulk_out: NUM_DONT_CARE,
- num_ports: 1,
- startup: keyspan_pda_fake_startup
-};
-static struct usb_serial_device_type keyspan_pda_device = {
- name: "Keyspan PDA",
- idVendor: &keyspan_vendor_id, /* the Keyspan PDA vendor ID */
- idProduct: &keyspan_pda_product_id, /* the Keyspan PDA product id */
- needs_interrupt_in: MUST_HAVE,
- needs_bulk_in: DONT_CARE,
- needs_bulk_out: MUST_HAVE,
- num_interrupt_in: 1,
- num_bulk_in: 0,
- num_bulk_out: 1,
- num_ports: 1,
- open: keyspan_pda_open,
- close: keyspan_pda_close,
- write: keyspan_pda_write,
- write_room: keyspan_pda_write_room,
- write_bulk_callback: keyspan_pda_write_bulk_callback,
- chars_in_buffer: keyspan_pda_chars_in_buffer,
- throttle: keyspan_pda_rx_throttle,
- unthrottle: keyspan_pda_rx_unthrottle,
- startup: keyspan_pda_startup,
- ioctl: keyspan_pda_ioctl,
- set_termios: keyspan_pda_set_termios,
- break_ctl: keyspan_pda_break_ctl,
-};
-#endif
-
-
-/* To add support for another serial converter, create a usb_serial_device_type
- structure for that device, and add it to this list, making sure that the
- last entry is NULL. */
-static struct usb_serial_device_type *usb_serial_devices[] = {
-#ifdef CONFIG_USB_SERIAL_GENERIC
- &generic_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_WHITEHEAT
- &whiteheat_fake_device,
- &whiteheat_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_VISOR
- &handspring_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_FTDI_SIO
- &ftdi_sio_device,
-#endif
-#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
- &keyspan_pda_fake_device,
- &keyspan_pda_device,
-#endif
- NULL
-};
+extern struct usb_serial_device_type handspring_device;
+extern struct usb_serial_device_type whiteheat_fake_device;
+extern struct usb_serial_device_type whiteheat_device;
+extern struct usb_serial_device_type ftdi_sio_device;
+extern struct usb_serial_device_type keyspan_pda_fake_device;
+extern struct usb_serial_device_type keyspan_pda_device;
/* determine if we should include the EzUSB loader functions */
#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_WHITEHEAT)
#define USES_EZUSB_FUNCTIONS
+ extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest);
+ extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit);
#else
#undef USES_EZUSB_FUNCTIONS
#endif
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static inline int serial_paranoia_check (struct usb_serial *serial, const char *function)
+{
+ if (!serial) {
+ dbg("%s - serial == NULL", function);
+ return -1;
+ }
+ if (serial->magic != USB_SERIAL_MAGIC) {
+ dbg("%s - bad magic number for serial", function);
+ return -1;
+ }
+ if (!serial->type) {
+ dbg("%s - serial->type == NULL!", function);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static inline int port_paranoia_check (struct usb_serial_port *port, const char *function)
+{
+ if (!port) {
+ dbg("%s - port == NULL", function);
+ return -1;
+ }
+ if (port->magic != USB_SERIAL_PORT_MAGIC) {
+ dbg("%s - bad magic number for port", function);
+ return -1;
+ }
+ if (!port->serial) {
+ dbg("%s - port->serial == NULL", function);
+ return -1;
+ }
+ if (!port->tty) {
+ dbg("%s - port->tty == NULL", function);
+ return -1;
+ }
+
+ return 0;
+}
+
+
#endif /* ifdef __LINUX_USB_SERIAL_H */
--- /dev/null
+/*
+ * USB Serial Converter driver
+ *
+ * (C) Copyright (C) 1999, 2000
+ * 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 was originally based on the ACM driver by Armin Fuerst (which was
+ * based on a driver by Brad Keryan)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (03/26/2000) gkh
+ * Split driver up into device specific pieces.
+ *
+ * (03/19/2000) gkh
+ * Fixed oops that could happen when device was removed while a program
+ * was talking to the device.
+ * Removed the static urbs and now all urbs are created and destroyed
+ * dynamically.
+ * Reworked the internal interface. Now everything is based on the
+ * usb_serial_port structure instead of the larger usb_serial structure.
+ * This fixes the bug that a multiport device could not have more than
+ * one port open at one time.
+ *
+ * (03/17/2000) gkh
+ * Added config option for debugging messages.
+ * Added patch for keyspan pda from Brian Warner.
+ *
+ * (03/06/2000) gkh
+ * Added the keyspan pda code from Brian Warner <warner@lothar.com>
+ * Moved a bunch of the port specific stuff into its own structure. This
+ * is in anticipation of the true multiport devices (there's a bug if you
+ * try to access more than one port of any multiport device right now)
+ *
+ * (02/21/2000) gkh
+ * Made it so that any serial devices only have to specify which functions
+ * they want to overload from the generic function calls (great,
+ * inheritance in C, in a driver, just what I wanted...)
+ * Added support for set_termios and ioctl function calls. No drivers take
+ * advantage of this yet.
+ * Removed the #ifdef MODULE, now there is no module specific code.
+ * Cleaned up a few comments in usb-serial.h that were wrong (thanks again
+ * to Miles Lott).
+ * Small fix to get_free_serial.
+ *
+ * (02/14/2000) gkh
+ * Removed the Belkin and Peracom functionality from the driver due to
+ * the lack of support from the vendor, and me not wanting people to
+ * accidenatly buy the device, expecting it to work with Linux.
+ * Added read_bulk_callback and write_bulk_callback to the type structure
+ * for the needs of the FTDI and WhiteHEAT driver.
+ * Changed all reverences to FTDI to FTDI_SIO at the request of Bill
+ * Ryder.
+ * Changed the output urb size back to the max endpoint size to make
+ * the ftdi_sio driver have it easier, and due to the fact that it didn't
+ * really increase the speed any.
+ *
+ * (02/11/2000) gkh
+ * Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a
+ * patch from Miles Lott (milos@insync.net).
+ * Fixed bug with not restoring the minor range that a device grabs, if
+ * the startup function fails (thanks Miles for finding this).
+ *
+ * (02/05/2000) gkh
+ * Added initial framework for the Keyspan PDA serial converter so that
+ * Brian Warner has a place to put his code.
+ * Made the ezusb specific functions generic enough that different
+ * devices can use them (whiteheat and keyspan_pda both need them).
+ * Split out a whole bunch of structure and other stuff to a seperate
+ * usb-serial.h file.
+ * Made the Visor connection messages a little more understandable, now
+ * that Miles Lott (milos@insync.net) has gotten the Generic channel to
+ * work. Also made them always show up in the log file.
+ *
+ * (01/25/2000) gkh
+ * Added initial framework for FTDI serial converter so that Bill Ryder
+ * has a place to put his code.
+ * Added the vendor specific info from Handspring. Now we can print out
+ * informational debug messages as well as understand what is happening.
+ *
+ * (01/23/2000) gkh
+ * Fixed problem of crash when trying to open a port that didn't have a
+ * device assigned to it. Made the minor node finding a little smarter,
+ * now it looks to find a continous space for the new device.
+ *
+ * (01/21/2000) gkh
+ * Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net)
+ * Fixed get_serial_by_minor which was all messed up for multi port
+ * devices. Fixed multi port problem for generic devices. Now the number
+ * of ports is determined by the number of bulk out endpoints for the
+ * generic device.
+ *
+ * (01/19/2000) gkh
+ * Removed lots of cruft that was around from the old (pre urb) driver
+ * interface.
+ * Made the serial_table dynamic. This should save lots of memory when
+ * the number of minor nodes goes up to 256.
+ * Added initial support for devices that have more than one port.
+ * Added more debugging comments for the Visor, and added a needed
+ * set_configuration call.
+ *
+ * (01/17/2000) gkh
+ * Fixed the WhiteHEAT firmware (my processing tool had a bug)
+ * and added new debug loader firmware for it.
+ * Removed the put_char function as it isn't really needed.
+ * Added visor startup commands as found by the Win98 dump.
+ *
+ * (01/13/2000) gkh
+ * Fixed the vendor id for the generic driver to the one I meant it to be.
+ *
+ * (01/12/2000) gkh
+ * Forget the version numbering...that's pretty useless...
+ * Made the driver able to be compiled so that the user can select which
+ * converter they want to use. This allows people who only want the Visor
+ * support to not pay the memory size price of the WhiteHEAT.
+ * Fixed bug where the generic driver (idVendor=0000 and idProduct=0000)
+ * grabbed the root hub. Not good.
+ *
+ * version 0.4.0 (01/10/2000) gkh
+ * Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT
+ * device. Added startup function to allow firmware to be downloaded to
+ * a device if it needs to be.
+ * Added firmware download logic to the WhiteHEAT device.
+ * Started to add #defines to split up the different drivers for potential
+ * configuration option.
+ *
+ * version 0.3.1 (12/30/99) gkh
+ * Fixed problems with urb for bulk out.
+ * Added initial support for multiple sets of endpoints. This enables
+ * the Handspring Visor to be attached successfully. Only the first
+ * bulk in / bulk out endpoint pair is being used right now.
+ *
+ * version 0.3.0 (12/27/99) gkh
+ * Added initial support for the Handspring Visor based on a patch from
+ * Miles Lott (milos@sneety.insync.net)
+ * Cleaned up the code a bunch and converted over to using urbs only.
+ *
+ * version 0.2.3 (12/21/99) gkh
+ * Added initial support for the Connect Tech WhiteHEAT converter.
+ * Incremented the number of ports in expectation of getting the
+ * WhiteHEAT to work properly (4 ports per connection).
+ * Added notification on insertion and removal of what port the
+ * device is/was connected to (and what kind of device it was).
+ *
+ * version 0.2.2 (12/16/99) gkh
+ * Changed major number to the new allocated number. We're legal now!
+ *
+ * version 0.2.1 (12/14/99) gkh
+ * Fixed bug that happens when device node is opened when there isn't a
+ * device attached to it. Thanks to marek@webdesign.no for noticing this.
+ *
+ * version 0.2.0 (11/10/99) gkh
+ * Split up internals to make it easier to add different types of serial
+ * converters to the code.
+ * Added a "generic" driver that gets it's vendor and product id
+ * from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net)
+ * for the idea and sample code (from the usb scanner driver.)
+ * Cleared up any licensing questions by releasing it under the GNU GPL.
+ *
+ * version 0.1.2 (10/25/99) gkh
+ * Fixed bug in detecting device.
+ *
+ * version 0.1.1 (10/05/99) gkh
+ * Changed the major number to not conflict with anything else.
+ *
+ * version 0.1 (09/28/99) gkh
+ * Can recognize the two different devices and start up a read from
+ * device when asked to. Writes also work. No control signals yet, this
+ * all is vendor specific data (i.e. no spec), also no control for
+ * different baud rates or other bit settings.
+ * Currently we are using the same devid as the acm driver. This needs
+ * to change.
+ *
+ */
+
+#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/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+#include <linux/usb.h>
+
+/* Module information */
+MODULE_AUTHOR("Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/");
+MODULE_DESCRIPTION("USB Serial Driver");
+
+#include "usb-serial.h"
+
+
+/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */
+/* need to always compile these in, as some of the other devices use these functions as their own. */
+/* if a driver does not provide a function pointer, the generic function will be called. */
+static int generic_open (struct usb_serial_port *port, struct file *filp);
+static void generic_close (struct usb_serial_port *port, struct file *filp);
+static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static int generic_write_room (struct usb_serial_port *port);
+static int generic_chars_in_buffer (struct usb_serial_port *port);
+static void generic_read_bulk_callback (struct urb *urb);
+static void generic_write_bulk_callback (struct urb *urb);
+
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+static __u16 vendor = 0x05f9;
+static __u16 product = 0xffff;
+MODULE_PARM(vendor, "i");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+MODULE_PARM(product, "i");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+
+/* All of the device info needed for the Generic Serial Converter */
+static struct usb_serial_device_type generic_device = {
+ name: "Generic",
+ idVendor: &vendor, /* use the user specified vendor id */
+ idProduct: &product, /* use the user specified product id */
+ needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */
+ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */
+ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */
+ num_interrupt_in: NUM_DONT_CARE,
+ num_bulk_in: NUM_DONT_CARE,
+ num_bulk_out: NUM_DONT_CARE,
+ num_ports: 1,
+};
+#endif
+
+
+/* To add support for another serial converter, create a usb_serial_device_type
+ structure for that device, and add it to this list, making sure that the
+ last entry is NULL. */
+static struct usb_serial_device_type *usb_serial_devices[] = {
+#ifdef CONFIG_USB_SERIAL_GENERIC
+ &generic_device,
+#endif
+#ifdef CONFIG_USB_SERIAL_WHITEHEAT
+ &whiteheat_fake_device,
+ &whiteheat_device,
+#endif
+#ifdef CONFIG_USB_SERIAL_VISOR
+ &handspring_device,
+#endif
+#ifdef CONFIG_USB_SERIAL_FTDI_SIO
+ &ftdi_sio_device,
+#endif
+#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA
+ &keyspan_pda_fake_device,
+ &keyspan_pda_device,
+#endif
+ NULL
+};
+
+
+
+/* local function prototypes */
+static int serial_open (struct tty_struct *tty, struct file * filp);
+static void serial_close (struct tty_struct *tty, struct file * filp);
+static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count);
+static int serial_write_room (struct tty_struct *tty);
+static int serial_chars_in_buffer (struct tty_struct *tty);
+static void serial_throttle (struct tty_struct * tty);
+static void serial_unthrottle (struct tty_struct * tty);
+static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg);
+static void serial_set_termios (struct tty_struct *tty, struct termios * old);
+
+static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum);
+static void usb_serial_disconnect(struct usb_device *dev, void *ptr);
+
+static struct usb_driver usb_serial_driver = {
+ name: "serial",
+ probe: usb_serial_probe,
+ disconnect: usb_serial_disconnect,
+};
+
+static int serial_refcount;
+static struct tty_struct * serial_tty[SERIAL_TTY_MINORS];
+static struct termios * serial_termios[SERIAL_TTY_MINORS];
+static struct termios * serial_termios_locked[SERIAL_TTY_MINORS];
+static struct usb_serial *serial_table[SERIAL_TTY_MINORS] = {NULL, };
+
+
+
+static struct usb_serial *get_serial_by_minor (int minor)
+{
+ return serial_table[minor];
+}
+
+
+static struct usb_serial *get_free_serial (int num_ports, int *minor)
+{
+ struct usb_serial *serial = NULL;
+ int i, j;
+ int good_spot;
+
+ dbg("get_free_serial %d", num_ports);
+
+ *minor = 0;
+ for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
+ if (serial_table[i])
+ continue;
+
+ good_spot = 1;
+ for (j = 1; j <= num_ports-1; ++j)
+ if (serial_table[i+j])
+ good_spot = 0;
+ if (good_spot == 0)
+ continue;
+
+ if (!(serial = kmalloc(sizeof(struct usb_serial), GFP_KERNEL))) {
+ err("Out of memory");
+ return NULL;
+ }
+ memset(serial, 0, sizeof(struct usb_serial));
+ serial->magic = USB_SERIAL_MAGIC;
+ serial_table[i] = serial;
+ *minor = i;
+ dbg("minor base = %d", *minor);
+ for (i = *minor+1; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)
+ serial_table[i] = serial;
+ return serial;
+ }
+ return NULL;
+}
+
+
+static void return_serial (struct usb_serial *serial)
+{
+ int i;
+
+ dbg("return_serial");
+
+ if (serial == NULL)
+ return;
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ serial_table[serial->minor + i] = NULL;
+ }
+
+ return;
+}
+
+
+#ifdef USES_EZUSB_FUNCTIONS
+/* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
+#define CPUCS_REG 0x7F92
+
+int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest)
+{
+ int result;
+ unsigned char *transfer_buffer = kmalloc (length, GFP_KERNEL);
+
+// dbg("ezusb_writememory %x, %d", address, length);
+
+ if (!transfer_buffer) {
+ err("ezusb_writememory: kmalloc(%d) failed.", length);
+ return -ENOMEM;
+ }
+ memcpy (transfer_buffer, data, length);
+ result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), bRequest, 0x40, address, 0, transfer_buffer, length, 300);
+ kfree (transfer_buffer);
+ return result;
+}
+
+
+int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit)
+{
+ int response;
+ dbg("ezusb_set_reset: %d", reset_bit);
+ response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0);
+ if (response < 0) {
+ err("ezusb_set_reset %d failed", reset_bit);
+ }
+ return response;
+}
+
+#endif /* USES_EZUSB_FUNCTIONS */
+
+
+/*****************************************************************************
+ * Driver tty interface functions
+ *****************************************************************************/
+static int serial_open (struct tty_struct *tty, struct file * filp)
+{
+ struct usb_serial *serial;
+ struct usb_serial_port *port;
+ int portNumber;
+
+ dbg("serial_open");
+
+ /* initialize the pointer incase something fails */
+ tty->driver_data = NULL;
+
+ /* get the serial object associated with this tty pointer */
+ serial = get_serial_by_minor (MINOR(tty->device));
+
+ if (serial_paranoia_check (serial, "serial_open")) {
+ return -ENODEV;
+ }
+
+ /* set up our port structure */
+ portNumber = MINOR(tty->device) - serial->minor;
+ port = &serial->port[portNumber];
+ port->number = portNumber;
+ port->serial = serial;
+ port->magic = USB_SERIAL_PORT_MAGIC;
+
+ /* make the tty driver remember our port object, and us it */
+ tty->driver_data = port;
+ port->tty = tty;
+
+ /* pass on to the driver specific version of this function if it is available */
+ if (serial->type->open) {
+ return (serial->type->open(port, filp));
+ } else {
+ return (generic_open(port, filp));
+ }
+}
+
+
+static void serial_close(struct tty_struct *tty, struct file * filp)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_close");
+
+ if (port_paranoia_check (port, "serial_close")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_close")) {
+ return;
+ }
+
+ dbg("serial_close port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not opened");
+ return;
+ }
+
+ /* pass on to the driver specific version of this function if it is available */
+ if (serial->type->close) {
+ serial->type->close(port, filp);
+ } else {
+ generic_close(port, filp);
+ }
+}
+
+
+static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_write");
+
+ if (port_paranoia_check (port, "serial_write")) {
+ return -ENODEV;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_write")) {
+ return -ENODEV;
+ }
+
+ dbg("serial_write port %d, %d byte(s)", port->number, count);
+
+ if (!port->active) {
+ dbg ("port not opened");
+ return -EINVAL;
+ }
+
+ /* pass on to the driver specific version of this function if it is available */
+ if (serial->type->write) {
+ return (serial->type->write(port, from_user, buf, count));
+ } else {
+ return (generic_write(port, from_user, buf, count));
+ }
+}
+
+
+static int serial_write_room (struct tty_struct *tty)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_write_room");
+
+ if (port_paranoia_check (port, "serial_write")) {
+ return -ENODEV;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_write")) {
+ return -ENODEV;
+ }
+
+ dbg("serial_write_room port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
+ return -EINVAL;
+ }
+
+ /* pass on to the driver specific version of this function if it is available */
+ if (serial->type->write_room) {
+ return (serial->type->write_room(port));
+ } else {
+ return (generic_write_room(port));
+ }
+}
+
+
+static int serial_chars_in_buffer (struct tty_struct *tty)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_chars_in_buffer");
+
+ if (port_paranoia_check (port, "serial_chars_in_buffer")) {
+ return -ENODEV;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_chars_in_buffer")) {
+ return -ENODEV;
+ }
+
+ if (!port->active) {
+ dbg ("port not open");
+ return -EINVAL;
+ }
+
+ /* pass on to the driver specific version of this function if it is available */
+ if (serial->type->chars_in_buffer) {
+ return (serial->type->chars_in_buffer(port));
+ } else {
+ return (generic_chars_in_buffer(port));
+ }
+}
+
+
+static void serial_throttle (struct tty_struct * tty)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_throttle");
+
+ if (port_paranoia_check (port, "serial_throttle")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_throttle")) {
+ return;
+ }
+
+ dbg("serial_throttle port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
+ return;
+ }
+
+ /* pass on to the driver specific version of this function */
+ if (serial->type->throttle) {
+ serial->type->throttle(port);
+ }
+
+ return;
+}
+
+
+static void serial_unthrottle (struct tty_struct * tty)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_unthrottle");
+
+ if (port_paranoia_check (port, "serial_unthrottle")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_unthrottle")) {
+ return;
+ }
+
+ dbg("serial_unthrottle port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
+ return;
+ }
+
+ /* pass on to the driver specific version of this function */
+ if (serial->type->unthrottle) {
+ serial->type->unthrottle(port);
+ }
+
+ return;
+}
+
+
+static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_ioctl");
+
+ if (port_paranoia_check (port, "serial_ioctl")) {
+ return -ENODEV;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_ioctl")) {
+ return -ENODEV;
+ }
+
+ dbg("serial_ioctl port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
+ return -ENODEV;
+ }
+
+ /* pass on to the driver specific version of this function if it is available */
+ if (serial->type->ioctl) {
+ return (serial->type->ioctl(port, file, cmd, arg));
+ } else {
+ return -ENOIOCTLCMD;
+ }
+}
+
+
+static void serial_set_termios (struct tty_struct *tty, struct termios * old)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_set_termios");
+
+ if (port_paranoia_check (port, "serial_set_termios")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_set_termios")) {
+ return;
+ }
+
+ dbg("serial_set_termios port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
+ return;
+ }
+
+ /* pass on to the driver specific version of this function if it is available */
+ if (serial->type->set_termios) {
+ serial->type->set_termios(port, old);
+ }
+
+ return;
+}
+
+
+static void serial_break (struct tty_struct *tty, int break_state)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+ struct usb_serial *serial;
+
+ dbg("serial_break");
+
+ if (port_paranoia_check (port, "serial_break")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "serial_break")) {
+ return;
+ }
+
+ dbg("serial_break port %d", port->number);
+
+ if (!port->active) {
+ dbg ("port not open");
+ return;
+ }
+
+ /* pass on to the driver specific version of this function if it is
+ available */
+ if (serial->type->break_ctl) {
+ serial->type->break_ctl(port, break_state);
+ }
+}
+
+
+
+/*****************************************************************************
+ * generic devices specific driver functions
+ *****************************************************************************/
+static int generic_open (struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial = port->serial;
+
+ dbg("generic_open port %d", port->number);
+
+ if (port->active) {
+ dbg ("device already open");
+ return -EINVAL;
+ }
+ port->active = 1;
+
+ /* if we have a bulk interrupt, start reading from it */
+ if (serial->num_bulk_in) {
+ /*Start reading from the device*/
+ if (usb_submit_urb(port->read_urb))
+ dbg("usb_submit_urb(read bulk) failed");
+ }
+
+ return (0);
+}
+
+
+static void generic_close (struct usb_serial_port *port, struct file * filp)
+{
+ struct usb_serial *serial = port->serial;
+
+ dbg("generic_close port %d", port->number);
+
+ /* shutdown any bulk reads that might be going on */
+ if (serial->num_bulk_out) {
+ usb_unlink_urb (port->write_urb);
+ }
+ if (serial->num_bulk_in) {
+ usb_unlink_urb (port->read_urb);
+ }
+
+ port->active = 0;
+}
+
+
+static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
+{
+ struct usb_serial *serial = port->serial;
+
+ dbg("generic_serial_write port %d", port->number);
+
+ if (count == 0) {
+ dbg("write request of 0 bytes");
+ return (0);
+ }
+
+ /* only do something if we have a bulk out endpoint */
+ if (serial->num_bulk_out) {
+ if (port->write_urb->status == -EINPROGRESS) {
+ dbg ("already writing");
+ return (0);
+ }
+
+ count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+
+ if (from_user) {
+ copy_from_user(port->write_urb->transfer_buffer, buf, count);
+ }
+ else {
+ memcpy (port->write_urb->transfer_buffer, buf, count);
+ }
+
+ /* send the data out the bulk port */
+ port->write_urb->transfer_buffer_length = count;
+
+ if (usb_submit_urb(port->write_urb))
+ dbg("usb_submit_urb(write bulk) failed");
+
+ return (count);
+ }
+
+ /* no bulk out, so return 0 bytes written */
+ return (0);
+}
+
+
+static int generic_write_room (struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+ int room;
+
+ dbg("generic_write_room port %d", port->number);
+
+ if (serial->num_bulk_out) {
+ if (port->write_urb->status == -EINPROGRESS)
+ room = 0;
+ else
+ room = port->bulk_out_size;
+ dbg("generic_write_room returns %d", room);
+ return (room);
+ }
+
+ return (0);
+}
+
+
+static int generic_chars_in_buffer (struct usb_serial_port *port)
+{
+ struct usb_serial *serial = port->serial;
+
+ dbg("generic_chars_in_buffer port %d", port->number);
+
+ if (serial->num_bulk_out) {
+ if (port->write_urb->status == -EINPROGRESS) {
+ return (port->bulk_out_size);
+ }
+ }
+
+ return (0);
+}
+
+
+static void generic_read_bulk_callback (struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
+ unsigned char *data = urb->transfer_buffer;
+ int i;
+
+ dbg("generic_read_bulk_callback");
+
+ if (port_paranoia_check (port, "generic_read_bulk_callback")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "generic_read_bulk_callback")) {
+ return;
+ }
+
+ if (urb->status) {
+ dbg("nonzero read bulk status received: %d", urb->status);
+ return;
+ }
+
+#ifdef DEBUG
+ if (urb->actual_length) {
+ printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length);
+ for (i = 0; i < urb->actual_length; ++i) {
+ printk ("%.2x ", data[i]);
+ }
+ printk ("\n");
+ }
+#endif
+
+ tty = port->tty;
+ if (urb->actual_length) {
+ for (i = 0; i < urb->actual_length ; ++i) {
+ tty_insert_flip_char(tty, data[i], 0);
+ }
+ tty_flip_buffer_push(tty);
+ }
+
+ /* Continue trying to always read */
+ if (usb_submit_urb(urb))
+ dbg("failed resubmitting read urb");
+
+ return;
+}
+
+
+static void generic_write_bulk_callback (struct urb *urb)
+{
+ struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+ struct usb_serial *serial;
+ struct tty_struct *tty;
+
+ dbg("generic_write_bulk_callback");
+
+ if (port_paranoia_check (port, "generic_write_bulk_callback")) {
+ return;
+ }
+
+ serial = port->serial;
+ if (serial_paranoia_check (serial, "generic_write_bulk_callback")) {
+ return;
+ }
+
+ if (urb->status) {
+ dbg("nonzero write bulk status received: %d", urb->status);
+ return;
+ }
+
+ tty = port->tty;
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+
+ wake_up_interruptible(&tty->write_wait);
+
+ return;
+}
+
+
+static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum)
+{
+ struct usb_serial *serial = NULL;
+ struct usb_serial_port *port;
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
+ struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
+ struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
+ struct usb_serial_device_type *type;
+ int device_num;
+ int minor;
+ int buffer_size;
+ int i;
+ char interrupt_pipe;
+ char bulk_in_pipe;
+ char bulk_out_pipe;
+ int num_interrupt_in = 0;
+ int num_bulk_in = 0;
+ int num_bulk_out = 0;
+ int num_ports;
+
+ /* loop through our list of known serial converters, and see if this device matches */
+ device_num = 0;
+ while (usb_serial_devices[device_num] != NULL) {
+ type = usb_serial_devices[device_num];
+ dbg ("Looking at %s Vendor id=%.4x Product id=%.4x", type->name, *(type->idVendor), *(type->idProduct));
+
+ /* look at the device descriptor */
+ if ((dev->descriptor.idVendor == *(type->idVendor)) &&
+ (dev->descriptor.idProduct == *(type->idProduct))) {
+
+ dbg("descriptor matches...looking at the endpoints");
+
+ /* descriptor matches, let's try to find the endpoints needed */
+ interrupt_pipe = bulk_in_pipe = bulk_out_pipe = HAS_NOT;
+
+ /* check out the endpoints */
+ interface = &dev->actconfig->interface[ifnum].altsetting[0];
+ for (i = 0; i < interface->bNumEndpoints; ++i) {
+ endpoint = &interface->endpoint[i];
+
+ if ((endpoint->bEndpointAddress & 0x80) &&
+ ((endpoint->bmAttributes & 3) == 0x02)) {
+ /* we found a bulk in endpoint */
+ dbg("found bulk in");
+ bulk_in_pipe = HAS;
+ bulk_in_endpoint[num_bulk_in] = endpoint;
+ ++num_bulk_in;
+ }
+
+ if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
+ ((endpoint->bmAttributes & 3) == 0x02)) {
+ /* we found a bulk out endpoint */
+ dbg("found bulk out");
+ bulk_out_pipe = HAS;
+ bulk_out_endpoint[num_bulk_out] = endpoint;
+ ++num_bulk_out;
+ }
+
+ if ((endpoint->bEndpointAddress & 0x80) &&
+ ((endpoint->bmAttributes & 3) == 0x03)) {
+ /* we found a interrupt in endpoint */
+ dbg("found interrupt in");
+ interrupt_pipe = HAS;
+ interrupt_in_endpoint[num_interrupt_in] = endpoint;
+ ++num_interrupt_in;
+ }
+
+ }
+
+ /* verify that we found all of the endpoints that we need */
+ if ((interrupt_pipe & type->needs_interrupt_in) &&
+ (bulk_in_pipe & type->needs_bulk_in) &&
+ (bulk_out_pipe & type->needs_bulk_out)) {
+ /* found all that we need */
+ info("%s converter detected", type->name);
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+ if (type == &generic_device)
+ num_ports = num_bulk_out;
+ else
+#endif
+ num_ports = type->num_ports;
+
+ serial = get_free_serial (num_ports, &minor);
+ if (serial == NULL) {
+ err("No more free serial devices");
+ return NULL;
+ }
+
+ serial->dev = dev;
+ serial->type = type;
+ serial->minor = minor;
+ serial->num_ports = num_ports;
+ serial->num_bulk_in = num_bulk_in;
+ serial->num_bulk_out = num_bulk_out;
+ serial->num_interrupt_in = num_interrupt_in;
+
+ /* collect interrupt_in endpoints now, because
+ the keyspan_pda startup function needs
+ to know about them */
+ for (i = 0; i < num_interrupt_in; ++i) {
+ port = &serial->port[i];
+ buffer_size = interrupt_in_endpoint[i]->wMaxPacketSize;
+ port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+ if (!port->interrupt_in_buffer) {
+ err("Couldn't allocate interrupt_in_buffer");
+ goto probe_error;
+ }
+ port->interrupt_in_endpoint = interrupt_in_endpoint[i];
+ }
+
+ /* if this device type has a startup function, call it */
+ if (type->startup) {
+ if (type->startup (serial)) {
+ return_serial (serial);
+ return NULL;
+ }
+ }
+
+ /* set up the endpoint information */
+ for (i = 0; i < num_bulk_in; ++i) {
+ port = &serial->port[i];
+ port->read_urb = usb_alloc_urb (0);
+ if (!port->read_urb) {
+ err("No free urbs available");
+ goto probe_error;
+ }
+ buffer_size = bulk_in_endpoint[i]->wMaxPacketSize;
+ port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+ if (!port->bulk_in_buffer) {
+ err("Couldn't allocate bulk_in_buffer");
+ goto probe_error;
+ }
+ if (serial->type->read_bulk_callback) {
+ FILL_BULK_URB(port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
+ port->bulk_in_buffer, buffer_size, serial->type->read_bulk_callback, port);
+ } else {
+ FILL_BULK_URB(port->read_urb, dev, usb_rcvbulkpipe (dev, bulk_in_endpoint[i]->bEndpointAddress),
+ port->bulk_in_buffer, buffer_size, generic_read_bulk_callback, port);
+ }
+ }
+
+ for (i = 0; i < num_bulk_out; ++i) {
+ port = &serial->port[i];
+ port->write_urb = usb_alloc_urb(0);
+ if (!port->write_urb) {
+ err("No free urbs available");
+ goto probe_error;
+ }
+ port->bulk_out_size = bulk_out_endpoint[i]->wMaxPacketSize;
+ port->bulk_out_buffer = kmalloc (port->bulk_out_size, GFP_KERNEL);
+ if (!port->bulk_out_buffer) {
+ err("Couldn't allocate bulk_out_buffer");
+ goto probe_error;
+ }
+ if (serial->type->write_bulk_callback) {
+ FILL_BULK_URB(port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
+ port->bulk_out_buffer, port->bulk_out_size, serial->type->write_bulk_callback, port);
+ } else {
+ FILL_BULK_URB(port->write_urb, dev, usb_sndbulkpipe (dev, bulk_out_endpoint[i]->bEndpointAddress),
+ port->bulk_out_buffer, port->bulk_out_size, generic_write_bulk_callback, port);
+ }
+ }
+
+#if 0 /* use this code when WhiteHEAT is up and running */
+ for (i = 0; i < num_interrupt_in; ++i) {
+ port = &serial->port[i];
+ port->control_urb = usb_alloc_urb(0);
+ if (!port->control_urb) {
+ err("No free urbs available");
+ goto probe_error;
+ }
+ buffer_size = interrupt_in_endpoint[i]->wMaxPacketSize;
+ port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+ if (!port->interrupt_in_buffer) {
+ err("Couldn't allocate interrupt_in_buffer");
+ goto probe_error;
+ }
+ FILL_INT_URB(port->control_urb, dev, usb_rcvintpipe (dev, interrupt_in_endpoint[i]->bEndpointAddress),
+ port->interrupt_in_buffer, buffer_size, serial_control_irq,
+ port, interrupt_in_endpoint[i]->bInterval);
+ }
+#endif
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ info("%s converter now attached to ttyUSB%d", type->name, serial->minor + i);
+ }
+
+ MOD_INC_USE_COUNT;
+
+ return serial;
+ } else {
+ info("descriptors matched, but endpoints did not");
+ }
+ }
+
+ /* look at the next type in our list */
+ ++device_num;
+ }
+
+probe_error:
+ if (serial) {
+ for (i = 0; i < num_bulk_in; ++i) {
+ port = &serial->port[i];
+ if (port->read_urb)
+ usb_free_urb (port->read_urb);
+ if (serial->port[i].bulk_in_buffer[i])
+ kfree (serial->port[i].bulk_in_buffer);
+ }
+ for (i = 0; i < num_bulk_out; ++i) {
+ port = &serial->port[i];
+ if (port->write_urb)
+ usb_free_urb (port->write_urb);
+ if (serial->port[i].bulk_out_buffer)
+ kfree (serial->port[i].bulk_out_buffer);
+ }
+ for (i = 0; i < num_interrupt_in; ++i) {
+ port = &serial->port[i];
+ if (port->control_urb)
+ usb_free_urb (port->control_urb);
+ if (serial->port[i].interrupt_in_buffer)
+ kfree (serial->port[i].interrupt_in_buffer);
+ }
+
+ /* return the minor range that this device had */
+ return_serial (serial);
+
+ /* free up any memory that we allocated */
+ kfree (serial);
+ }
+ return NULL;
+}
+
+
+static void usb_serial_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct usb_serial *serial = (struct usb_serial *) ptr;
+ struct usb_serial_port *port;
+ int i;
+
+ if (serial) {
+ for (i = 0; i < serial->num_ports; ++i)
+ serial->port[i].active = 0;
+
+ for (i = 0; i < serial->num_bulk_in; ++i) {
+ port = &serial->port[i];
+ if (port->read_urb) {
+ usb_unlink_urb (port->read_urb);
+ usb_free_urb (port->read_urb);
+ }
+ if (port->bulk_in_buffer)
+ kfree (port->bulk_in_buffer);
+ }
+ for (i = 0; i < serial->num_bulk_out; ++i) {
+ port = &serial->port[i];
+ if (port->write_urb) {
+ usb_unlink_urb (port->write_urb);
+ usb_free_urb (port->write_urb);
+ }
+ if (port->bulk_out_buffer)
+ kfree (port->bulk_out_buffer);
+ }
+ for (i = 0; i < serial->num_interrupt_in; ++i) {
+ port = &serial->port[i];
+ if (port->control_urb) {
+ usb_unlink_urb (port->control_urb);
+ usb_free_urb (port->control_urb);
+ }
+ if (port->interrupt_in_buffer)
+ kfree (port->interrupt_in_buffer);
+ }
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->minor + i);
+ }
+
+ /* return the minor range that this device had */
+ return_serial (serial);
+
+ /* free up any memory that we allocated */
+ kfree (serial);
+
+ } else {
+ info("device disconnected");
+ }
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+static struct tty_driver serial_tty_driver = {
+ magic: TTY_DRIVER_MAGIC,
+ driver_name: "usb",
+ name: "ttyUSB%d",
+ major: SERIAL_TTY_MAJOR,
+ minor_start: 0,
+ num: SERIAL_TTY_MINORS,
+ type: TTY_DRIVER_TYPE_SERIAL,
+ subtype: SERIAL_TYPE_NORMAL,
+ flags: TTY_DRIVER_REAL_RAW,
+ refcount: &serial_refcount,
+ table: serial_tty,
+ proc_entry: NULL,
+ other: NULL,
+ termios: serial_termios,
+ termios_locked: serial_termios_locked,
+
+ open: serial_open,
+ close: serial_close,
+ write: serial_write,
+ put_char: NULL,
+ flush_chars: NULL,
+ write_room: serial_write_room,
+ ioctl: serial_ioctl,
+ set_termios: serial_set_termios,
+ set_ldisc: NULL,
+ throttle: serial_throttle,
+ unthrottle: serial_unthrottle,
+ stop: NULL,
+ start: NULL,
+ hangup: NULL,
+ break_ctl: serial_break,
+ wait_until_sent: NULL,
+ send_xchar: NULL,
+ read_proc: NULL,
+ chars_in_buffer: serial_chars_in_buffer,
+ flush_buffer: NULL
+};
+
+
+int usb_serial_init(void)
+{
+ int i;
+
+ /* Initalize our global data */
+ for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
+ serial_table[i] = NULL;
+ }
+
+ /* register the tty driver */
+ serial_tty_driver.init_termios = tty_std_termios;
+ serial_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ if (tty_register_driver (&serial_tty_driver)) {
+ err("failed to register tty driver");
+ return -EPERM;
+ }
+
+ /* register the USB driver */
+ if (usb_register(&usb_serial_driver) < 0) {
+ tty_unregister_driver(&serial_tty_driver);
+ return -1;
+ }
+
+ info("support registered");
+ return 0;
+}
+
+
+void usb_serial_exit(void)
+{
+ tty_unregister_driver(&serial_tty_driver);
+ usb_deregister(&usb_serial_driver);
+}
+
+
+module_init(usb_serial_init);
+module_exit(usb_serial_exit);
+
+
--- /dev/null
+/*
+ * USB HandSpring Visor driver
+ *
+ * (C) Copyright (C) 1999, 2000
+ * 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.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (03/26/2000) gkh
+ * Split driver up into device specific pieces.
+ *
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_SERIAL_VISOR
+
+#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/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+
+#include "visor.h"
+
+
+/* function prototypes for a handspring visor */
+static int visor_open (struct usb_serial_port *port, struct file *filp);
+static void visor_close (struct usb_serial_port *port, struct file *filp);
+static void visor_throttle (struct usb_serial_port *port);
+static void visor_unthrottle (struct usb_serial_port *port);
+static int visor_startup (struct usb_serial *serial);
+
+/* All of the device info needed for the Handspring Visor */
+static __u16 handspring_vendor_id = HANDSPRING_VENDOR_ID;
+static __u16 handspring_product_id = HANDSPRING_VISOR_ID;
+struct usb_serial_device_type handspring_device = {
+ name: "Handspring Visor",
+ idVendor: &handspring_vendor_id, /* the Handspring vendor ID */
+ idProduct: &handspring_product_id, /* the Handspring Visor product id */
+ needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */
+ needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */
+ needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */
+ num_interrupt_in: 0,
+ num_bulk_in: 2,
+ num_bulk_out: 2,
+ num_ports: 2,
+ open: visor_open,
+ close: visor_close,
+ throttle: visor_throttle,
+ unthrottle: visor_unthrottle,
+ startup: visor_startup,
+};
+
+
+/******************************************************************************
+ * Handspring Visor specific driver functions
+ ******************************************************************************/
+static int visor_open (struct usb_serial_port *port, struct file *filp)
+{
+ dbg("visor_open port %d", port->number);
+
+ if (port->active) {
+ dbg ("device already open");
+ return -EINVAL;
+ }
+
+ port->active = 1;
+
+ /*Start reading from the device*/
+ if (usb_submit_urb(port->read_urb))
+ dbg("usb_submit_urb(read bulk) failed");
+
+ return (0);
+}
+
+
+static void visor_close (struct usb_serial_port *port, struct file * filp)
+{
+ struct usb_serial *serial = port->serial;
+ unsigned char *transfer_buffer = kmalloc (0x12, GFP_KERNEL);
+
+ dbg("visor_close port %d", port->number);
+
+ if (!transfer_buffer) {
+ err("visor_close: kmalloc(%d) failed.", 0x12);
+ } else {
+ /* send a shutdown message to the device */
+ usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION,
+ 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
+ }
+
+ /* shutdown our bulk reads and writes */
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
+ port->active = 0;
+}
+
+
+static void visor_throttle (struct usb_serial_port *port)
+{
+ dbg("visor_throttle port %d", port->number);
+
+ usb_unlink_urb (port->read_urb);
+
+ return;
+}
+
+
+static void visor_unthrottle (struct usb_serial_port *port)
+{
+ dbg("visor_unthrottle port %d", port->number);
+
+ if (usb_unlink_urb (port->read_urb))
+ dbg("usb_submit_urb(read bulk) failed");
+
+ return;
+}
+
+
+static int visor_startup (struct usb_serial *serial)
+{
+ int response;
+ int i;
+ unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL);
+
+ if (!transfer_buffer) {
+ err("visor_startup: kmalloc(%d) failed.", 256);
+ return -ENOMEM;
+ }
+
+ dbg("visor_startup");
+
+ dbg("visor_setup: Set config to 1");
+ usb_set_configuration (serial->dev, 1);
+
+ /* send a get connection info request */
+ response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_GET_CONNECTION_INFORMATION,
+ 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300);
+ if (response < 0) {
+ err("visor_startup: error getting connection information");
+ } else {
+ struct visor_connection_info *connection_info = (struct visor_connection_info *)transfer_buffer;
+ char *string;
+ info("%s: Number of ports: %d", serial->type->name, connection_info->num_ports);
+ for (i = 0; i < connection_info->num_ports; ++i) {
+ switch (connection_info->connections[i].port_function_id) {
+ case VISOR_FUNCTION_GENERIC:
+ string = "Generic";
+ break;
+ case VISOR_FUNCTION_DEBUGGER:
+ string = "Debugger";
+ break;
+ case VISOR_FUNCTION_HOTSYNC:
+ string = "HotSync";
+ break;
+ case VISOR_FUNCTION_CONSOLE:
+ string = "Console";
+ break;
+ case VISOR_FUNCTION_REMOTE_FILE_SYS:
+ string = "Remote File System";
+ break;
+ default:
+ string = "unknown";
+ break;
+ }
+ info("%s: port %d, is for %s use and is bound to ttyUSB%d", serial->type->name, connection_info->connections[i].port, string, serial->minor + i);
+ }
+ }
+
+ /* ask for the number of bytes available, but ignore the response as it is broken */
+ response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE,
+ 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300);
+ if (response < 0) {
+ err("visor_startup: error getting bytes available request");
+ }
+
+ kfree (transfer_buffer);
+
+ /* continue on with initialization */
+ return (0);
+}
+
+
+#endif /* CONFIG_USB_SERIAL_VISOR*/
+
+
--- /dev/null
+/*
+ * USB HandSpring Visor driver
+ *
+ * (C) Copyright (C) 1999, 2000
+ * 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.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ */
+
+#ifndef __LINUX_USB_SERIAL_VISOR_H
+#define __LINUX_USB_SERIAL_VISOR_H
+
+
+#define HANDSPRING_VENDOR_ID 0x082d
+#define HANDSPRING_VISOR_ID 0x0100
+
+/****************************************************************************
+ * Handspring Visor Vendor specific request codes (bRequest values)
+ * A big thank you to Handspring for providing the following information.
+ * If anyone wants the original file where these values and structures came
+ * from, send email to <greg@kroah.com>.
+ ****************************************************************************/
+
+/****************************************************************************
+ * VISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that
+ * are available to be transfered to the host for the specified endpoint.
+ * Currently this is not used, and always returns 0x0001
+ ****************************************************************************/
+#define VISOR_REQUEST_BYTES_AVAILABLE 0x01
+
+/****************************************************************************
+ * VISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host
+ * is now closing the pipe. An empty packet is sent in response.
+ ****************************************************************************/
+#define VISOR_CLOSE_NOTIFICATION 0x02
+
+/****************************************************************************
+ * VISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to
+ * get the endpoints used by the connection.
+ ****************************************************************************/
+#define VISOR_GET_CONNECTION_INFORMATION 0x03
+
+
+/****************************************************************************
+ * VISOR_GET_CONNECTION_INFORMATION returns data in the following format
+ ****************************************************************************/
+struct visor_connection_info {
+ __u16 num_ports;
+ struct {
+ __u8 port_function_id;
+ __u8 port;
+ } connections[2];
+};
+
+
+/* struct visor_connection_info.connection[x].port defines: */
+#define VISOR_ENDPOINT_1 0x01
+#define VISOR_ENDPOINT_2 0x02
+
+/* struct visor_connection_info.connection[x].port_function_id defines: */
+#define VISOR_FUNCTION_GENERIC 0x00
+#define VISOR_FUNCTION_DEBUGGER 0x01
+#define VISOR_FUNCTION_HOTSYNC 0x02
+#define VISOR_FUNCTION_CONSOLE 0x03
+#define VISOR_FUNCTION_REMOTE_FILE_SYS 0x04
+
+#endif
+
--- /dev/null
+/*
+ * USB ConnectTech WhiteHEAT driver
+ *
+ * (C) Copyright (C) 1999, 2000
+ * 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.
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (03/26/2000) gkh
+ * Split driver up into device specific pieces.
+ *
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_USB_SERIAL_WHITEHEAT
+
+#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/malloc.h>
+#include <linux/fcntl.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
+#include <linux/usb.h>
+
+#include "usb-serial.h"
+
+#include "whiteheat.h" /* firmware for the ConnectTech WhiteHEAT device */
+
+
+#define CONNECT_TECH_VENDOR_ID 0x0710
+#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001
+#define CONNECT_TECH_WHITE_HEAT_ID 0x8001
+
+/* function prototypes for the Connect Tech WhiteHEAT serial converter */
+static int whiteheat_open (struct usb_serial_port *port, struct file *filp);
+static void whiteheat_close (struct usb_serial_port *port, struct file *filp);
+static void whiteheat_set_termios (struct usb_serial_port *port, struct termios * old);
+static void whiteheat_throttle (struct usb_serial_port *port);
+static void whiteheat_unthrottle (struct usb_serial_port *port);
+static int whiteheat_startup (struct usb_serial *serial);
+
+/* All of the device info needed for the Connect Tech WhiteHEAT */
+static __u16 connecttech_vendor_id = CONNECT_TECH_VENDOR_ID;
+static __u16 connecttech_whiteheat_fake_product_id = CONNECT_TECH_FAKE_WHITE_HEAT_ID;
+static __u16 connecttech_whiteheat_product_id = CONNECT_TECH_WHITE_HEAT_ID;
+struct usb_serial_device_type whiteheat_fake_device = {
+ name: "Connect Tech - WhiteHEAT - (prerenumeration)",
+ idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */
+ idProduct: &connecttech_whiteheat_fake_product_id, /* the White Heat initial product id */
+ needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */
+ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */
+ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */
+ num_interrupt_in: NUM_DONT_CARE,
+ num_bulk_in: NUM_DONT_CARE,
+ num_bulk_out: NUM_DONT_CARE,
+ num_ports: 1,
+ startup: whiteheat_startup
+};
+struct usb_serial_device_type whiteheat_device = {
+ name: "Connect Tech - WhiteHEAT",
+ idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */
+ idProduct: &connecttech_whiteheat_product_id, /* the White Heat real product id */
+ needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */
+ needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */
+ needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */
+ num_interrupt_in: NUM_DONT_CARE,
+ num_bulk_in: NUM_DONT_CARE,
+ num_bulk_out: NUM_DONT_CARE,
+ num_ports: 4,
+ open: whiteheat_open,
+ close: whiteheat_close,
+ throttle: whiteheat_throttle,
+ unthrottle: whiteheat_unthrottle,
+ set_termios: whiteheat_set_termios,
+};
+
+
+/*****************************************************************************
+ * Connect Tech's White Heat specific driver functions
+ *****************************************************************************/
+static int whiteheat_open (struct usb_serial_port *port, struct file *filp)
+{
+ dbg("whiteheat_open port %d", port->number);
+
+ if (port->active) {
+ dbg ("device already open");
+ return -EINVAL;
+ }
+ port->active = 1;
+
+ /*Start reading from the device*/
+ if (usb_submit_urb(port->read_urb))
+ dbg("usb_submit_urb(read bulk) failed");
+
+ /* Need to do device specific setup here (control lines, baud rate, etc.) */
+ /* FIXME!!! */
+
+ return (0);
+}
+
+
+static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
+{
+ dbg("whiteheat_close port %d", port->number);
+
+ /* Need to change the control lines here */
+ /* FIXME */
+
+ /* shutdown our bulk reads and writes */
+ usb_unlink_urb (port->write_urb);
+ usb_unlink_urb (port->read_urb);
+ port->active = 0;
+}
+
+
+static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+{
+ unsigned int cflag = port->tty->termios->c_cflag;
+
+ dbg("whiteheat_set_termios port %d", port->number);
+
+ /* check that they really want us to change something */
+ if (old_termios) {
+ if ((cflag == old_termios->c_cflag) &&
+ (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
+ dbg("nothing to change...");
+ return;
+ }
+ }
+
+ /* do the parsing of the cflag to see what to set the line to */
+ /* FIXME!! */
+
+ return;
+}
+
+static void whiteheat_throttle (struct usb_serial_port *port)
+{
+ dbg("whiteheat_throttle port %d", port->number);
+
+ /* Change the control signals */
+ /* FIXME!!! */
+
+ return;
+}
+
+
+static void whiteheat_unthrottle (struct usb_serial_port *port)
+{
+ dbg("whiteheat_unthrottle port %d", port->number);
+
+ /* Change the control signals */
+ /* FIXME!!! */
+
+ return;
+}
+
+
+/* steps to download the firmware to the WhiteHEAT device:
+ - hold the reset (by writing to the reset bit of the CPUCS register)
+ - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD
+ - release the reset (by writing to the CPUCS register)
+ - download the WH.HEX file for all addresses greater than 0x1b3f using
+ VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD
+ - hold the reset
+ - download the WH.HEX file for all addresses less than 0x1b40 using
+ VENDOR_REQUEST_ANCHOR_LOAD
+ - release the reset
+ - device renumerated itself and comes up as new device id with all
+ firmware download completed.
+*/
+static int whiteheat_startup (struct usb_serial *serial)
+{
+ int response;
+ const struct whiteheat_hex_record *record;
+
+ dbg("whiteheat_startup");
+
+ response = ezusb_set_reset (serial, 1);
+
+ record = &whiteheat_loader[0];
+ while (record->address != 0xffff) {
+ response = ezusb_writememory (serial, record->address,
+ (unsigned char *)record->data, record->data_size, 0xa0);
+ if (response < 0) {
+ err("ezusb_writememory failed for loader (%d %04X %p %d)",
+ response, record->address, record->data, record->data_size);
+ break;
+ }
+ ++record;
+ }
+
+ response = ezusb_set_reset (serial, 0);
+
+ record = &whiteheat_firmware[0];
+ while (record->address < 0x1b40) {
+ ++record;
+ }
+ while (record->address != 0xffff) {
+ response = ezusb_writememory (serial, record->address,
+ (unsigned char *)record->data, record->data_size, 0xa0);
+ if (response < 0) {
+ err("ezusb_writememory failed for first firmware step (%d %04X %p %d)",
+ response, record->address, record->data, record->data_size);
+ break;
+ }
+ ++record;
+ }
+
+ response = ezusb_set_reset (serial, 1);
+
+ record = &whiteheat_firmware[0];
+ while (record->address < 0x1b40) {
+ response = ezusb_writememory (serial, record->address,
+ (unsigned char *)record->data, record->data_size, 0xa0);
+ if (response < 0) {
+ err("ezusb_writememory failed for second firmware step (%d %04X %p %d)",
+ response, record->address, record->data, record->data_size);
+ break;
+ }
+ ++record;
+ }
+
+ response = ezusb_set_reset (serial, 0);
+
+ /* we want this device to fail to have a driver assigned to it. */
+ return (1);
+}
+
+#endif /* CONFIG_USB_SERIAL_WHITEHEAT */
+
+
*/
int usb_audio_init(void);
-int usb_cpia_init(void);
int usb_ibmcam_init(void);
int dabusb_init(void);
int plusb_init(void);
#ifdef CONFIG_USB_AUDIO
usb_audio_init();
#endif
-#ifdef CONFIG_USB_CPIA
- usb_cpia_init();
-#endif
#ifdef CONFIG_USB_IBMCAM
usb_ibmcam_init();
#endif
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/malloc.h>
-#define DEBUG
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
#include <linux/usb.h>
static void usb_show_endpoint(struct usb_endpoint_descriptor *endpoint)
/* FIXME: This doesn't actually abort anything */
static int us_abort( Scsi_Cmnd *srb )
{
+ printk(KERN_CRIT "usb-storage: abort() requested but not implemented\n" );
return 0;
}
{
struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+ printk(KERN_CRIT "usb-storage: bus_reset() requested but not implemented\n" );
US_DEBUGP("Bus reset requested\n");
if (us->ip_wanted)
up(&(us->ip_waitq));
/* FIXME: This doesn't actually reset anything */
static int us_host_reset( Scsi_Cmnd *srb )
{
+ printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" );
return 0;
}
*/
static Scsi_Host_Template my_host_template = {
- NULL, /* next */
- NULL, /* module */
- NULL, /* proc_dir */
- usb_stor_proc_info,
- NULL, /* name - points to unique */
- us_detect,
- us_release,
- NULL, /* info */
- NULL, /* ioctl */
- us_command,
- us_queuecommand,
- NULL, /* eh_strategy */
- us_abort,
- us_bus_reset,
- us_bus_reset,
- us_host_reset,
- NULL, /* abort */
- NULL, /* reset */
- NULL, /* slave_attach */
- NULL, /* bios_param */
- NULL, /* select_queue_depths */
- 1, /* can_queue */
- -1, /* this_id */
- SG_ALL, /* sg_tablesize */
- 1, /* cmd_per_lun */
- 0, /* present */
- FALSE, /* unchecked_isa_dma */
- TRUE, /* use_clustering */
- TRUE, /* use_new_eh_code */
- TRUE /* emulated */
+ proc_info: usb_stor_proc_info,
+ detect: us_detect,
+ release: us_release,
+ command: us_command,
+ queuecommand: us_queuecommand,
+
+ eh_abort_handler: us_abort,
+ eh_device_reset_handler:us_bus_reset,
+ eh_bus_reset_handler: us_bus_reset,
+ eh_host_reset_handler: us_host_reset,
+
+ can_queue: 1,
+ this_id: -1,
+
+ sg_tablesize: SG_ALL,
+ cmd_per_lun: 1,
+ present: 0,
+ unchecked_isa_dma: FALSE,
+ use_clustering: TRUE,
+ use_new_eh_code: TRUE,
+ emulated: TRUE,
};
static unsigned char sense_notready[] = {
down(&(ss->notify));
/* now register - our detect function will be called */
- ss->htmplt.module = &__this_module;
+ ss->htmplt.module = THIS_MODULE;
scsi_register_module(MODULE_SCSI_HA, &(ss->htmplt));
/* put us in the list */
int __init usb_stor_init(void)
{
- /*
- * Check to see if the host template is a different size from
- * what we're expected -- people have updated this in the past
- * and forgotten about this driver.
- */
- if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
- printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE bad\n");
- printk(KERN_ERR
- "usb-storage: expected %d bytes, got %d bytes\n",
- SCSI_HOST_TEMPLATE_SIZE, sizeof(my_host_template)) ;
- return -1 ;
- }
-
/* register the driver, return -1 if error */
if (usb_register(&storage_driver) < 0)
return -1;
#include <linux/bitops.h>
#include <linux/malloc.h>
#include <linux/interrupt.h> /* for in_interrupt() */
-#define DEBUG
+#ifdef CONFIG_USB_DEBUG
+ #define DEBUG
+#else
+ #undef DEBUG
+#endif
#include <linux/usb.h>
/*
err("config descriptor too short (expected %i, got %i)",tmp,result);
kfree(bigbuffer);
goto err;
- }
+ }
result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer);
kfree(bigbuffer);
}
return 0;
- err:
- dev->descriptor.bNumConfigurations=cfgno;
+err:
+ dev->descriptor.bNumConfigurations = cfgno;
return result;
}
dev->have_langid = -1;
dev->string_langid = tbuf[2] | (tbuf[3]<< 8);
/* always use the first langid listed */
- info("USB device number %d default language ID 0x%x",
+ dbg("USB device number %d default language ID 0x%x",
dev->devnum, dev->string_langid);
}
}
return -1;
}
- info("new device strings: Mfr=%d, Product=%d, SerialNumber=%d",
+ dbg("new device strings: Mfr=%d, Product=%d, SerialNumber=%d",
dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber);
+#ifdef DEBUG
if (dev->descriptor.iManufacturer)
usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer);
if (dev->descriptor.iProduct)
usb_show_string(dev, "Product", dev->descriptor.iProduct);
if (dev->descriptor.iSerialNumber)
usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
+#endif
/* now that the basic setup is over, add a /proc/bus/usb entry */
usbdevfs_add_device(dev);
if [ "$CONFIG_ARCH_NETWINDER" = "y" ]; then
tristate ' Cyber2000 support' CONFIG_FB_CYBER2000
fi
+ if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+ bool ' SA-1100 LCD support' CONFIG_FB_SA1100
+ fi
if [ "$CONFIG_APOLLO" = "y" ]; then
define_bool CONFIG_FB_APOLLO y
fi
fi
fi
if [ "$CONFIG_FB_ACORN" = "y" -o "$CONFIG_FB_MAC" = "y" -o \
- "$CONFIG_FB_VIRTUAL" = "y" ]; then
+ "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_VIRTUAL" = "y" ]; then
define_tristate CONFIG_FBCON_CFB2 y
define_tristate CONFIG_FBCON_CFB4 y
else
if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_MAC" = "m" -o \
- "$CONFIG_FB_VIRTUAL" = "m" ]; then
+ "$CONFIG_FB_SA1100" = "m" -o "$CONFIG_FB_VIRTUAL" = "m" ]; then
define_tristate CONFIG_FBCON_CFB2 m
define_tristate CONFIG_FBCON_CFB4 m
fi
"$CONFIG_FB_P9100" = "y" -o "$CONFIG_FB_ATY128" = "y" -o \
"$CONFIG_FB_RIVA" = "y" -o \
"$CONFIG_FB_SGIVW" = "y" -o "$CONFIG_FB_CYBER2000" = "y" -o \
- "$CONFIG_FB_3DFX" = "y" ]; then
+ "$CONFIG_FB_SA1100" = "y" -o "$CONFIG_FB_3DFX" = "y" ]; then
define_tristate CONFIG_FBCON_CFB8 y
else
if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \
"$CONFIG_FB_IGA" = "m" -o "$CONFIG_FB_MATROX" = "m" -o \
"$CONFIG_FB_CT65550" = "m" -o "$CONFIG_FB_PM2" = "m" -o \
"$CONFIG_FB_P9100" = "m" -o "$CONFIG_FB_ATY128" = "m" -o \
- "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" ]; then
+ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_CYBER2000" = "m" -o \
+ "$CONFIG_FB_SA1100" = "m" ]; then
define_tristate CONFIG_FBCON_CFB8 m
fi
fi
obj-$(CONFIG_FB_SUN3) += sun3fb.o
obj-$(CONFIG_FB_BWTWO) += bwtwofb.o
obj-$(CONFIG_FB_HGA) += hgafb.o
+obj-$(CONFIG_FB_SA1100) += sa1100fb.o
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
# Generic Low Level Drivers
if (par->accel_flags & FB_ACCELF_TEXT)
aty128_init_engine(par, info);
-#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0/*def CONFIG_FB_COMPAT_XPMAC*/
#if 0 /* use this when macmodes gets updated */
if (!console_fb_info || console_fb_info == &info->fb_info) {
disp_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
extern int control_init(void);
extern int control_setup(char*);
extern int g364fb_init(void);
+extern void sa1100fb_init(void);
+extern void sa1100fb_setup(char*);
extern int fm2fb_init(void);
extern int fm2fb_setup(char*);
extern int q40fb_init(void);
#ifdef CONFIG_FB_G364
{ "g364", g364fb_init, NULL },
#endif
+#ifdef CONFIG_FB_SA1100
+ { "sa1100", sa1100fb_init, sa1100fb_setup },
+#endif
#ifdef CONFIG_FB_FM2
{ "fm2fb", fm2fb_init, fm2fb_setup },
#endif
--- /dev/null
+/*
+ * linux/drivers/video/sa1100fb.c -- StrongARM 1100 LCD Controller Frame Buffer Device
+ *
+ * Copyright (C) 1999 Eric A. Thomas
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ */
+
+
+/*
+ * Code Status:
+ * 4/1/99 - Driver appears to be working for Brutus 320x200x8bpp mode. Other
+ * resolutions are working, but only the 8bpp mode is supported.
+ * Changes need to be made to the palette encode and decode routines
+ * to support 4 and 16 bpp modes.
+ * Driver is not designed to be a module. The FrameBuffer is statically
+ * allocated since dynamic allocation of a 300k buffer cannot be guaranteed.
+ *
+ * 6/17/99 - FrameBuffer memory is now allocated at run-time when the
+ * driver is initialized.
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/proc/pgtable.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-mfb.h>
+#include <video/fbcon-cfb4.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+
+
+/*
+ * Debug macros
+ */
+// #define DEBUG
+#ifdef DEBUG
+# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+# define DPRINTK(fmt, args...)
+#endif
+
+
+/*
+ * The MAX_x defines are used to specify the maximum ranges for
+ * parameters that affect the size of the frame buffer memory
+ * region. Since the frame buffer memory is not dynamically
+ * alocated, the maximum size that will be used is allocated.
+ */
+#if defined (CONFIG_SA1100_PENNY)
+
+#define MAX_BITS_PER_PIXEL 8
+#define MAX_SCREEN_SIZE_H 640
+#define MAX_SCREEN_SIZE_V 480
+
+#elif defined(CONFIG_SA1100_BRUTUS)
+
+#define MAX_BITS_PER_PIXEL 8
+#define MAX_SCREEN_SIZE_H 320
+#define MAX_SCREEN_SIZE_V 240
+
+#elif defined (CONFIG_SA1100_THINCLIENT)
+
+#define MAX_BITS_PER_PIXEL 8
+/*#define MAX_BITS_PER_PIXEL 16*/
+
+#define MAX_SCREEN_SIZE_H 640
+#define MAX_SCREEN_SIZE_V 480
+
+#elif defined(CONFIG_SA1100_TIFON)
+
+#define MAX_BITS_PER_PIXEL 4
+#define MAX_SCREEN_SIZE_H 640
+#define MAX_SCREEN_SIZE_V 200
+
+#define REVERSE_VIDEO_4BIT
+
+#elif defined(CONFIG_SA1100_LART)
+
+#define MAX_BITS_PER_PIXEL 4
+#define MAX_SCREEN_SIZE_H 320
+#define MAX_SCREEN_SIZE_V 240
+
+#endif
+
+/* Default resolutions */
+#if defined(CONFIG_SA1100_PENNY)
+#define DEFAULT_XRES 640
+#define DEFAULT_YRES 480
+#define DEFAULT_BPP 8
+#else
+#define DEFAULT_XRES MAX_SCREEN_SIZE_H
+#define DEFAULT_YRES MAX_SCREEN_SIZE_V
+#define DEFAULT_BPP MAX_BITS_PER_PIXEL
+#endif
+
+/* Memory size macros for determining required FrameBuffer size */
+#define MAX_PALETTE_NUM_ENTRIES 256
+#define ADJUSTED_MAX_BITS_PER_PIXEL (MAX_BITS_PER_PIXEL > 8 ? 16 : MAX_BITS_PER_PIXEL)
+#define MAX_PALETTE_MEM_SIZE (MAX_PALETTE_NUM_ENTRIES * 2)
+#define MAX_PIXEL_MEM_SIZE ((MAX_SCREEN_SIZE_H * MAX_SCREEN_SIZE_V * ADJUSTED_MAX_BITS_PER_PIXEL ) / 8)
+#define MAX_FRAMEBUFFER_MEM_SIZE (MAX_PIXEL_MEM_SIZE + MAX_PALETTE_MEM_SIZE + 32)
+#define ALLOCATED_FB_MEM_SIZE (PAGE_ALIGN (MAX_FRAMEBUFFER_MEM_SIZE + PAGE_SIZE * 2))
+
+#define SA1100_PALETTE_MEM_SIZE(bpp) (((bpp)==8?256:16)*2)
+#define SA1100_PALETTE_MODE_VAL(bpp) (((bpp) & 0x018) << 9)
+
+/* Minimum X and Y resolutions */
+#define MIN_XRES 64
+#define MIN_YRES 64
+
+/* Possible controller_state modes */
+#define LCD_MODE_DISABLED 0 // Controller is disabled and Disable Done received
+#define LCD_MODE_DISABLE_BEFORE_ENABLE 1 // Re-enable after Disable Done IRQ is received
+#define LCD_MODE_ENABLED 2 // Controller is enabled
+
+#define SA1100_NAME "SA1100"
+#define NR_MONTYPES 1
+
+static u_char *VideoMemRegion = NULL;
+static u_char *VideoMemRegion_phys = NULL;
+
+/* Local LCD controller parameters */
+/* These can be reduced by making better use of fb_var_screeninfo parameters. */
+/* Several duplicates exist in the two structures. */
+struct sa1100fb_par {
+ u_char *p_screen_base;
+ u_char *v_screen_base;
+ u_short *p_palette_base;
+ u_short *v_palette_base;
+ unsigned long screen_size;
+ unsigned int palette_size;
+ unsigned int xres;
+ unsigned int yres;
+ unsigned int xres_virtual;
+ unsigned int yres_virtual;
+ unsigned int bits_per_pixel;
+ signed int montype;
+ unsigned int currcon;
+ unsigned int visual;
+ unsigned int allow_modeset : 1;
+ unsigned int active_lcd : 1;
+ volatile u_char controller_state;
+};
+
+/* Shadows for LCD controller registers */
+struct sa1100fb_lcd_reg {
+ Address dbar1;
+ Word lccr0;
+ Word lccr1;
+ Word lccr2;
+ Word lccr3;
+};
+
+/* Fake monspecs to fill in fbinfo structure */
+static struct fb_monspecs monspecs __initdata = {
+ 30000, 70000, 50, 65, 0 /* Generic */
+};
+
+static struct display global_disp; /* Initial (default) Display Settings */
+static struct fb_info fb_info;
+static struct sa1100fb_par current_par;
+static struct fb_var_screeninfo __initdata init_var = {};
+static struct sa1100fb_lcd_reg lcd_shadow;
+
+
+static int sa1100fb_open(struct fb_info *info, int user);
+static int sa1100fb_release(struct fb_info *info, int user);
+static int sa1100fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info);
+static int sa1100fb_ioctl(struct inode *ino, struct file *file, unsigned int cmd,
+ unsigned long arg, int con, struct fb_info *info);
+static int sa1100fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info);
+static int sa1100fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info);
+static int sa1100fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
+static int sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
+static int sa1100fb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info);
+
+static int sa1100fb_switch(int con, struct fb_info *info);
+static void sa1100fb_blank(int blank, struct fb_info *info);
+static int sa1100fb_map_video_memory(void);
+static int sa1100fb_activate_var(struct fb_var_screeninfo *var);
+static void sa1100fb_enable_lcd_controller(void);
+static void sa1100fb_disable_lcd_controller(void);
+
+static struct fb_ops sa1100fb_ops = {
+ sa1100fb_open,
+ sa1100fb_release,
+ sa1100fb_get_fix,
+ sa1100fb_get_var,
+ sa1100fb_set_var,
+ sa1100fb_get_cmap,
+ sa1100fb_set_cmap,
+ sa1100fb_pan_display,
+ sa1100fb_ioctl
+};
+
+
+
+/*
+ * sa1100fb_palette_write:
+ * Write palette data to the LCD frame buffer's palette area
+ */
+static inline void
+sa1100fb_palette_write(u_int regno, u_short pal)
+{
+ current_par.v_palette_base[regno] = (regno ? pal : pal |
+ SA1100_PALETTE_MODE_VAL(current_par.bits_per_pixel));
+}
+
+
+static inline u_short
+sa1100fb_palette_encode(u_int regno, u_int red, u_int green, u_int blue, u_int trans)
+{
+ u_short pal;
+
+
+ if(current_par.bits_per_pixel == 4){
+ /*
+ * RGB -> luminance is defined to be
+ * Y = 0.299 * R + 0.587 * G + 0.114 * B
+ */
+ pal = ((19595 * red + 38470 * green + 7471 * blue) >> 28) & 0x00f;
+#ifdef REVERSE_VIDEO_4BIT
+ pal = 15 - pal;
+#endif
+ }
+ else{
+ pal = ((red >> 4) & 0xf00);
+ pal |= ((green >> 8) & 0x0f0);
+ pal |= ((blue >> 12) & 0x00f);
+ }
+
+ return pal;
+}
+
+static inline u_short
+sa1100fb_palette_read(u_int regno)
+{
+ return (current_par.v_palette_base[regno] & 0x0FFF);
+}
+
+
+static void
+sa1100fb_palette_decode(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *trans)
+{
+ u_short pal;
+
+ pal = sa1100fb_palette_read(regno);
+
+ if( current_par.bits_per_pixel == 4){
+#ifdef REVERSE_VIDEO_4BIT
+ pal = 15 - pal;
+#endif
+ pal &= 0x000f;
+ pal |= pal << 4;
+ pal |= pal << 8;
+ *blue = *green = *red = pal;
+ }
+ else{
+ *blue = (pal & 0x000f) << 12;
+ *green = (pal & 0x00f0) << 8;
+ *red = (pal & 0x0f00) << 4;
+ }
+ *trans = 0;
+}
+
+static int
+sa1100fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *trans, struct fb_info *info)
+{
+ if (regno >= current_par.palette_size)
+ return 1;
+
+ sa1100fb_palette_decode(regno, red, green, blue, trans);
+
+ return 0;
+}
+
+
+static int
+sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info)
+{
+ u_short pal;
+
+ if (regno >= current_par.palette_size)
+ return 1;
+
+ pal = sa1100fb_palette_encode(regno, red, green, blue, trans);
+
+ sa1100fb_palette_write(regno, pal);
+
+ return 0;
+}
+
+static int
+sa1100fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ int err = 0;
+
+ if (con == current_par.currcon)
+ err = fb_get_cmap(cmap, kspc, sa1100fb_getcolreg, info);
+ else if (fb_display[con].cmap.len)
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(current_par.palette_size),
+ cmap, kspc ? 0 : 2);
+ return err;
+}
+
+static int
+sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ int err = 0;
+
+ if (!fb_display[con].cmap.len)
+ err = fb_alloc_cmap(&fb_display[con].cmap,
+
+ current_par.palette_size, 0);
+ if (!err) {
+ if (con == current_par.currcon)
+ err = fb_set_cmap(cmap, kspc, sa1100fb_setcolreg,
+ info);
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ }
+ return err;
+}
+
+static void inline
+sa1100fb_get_par(struct sa1100fb_par *par)
+{
+ *par = current_par;
+}
+
+
+/*
+ * sa1100fb_encode_var():
+ * Modify var structure using values in par
+ */
+static int
+sa1100fb_encode_var(struct fb_var_screeninfo *var,
+ struct sa1100fb_par *par)
+{
+ // Don't know if really want to var on entry.
+ // Look at set_var to see. If so, may need to add extra params to par
+// memset(var, 0, sizeof(struct fb_var_screeninfo));
+
+ var->xres = par->xres;
+ var->yres = par->yres;
+ var->xres_virtual = par->xres_virtual;
+ var->yres_virtual = par->yres_virtual;
+
+ var->bits_per_pixel = par->bits_per_pixel;
+
+ switch(var->bits_per_pixel) {
+ case 2:
+ case 4:
+ case 8:
+ var->red.length = 4;
+ var->green = var->red;
+ var->blue = var->red;
+ var->transp.length = 0;
+ break;
+ case 12: // This case should differ for Active/Passive mode
+ case 16:
+ var->red.length =
+ var->blue.length =
+ var->green.length = 5;
+ var->transp.length = 0;
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->transp.offset = 0;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * sa1100fb_decode_var():
+ * Get the video params out of 'var'. If a value doesn't fit, round it up,
+ * if it's too big, return -EINVAL.
+ *
+ * Suggestion: Round up in the following order: bits_per_pixel, xres,
+ * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
+ * bitfields, horizontal timing, vertical timing.
+ */
+static int
+sa1100fb_decode_var(struct fb_var_screeninfo *var,
+ struct sa1100fb_par *par)
+{
+ u_long palette_mem_phys;
+ u_long palette_mem_size;
+
+ *par = current_par;
+
+ if ((par->xres = var->xres) < MIN_XRES)
+ par->xres = MIN_XRES;
+ if ((par->yres = var->yres) < MIN_YRES)
+ par->yres = MIN_YRES;
+ if (par->xres > MAX_SCREEN_SIZE_H)
+ par->xres = MAX_SCREEN_SIZE_H;
+ if (par->yres > MAX_SCREEN_SIZE_V)
+ par->yres = MAX_SCREEN_SIZE_V;
+ par->xres_virtual =
+ var->xres_virtual < par->xres ? par->xres : var->xres_virtual;
+ par->yres_virtual =
+ var->yres_virtual < par->yres ? par->yres : var->yres_virtual;
+ par->bits_per_pixel = var->bits_per_pixel;
+
+ switch (par->bits_per_pixel) {
+#ifdef FBCON_HAS_CFB4
+ case 4:
+ par->visual = FB_VISUAL_PSEUDOCOLOR;
+ par->palette_size = 16;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB8
+ case 8:
+ par->visual = FB_VISUAL_PSEUDOCOLOR;
+ par->palette_size = 256;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB16
+ case 16: /* RGB 555 */
+ par->visual = FB_VISUAL_TRUECOLOR;
+ par->palette_size = -1;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ palette_mem_size = SA1100_PALETTE_MEM_SIZE(par->bits_per_pixel);
+ palette_mem_phys = (u_long)VideoMemRegion_phys + PAGE_SIZE -
+ palette_mem_size;
+ par->p_palette_base = (u_short *)palette_mem_phys;
+ par->v_palette_base = (u_short *)((u_long)VideoMemRegion + PAGE_SIZE - palette_mem_size);
+ par->p_screen_base = (u_char *)((u_long)VideoMemRegion_phys + PAGE_SIZE);
+ par->v_screen_base = (u_char *)((u_long)VideoMemRegion + PAGE_SIZE);
+
+ DPRINTK("p_palette_base = 0x%08lx\n",(u_long)par->p_palette_base);
+ DPRINTK("v_palette_base = 0x%08lx\n",(u_long)par->v_palette_base);
+ DPRINTK("p_screen_base = 0x%08lx\n",(u_long)par->p_screen_base);
+ DPRINTK("v_screen_base = 0x%08lx\n",(u_long)par->v_screen_base);
+ DPRINTK("VideoMemRegion = 0x%08lx\n",(u_long)VideoMemRegion);
+ DPRINTK("VideoMemRegion_phys = 0x%08lx\n",(u_long)VideoMemRegion_phys);
+ return 0;
+}
+
+static int
+sa1100fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+ struct sa1100fb_par par;
+
+ if (con == -1) {
+ sa1100fb_get_par(&par);
+ sa1100fb_encode_var(var, &par);
+ } else
+ *var = fb_display[con].var;
+
+ return 0;
+}
+
+/*
+ * sa1100fb_set_var():
+ * Set the user defined part of the display for the specified console
+ */
+static int
+sa1100fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+ struct display *display;
+ int err, chgvar = 0;
+ struct sa1100fb_par par;
+
+ if (con >= 0)
+ display = &fb_display[con]; /* Display settings for console */
+ else
+ display = &global_disp; /* Default display settings */
+
+
+ DPRINTK("xres = %d, yres = %d\n",var->xres, var->yres);
+ // Decode var contents into a par structure, adjusting any
+ // out of range values.
+ if ((err = sa1100fb_decode_var(var, &par)))
+ return err;
+ // Store adjusted par values into var structure
+ sa1100fb_encode_var(var, &par);
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
+ return 0;
+ else if (((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) &&
+ ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NXTOPEN))
+ return -EINVAL;
+
+ if (con >= 0) {
+ if ((display->var.xres != var->xres) ||
+ (display->var.yres != var->yres) ||
+ (display->var.xres_virtual != var->xres_virtual) ||
+ (display->var.yres_virtual != var->yres_virtual) ||
+ (display->var.sync != var->sync) ||
+ (display->var.bits_per_pixel != var->bits_per_pixel) ||
+ (memcmp(&display->var.red, &var->red, sizeof(var->red))) ||
+ (memcmp(&display->var.green, &var->green, sizeof(var->green))) ||
+ (memcmp(&display->var.blue, &var->blue, sizeof(var->blue))))
+ chgvar = 1;
+ }
+
+ display->var = *var;
+ display->screen_base = par.v_screen_base;
+ display->visual = par.visual;
+ display->type = FB_TYPE_PACKED_PIXELS;
+ display->type_aux = 0;
+ display->ypanstep = 0;
+ display->ywrapstep = 0;
+ display->line_length =
+ display->next_line = (var->xres * var->bits_per_pixel) / 8;
+
+ display->can_soft_blank = 1;
+ display->inverse = 0;
+
+ switch (display->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB4
+ case 4:
+ display->dispsw = &fbcon_cfb4;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB8
+ case 8:
+ display->dispsw = &fbcon_cfb8;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB16
+ case 16:
+ display->dispsw = &fbcon_cfb16;
+ break;
+#endif
+ default:
+ display->dispsw = &fbcon_dummy;
+ break;
+ }
+
+ // If the console has changed and the console has defined
+ // a changevar function, call that function.
+ if (chgvar && info && info->changevar)
+ info->changevar(con);
+
+ // If the current console is selected, update the palette
+ if (con == current_par.currcon)
+ {
+ struct fb_cmap *cmap;
+
+ current_par = par;
+ if (display->cmap.len)
+ cmap = &display->cmap;
+ else
+ cmap = fb_default_cmap(current_par.palette_size);
+
+ fb_set_cmap(cmap, 1, sa1100fb_setcolreg, info);
+
+ sa1100fb_activate_var(var);
+ }
+ return 0;
+}
+
+static int
+sa1100fb_updatevar(int con, struct fb_info *info)
+{
+ DPRINTK("entered\n");
+ return 0;
+}
+
+static int
+sa1100fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
+{
+ struct display *display;
+
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strcpy(fix->id, SA1100_NAME);
+
+ if (con >= 0)
+ {
+ DPRINTK("Using console specific display for con=%d\n",con);
+ display = &fb_display[con]; /* Display settings for console */
+ }
+ else
+ display = &global_disp; /* Default display settings */
+
+ fix->smem_start = current_par.p_screen_base;
+ fix->smem_len = current_par.screen_size;
+ fix->type = display->type;
+ fix->type_aux = display->type_aux;
+ fix->xpanstep = 0;
+ fix->ypanstep = display->ypanstep;
+ fix->ywrapstep = display->ywrapstep;
+ fix->visual = display->visual;
+ fix->line_length = display->line_length;
+ fix->accel = FB_ACCEL_NONE;
+
+ return 0;
+}
+
+
+static void
+__init sa1100fb_init_fbinfo(void)
+{
+ strcpy(fb_info.modename, SA1100_NAME);
+ strcpy(fb_info.fontname, "Acorn8x8");
+
+ fb_info.node = -1;
+ fb_info.flags = FBINFO_FLAG_DEFAULT;
+ fb_info.fbops = &sa1100fb_ops;
+ fb_info.monspecs = monspecs;
+ fb_info.disp = &global_disp;
+ fb_info.changevar = NULL;
+ fb_info.switch_con = sa1100fb_switch;
+ fb_info.updatevar = sa1100fb_updatevar;
+ fb_info.blank = sa1100fb_blank;
+
+ /*
+ * setup initial parameters
+ */
+ memset(&init_var, 0, sizeof(init_var));
+
+ init_var.xres = DEFAULT_XRES;
+ init_var.yres = DEFAULT_YRES;
+ init_var.xres_virtual = init_var.xres;
+ init_var.yres_virtual = init_var.yres;
+ init_var.xoffset = 0;
+ init_var.yoffset = 0;
+ init_var.bits_per_pixel = DEFAULT_BPP;
+
+ init_var.transp.length = 0;
+ init_var.nonstd = 0;
+ init_var.activate = FB_ACTIVATE_NOW;
+ init_var.height = -1;
+ init_var.width = -1;
+ init_var.vmode = FB_VMODE_NONINTERLACED;
+
+#if defined(CONFIG_SA1100_PENNY)
+ init_var.red.length = 4;
+ init_var.green = init_var.red;
+ init_var.blue = init_var.red;
+ init_var.sync = 0;
+#elif defined(CONFIG_SA1100_BRUTUS)
+ init_var.red.length = 4;
+ init_var.green = init_var.red;
+ init_var.blue = init_var.red;
+ init_var.sync = 0;
+#elif defined(CONFIG_SA1100_THINCLIENT)
+ init_var.red.length = 4;
+ init_var.green = init_var.red;
+ init_var.blue = init_var.red;
+ init_var.sync = 0;
+#elif defined(CONFIG_SA1100_TIFON)
+ init_var.red.length = 4;
+ init_var.green = init_var.red;
+ init_var.blue = init_var.red;
+ init_var.grayscale = 1;
+ init_var.pixclock = 150000;
+ init_var.left_margin = 20;
+ init_var.right_margin = 255;
+ init_var.upper_margin = 20;
+ init_var.lower_margin = 0;
+ init_var.hsync_len = 2;
+ init_var.vsync_len = 1;
+ init_var.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT;
+ init_var.vmode = 0;
+#elif defined(CONFIG_SA1100_LART)
+ init_var.red.length = 4;
+ init_var.green = init_var.red;
+ init_var.blue = init_var.red;
+ init_var.grayscale = 1;
+ init_var.pixclock = 150000;
+ init_var.sync = 0;
+#endif
+
+ current_par.montype = -1;
+ current_par.currcon = -1;
+ current_par.allow_modeset = 1;
+ current_par.controller_state = LCD_MODE_DISABLED;
+}
+
+
+
+/*
+ * sa1100fb_map_video_memory():
+ * Allocates the DRAM memory for the frame buffer. This buffer is
+ * remapped into a non-cached, non-buffered, memory region to
+ * allow palette and pixel writes to occur without flushing the
+ * cache. Once this area is remapped, all virtual memory
+ * access to the video memory should occur at the new region.
+ */
+static int
+__init sa1100fb_map_video_memory(void)
+{
+ u_int required_pages;
+ u_int extra_pages;
+ u_int order;
+ u_int i;
+ char *allocated_region;
+
+ if (VideoMemRegion != NULL)
+ return -EINVAL;
+
+ /* Find order required to allocate enough memory for framebuffer */
+ required_pages = ALLOCATED_FB_MEM_SIZE >> PAGE_SHIFT;
+ for (order = 0 ; required_pages >> order ; order++) {;}
+ extra_pages = (1 << order) - required_pages;
+
+ if ((allocated_region =
+ (char *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)) == NULL)
+ return -ENOMEM;
+
+ VideoMemRegion = (u_char *)allocated_region + (extra_pages << PAGE_SHIFT);
+ VideoMemRegion_phys = (u_char *)__virt_to_phys((u_long)VideoMemRegion);
+
+ /* Free all pages that we don't need but were given to us because */
+ /* __get_free_pages() works on powers of 2. */
+ for (;extra_pages;extra_pages--)
+ free_page((u_int)allocated_region + ((extra_pages-1) << PAGE_SHIFT));
+
+ /* Set reserved flag for fb memory to allow it to be remapped into */
+ /* user space by the common fbmem driver using remap_page_range(). */
+ for(i = MAP_NR(VideoMemRegion);
+ i < MAP_NR(VideoMemRegion + ALLOCATED_FB_MEM_SIZE); i++)
+ set_bit(PG_reserved, &mem_map[i].flags);
+
+ /* Remap the fb memory to a non-buffered, non-cached region */
+ VideoMemRegion = (u_char *)__ioremap((u_long)VideoMemRegion_phys,
+ ALLOCATED_FB_MEM_SIZE,
+ L_PTE_PRESENT |
+ L_PTE_YOUNG |
+ L_PTE_DIRTY |
+ L_PTE_WRITE);
+ return (VideoMemRegion == NULL ? -EINVAL : 0);
+}
+
+static const int frequency[16] = {
+ 59000000,
+ 73700000,
+ 88500000,
+ 103200000,
+ 118000000,
+ 132700000,
+ 147500000,
+ 162200000,
+ 176900000,
+ 191700000,
+ 206400000,
+ 230000000,
+ 245000000,
+ 260000000,
+ 275000000,
+ 290000000
+};
+
+
+static int get_pcd(unsigned int pixclock)
+{
+ unsigned int pcd;
+ pcd = frequency[PPCR &0xf] / 1000;
+ pcd *= pixclock/1000;
+ return pcd / 10000000 * 12;
+ /* the last multiplication by 1.2 is to handle */
+ /* sync problems */
+}
+
+/*
+ * sa1100fb_activate_var():
+ * Configures LCD Controller based on entries in var parameter. Settings are
+ * only written to the controller if changes were made.
+ */
+static int
+sa1100fb_activate_var(struct fb_var_screeninfo *var)
+{
+ u_long flags;
+ int pcd = get_pcd(var->pixclock);
+
+ if (current_par.p_palette_base == NULL)
+ return -EINVAL;
+
+ /* Disable interrupts and save status */
+ save_flags_cli(flags); // disable the interrupts and save flags
+
+ /* Reset the LCD Controller's DMA address if it has changed */
+ lcd_shadow.dbar1 = (Address)current_par.p_palette_base;
+
+#if defined(CONFIG_SA1100_PENNY)
+ DPRINTK("Configuring SA1100 LCD\n");
+
+ DPRINTK("Configuring xres = %d, yres = %d\n",var->xres, var->yres);
+
+ lcd_shadow.lccr0 = LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
+ LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+ LCCR0_DMADel(0);
+ lcd_shadow.lccr1 = LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth (65) +
+ LCCR1_EndLnDel (43) + LCCR1_BegLnDel(43) ;
+ lcd_shadow.lccr2 = LCCR2_DisHght (var->yres) + LCCR2_VrtSnchWdth (35) +
+ LCCR2_EndFrmDel (0) + LCCR2_BegFrmDel (0) ;
+ lcd_shadow.lccr3 = LCCR3_PixClkDiv(16) +
+ LCCR3_ACBsDiv (2) + LCCR3_ACBsCntOff +
+ ((var->sync & FB_SYNC_HOR_HIGH_ACT) ?LCCR3_HorSnchH:LCCR3_HorSnchL) +
+ ((var->sync & FB_SYNC_VERT_HIGH_ACT)?LCCR3_VrtSnchH:LCCR3_VrtSnchL);
+
+#elif defined(CONFIG_SA1100_BRUTUS)
+ DPRINTK("Configuring BRUTUS LCD\n");
+ lcd_shadow.lccr0 = LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Pas +
+ LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+ LCCR0_DMADel(0);
+ lcd_shadow.lccr1 = LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(4) +
+ LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
+ lcd_shadow.lccr2 = LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
+ LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
+ lcd_shadow.lccr3 = LCCR3_OutEnH + LCCR3_PixFlEdg + LCCR3_VrtSnchH + LCCR3_HorSnchH +
+ LCCR3_ACBsCntOff + LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(44);
+#elif defined(CONFIG_SA1100_THINCLIENT)
+ DPRINTK("Configuring ThinClient LCD\n");
+ DPRINTK("Configuring xres = %d, yres = %d\n",var->xres, var->yres);
+
+ lcd_shadow.lccr0 = LCCR0_LEN + LCCR0_Color + LCCR0_Sngl +
+ LCCR0_Act;
+ lcd_shadow.lccr1 = LCCR1_DisWdth(var->xres) +LCCR1_HorSnchWdth(10)+
+ LCCR1_EndLnDel (81) + LCCR1_BegLnDel(81) ;
+ lcd_shadow.lccr2 = LCCR2_DisHght (var->yres) +LCCR2_VrtSnchWdth(9) +
+ LCCR2_EndFrmDel (20) + LCCR2_BegFrmDel (20) ;
+ lcd_shadow.lccr3 = LCCR3_PixClkDiv(6) +
+ LCCR3_ACBsDiv (2) + LCCR3_ACBsCntOff +
+ LCCR3_HorSnchL + LCCR3_VrtSnchL;
+
+#elif defined(CONFIG_SA1100_TIFON)
+ DPRINTK("Configuring TIFON LCD\n");
+
+ lcd_shadow.lccr0 = LCCR0_LEN + LCCR0_Mono + LCCR0_Sngl + LCCR0_Pas +
+ LCCR0_BigEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+ LCCR0_8PixMono + LCCR0_DMADel(0);
+ lcd_shadow.lccr1 = LCCR1_DisWdth( var->xres ) +
+ LCCR1_HorSnchWdth( var->hsync_len) +
+ LCCR1_BegLnDel( var->left_margin) +
+ LCCR1_EndLnDel( var->right_margin);
+ lcd_shadow.lccr2 = LCCR2_DisHght( var->yres ) +
+ LCCR2_VrtSnchWdth( var->vsync_len )+
+ LCCR2_BegFrmDel( var->upper_margin ) +
+ LCCR2_EndFrmDel( var->lower_margin );
+ lcd_shadow.lccr3 = LCCR3_PixClkDiv( pcd ) +
+ LCCR3_ACBsDiv( 512 ) +
+ LCCR3_ACBsCnt(0) +
+ LCCR3_HorSnchH + LCCR3_VrtSnchH;
+ /*
+ ((current_var.sync & FB_SYNC_HOR_HIGH_ACT) ?
+ LCCR3_HorSnchH : LCCR3_HorSnchL) +
+ ((current_var.sync & FB_SYNC_VERT_HIGH_ACT) ?
+ LCCR3_VrtSnchH : LCCR3_VrtSnchL);
+ */
+#elif defined(CONFIG_SA1100_LART)
+ DPRINTK("Configuring LART LCD\n");
+
+ lcd_shadow.lccr0 = LCCR0_LEN + LCCR0_Mono + LCCR0_Sngl + LCCR0_Pas +
+ LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
+ LCCR0_DMADel(0);
+ lcd_shadow.lccr1 = LCCR1_DisWdth( var->xres ) +
+ LCCR1_HorSnchWdth( 2 ) +
+ LCCR1_BegLnDel( 4 ) +
+ LCCR1_EndLnDel( 2 );
+ lcd_shadow.lccr2 = LCCR2_DisHght( var->yres ) +
+ LCCR2_VrtSnchWdth( 1 )+
+ LCCR2_BegFrmDel( 0 ) +
+ LCCR2_EndFrmDel( 0 );
+ lcd_shadow.lccr3 = LCCR3_PixClkDiv( 34 ) +
+ LCCR3_ACBsDiv( 512 ) +
+ LCCR3_ACBsCntOff +
+ LCCR3_HorSnchH +
+ LCCR3_VrtSnchH;
+#endif
+
+ /* Restore status of interrupts */
+ restore_flags(flags);
+
+
+ if (( LCCR0 != lcd_shadow.lccr0 ) ||
+ ( LCCR1 != lcd_shadow.lccr1 ) ||
+ ( LCCR2 != lcd_shadow.lccr2 ) ||
+ ( LCCR3 != lcd_shadow.lccr3 ) ||
+ ( DBAR1 != lcd_shadow.dbar1 ))
+ {
+ sa1100fb_enable_lcd_controller();
+ }
+
+ return 0;
+}
+
+
+/*
+ * sa1100fb_inter_handler():
+ * Interrupt handler for LCD controller. Processes disable done interrupt (LDD)
+ * to reenable controller if controller was disabled to change register values.
+ */
+static void sa1100fb_inter_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ if (LCSR & LCSR_LDD) {
+ /* Disable Done Flag is set */
+ LCCR0 |= LCCR0_LDM; /* Mask LCD Disable Done Interrupt */
+ current_par.controller_state = LCD_MODE_DISABLED;
+ if (current_par.controller_state == LCD_MODE_DISABLE_BEFORE_ENABLE) {
+ sa1100fb_enable_lcd_controller();
+ }
+ }
+ LCSR = 0; /* Clear LCD Status Register */
+}
+
+
+/*
+ * sa1100fb_disable_lcd_controller():
+ * Disables LCD controller by and enables LDD interrupt. The controller_state
+ * is not changed until the LDD interrupt is received to indicate the current
+ * frame has completed. Platform specific hardware disabling is also included.
+ */
+static void sa1100fb_disable_lcd_controller(void)
+{
+ DPRINTK("sa1100fb: Disabling LCD controller\n");
+
+ /* Exit if already LCD disabled, or LDD IRQ unmasked */
+ if ((current_par.controller_state == LCD_MODE_DISABLED) ||
+ (!(LCCR0 & LCCR0_LDM))) {
+ DPRINTK("sa1100fb: LCD already disabled\n");
+ return;
+ }
+
+#if defined(CONFIG_SA1100_PENNY)
+ FpgaLcdCS1 = 0x000; /* LCD Backlight to 0% */
+ FpgaPortI &= ~LCD_ON; /* Turn off LCD Backlight */
+#elif defined(CONFIG_SA1100_TIFON)
+ GPCR = GPIO_GPIO(24); /* turn off display */
+#endif
+
+ LCSR = 0; /* Clear LCD Status Register */
+ LCCR0 &= ~(LCCR0_LDM); /* Enable LCD Disable Done Interrupt */
+ enable_irq(IRQ_LCD); /* Enable LCD IRQ */
+ LCCR0 &= ~(LCCR0_LEN); /* Disable LCD Controller */
+
+}
+
+/*
+ * sa1100fb_enable_lcd_controller():
+ * Enables LCD controller. If the controller is already enabled, it is first disabled.
+ * This forces all changes to the LCD controller registers to be done when the
+ * controller is disabled. Platform specific hardware enabling is also included.
+ */
+static void sa1100fb_enable_lcd_controller(void)
+{
+ u_long flags;
+
+ save_flags_cli(flags);
+
+ /* Disable controller before changing parameters */
+ if (current_par.controller_state == LCD_MODE_ENABLED) {
+ current_par.controller_state = LCD_MODE_DISABLE_BEFORE_ENABLE;
+ sa1100fb_disable_lcd_controller();
+ } else {
+ DPRINTK("sa1100fb: Enabling LCD controller\n");
+
+ /* Make sure the mode bits are present in the first palette entry */
+ current_par.v_palette_base[0] &= 0x0FFF;
+ current_par.v_palette_base[0] |= SA1100_PALETTE_MODE_VAL(current_par.bits_per_pixel);
+
+ /* disable the interrupts and save flags */
+ save_flags_cli(flags);
+
+ DBAR1 = lcd_shadow.dbar1;
+ LCCR3 = lcd_shadow.lccr3;
+ LCCR2 = lcd_shadow.lccr2;
+ LCCR1 = lcd_shadow.lccr1;
+ LCCR0 = lcd_shadow.lccr0;
+
+#if defined(CONFIG_SA1100_PENNY)
+ FpgaLcdCS1 = 0x0FF; /* LCD Backlight to 100% */
+ FpgaPortI |= LCD_ON; /* Turn on LCD Backlight */
+#elif defined(CONFIG_SA1100_TIFON)
+ GPCR = GPIO_GPIO(24); /* cycle on/off-switch */
+ udelay(150);
+ GPSR = GPIO_GPIO(24); /* turn on display */
+ udelay(150);
+ GPSR = GPIO_GPIO(24); /* turn on display */
+#endif
+ current_par.controller_state = LCD_MODE_ENABLED;
+
+ /* Restore status of interrupts */
+ }
+ restore_flags(flags);
+}
+
+
+
+static int
+sa1100fb_open(struct fb_info *info, int user)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+sa1100fb_release(struct fb_info *info, int user)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+static int
+sa1100fb_pan_display(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ DPRINTK("entered\n");
+ return -EINVAL;
+}
+
+
+/*
+ * sa1100fb_blank():
+ * Blank the display by setting all palette values to zero. Note, the
+ * 12 and 16 bpp modes don't really use the palette, so this will not
+ * blank the display in all modes.
+ */
+static void
+sa1100fb_blank(int blank, struct fb_info *info)
+{
+ int i;
+
+ if (blank) {
+ for (i = 0; i < current_par.palette_size; i++)
+ sa1100fb_palette_write(i, sa1100fb_palette_encode(i, 0, 0, 0, 0));
+ sa1100fb_disable_lcd_controller();
+ }
+ else {
+ sa1100fb_set_cmap(&fb_display[current_par.currcon].cmap, 1,
+ current_par.currcon, info);
+ sa1100fb_enable_lcd_controller();
+ }
+}
+
+
+/*
+ * sa1100fb_switch():
+ * Change to the specified console. Palette and video mode
+ * are changed to the console's stored parameters.
+ */
+static int
+sa1100fb_switch(int con, struct fb_info *info)
+{
+ struct fb_cmap *cmap;
+
+ if (current_par.currcon >= 0) {
+ // Get the colormap for the selected console
+ cmap = &fb_display[current_par.currcon].cmap;
+
+ if (cmap->len)
+ fb_get_cmap(cmap, 1, sa1100fb_getcolreg, info);
+ }
+
+ current_par.currcon = con;
+ fb_display[con].var.activate = FB_ACTIVATE_NOW;
+ sa1100fb_set_var(&fb_display[con].var, con, info);
+ return 0;
+}
+
+
+void __init sa1100fb_init(void)
+{
+ current_par.p_palette_base = NULL;
+ current_par.v_palette_base = NULL;
+ current_par.p_screen_base = NULL;
+ current_par.v_screen_base = NULL;
+ current_par.palette_size = MAX_PALETTE_NUM_ENTRIES;
+ current_par.screen_size = MAX_PIXEL_MEM_SIZE;
+
+#if defined(CONFIG_SA1100_PENNY)
+ GPDR |= GPIO_GPDR_GFX; /* GPIO Data Direction register for LCD data bits 8-11 */
+ GAFR |= GPIO_GAFR_GFX; /* GPIO Alternate Function register for LCD data bits 8-11 */
+#elif defined(CONFIG_SA1100_TIFON)
+
+ GPDR = GPDR | GPIO_GPIO(24); /* set GPIO24 to output */
+#endif
+
+ /* Initialize video memory */
+ if (sa1100fb_map_video_memory())
+ return;
+
+ sa1100fb_init_fbinfo();
+ if (current_par.montype < 0 || current_par.montype > NR_MONTYPES)
+ current_par.montype = 1;
+
+ /* Request the interrupt in the init routine only because */
+ /* this driver will not be used as a module. */
+ if (request_irq(IRQ_LCD, sa1100fb_inter_handler, SA_INTERRUPT, "SA1100 LCD", NULL)!= 0) {
+ DPRINTK("sa1100fb: failed in request_irq\n");
+ }
+ DPRINTK("sa1100fb: request_irq succeeded\n");
+ disable_irq(IRQ_LCD);
+
+ if (sa1100fb_set_var(&init_var, -1, &fb_info))
+ current_par.allow_modeset = 0;
+ sa1100fb_decode_var(&init_var, ¤t_par);
+
+ register_framebuffer(&fb_info);
+
+ /* This driver cannot be unloaded */
+ MOD_INC_USE_COUNT;
+}
+
+void __init sa1100fb_setup(char *options)
+{
+ if (!options || !*options)
+ return;
+
+ return;
+}
+
+
+
+static int
+sa1100fb_ioctl(struct inode *ino, struct file *file, unsigned int cmd,
+ unsigned long arg, int con, struct fb_info *info)
+{
+ return -ENOIOCTLCMD;
+}
+
+
dep_tristate 'ADFS file system support' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
-if [ "$CONFIG_ADFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW
-fi
+dep_mbool ' ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW $CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
tristate 'Amiga FFS file system support' CONFIG_AFFS_FS
tristate 'Compressed ROM file system support' CONFIG_CRAMFS
tristate 'ISO 9660 CDROM file system support' CONFIG_ISO9660_FS
-if [ "$CONFIG_ISO9660_FS" != "n" ]; then
- bool ' Microsoft Joliet CDROM extensions' CONFIG_JOLIET
-else
- # needed by nls/Config.in
- define_bool CONFIG_JOLIET n
-fi
+dep_mbool ' Microsoft Joliet CDROM extensions' CONFIG_JOLIET $CONFIG_ISO9660_FS
tristate 'Minix fs support' CONFIG_MINIX_FS
tristate 'NTFS file system support (read only)' CONFIG_NTFS_FS
-if [ "$CONFIG_NTFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' NTFS write support (DANGEROUS)' CONFIG_NTFS_RW
-fi
+dep_mbool ' NTFS write support (DANGEROUS)' CONFIG_NTFS_RW $CONFIG_NTFS_FS $CONFIG_EXPERIMENTAL
tristate 'OS/2 HPFS file system support' CONFIG_HPFS_FS
dep_bool '/dev/pts file system for Unix98 PTYs' CONFIG_DEVPTS_FS $CONFIG_UNIX98_PTYS
dep_tristate 'QNX4 file system support (read only) (EXPERIMENTAL)' CONFIG_QNX4FS_FS $CONFIG_EXPERIMENTAL
-if [ "$CONFIG_QNX4FS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' QNX4FS write support (DANGEROUS)' CONFIG_QNX4FS_RW
-fi
+dep_mbool ' QNX4FS write support (DANGEROUS)' CONFIG_QNX4FS_RW $CONFIG_QNX4FS_FS $CONFIG_EXPERIMENTAL
tristate 'ROM file system support' CONFIG_ROMFS_FS
tristate 'Second extended fs support' CONFIG_EXT2_FS
tristate 'System V and Coherent file system support (read only)' CONFIG_SYSV_FS
-if [ "$CONFIG_SYSV_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' SYSV file system write support (DANGEROUS)' CONFIG_SYSV_FS_WRITE
-fi
+dep_mbool ' SYSV file system write support (DANGEROUS)' CONFIG_SYSV_FS_WRITE $CONFIG_SYSV_FS $CONFIG_EXPERIMENTAL
tristate 'UDF file system support (read only)' CONFIG_UDF_FS
-if [ "$CONFIG_UDF_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' UDF write support (DANGEROUS)' CONFIG_UDF_RW
-fi
+dep_mbool ' UDF write support (DANGEROUS)' CONFIG_UDF_RW $CONFIG_UDF_FS $CONFIG_EXPERIMENTAL
tristate 'UFS file system support (read only)' CONFIG_UFS_FS
-if [ "$CONFIG_UFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' UFS file system write support (DANGEROUS)' CONFIG_UFS_FS_WRITE
-fi
+dep_mbool ' UFS file system write support (DANGEROUS)' CONFIG_UFS_FS_WRITE $CONFIG_UFS_FS $CONFIG_EXPERIMENTAL
if [ "$CONFIG_NET" = "y" ]; then
-mainmenu_option next_comment
-comment 'Network File Systems'
-
-if [ "$CONFIG_INET" = "y" ]; then
- tristate 'Coda file system support (advanced network fs)' CONFIG_CODA_FS
+ mainmenu_option next_comment
+ comment 'Network File Systems'
- tristate 'NFS file system support' CONFIG_NFS_FS
+ dep_tristate 'Coda file system support (advanced network fs)' CONFIG_CODA_FS $CONFIG_INET
+ dep_tristate 'NFS file system support' CONFIG_NFS_FS $CONFIG_INET
dep_bool ' Root file system on NFS' CONFIG_ROOT_NFS $CONFIG_NFS_FS $CONFIG_IP_PNP
- tristate 'NFS server support' CONFIG_NFSD
- if [ "$CONFIG_NFSD" != "n" ]; then
- bool ' Provide NFSv3 server support (EXPERIMENTAL)' CONFIG_NFSD_V3
- fi
+ dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET
+ dep_mbool ' Provide NFSv3 server support (EXPERIMENTAL)' CONFIG_NFSD_V3 $CONFIG_NFSD
if [ "$CONFIG_NFS_FS" = "y" -o "$CONFIG_NFSD" = "y" ]; then
define_tristate CONFIG_SUNRPC y
if [ "$CONFIG_NFSD_V3" = "y" ]; then
define_bool CONFIG_LOCKD_V4 y
fi
- tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS
-fi
-if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then
- tristate 'NCP file system support (to mount NetWare volumes)' CONFIG_NCP_FS
- if [ "$CONFIG_NCP_FS" != "n" ]; then
+
+ dep_tristate 'SMB file system support (to mount Windows shares etc.)' CONFIG_SMB_FS $CONFIG_INET
+
+ if [ "$CONFIG_IPX" != "n" -o "$CONFIG_INET" != "n" ]; then
+ tristate 'NCP file system support (to mount NetWare volumes)' CONFIG_NCP_FS
source fs/ncpfs/Config.in
+ else
+ # for fs/nls/Config.in
+ define_bool CONFIG_NCPFS_NLS n
fi
-fi
+ endmenu
-endmenu
+else
+ # for fs/nls/Config.in
+ define_bool CONFIG_NCPFS_NLS n
fi
mainmenu_option next_comment
/* Right now the dcache depends on the kernel lock */
#define check_lock() if (!kernel_locked()) BUG()
-/* For managing the dcache */
-extern unsigned long num_physpages, page_cache_size;
-extern int inodes_stat[];
-#define nr_inodes (inodes_stat[0])
-
kmem_cache_t *dentry_cache;
/*
{
check_lock();
- check_lock();
-
/*
* Are we the only user?
*/
return NULL;
}
+static inline void ext2_set_de_type(struct super_block *sb,
+ struct ext2_dir_entry_2 *de,
+ umode_t mode) {
+ if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
+ return;
+ if (S_ISREG(mode))
+ de->file_type = EXT2_FT_REG_FILE;
+ else if (S_ISDIR(mode))
+ de->file_type = EXT2_FT_DIR;
+ else if (S_ISLNK(mode))
+ de->file_type = EXT2_FT_SYMLINK;
+ else if (S_ISSOCK(mode))
+ de->file_type = EXT2_FT_SOCK;
+ else if (S_ISFIFO(mode))
+ de->file_type = EXT2_FT_FIFO;
+ else if (S_ISCHR(mode))
+ de->file_type = EXT2_FT_CHRDEV;
+ else if (S_ISBLK(mode))
+ de->file_type = EXT2_FT_BLKDEV;
+}
+
/*
* ext2_add_entry()
*
- * adds a file entry to the specified directory, using the same
- * semantics as ext2_find_entry(). It returns NULL if it failed.
- *
- * NOTE!! The inode part of 'de' is left at 0 - which means you
- * may not sleep between calling this and putting something into
- * the entry, as someone else might have used it while you slept.
+ * adds a file entry to the specified directory.
*/
-static struct buffer_head * ext2_add_entry (struct inode * dir,
- const char * name, int namelen,
- struct ext2_dir_entry_2 ** res_dir,
- int *err)
+int ext2_add_entry (struct inode * dir, const char * name, int namelen,
+ struct inode *inode)
{
unsigned long offset;
unsigned short rec_len;
struct buffer_head * bh;
struct ext2_dir_entry_2 * de, * de1;
struct super_block * sb;
+ int retval;
- *err = -EINVAL;
- *res_dir = NULL;
if (!dir || !dir->i_nlink)
- return NULL;
+ return -EINVAL;
sb = dir->i_sb;
if (!namelen)
- return NULL;
+ return -EINVAL;
/*
* Is this a busy deleted directory? Can't create new files if so
*/
if (dir->i_size == 0)
{
- *err = -ENOENT;
- return NULL;
+ return -ENOENT;
}
- bh = ext2_bread (dir, 0, 0, err);
+ bh = ext2_bread (dir, 0, 0, &retval);
if (!bh)
- return NULL;
+ return retval;
rec_len = EXT2_DIR_REC_LEN(namelen);
offset = 0;
de = (struct ext2_dir_entry_2 *) bh->b_data;
- *err = -ENOSPC;
while (1) {
if ((char *)de >= sb->s_blocksize + bh->b_data) {
brelse (bh);
bh = NULL;
- bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, err);
+ bh = ext2_bread (dir, offset >> EXT2_BLOCK_SIZE_BITS(sb), 1, &retval);
if (!bh)
- return NULL;
+ return retval;
if (dir->i_size <= offset) {
if (dir->i_size == 0) {
- *err = -ENOENT;
- return NULL;
+ return -ENOENT;
}
ext2_debug ("creating next block\n");
}
if (!ext2_check_dir_entry ("ext2_add_entry", dir, de, bh,
offset)) {
- *err = -ENOENT;
brelse (bh);
- return NULL;
+ return -ENOENT;
}
if (ext2_match (namelen, name, de)) {
- *err = -EEXIST;
brelse (bh);
- return NULL;
+ return -EEXIST;
}
if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) ||
(le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) {
de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len));
de = de1;
}
- de->inode = 0;
+ if (inode) {
+ de->inode = cpu_to_le32(inode->i_ino);
+ ext2_set_de_type(dir->i_sb, de, inode->i_mode);
+ } else
+ de->inode = 0;
de->name_len = namelen;
de->file_type = 0;
memcpy (de->name, name, namelen);
mark_inode_dirty(dir);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
- *res_dir = de;
- *err = 0;
- return bh;
+ if (IS_SYNC(dir)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
+ brelse(bh);
+ return 0;
}
offset += le16_to_cpu(de->rec_len);
de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len));
}
brelse (bh);
- return NULL;
+ return -ENOSPC;
}
/*
* ext2_delete_entry deletes a directory entry by merging it with the
* previous entry
*/
-static int ext2_delete_entry (struct ext2_dir_entry_2 * dir,
+static int ext2_delete_entry (struct inode * dir,
+ struct ext2_dir_entry_2 * de_del,
struct buffer_head * bh)
{
struct ext2_dir_entry_2 * de, * pde;
if (!ext2_check_dir_entry ("ext2_delete_entry", NULL,
de, bh, i))
return -EIO;
- if (de == dir) {
+ if (de == de_del) {
if (pde)
pde->rec_len =
cpu_to_le16(le16_to_cpu(pde->rec_len) +
- le16_to_cpu(dir->rec_len));
+ le16_to_cpu(de->rec_len));
else
- dir->inode = 0;
+ de->inode = 0;
+ dir->i_version = ++event;
+ mark_buffer_dirty(bh, 1);
+ if (IS_SYNC(dir)) {
+ ll_rw_block (WRITE, 1, &bh);
+ wait_on_buffer (bh);
+ }
return 0;
}
i += le16_to_cpu(de->rec_len);
return -ENOENT;
}
-static inline void ext2_set_de_type(struct super_block *sb,
- struct ext2_dir_entry_2 *de,
- umode_t mode) {
- if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE))
- return;
- if (S_ISREG(mode))
- de->file_type = EXT2_FT_REG_FILE;
- else if (S_ISDIR(mode))
- de->file_type = EXT2_FT_DIR;
- else if (S_ISLNK(mode))
- de->file_type = EXT2_FT_SYMLINK;
- else if (S_ISSOCK(mode))
- de->file_type = EXT2_FT_SOCK;
- else if (S_ISFIFO(mode))
- de->file_type = EXT2_FT_FIFO;
- else if (S_ISCHR(mode))
- de->file_type = EXT2_FT_CHRDEV;
- else if (S_ISBLK(mode))
- de->file_type = EXT2_FT_BLKDEV;
-}
-
/*
* By the time this is called, we already have created
* the directory cache entry for the new file, but it
static int ext2_create (struct inode * dir, struct dentry * dentry, int mode)
{
struct inode * inode;
- struct buffer_head * bh;
- struct ext2_dir_entry_2 * de;
- int err = -EIO;
+ int err;
/*
* N.B. Several error exits in ext2_new_inode don't set err.
*/
inode = ext2_new_inode (dir, mode, &err);
if (!inode)
- return err;
+ return -EIO;
inode->i_op = &ext2_file_inode_operations;
inode->i_fop = &ext2_file_operations;
inode->i_mapping->a_ops = &ext2_aops;
inode->i_mode = mode;
mark_inode_dirty(inode);
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh) {
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err) {
inode->i_nlink--;
mark_inode_dirty(inode);
iput (inode);
return err;
}
- de->inode = cpu_to_le32(inode->i_ino);
- ext2_set_de_type(dir->i_sb, de, S_IFREG);
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- brelse (bh);
d_instantiate(dentry, inode);
return 0;
}
static int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
{
struct inode * inode;
- struct buffer_head * bh;
- struct ext2_dir_entry_2 * de;
- int err = -EIO;
+ int err;
inode = ext2_new_inode (dir, mode, &err);
if (!inode)
- goto out;
+ return -EIO;
inode->i_uid = current->fsuid;
init_special_inode(inode, mode, rdev);
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh)
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err)
goto out_no_entry;
- de->inode = cpu_to_le32(inode->i_ino);
- dir->i_version = ++event;
- ext2_set_de_type(dir->i_sb, de, inode->i_mode);
mark_inode_dirty(inode);
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
d_instantiate(dentry, inode);
- brelse(bh);
- err = 0;
-out:
- return err;
+ return 0;
out_no_entry:
inode->i_nlink--;
mark_inode_dirty(inode);
iput(inode);
- goto out;
+ return err;
}
static int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
{
struct inode * inode;
- struct buffer_head * bh, * dir_block;
+ struct buffer_head * dir_block;
struct ext2_dir_entry_2 * de;
int err;
- err = -EMLINK;
if (dir->i_nlink >= EXT2_LINK_MAX)
- goto out;
+ return -EMLINK;
- err = -EIO;
inode = ext2_new_inode (dir, S_IFDIR, &err);
if (!inode)
- goto out;
+ return -EIO;
inode->i_op = &ext2_dir_inode_operations;
inode->i_fop = &ext2_dir_operations;
inode->i_nlink--; /* is this nlink == 0? */
mark_inode_dirty(inode);
iput (inode);
- return err;
+ return -EIO;
}
de = (struct ext2_dir_entry_2 *) dir_block->b_data;
de->inode = cpu_to_le32(inode->i_ino);
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
mark_inode_dirty(inode);
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh)
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err)
goto out_no_entry;
- de->inode = cpu_to_le32(inode->i_ino);
- ext2_set_de_type(dir->i_sb, de, S_IFDIR);
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
dir->i_nlink++;
dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
d_instantiate(dentry, inode);
- brelse (bh);
- err = 0;
-out:
- return err;
+ return 0;
out_no_entry:
inode->i_nlink = 0;
mark_inode_dirty(inode);
iput (inode);
- goto out;
+ return err;
}
/*
if (!empty_dir (inode))
goto end_rmdir;
- retval = ext2_delete_entry (de, bh);
- dir->i_version = ++event;
+ retval = ext2_delete_entry(dir, de, bh);
if (retval)
goto end_rmdir;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
if (inode->i_nlink != 2)
ext2_warning (inode->i_sb, "ext2_rmdir",
"empty directory has nlink!=2 (%d)",
inode->i_ino, inode->i_nlink);
inode->i_nlink = 1;
}
- retval = ext2_delete_entry (de, bh);
+ retval = ext2_delete_entry(dir, de, bh);
if (retval)
goto end_unlink;
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
static int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname)
{
struct inode * inode;
- struct ext2_dir_entry_2 * de;
- struct buffer_head * bh = NULL;
int l, err;
- err = -ENAMETOOLONG;
l = strlen(symname)+1;
if (l > dir->i_sb->s_blocksize)
- goto out;
+ return -ENAMETOOLONG;
- err = -EIO;
if (!(inode = ext2_new_inode (dir, S_IFLNK, &err)))
- goto out;
+ return -EIO;
inode->i_mode = S_IFLNK | S_IRWXUGO;
}
mark_inode_dirty(inode);
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh)
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err)
goto out_no_entry;
- de->inode = cpu_to_le32(inode->i_ino);
- ext2_set_de_type(dir->i_sb, de, S_IFLNK);
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- brelse (bh);
d_instantiate(dentry, inode);
- err = 0;
-out:
- return err;
+ return 0;
out_no_entry:
inode->i_nlink--;
mark_inode_dirty(inode);
iput (inode);
- goto out;
+ return err;
}
static int ext2_link (struct dentry * old_dentry,
struct inode * dir, struct dentry *dentry)
{
struct inode *inode = old_dentry->d_inode;
- struct ext2_dir_entry_2 * de;
- struct buffer_head * bh;
int err;
if (S_ISDIR(inode->i_mode))
if (inode->i_nlink >= EXT2_LINK_MAX)
return -EMLINK;
-
- bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
- if (!bh)
+
+ err = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len,
+ inode);
+ if (err)
return err;
- de->inode = cpu_to_le32(inode->i_ino);
- ext2_set_de_type(dir->i_sb, de, inode->i_mode);
- dir->i_version = ++event;
- mark_buffer_dirty(bh, 1);
- if (IS_SYNC(dir)) {
- ll_rw_block (WRITE, 1, &bh);
- wait_on_buffer (bh);
- }
- brelse (bh);
inode->i_nlink++;
inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
goto end_rename;
}
if (!new_bh) {
- new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name,
- new_dentry->d_name.len, &new_de,
- &retval);
- if (!new_bh)
+ retval = ext2_add_entry (new_dir, new_dentry->d_name.name,
+ new_dentry->d_name.len,
+ old_inode);
+ if (retval)
goto end_rename;
+ } else {
+ new_de->inode = le32_to_cpu(old_inode->i_ino);
+ if (EXT2_HAS_INCOMPAT_FEATURE(new_dir->i_sb,
+ EXT2_FEATURE_INCOMPAT_FILETYPE))
+ new_de->file_type = old_de->file_type;
+ new_dir->i_version = ++event;
+ mark_buffer_dirty(new_bh, 1);
+ if (IS_SYNC(new_dir)) {
+ ll_rw_block (WRITE, 1, &new_bh);
+ wait_on_buffer (new_bh);
+ }
+ brelse(new_bh);
+ new_bh = NULL;
}
- new_dir->i_version = ++event;
-
+
/*
* Like most other Unix systems, set the ctime for inodes on a
* rename.
/*
* ok, that's it
*/
- new_de->inode = le32_to_cpu(old_inode->i_ino);
- if (EXT2_HAS_INCOMPAT_FEATURE(new_dir->i_sb,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
- new_de->file_type = old_de->file_type;
-
- ext2_delete_entry (old_de, old_bh);
+ ext2_delete_entry(old_dir, old_de, old_bh);
- old_dir->i_version = ++event;
if (new_inode) {
new_inode->i_nlink--;
new_inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(new_dir);
}
}
- mark_buffer_dirty(old_bh, 1);
- if (IS_SYNC(old_dir)) {
- ll_rw_block (WRITE, 1, &old_bh);
- wait_on_buffer (old_bh);
- }
- mark_buffer_dirty(new_bh, 1);
- if (IS_SYNC(new_dir)) {
- ll_rw_block (WRITE, 1, &new_bh);
- wait_on_buffer (new_bh);
- }
retval = 0;
set_opt (*mount_options, GRPID);
else if (!strcmp (this_char, "minixdf"))
set_opt (*mount_options, MINIX_DF);
+ else if (!strcmp (this_char, "nocheck"))
+ clear_opt (*mount_options, CHECK);
else if (!strcmp (this_char, "nogrpid") ||
!strcmp (this_char, "sysvgroups"))
clear_opt (*mount_options, GRPID);
}
#endif
}
-#if 0 /* ibasket's still have unresolved bugs... -DaveM */
-
- /* [T. Schoebel-Theuer] This limit should be maintained on disk.
- * This is just provisionary.
- */
- sb->s_ibasket_max = 100;
-#endif
}
static int ext2_check_descriptors (struct super_block * sb)
#
# NCP Filesystem configuration
#
-bool ' Packet signatures' CONFIG_NCPFS_PACKET_SIGNING
-bool ' Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING
-bool ' Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG
-bool ' Use NFS namespace if available' CONFIG_NCPFS_NFS_NS
-bool ' Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS
-bool ' Lowercase DOS filenames' CONFIG_NCPFS_SMALLDOS
-bool ' Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR
-bool ' NDS authentication support' CONFIG_NCPFS_NDS_DOMAINS
-bool ' Use Native Language Support' CONFIG_NCPFS_NLS
-bool ' Enable symbolic links and execute flags' CONFIG_NCPFS_EXTRAS
+dep_mbool ' Packet signatures' CONFIG_NCPFS_PACKET_SIGNING $CONFIG_NCP_FS
+dep_mbool ' Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING $CONFIG_NCP_FS
+dep_mbool ' Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG $CONFIG_NCP_FS
+dep_mbool ' Use NFS namespace if available' CONFIG_NCPFS_NFS_NS $CONFIG_NCP_FS
+dep_mbool ' Use LONG (OS/2) namespace if available' CONFIG_NCPFS_OS2_NS $CONFIG_NCP_FS
+dep_mbool ' Lowercase DOS filenames' CONFIG_NCPFS_SMALLDOS $CONFIG_NCP_FS
+dep_mbool ' Allow mounting of volume subdirectories' CONFIG_NCPFS_MOUNT_SUBDIR $CONFIG_NCP_FS
+dep_mbool ' NDS authentication support' CONFIG_NCPFS_NDS_DOMAINS $CONFIG_NCP_FS
+dep_mbool ' Use Native Language Support' CONFIG_NCPFS_NLS $CONFIG_NCP_FS
+dep_mbool ' Enable symbolic links and execute flags' CONFIG_NCPFS_EXTRAS $CONFIG_NCP_FS
* 6 Jun 1999 Cache readdir lookups in the page cache. -DaveM
*/
-#define NFS_NEED_XDR_TYPES
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
-#include <linux/nfs.h>
+#include <linux/nfs_mount.h>
#include <linux/pagemap.h>
#include <asm/segment.h> /* for fs functions */
setattr: nfs_notify_change,
};
-/* Each readdir response is composed of entries which look
- * like the following, as per the NFSv2 RFC:
- *
- * __u32 not_end zero if end of response
- * __u32 file ID opaque ino_t
- * __u32 namelen size of name string
- * VAR name string the string, padded to modulo 4 bytes
- * __u32 cookie opaque ID of next entry
- *
- * When you hit not_end being zero, the next __u32 is non-zero if
- * this is the end of the complete set of readdir entires for this
- * directory. This can be used, for example, to initiate pre-fetch.
- *
- * In order to know what to ask the server for, we only need to know
- * the final cookie of the previous page, and offset zero has cookie
- * zero, so we cache cookie to page offset translations in chunks.
- */
-#define COOKIES_PER_CHUNK (8 - ((sizeof(void *) / sizeof(__u32))))
-struct nfs_cookie_table {
- struct nfs_cookie_table *next;
- __u32 cookies[COOKIES_PER_CHUNK];
-};
-static kmem_cache_t *nfs_cookie_cachep;
+typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int);
-/* This whole scheme relies on the fact that dirent cookies
- * are monotonically increasing.
- *
- * Another invariant is that once we have a valid non-zero
- * EOF marker cached, we also have the complete set of cookie
- * table entries.
+/*
+ * Given a pointer to a buffer that has already been filled by a call
+ * to readdir, find the next entry.
*
- * We return the page offset assosciated with the page where
- * cookie must be if it exists at all, however if we can not
- * figure that out conclusively, we return < 0.
+ * If the end of the buffer has been reached, return -EAGAIN, if not,
+ * return the offset within the buffer of the next entry to be
+ * read.
*/
-static long __nfs_readdir_offset(struct inode *inode, __u32 cookie)
+static inline
+long find_dirent(struct page *page, loff_t offset,
+ struct nfs_entry *entry,
+ decode_dirent_t decode, int plus, int use_cookie)
{
- struct nfs_cookie_table *p;
- unsigned long ret = 0;
-
- for(p = NFS_COOKIES(inode); p != NULL; p = p->next) {
- int i;
-
- for (i = 0; i < COOKIES_PER_CHUNK; i++) {
- __u32 this_cookie = p->cookies[i];
-
- /* End of known cookies, EOF is our only hope. */
- if (!this_cookie)
- goto check_eof;
-
- /* Next cookie is larger, must be in previous page. */
- if (this_cookie > cookie)
- return ret;
-
- ret += 1;
-
- /* Exact cookie match, it must be in this page :-) */
- if (this_cookie == cookie)
- return ret;
+ u8 *p = (u8 *)kmap(page),
+ *start = p;
+ unsigned long base = page_offset(page),
+ pg_offset = 0;
+ int loop_count = 0;
+
+ if (!p)
+ return -EIO;
+ for(;;) {
+ p = (u8*)decode((__u32*)p, entry, plus);
+ if (IS_ERR(p))
+ break;
+ pg_offset = p - start;
+ entry->prev = entry->offset;
+ entry->offset = base + pg_offset;
+ if ((use_cookie ? entry->cookie : entry->offset) > offset)
+ break;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
}
}
-check_eof:
- if (NFS_DIREOF(inode) != 0)
- return ret;
- return -1L;
-}
-
-static __inline__ long nfs_readdir_offset(struct inode *inode, __u32 cookie)
-{
- /* Cookie zero is always at page offset zero. Optimize the
- * other common case since most directories fit entirely
- * in one page.
- */
- if (!cookie || (!NFS_COOKIES(inode) && NFS_DIREOF(inode)))
- return 0;
- return __nfs_readdir_offset(inode, cookie);
+ kunmap(page);
+ return (IS_ERR(p)) ? PTR_ERR(p) : (long)pg_offset;
}
-/* Since a cookie of zero is declared special by the NFS
- * protocol, we easily can tell if a cookie in an existing
- * table chunk is valid or not.
+/*
+ * Find the given page, and call find_dirent() in order to try to
+ * return the next entry.
*
- * NOTE: The cookies are indexed off-by-one because zero
- * need not an entry.
+ * Returns -EIO if the page is not available, or up to date.
*/
-static __inline__ __u32 *find_cookie(struct inode *inode, unsigned long off)
+static inline
+long find_dirent_page(struct inode *inode, loff_t offset,
+ struct nfs_entry *entry)
{
- static __u32 cookie_zero = 0;
- struct nfs_cookie_table *p;
- __u32 *ret;
-
- if (!off)
- return &cookie_zero;
- off -= 1;
- p = NFS_COOKIES(inode);
- while(off >= COOKIES_PER_CHUNK && p) {
- off -= COOKIES_PER_CHUNK;
- p = p->next;
- }
- ret = NULL;
- if (p) {
- ret = &p->cookies[off];
- if (!*ret)
- ret = NULL;
- }
- return ret;
-}
+ decode_dirent_t decode = nfs_decode_dirent;
+ struct page *page;
+ unsigned long index = entry->offset >> PAGE_CACHE_SHIFT;
+ long status = -EIO;
+ int plus = NFS_USE_READDIRPLUS(inode),
+ use_cookie = NFS_MONOTONE_COOKIES(inode);
-#define NFS_NAMELEN_ALIGN(__len) ((((__len)+3)>>2)<<2)
-static int create_cookie(__u32 cookie, unsigned long off, struct inode *inode)
-{
- struct nfs_cookie_table **cpp;
+ dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", entry->offset & PAGE_CACHE_MASK);
- cpp = (struct nfs_cookie_table **) &NFS_COOKIES(inode);
- while (off >= COOKIES_PER_CHUNK && *cpp) {
- off -= COOKIES_PER_CHUNK;
- cpp = &(*cpp)->next;
- }
- if (*cpp) {
- (*cpp)->cookies[off] = cookie;
- } else {
- struct nfs_cookie_table *new;
- int i;
-
- new = kmem_cache_alloc(nfs_cookie_cachep, SLAB_ATOMIC);
- if(!new)
- return -1;
- *cpp = new;
- new->next = NULL;
- for(i = 0; i < COOKIES_PER_CHUNK; i++) {
- if (i == off) {
- new->cookies[i] = cookie;
- } else {
- new->cookies[i] = 0;
- }
- }
- }
- return 0;
+ if (entry->page)
+ page_cache_release(entry->page);
+
+ page = find_get_page(&inode->i_data, index);
+
+ if (page && Page_Uptodate(page))
+ status = find_dirent(page, offset, entry, decode, plus, use_cookie);
+
+ /* NB: on successful return we will be holding the page */
+ if (status < 0) {
+ entry->page = NULL;
+ if (page)
+ page_cache_release(page);
+ } else
+ entry->page = page;
+
+ dfprintk(VFS, "NFS: find_dirent_page() returns %ld\n", status);
+ return status;
}
-static struct page *try_to_get_dirent_page(struct file *, __u32, int);
-/* Recover from a revalidation flush. The case here is that
- * the inode for the directory got invalidated somehow, and
- * all of our cached information is lost. In order to get
- * a correct cookie for the current readdir request from the
- * user, we must (re-)fetch older readdir page cache entries.
+/*
+ * Recurse through the page cache pages, and return a
+ * filled nfs_entry structure of the next directory entry if possible.
*
- * Returns < 0 if some error occurrs, else it is the page offset
- * to fetch.
+ * The target for the search is position 'offset'.
+ * The latter may either be an offset into the page cache, or (better)
+ * a cookie depending on whether we're interested in strictly following
+ * the RFC wrt. not assuming monotonicity of cookies or not.
+ *
+ * For most systems, the latter is more reliable since it naturally
+ * copes with holes in the directory.
*/
-static long refetch_to_readdir_cookie(struct file *file, struct inode *inode)
+static inline
+long search_cached_dirent_pages(struct inode *inode, loff_t offset,
+ struct nfs_entry *entry)
{
- struct page *page;
- u32 goal_cookie = file->f_pos;
- long cur_off, ret = -1L;
+ long res = 0;
+ int loop_count = 0;
-again:
- cur_off = 0;
+ dfprintk(VFS, "NFS: search_cached_dirent_pages() searching for cookie %Ld\n", (long long)offset);
for (;;) {
- page = find_get_page(&inode->i_data, cur_off);
- if (page) {
- if (!Page_Uptodate(page))
- goto out_error;
- } else {
- __u32 *cp = find_cookie(inode, cur_off);
-
- if (!cp)
- goto out_error;
-
- page = try_to_get_dirent_page(file, *cp, 0);
- if (!page) {
- if (!cur_off)
- goto out_error;
-
- /* Someone touched the dir on us. */
- goto again;
- }
+ res = find_dirent_page(inode, offset, entry);
+ if (res == -EAGAIN) {
+ /* Align to beginning of next page */
+ entry->offset &= PAGE_CACHE_MASK;
+ entry->offset += PAGE_CACHE_SIZE;
+ }
+ if (res != -EAGAIN)
+ break;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
}
- page_cache_release(page);
-
- if ((ret = nfs_readdir_offset(inode, goal_cookie)) >= 0)
- goto out;
-
- cur_off += 1;
}
-out:
- return ret;
-
-out_error:
- if (page)
- page_cache_release(page);
- goto out;
+ if (res < 0 && entry->page) {
+ page_cache_release(entry->page);
+ entry->page = NULL;
+ }
+ dfprintk(VFS, "NFS: search_cached_dirent_pages() returned %ld\n", res);
+ return res;
}
+
/* Now we cache directories properly, by stuffing the dirent
* data directly in the page cache.
*
* page-in of the RPC reply, nowhere else, this simplies
* things substantially.
*/
-
-static int nfs_dir_filler(struct dentry *dentry, struct page *page)
+static inline
+long try_to_get_dirent_page(struct file *file, struct inode *inode,
+ struct nfs_entry *entry)
{
- struct nfs_readdirargs rd_args;
- struct nfs_readdirres rd_res;
- struct inode *inode = dentry->d_inode;
- long offset = page->index;
- __u32 *cookiep;
- int err;
+ struct dentry *dir = file->f_dentry;
+ struct page *page;
+ struct nfs_fattr dir_attr;
+ __u32 *p;
+ unsigned long index = entry->offset >> PAGE_CACHE_SHIFT;
+ long res = 0;
+ unsigned int dtsize = NFS_SERVER(inode)->dtsize;
+ int plus = NFS_USE_READDIRPLUS(inode);
- kmap(page);
+ dfprintk(VFS, "NFS: try_to_get_dirent_page() reading directory page @ index %ld\n", index);
- err = -EIO;
- cookiep = find_cookie(inode, offset);
- if (!cookiep)
- goto fail;
+ page = grab_cache_page(&inode->i_data, index);
- rd_args.fh = NFS_FH(dentry);
- rd_res.buffer = (char *)page_address(page);
- rd_res.bufsiz = PAGE_CACHE_SIZE;
- rd_res.cookie = *cookiep;
- do {
- rd_args.buffer = rd_res.buffer;
- rd_args.bufsiz = rd_res.bufsiz;
- rd_args.cookie = rd_res.cookie;
- err = rpc_call(NFS_CLIENT(inode),
- NFSPROC_READDIR, &rd_args, &rd_res, 0);
- if (err < 0)
- goto fail;
- } while(rd_res.bufsiz > 0);
-
- err = -EIO;
- if (rd_res.bufsiz < 0)
- NFS_DIREOF(inode) = rd_res.cookie;
- else if (create_cookie(rd_res.cookie, offset, inode))
- goto fail;
+ if (!page) {
+ res = -ENOMEM;
+ goto out;
+ }
- SetPageUptodate(page);
- kunmap(page);
- UnlockPage(page);
- return 0;
-fail:
- SetPageError(page);
- kunmap(page);
- UnlockPage(page);
- return err;
-}
+ if (Page_Uptodate(page)) {
+ dfprintk(VFS, "NFS: try_to_get_dirent_page(): page already up to date.\n");
+ goto unlock_out;
+ }
-static struct page *try_to_get_dirent_page(struct file *file, __u32 cookie, int refetch_ok)
-{
- struct dentry *dentry = file->f_dentry;
- struct inode *inode = dentry->d_inode;
- struct page *page;
- long offset;
+ p = (__u32 *)kmap(page);
- if ((offset = nfs_readdir_offset(inode, cookie)) < 0) {
- if (!refetch_ok ||
- (offset = refetch_to_readdir_cookie(file, inode)) < 0) {
- goto fail;
- }
- }
+ if (dtsize > PAGE_CACHE_SIZE)
+ dtsize = PAGE_CACHE_SIZE;
+ res = nfs_proc_readdir(dir, &dir_attr, entry->cookie, p, dtsize, plus);
- page = read_cache_page(&inode->i_data, offset,
- (filler_t *)nfs_dir_filler, dentry);
- if (IS_ERR(page))
- goto fail;
- if (!Page_Uptodate(page))
- goto fail2;
- return page;
+ kunmap(page);
+
+ if (res < 0)
+ goto error;
+ nfs_refresh_inode(inode, &dir_attr);
+ if (PageError(page))
+ ClearPageError(page);
+ SetPageUptodate(page);
-fail2:
+ unlock_out:
+ UnlockPage(page);
page_cache_release(page);
-fail:
- return NULL;
+ out:
+ dfprintk(VFS, "NFS: try_to_get_dirent_page() returns %ld\n", res);
+ return res;
+ error:
+ SetPageError(page);
+ goto unlock_out;
}
-/* Seek up to dirent assosciated with the passed in cookie,
- * then fill in dirents found. Return the last cookie
- * actually given to the user, to update the file position.
+/* Recover from a revalidation flush. The case here is that
+ * the inode for the directory got invalidated somehow, and
+ * all of our cached information is lost. In order to get
+ * a correct cookie for the current readdir request from the
+ * user, we must (re-)fetch all the older readdir page cache
+ * entries.
+ *
+ * Returns < 0 if some error occurs.
*/
-static __inline__ u32 nfs_do_filldir(__u32 *p, u32 cookie,
- void *dirent, filldir_t filldir)
+static inline
+long refetch_to_readdir(struct file *file, struct inode *inode,
+ loff_t off, struct nfs_entry *entry)
{
- u32 end;
-
- while((end = *p++) != 0) {
- __u32 fileid, len, skip, this_cookie;
- char *name;
+ struct nfs_entry my_dirent,
+ *dirent = &my_dirent;
+ long res;
+ int plus = NFS_USE_READDIRPLUS(inode),
+ use_cookie = NFS_MONOTONE_COOKIES(inode),
+ loop_count = 0;
+
+ dfprintk(VFS, "NFS: refetch_to_readdir() searching for cookie %Ld\n", (long long)off);
+ *dirent = *entry;
+ entry->page = NULL;
+
+ for (res = 0;res >= 0;) {
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
+ }
- fileid = *p++;
- len = *p++;
- name = (char *) p;
- skip = NFS_NAMELEN_ALIGN(len);
- p += (skip >> 2);
- this_cookie = *p++;
+ /* Search for last cookie in page cache */
+ res = search_cached_dirent_pages(inode, off, dirent);
- if (this_cookie < cookie)
+ if (res >= 0) {
+ /* Cookie was found */
+ if ((use_cookie?dirent->cookie:dirent->offset) > off) {
+ *entry = *dirent;
+ dirent->page = NULL;
+ break;
+ }
continue;
+ }
+
+ if (dirent->page)
+ page_cache_release(dirent->page);
+ dirent->page = NULL;
- cookie = this_cookie;
- if (filldir(dirent, name, len, cookie, fileid) < 0)
+ if (res != -EIO) {
+ *entry = *dirent;
break;
+ }
+
+ /* Read in a new page */
+ res = try_to_get_dirent_page(file, inode, dirent);
+ if (res == -EBADCOOKIE) {
+ memset(dirent, 0, sizeof(*dirent));
+ nfs_zap_caches(inode);
+ res = 0;
+ }
+ /* We requested READDIRPLUS, but the server doesn't grok it */
+ if (plus && res == -ENOTSUPP) {
+ NFS_FLAGS(inode) &= ~NFS_INO_ADVISE_RDPLUS;
+ memset(dirent, 0, sizeof(*dirent));
+ nfs_zap_caches(inode);
+ plus = 0;
+ res = 0;
+ }
}
+ if (dirent->page)
+ page_cache_release(dirent->page);
- return cookie;
+ dfprintk(VFS, "NFS: refetch_to_readdir() returns %ld\n", res);
+ return res;
}
-/* The file offset position is represented in pure bytes, to
- * make the page cache interface straight forward.
- *
- * However, some way is needed to make the connection between the
- * opaque NFS directory entry cookies and our offsets, so a per-inode
- * cookie cache table is used.
+/*
+ * Once we've found the start of the dirent within a page: fill 'er up...
*/
-static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+static
+int nfs_do_filldir(struct file *file, struct inode *inode,
+ struct nfs_entry *entry, void *dirent, filldir_t filldir)
{
- struct dentry *dentry = filp->f_dentry;
- struct inode *inode = dentry->d_inode;
- struct page *page;
- long offset;
- int res;
-
- res = nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
- if (res < 0)
- return res;
-
- if (NFS_DIREOF(inode) && filp->f_pos >= NFS_DIREOF(inode))
- return 0;
-
- if ((offset = nfs_readdir_offset(inode, filp->f_pos)) < 0)
- goto no_dirent_page;
-
- page = find_get_page(&inode->i_data, offset);
- if (!page)
- goto no_dirent_page;
- if (!Page_Uptodate(page))
- goto dirent_read_error;
-success:
- kmap(page);
- filp->f_pos = nfs_do_filldir((__u32 *) page_address(page),
- filp->f_pos, dirent, filldir);
+ decode_dirent_t decode = nfs_decode_dirent;
+ struct page *page = entry->page;
+ __u8 *p,
+ *start;
+ unsigned long base = page_offset(page),
+ offset = entry->offset,
+ pg_offset,
+ fileid;
+ int plus = NFS_USE_READDIRPLUS(inode),
+ use_cookie = NFS_MONOTONE_COOKIES(inode),
+ loop_count = 0,
+ res = 0;
+
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling starting @ offset %ld\n", entry->offset);
+ pg_offset = offset & ~PAGE_CACHE_MASK;
+ start = (u8*)kmap(page);
+ p = start + pg_offset;
+
+ for(;;) {
+ /* Note: entry->prev contains the offset of the start of the
+ * current dirent */
+ fileid = nfs_fileid_to_ino_t(entry->ino);
+ if (use_cookie)
+ res = filldir(dirent, entry->name, entry->len, entry->prev_cookie, fileid);
+ else
+ res = filldir(dirent, entry->name, entry->len, entry->prev, fileid);
+ if (res < 0)
+ break;
+ file->f_pos = (use_cookie) ? entry->cookie : entry->offset;
+ p = (u8*)decode((__u32*)p, entry, plus);
+ if (!p || IS_ERR(p))
+ break;
+ pg_offset = p - start;
+ entry->prev = entry->offset;
+ entry->offset = base + pg_offset;
+ if (loop_count++ > 200) {
+ loop_count = 0;
+ schedule();
+ }
+ }
kunmap(page);
- page_cache_release(page);
- return 0;
-
-no_dirent_page:
- page = try_to_get_dirent_page(filp, filp->f_pos, 1);
- if (!page)
- goto no_page;
- if (Page_Uptodate(page))
- goto success;
-dirent_read_error:
- page_cache_release(page);
-no_page:
- return -EIO;
+ dfprintk(VFS, "NFS: nfs_do_filldir() filling ended @ offset %ld; returning = %d\n", entry->offset, res);
+ return res;
}
-/* Flush directory cookie and EOF caches for an inode.
- * So we don't thrash allocating/freeing cookie tables,
- * we keep the cookies around until the inode is
- * deleted/reused.
+/* The file offset position is now represented as a true offset into the
+ * page cache as is the case in most of the other filesystems.
*/
-__inline__ void nfs_flush_dircache(struct inode *inode)
+static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
- struct nfs_cookie_table *p = NFS_COOKIES(inode);
+ struct dentry *dentry = filp->f_dentry;
+ struct inode *inode = dentry->d_inode;
+ struct page *page;
+ struct nfs_entry my_entry,
+ *entry = &my_entry;
+ loff_t offset;
+ long res;
+
+ res = nfs_revalidate(dentry);
+ if (res < 0)
+ return res;
- while (p != NULL) {
- int i;
+ /*
+ * filp->f_pos points to the file offset in the page cache.
+ * but if the cache has meanwhile been zapped, we need to
+ * read from the last dirent to revalidate f_pos
+ * itself.
+ */
+ memset(entry, 0, sizeof(*entry));
- for(i = 0; i < COOKIES_PER_CHUNK; i++)
- p->cookies[i] = 0;
+ offset = filp->f_pos;
- p = p->next;
- }
- NFS_DIREOF(inode) = 0;
-}
+ while(!entry->eof) {
+ res = search_cached_dirent_pages(inode, offset, entry);
-/* Free up directory cache state, this happens when
- * nfs_delete_inode is called on an NFS directory.
- */
-void nfs_free_dircache(struct inode *inode)
-{
- struct nfs_cookie_table *p = NFS_COOKIES(inode);
+ if (res < 0) {
+ if (entry->eof)
+ break;
+ res = refetch_to_readdir(filp, inode, offset, entry);
+ if (res < 0)
+ break;
+ }
- while (p != NULL) {
- struct nfs_cookie_table *next = p->next;
- kmem_cache_free(nfs_cookie_cachep, p);
- p = next;
+ page = entry->page;
+ if (!page)
+ printk(KERN_ERR "NFS: Missing page...\n");
+ res = nfs_do_filldir(filp, inode, entry, dirent, filldir);
+ page_cache_release(page);
+ entry->page = NULL;
+ if (res < 0) {
+ res = 0;
+ break;
+ }
+ offset = filp->f_pos;
}
- NFS_COOKIES(inode) = NULL;
- NFS_DIREOF(inode) = 0;
+ if (entry->page)
+ page_cache_release(entry->page);
+ if (res < 0 && res != -EBADCOOKIE)
+ return res;
+ return 0;
}
/*
goto out_bad;
/* Inode number matches? */
- if (NFS_FSID(inode) != fattr.fsid ||
+ if (!(fattr.valid & NFS_ATTR_FATTR) ||
+ NFS_FSID(inode) != fattr.fsid ||
NFS_FILEID(inode) != fattr.fileid)
goto out_bad;
goto out_valid;
d_drop(dentry);
/* Purge readdir caches. */
- if (dentry->d_parent->d_inode) {
+ if (dentry->d_parent->d_inode)
nfs_zap_caches(dentry->d_parent->d_inode);
- NFS_CACHEINV(dentry->d_parent->d_inode);
- }
+ if (inode && S_ISDIR(inode->i_mode))
+ nfs_zap_caches(inode);
return 0;
}
/*
* Invalidate the dir cache before the operation to avoid a race.
*/
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
+ nfs_zap_caches(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &attr, &fhandle, &fattr);
if (!error)
attr.ia_valid |= ATTR_SIZE;
}
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
+ nfs_zap_caches(dir);
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, &attr, &fhandle, &fattr);
if (!error)
* depending on potentially bogus information.
*/
d_drop(dentry);
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
+ nfs_zap_caches(dir);
error = nfs_proc_mkdir(NFS_DSERVER(dentry), NFS_FH(dentry->d_parent),
dentry->d_name.name, &attr, &fhandle, &fattr);
if (!error)
dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
dir->i_dev, dir->i_ino, dentry->d_name.name);
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
+ nfs_zap_caches(dir);
error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name);
goto out;
} while(sdentry->d_inode != NULL); /* need negative lookup */
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
+ nfs_zap_caches(dir);
error = nfs_proc_rename(NFS_SERVER(dir),
NFS_FH(dentry->d_parent), dentry->d_name.name,
NFS_FH(dentry->d_parent), silly);
inode->i_nlink --;
d_delete(dentry);
}
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
+ nfs_zap_caches(dir);
error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name);
/*
* can't instantiate the new inode.
*/
d_drop(dentry);
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
+ nfs_zap_caches(dir);
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dentry->d_parent),
dentry->d_name.name, symname, &attr);
if (!error) {
* we can't use the existing dentry.
*/
d_drop(dentry);
- invalidate_inode_pages(dir);
- nfs_flush_dircache(dir);
+ nfs_zap_caches(dir);
error = nfs_proc_link(NFS_DSERVER(old_dentry), NFS_FH(old_dentry),
NFS_FH(dentry->d_parent), dentry->d_name.name);
if (!error) {
if (new_inode)
d_delete(new_dentry);
- invalidate_inode_pages(new_dir);
- nfs_flush_dircache(new_dir);
- invalidate_inode_pages(old_dir);
- nfs_flush_dircache(old_dir);
+ nfs_zap_caches(new_dir);
+ nfs_zap_caches(old_dir);
error = nfs_proc_rename(NFS_DSERVER(old_dentry),
NFS_FH(old_dentry->d_parent), old_dentry->d_name.name,
NFS_FH(new_dentry->d_parent), new_dentry->d_name.name);
if (nfs_fh_cachep == NULL)
return -ENOMEM;
- nfs_cookie_cachep = kmem_cache_create("nfs_dcookie",
- sizeof(struct nfs_cookie_table),
- 0, SLAB_HWCACHE_ALIGN,
- NULL, NULL);
- if (nfs_cookie_cachep == NULL)
- return -ENOMEM;
-
return 0;
}
#include <linux/fcntl.h>
#include <linux/stat.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/pagemap.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/stats.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
#include <linux/nfs_flushd.h>
#include <linux/lockd/bind.h>
#include <linux/smp_lock.h>
struct rpc_stat nfs_rpcstat = { &nfs_program };
+static inline unsigned long
+nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
+{
+ return nfs_fileid_to_ino_t(fattr->fileid);
+}
+
/*
* The "read_inode" function doesn't actually do anything:
* the real data is filled in later in nfs_fhget. Here we
inode->u.nfs_i.npages = 0;
NFS_CACHEINV(inode);
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
}
static void
{
dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
- lock_kernel();
- if (S_ISDIR(inode->i_mode)) {
- nfs_free_dircache(inode);
- } else {
- /*
- * The following can never actually happen...
- */
- if (nfs_have_writebacks(inode)) {
- printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
- }
+ /*
+ * The following can never actually happen...
+ */
+ if (nfs_have_writebacks(inode)) {
+ printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
}
- unlock_kernel();
clear_inode(inode);
}
rpc_killall_tasks(rpc);
}
-/*
- * Compute and set NFS server blocksize
- */
-static unsigned int
-nfs_block_size(unsigned int bsize, unsigned char *nrbitsp)
-{
- if (bsize < 1024)
- bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
- else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
- bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
+static inline unsigned long
+nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp)
+{
/* make sure blocksize is a power of two */
if ((bsize & (bsize - 1)) || nrbitsp) {
unsigned int nrbits;
bsize = 1 << nrbits;
if (nrbitsp)
*nrbitsp = nrbits;
- if (bsize < NFS_DEF_FILE_IO_BUFFER_SIZE)
- bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
}
return bsize;
}
+/*
+ * Calculate the number of 512byte blocks used.
+ */
+static inline unsigned long
+nfs_calc_block_size(u64 tsize)
+{
+ loff_t used = (tsize + 511) / 512;
+ return (used > ULONG_MAX) ? ULONG_MAX : used;
+}
+
+/*
+ * Compute and set NFS server blocksize
+ */
+static inline unsigned long
+nfs_block_size(unsigned long bsize, unsigned char *nrbitsp)
+{
+ if (bsize < 1024)
+ bsize = NFS_DEF_FILE_IO_BUFFER_SIZE;
+ else if (bsize >= NFS_MAX_FILE_IO_BUFFER_SIZE)
+ bsize = NFS_MAX_FILE_IO_BUFFER_SIZE;
+
+ return nfs_block_bits(bsize, nrbitsp);
+}
+
extern struct nfs_fh *nfs_fh_alloc(void);
extern void nfs_fh_free(struct nfs_fh *p);
sb->s_magic = NFS_SUPER_MAGIC;
sb->s_op = &nfs_sops;
sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits);
- sb->u.nfs_sb.s_root = data->root;
server = &sb->u.nfs_sb.s_server;
- server->rsize = nfs_block_size(data->rsize, NULL);
+ server->dtsize = server->rsize = nfs_block_size(data->rsize, NULL);
server->wsize = nfs_block_size(data->wsize, NULL);
server->flags = data->flags;
int error;
struct nfs_fsinfo res;
- error = nfs_proc_statfs(&sb->u.nfs_sb.s_server, &sb->u.nfs_sb.s_root,
+ error = nfs_proc_statfs(&sb->u.nfs_sb.s_server, NFS_FH(sb->s_root),
&res);
if (error) {
printk("nfs_statfs: statfs error = %d\n", -error);
nfs_zap_caches(struct inode *inode)
{
NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
- NFS_CACHEINV(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
invalidate_inode_pages(inode);
- if (S_ISDIR(inode->i_mode))
- nfs_flush_dircache(inode);
+
+ memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
+ NFS_CACHEINV(inode);
}
/*
* Preset the size and mtime, as there's no need
* to invalidate the caches.
*/
- inode->i_size = fattr->size;
- inode->i_mtime = fattr->mtime.seconds;
- NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+ inode->i_size = nfs_size_to_loff_t(fattr->size);
+ inode->i_mtime = nfs_time_to_secs(fattr->mtime);
+ inode->i_atime = nfs_time_to_secs(fattr->atime);
+ inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+ NFS_CACHE_CTIME(inode) = fattr->ctime;
+ NFS_CACHE_MTIME(inode) = fattr->mtime;
+ NFS_CACHE_ATIME(inode) = fattr->atime;
+ NFS_CACHE_ISIZE(inode) = fattr->size;
+ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
}
nfs_refresh_inode(inode, fattr);
}
{
struct super_block *sb = dentry->d_sb;
- dprintk("NFS: nfs_fhget(%s/%s fileid=%d)\n",
+ dprintk("NFS: nfs_fhget(%s/%s fileid=%Ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- fattr->fileid);
+ (long long)fattr->fileid);
/* Install the file handle in the dentry */
*((struct nfs_fh *) dentry->d_fsdata) = *fhandle;
inode->i_sb = sb;
inode->i_dev = sb->s_dev;
inode->i_flags = 0;
- inode->i_ino = fattr->fileid;
+ inode->i_ino = nfs_fattr_to_ino_t(fattr);
nfs_read_inode(inode);
nfs_fill_inode(inode, fattr);
inode->u.nfs_i.flags |= NFS_IS_SNAPSHOT;
struct inode *inode = NULL;
unsigned long ino;
+ if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+ goto out_no_inode;
+
if (!fattr->nlink) {
printk("NFS: Buggy server - nlink == 0!\n");
goto out_no_inode;
}
- ino = fattr->fileid;
+ ino = nfs_fattr_to_ino_t(fattr);
while((inode = iget4(sb, ino, nfs_find_actor, fattr)) != NULL) {
*/
if (attr->ia_valid & ATTR_SIZE) {
if (attr->ia_size != fattr.size)
- printk("nfs_notify_change: attr=%Ld, fattr=%d??\n",
- (long long) attr->ia_size, fattr.size);
- inode->i_mtime = fattr.mtime.seconds;
+ printk("nfs_notify_change: attr=%Ld, fattr=%Ld??\n",
+ (long long) attr->ia_size, (long long)fattr.size);
vmtruncate(inode, attr->ia_size);
}
- if (attr->ia_valid & ATTR_MTIME)
- inode->i_mtime = fattr.mtime.seconds;
+
+ /*
+ * If we changed the size or mtime, update the inode
+ * now to avoid invalidating the page cache.
+ */
+ if (!(fattr.valid & NFS_ATTR_WCC)) {
+ fattr.pre_size = NFS_CACHE_ISIZE(inode);
+ fattr.pre_mtime = NFS_CACHE_MTIME(inode);
+ fattr.pre_ctime = NFS_CACHE_CTIME(inode);
+ fattr.valid |= NFS_ATTR_WCC;
+ }
error = nfs_refresh_inode(inode, &fattr);
out:
return error;
int
nfs_wait_on_inode(struct inode *inode, int flag)
{
- struct task_struct *tsk = current;
- DECLARE_WAITQUEUE(wait, tsk);
- int intr, error = 0;
-
- intr = NFS_SERVER(inode)->flags & NFS_MOUNT_INTR;
- add_wait_queue(&inode->i_wait, &wait);
- for (;;) {
- set_task_state(tsk, (intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE));
- error = 0;
- if (!(NFS_FLAGS(inode) & flag))
- break;
- error = -ERESTARTSYS;
- if (intr && signalled())
- break;
- schedule();
- }
- set_task_state(tsk, TASK_RUNNING);
- remove_wait_queue(&inode->i_wait, &wait);
+ struct rpc_clnt *clnt = NFS_CLIENT(inode);
+ int error;
+ if (!(NFS_FLAGS(inode) & flag))
+ return 0;
+ inode->i_count++;
+ error = nfs_wait_event(clnt, inode->i_wait, !(NFS_FLAGS(inode) & flag));
+ iput(inode);
return error;
}
int
nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
{
- int invalid = 0;
- int error = -EIO;
-
- dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n",
- inode->i_dev, inode->i_ino, inode->i_count);
+ __u64 new_size, new_mtime;
+ loff_t new_isize;
+ int invalid = 0;
+ int error = -EIO;
if (!inode || !fattr) {
- printk("nfs_refresh_inode: inode or fattr is NULL\n");
+ printk(KERN_ERR "nfs_refresh_inode: inode or fattr is NULL\n");
+ goto out;
+ }
+ if (inode->i_mode == 0) {
+ printk(KERN_ERR "nfs_refresh_inode: empty inode\n");
goto out;
}
- if (inode->i_ino != fattr->fileid) {
- printk("nfs_refresh_inode: mismatch, ino=%ld, fattr=%d\n",
- inode->i_ino, fattr->fileid);
+
+ if ((fattr->valid & NFS_ATTR_FATTR) == 0)
+ goto out;
+
+ if (is_bad_inode(inode))
+ goto out;
+
+ dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d info=0x%x)\n",
+ inode->i_dev, inode->i_ino, inode->i_count,
+ fattr->valid);
+
+
+ if (NFS_FSID(inode) != fattr->fsid ||
+ NFS_FILEID(inode) != fattr->fileid) {
+ printk(KERN_ERR "nfs_refresh_inode: inode number mismatch\n"
+ "expected (0x%Lx/0x%Lx), got (0x%Lx/0x%Lx)\n",
+ (long long)NFS_FSID(inode), (long long)NFS_FILEID(inode),
+ (long long)fattr->fsid, (long long)fattr->fileid);
goto out;
}
if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT))
goto out_changed;
- inode->i_mode = fattr->mode;
- inode->i_nlink = fattr->nlink;
- inode->i_uid = fattr->uid;
- inode->i_gid = fattr->gid;
+ new_mtime = fattr->mtime;
+ new_size = fattr->size;
+ new_isize = nfs_size_to_loff_t(fattr->size);
- inode->i_blocks = fattr->blocks;
- inode->i_atime = fattr->atime.seconds;
- inode->i_ctime = fattr->ctime.seconds;
+ error = 0;
/*
* Update the read time so we don't revalidate too often.
*/
NFS_READTIME(inode) = jiffies;
- error = 0;
/*
- * If we have pending write-back entries, we don't want
- * to look at the size or the mtime the server sends us
- * too closely, as we're in the middle of modifying them.
+ * Note: NFS_CACHE_ISIZE(inode) reflects the state of the cache.
+ * NOT inode->i_size!!!
*/
- if (nfs_have_writebacks(inode))
- goto out;
-
- if (inode->i_size != fattr->size) {
+ if (NFS_CACHE_ISIZE(inode) != new_size) {
#ifdef NFS_DEBUG_VERBOSE
-printk("NFS: size change on %x/%ld\n", inode->i_dev, inode->i_ino);
+ printk(KERN_DEBUG "NFS: isize change on %x/%ld\n", inode->i_dev, inode->i_ino);
#endif
- inode->i_size = fattr->size;
invalid = 1;
}
- if (inode->i_mtime != fattr->mtime.seconds) {
+ /*
+ * Note: we don't check inode->i_mtime since pipes etc.
+ * can change this value in VFS without requiring a
+ * cache revalidation.
+ */
+ if (NFS_CACHE_MTIME(inode) != new_mtime) {
#ifdef NFS_DEBUG_VERBOSE
-printk("NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
+ printk(KERN_DEBUG "NFS: mtime change on %x/%ld\n", inode->i_dev, inode->i_ino);
#endif
- inode->i_mtime = fattr->mtime.seconds;
invalid = 1;
}
- if (invalid)
- goto out_invalid;
+ /* Check Weak Cache Consistency data.
+ * If size and mtime match the pre-operation values, we can
+ * assume that any attribute changes were caused by our NFS
+ * operation, so there's no need to invalidate the caches.
+ */
+ if ((fattr->valid & NFS_ATTR_WCC)
+ && NFS_CACHE_ISIZE(inode) == fattr->pre_size
+ && NFS_CACHE_MTIME(inode) == fattr->pre_mtime) {
+ invalid = 0;
+ }
+
+ /*
+ * If we have pending writebacks, things can get
+ * messy.
+ */
+ if (nfs_have_writebacks(inode) && new_isize < inode->i_size)
+ new_isize = inode->i_size;
+
+ NFS_CACHE_CTIME(inode) = fattr->ctime;
+ inode->i_ctime = nfs_time_to_secs(fattr->ctime);
+ /* If we've been messing around with atime, don't
+ * update it. Save the server value in NFS_CACHE_ATIME.
+ */
+ NFS_CACHE_ATIME(inode) = fattr->atime;
+ if (time_before(inode->i_atime, nfs_time_to_secs(fattr->atime)))
+ inode->i_atime = nfs_time_to_secs(fattr->atime);
+ NFS_CACHE_MTIME(inode) = new_mtime;
+ inode->i_mtime = nfs_time_to_secs(new_mtime);
+
+ NFS_CACHE_ISIZE(inode) = new_size;
+ inode->i_size = new_isize;
+
+ inode->i_mode = fattr->mode;
+ inode->i_nlink = fattr->nlink;
+ inode->i_uid = fattr->uid;
+ inode->i_gid = fattr->gid;
+
+ if (fattr->valid & NFS_ATTR_FATTR_V3) {
+ /*
+ * report the blocks in 512byte units
+ */
+ inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
+ inode->i_blksize = inode->i_sb->s_blocksize;
+ } else {
+ inode->i_blocks = fattr->du.nfs2.blocks;
+ inode->i_blksize = fattr->du.nfs2.blocksize;
+ }
+ inode->i_rdev = 0;
+ if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+ inode->i_rdev = to_kdev_t(fattr->rdev);
+
/* Update attrtimeo value */
- if (fattr->mtime.seconds == NFS_OLDMTIME(inode)) {
+ if (!invalid && time_after(jiffies, NFS_ATTRTIMEO_UPDATE(inode)+NFS_ATTRTIMEO(inode))) {
if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
NFS_ATTRTIMEO(inode) = NFS_MAXATTRTIMEO(inode);
+ NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
}
- NFS_OLDMTIME(inode) = fattr->mtime.seconds;
+
+ if (invalid)
+ nfs_zap_caches(inode);
out:
return error;
* Big trouble! The inode has become a different object.
*/
#ifdef NFS_PARANOIA
-printk("nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
-inode->i_ino, inode->i_mode, fattr->mode);
+ printk(KERN_DEBUG "nfs_refresh_inode: inode %ld mode changed, %07o to %07o\n",
+ inode->i_ino, inode->i_mode, fattr->mode);
#endif
/*
* No need to worry about unhashing the dentry, as the
* lookup validation will know that the inode is bad.
+ * (But we fall through to invalidate the caches.)
*/
nfs_invalidate_inode(inode);
goto out;
-
-out_invalid:
-#ifdef NFS_DEBUG_VERBOSE
-printk("nfs_refresh_inode: invalidating %ld pages\n", inode->i_nrpages);
-#endif
- nfs_zap_caches(inode);
- goto out;
}
/*
#include <linux/pagemap.h>
#include <linux/proc_fs.h>
#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
#include <linux/nfs_fs.h>
/* Uncomment this to support servers requiring longword lengths */
return p + QUADLEN(*len);
}
+static inline u32*
+xdr_decode_time(u32 *p, u64 *timep)
+{
+ *timep = ((u64)ntohl(*p++) << 32) + (u64)ntohl(*p++);
+ return p;
+}
+
static inline u32 *
xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
{
fattr->uid = ntohl(*p++);
fattr->gid = ntohl(*p++);
fattr->size = ntohl(*p++);
- fattr->blocksize = ntohl(*p++);
+ fattr->du.nfs2.blocksize = ntohl(*p++);
fattr->rdev = ntohl(*p++);
- fattr->blocks = ntohl(*p++);
+ fattr->du.nfs2.blocks = ntohl(*p++);
fattr->fsid = ntohl(*p++);
fattr->fileid = ntohl(*p++);
- fattr->atime.seconds = ntohl(*p++);
- fattr->atime.useconds = ntohl(*p++);
- fattr->mtime.seconds = ntohl(*p++);
- fattr->mtime.useconds = ntohl(*p++);
- fattr->ctime.seconds = ntohl(*p++);
- fattr->ctime.useconds = ntohl(*p++);
+ p = xdr_decode_time(p, &fattr->atime);
+ p = xdr_decode_time(p, &fattr->mtime);
+ p = xdr_decode_time(p, &fattr->ctime);
+ fattr->valid |= NFS_ATTR_FATTR;
+ if (fattr->type == NFCHR && fattr->rdev == NFS_FIFO_DEV) {
+ fattr->type = NFFIFO;
+ fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
+ fattr->rdev = 0;
+ }
return p;
}
struct rpc_task *task = req->rq_task;
struct rpc_auth *auth = task->tk_auth;
int bufsiz = args->bufsiz;
- int replen;
+ int buflen, replen;
p = xdr_encode_fhandle(p, args->fh);
*p++ = htonl(args->cookie);
/* set up reply iovec */
replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
+ buflen = req->rq_rvec[0].iov_len;
req->rq_rvec[0].iov_len = replen;
req->rq_rvec[1].iov_base = args->buffer;
req->rq_rvec[1].iov_len = bufsiz;
- req->rq_rlen = replen + bufsiz;
- req->rq_rnr = 2;
+ req->rq_rvec[2].iov_base = (u8 *) req->rq_rvec[0].iov_base + replen;
+ req->rq_rvec[2].iov_len = buflen - replen;
+ req->rq_rlen = buflen + bufsiz;
+ req->rq_rnr += 2;
return 0;
}
/*
* Decode the result of a readdir call.
+ * We're not really decoding anymore, we just leave the buffer untouched
+ * and only check that it is syntactically correct.
+ * The real decoding happens in nfs_decode_entry below, called directly
+ * from nfs_readdir for each entry.
*/
-#define NFS_DIRENT_MAXLEN (5 * sizeof(u32) + (NFS_MAXNAMLEN + 1))
static int
nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
{
struct iovec *iov = req->rq_rvec;
int status, nr;
- u32 *end;
- u32 last_cookie = res->cookie;
+ u32 *end, *entry, len;
- status = ntohl(*p++);
- if (status) {
- nr = -nfs_stat_to_errno(status);
- goto error;
- }
+ if ((status = ntohl(*p++)))
+ return -nfs_stat_to_errno(status);
if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
/* Unexpected reply header size. Punt. */
- printk("NFS: Odd RPC header size in readdirres reply\n");
- nr = -errno_NFSERR_IO;
- goto error;
+ printk(KERN_WARNING "NFS: Odd RPC header size in readdirres reply\n");
+ return -errno_NFSERR_IO;
}
- /* Get start and end address of XDR readdir response. */
+ /* Get start and end address of XDR data */
p = (u32 *) iov[1].iov_base;
end = (u32 *) ((u8 *) p + iov[1].iov_len);
- for (nr = 0; *p++; nr++) {
- __u32 len;
-
- /* Convert fileid. */
- *p = ntohl(*p);
- p++;
-
- /* Convert and capture len */
- len = *p = ntohl(*p);
- p++;
- if ((p + QUADLEN(len) + 3) > end) {
- struct rpc_clnt *clnt = req->rq_task->tk_client;
+ /* Get start and end of dirent buffer */
+ if (res->buffer != p) {
+ printk(KERN_ERR "NFS: Bad result buffer in readdir\n");
+ return -errno_NFSERR_IO;
+ }
- clnt->cl_flags |= NFS_CLNTF_BUFSIZE;
- p -= 2;
- p[-1] = 0;
- p[0] = 0;
- break;
+ for (nr = 0; *p++; nr++) {
+ entry = p - 1;
+ p++; /* fileid */
+ len = ntohl(*p++);
+ p += XDR_QUADLEN(len) + 1; /* name plus cookie */
+ if (len > NFS2_MAXNAMLEN) {
+ printk(KERN_WARNING "NFS: giant filename in readdir (len 0x%x)!\n",
+ len);
+ return -errno_NFSERR_IO;
}
- if (len > NFS_MAXNAMLEN) {
- nr = -errno_NFSERR_IO;
- goto error;
+ if (p + 2 > end) {
+ printk(KERN_NOTICE
+ "NFS: short packet in readdir reply!\n");
+ entry[0] = entry[1] = 0;
+ break;
}
- p += QUADLEN(len);
-
- /* Convert and capture cookie. */
- last_cookie = *p = ntohl(*p);
- p++;
}
- p -= 1;
- status = ((end - p) << 2);
- if (!p[1] && (status >= NFS_DIRENT_MAXLEN)) {
- status = ((__u8 *)p - (__u8 *)iov[1].iov_base);
- res->buffer += status;
- res->bufsiz -= status;
- } else if (p[1]) {
- status = (int)((long)p & ~PAGE_CACHE_MASK);
- res->bufsiz = -status;
- } else {
- res->bufsiz = 0;
+ p++; /* EOF flag */
+
+ if (p > end) {
+ printk(KERN_NOTICE
+ "NFS: short packet in readdir reply!\n");
+ return -errno_NFSERR_IO;
}
- res->cookie = last_cookie;
return nr;
+}
-error:
- res->bufsiz = 0;
- return nr;
+u32 *
+nfs_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+{
+ if (!*p++) {
+ if (!*p)
+ return ERR_PTR(-EAGAIN);
+ entry->eof = 1;
+ return ERR_PTR(-EBADCOOKIE);
+ }
+
+ entry->ino = ntohl(*p++);
+ entry->len = ntohl(*p++);
+ entry->name = (const char *) p;
+ p += XDR_QUADLEN(entry->len);
+ entry->prev_cookie = entry->cookie;
+ entry->cookie = ntohl(*p++);
+ entry->eof = !p[0] && p[1];
+
+ return p;
}
/*
if ((status = ntohl(*p++)))
return -nfs_stat_to_errno(status);
xdr_decode_fattr(p, fattr);
- dprintk("RPC: attrstat OK type %d mode %o dev %x ino %x\n",
- fattr->type, fattr->mode, fattr->fsid, fattr->fileid);
+ dprintk("RPC: attrstat OK type %d mode %o dev %Lx ino %Lx\n",
+ fattr->type, fattr->mode,
+ (long long)fattr->fsid, (long long)fattr->fileid);
return 0;
}
return -nfs_stat_to_errno(status);
p = xdr_decode_fhandle(p, res->fh);
xdr_decode_fattr(p, res->fattr);
- dprintk("RPC: diropres OK type %x mode %o dev %x ino %x\n",
+ dprintk("RPC: diropres OK type %x mode %o dev %Lx ino %Lx\n",
res->fattr->type, res->fattr->mode,
- res->fattr->fsid, res->fattr->fileid);
+ (long long)res->fattr->fsid, (long long)res->fattr->fileid);
return 0;
}
{ "nocto", ~NFS_MOUNT_NOCTO, NFS_MOUNT_NOCTO },
{ "ac", ~NFS_MOUNT_NOAC, 0 },
{ "noac", ~NFS_MOUNT_NOAC, NFS_MOUNT_NOAC },
- { "lock", ~NFS_MOUNT_LOCK, 0 },
+ { "lock", ~NFS_MOUNT_NONLM, 0 },
{ "nolock", ~NFS_MOUNT_NONLM, NFS_MOUNT_NONLM },
{ NULL, 0, 0 }
};
#include <linux/in.h>
#include <linux/pagemap.h>
#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+#include <linux/nfs2.h>
#include <linux/nfs_fs.h>
#include <asm/segment.h>
int status;
dprintk("NFS call getattr\n");
+ fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_GETATTR, fhandle, fattr, 0);
dprintk("NFS reply getattr\n");
return status;
int status;
dprintk("NFS call setattr\n");
+ fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_SETATTR, &arg, fattr, 0);
dprintk("NFS reply setattr\n");
return status;
int status;
dprintk("NFS call lookup %s\n", name);
+ fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_LOOKUP, &arg, &res, 0);
dprintk("NFS reply lookup: %d\n", status);
return status;
int status;
dprintk("NFS call read %d @ %ld\n", count, offset);
+ fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_READ, &arg, &res,
swap? NFS_RPC_SWAPFLAGS : 0);
dprintk("NFS reply read: %d\n", status);
unsigned long offset, unsigned int count,
const void *buffer, struct nfs_fattr *fattr)
{
- struct nfs_writeargs arg = { fhandle, offset, count, 1, 1,
+ struct nfs_writeargs arg = { fhandle, offset, count,
+ NFS_FILE_SYNC, 1,
{{(void *) buffer, count}, {0,0}, {0,0}, {0,0},
{0,0}, {0,0}, {0,0}, {0,0}}};
struct nfs_writeverf verf;
int status;
dprintk("NFS call write %d @ %ld\n", count, offset);
+ fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_WRITE, &arg, &res,
swap? (RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS) : 0);
dprintk("NFS reply read: %d\n", status);
int status;
dprintk("NFS call create %s\n", name);
+ fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_CREATE, &arg, &res, 0);
dprintk("NFS reply create: %d\n", status);
return status;
int status;
dprintk("NFS call mkdir %s\n", name);
+ fattr->valid = 0;
status = rpc_call(server->client, NFSPROC_MKDIR, &arg, &res, 0);
dprintk("NFS reply mkdir: %d\n", status);
return status;
return status;
}
+/*
+ * The READDIR implementation is somewhat hackish - we pass a temporary
+ * buffer to the encode function, which installs it in the receive
+ * the receive iovec. The decode function just parses the reply to make
+ * sure it is syntactically correct; the entries itself are decoded
+ * from nfs_readdir by calling the decode_entry function directly.
+ */
+int
+nfs_proc_readdir(struct dentry *dir, struct nfs_fattr *dir_attr,
+ __u64 cookie, void *entry, unsigned int size, int plus)
+{
+ struct nfs_readdirargs arg;
+ struct nfs_readdirres res;
+ struct rpc_message msg = { NFSPROC_READDIR, &arg, &res, NULL };
+ struct nfs_server *server = NFS_DSERVER(dir);
+ int status;
+
+ if (server->rsize < size)
+ size = server->rsize;
+
+ dir_attr->valid = 0;
+ arg.fh = NFS_FH(dir);
+ arg.cookie = cookie;
+ arg.buffer = entry;
+ arg.bufsiz = size;
+ res.buffer = entry;
+ res.bufsiz = size;
+
+ dir_attr->valid = 0;
+ dprintk("NFS call readdir %d\n", (unsigned int)cookie);
+ status = rpc_call_sync(NFS_CLIENT(dir->d_inode), &msg, 0);
+
+ dprintk("NFS reply readdir: %d\n", status);
+ return status;
+}
+
int
nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
req->ra_args.buffer = buffer;
req->ra_res.fattr = &req->ra_fattr;
req->ra_res.count = rsize;
+ req->ra_fattr.valid = 0;
}
lock_kernel();
result = rpc_call(NFS_CLIENT(inode), NFSPROC_READ, &rqst.ra_args, &rqst.ra_res, flags);
unlock_kernel();
+ nfs_refresh_inode(inode, &rqst.ra_fattr);
/*
* Even if we had a partial success we can't mark the page
io_error:
kunmap(page);
UnlockPage(page);
- /* Note: we don't refresh if the call returned error */
- if (refresh && result >= 0)
- nfs_refresh_inode(inode, &rqst.ra_fattr);
return result;
}
dprintk("NFS: %4d received callback for page %lx, result %d\n",
task->tk_pid, address, result);
+ nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
if (result >= 0) {
result = req->ra_res.count;
if (result < PAGE_SIZE) {
memset((char *) address + result, 0, PAGE_SIZE - result);
}
- nfs_refresh_inode(req->ra_inode, &req->ra_fattr);
SetPageUptodate(page);
succ++;
} else {
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/sunrpc/clnt.h>
-#include <linux/nfs_fs.h>
#include <linux/nfs.h>
+#include <linux/nfs2.h>
+#include <linux/nfs_fs.h>
#include <linux/pagemap.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
#include <linux/nfs_flushd.h>
#include <asm/uaccess.h>
#include <linux/smp_lock.h>
static __inline__ int
nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
{
+ if ((fattr->valid & NFS_ATTR_FATTR) && !(fattr->valid & NFS_ATTR_WCC)) {
+ fattr->pre_size = NFS_CACHE_ISIZE(inode);
+ fattr->pre_mtime = NFS_CACHE_MTIME(inode);
+ fattr->pre_ctime = NFS_CACHE_CTIME(inode);
+ fattr->valid |= NFS_ATTR_WCC;
+ }
return nfs_refresh_inode(inode, fattr);
}
result = nfs_proc_write(NFS_DSERVER(dentry), NFS_FH(dentry),
IS_SWAPFILE(inode), offset, wsize,
buffer, &fattr);
+ nfs_write_attributes(inode, &fattr);
if (result < 0) {
/* Must mark the page invalid after I/O error */
io_error:
kunmap(page);
- /* Note: we don't refresh if the call failed (fattr invalid) */
- if (refresh && result >= 0) {
- /* See comments in nfs_wback_result */
- /* N.B. I don't think this is right -- sync writes in order */
- if (fattr.size < inode->i_size)
- fattr.size = inode->i_size;
- if (fattr.mtime.seconds < inode->i_mtime)
- printk("nfs_writepage_sync: prior time??\n");
- /* Solaris 2.5 server seems to send garbled
- * fattrs occasionally */
- if (inode->i_ino == fattr.fileid) {
- /*
- * We expect the mtime value to change, and
- * don't want to invalidate the caches.
- */
- inode->i_mtime = fattr.mtime.seconds;
- nfs_refresh_inode(inode, &fattr);
- }
- else
- printk("nfs_writepage_sync: inode %ld, got %u?\n",
- inode->i_ino, fattr.fileid);
- }
unlock_kernel();
return written? written : result;
* Interruptible by signals only if mounted with intr flag.
*/
static int
-nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long start, unsigned int count)
+nfs_wait_on_requests(struct inode *inode, struct file *file, unsigned long idx_start, unsigned int npages)
{
struct list_head *p, *head;
- unsigned long idx_start, idx_end;
- unsigned int pages = 0;
+ unsigned long idx_end;
+ unsigned int res = 0;
int error;
- idx_start = start >> PAGE_CACHE_SHIFT;
- if (count == 0)
+ if (npages == 0)
idx_end = ~0;
- else {
- unsigned long idx_count = (count-1) >> PAGE_CACHE_SHIFT;
- idx_end = idx_start + idx_count;
- }
+ else
+ idx_end = idx_start + npages - 1;
+
spin_lock(&nfs_wreq_lock);
head = &inode->u.nfs_i.writeback;
p = head->next;
return error;
spin_lock(&nfs_wreq_lock);
p = head->next;
- pages++;
+ res++;
}
spin_unlock(&nfs_wreq_lock);
- return pages;
+ return res;
}
/*
#endif
static int
-nfs_scan_list(struct list_head *src, struct list_head *dst, struct file *file, unsigned long start, unsigned int count)
+nfs_scan_list(struct list_head *src, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
{
struct list_head *p;
struct nfs_page *req;
- unsigned long idx_start, idx_end;
- int pages;
+ unsigned long idx_end;
+ int res;
- pages = 0;
- idx_start = start >> PAGE_CACHE_SHIFT;
- if (count == 0)
+ res = 0;
+ if (npages == 0)
idx_end = ~0;
else
- idx_end = idx_start + ((count-1) >> PAGE_CACHE_SHIFT);
+ idx_end = idx_start + npages - 1;
p = src->next;
while (p != src) {
unsigned long pg_idx;
continue;
nfs_list_remove_request(req);
nfs_list_add_request(req, dst);
- pages++;
+ res++;
}
- return pages;
+ return res;
}
static int
-nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long start, unsigned int count)
+nfs_scan_dirty(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
{
- int pages;
+ int res;
spin_lock(&nfs_wreq_lock);
- pages = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, start, count);
- inode->u.nfs_i.ndirty -= pages;
+ res = nfs_scan_list(&inode->u.nfs_i.dirty, dst, file, idx_start, npages);
+ inode->u.nfs_i.ndirty -= res;
if ((inode->u.nfs_i.ndirty == 0) != list_empty(&inode->u.nfs_i.dirty))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n");
spin_unlock(&nfs_wreq_lock);
- return pages;
+ return res;
}
#ifdef CONFIG_NFS_V3
static int
-nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long start, unsigned int count)
+nfs_scan_commit(struct inode *inode, struct list_head *dst, struct file *file, unsigned long idx_start, unsigned int npages)
{
- int pages;
+ int res;
spin_lock(&nfs_wreq_lock);
- pages = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, start, count);
- inode->u.nfs_i.ncommit -= pages;
+ res = nfs_scan_list(&inode->u.nfs_i.commit, dst, file, idx_start, npages);
+ inode->u.nfs_i.ncommit -= res;
if ((inode->u.nfs_i.ncommit == 0) != list_empty(&inode->u.nfs_i.commit))
printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n");
spin_unlock(&nfs_wreq_lock);
- return pages;
+ return res;
}
#endif
dprintk("NFS: nfs_updatepage(%s/%s %d@%Ld)\n",
dentry->d_parent->d_name.name, dentry->d_name.name,
- count, page_offset(page) +offset);
+ count, (long long)(page_offset(page) +offset));
/*
* If wsize is smaller than page size, update and write
if (synchronous) {
int error;
- error = nfs_sync_file(inode, file, page_offset(page) + offset, count, FLUSH_SYNC|FLUSH_STABLE);
+ error = nfs_sync_file(inode, file, page_index(page), 1, FLUSH_SYNC|FLUSH_STABLE);
if (error < 0 || (error = file->f_error) < 0)
status = error;
file->f_error = 0;
nfs_release_request(req);
done:
dprintk("NFS: nfs_updatepage returns %d (isize %Ld)\n",
- status, inode->i_size);
+ status, (long long)inode->i_size);
if (status < 0)
clear_bit(PG_uptodate, &page->flags);
return status;
/* We tried a write call, but the server did not
* commit data to stable storage even though we
* requested it.
+ * Note: There is a known bug in Tru64 < 5.0 in which
+ * the server reports NFS_DATA_SYNC, but performs
+ * NFS_FILE_SYNC. We therefore implement this checking
+ * as a dprintk() in order to avoid filling syslog.
*/
static unsigned long complain = 0;
if (time_before(complain, jiffies)) {
- printk(KERN_NOTICE "NFS: faulty NFSv3 server %s:"
- " (committed = %d) != (stable = %d)\n",
- NFS_SERVER(inode)->hostname,
- resp->verf->committed, argp->stable);
+ dprintk("NFS: faulty NFSv3 server %s:"
+ " (committed = %d) != (stable = %d)\n",
+ NFS_SERVER(inode)->hostname,
+ resp->verf->committed, argp->stable);
complain = jiffies + 300 * HZ;
}
}
#endif
/* Update attributes as result of writeback. */
- if (task->tk_status >= 0)
- nfs_write_attributes(inode, resp->fattr);
+ nfs_write_attributes(inode, resp->fattr);
while (!list_empty(&data->pages)) {
req = nfs_list_entry(data->pages.next);
req->wb_file->f_dentry->d_parent->d_name.name,
req->wb_file->f_dentry->d_name.name,
req->wb_bytes,
- page_offset(req->wb_page) + req->wb_offset);
+ (long long)(page_offset(req->wb_page) + req->wb_offset));
if (task->tk_status < 0) {
req->wb_file->f_error = task->tk_status;
}
#endif
-int nfs_flush_file(struct inode *inode, struct file *file, unsigned long start,
- unsigned int count, int how)
+int nfs_flush_file(struct inode *inode, struct file *file, unsigned long idx_start,
+ unsigned int npages, int how)
{
LIST_HEAD(head);
- int pages,
+ int res,
error = 0;
- pages = nfs_scan_dirty(inode, &head, file, start, count);
- if (pages)
+ res = nfs_scan_dirty(inode, &head, file, idx_start, npages);
+ if (res)
error = nfs_flush_list(inode, &head, how);
if (error < 0)
return error;
- return pages;
+ return res;
}
int nfs_flush_timeout(struct inode *inode, int how)
}
#ifdef CONFIG_NFS_V3
-int nfs_commit_file(struct inode *inode, struct file *file, unsigned long start,
- unsigned int count, int how)
+int nfs_commit_file(struct inode *inode, struct file *file, unsigned long idx_start,
+ unsigned int npages, int how)
{
LIST_HEAD(head);
- int pages,
+ int res,
error = 0;
- pages = nfs_scan_commit(inode, &head, file, start, count);
- if (pages)
+ res = nfs_scan_commit(inode, &head, file, idx_start, npages);
+ if (res)
error = nfs_commit_list(&head, how);
if (error < 0)
return error;
- return pages;
+ return res;
}
int nfs_commit_timeout(struct inode *inode, int how)
}
#endif
-int nfs_sync_file(struct inode *inode, struct file *file, unsigned long start,
- unsigned int count, int how)
+int nfs_sync_file(struct inode *inode, struct file *file, unsigned long idx_start,
+ unsigned int npages, int how)
{
int error,
wait;
do {
error = 0;
if (wait)
- error = nfs_wait_on_requests(inode, file, start, count);
+ error = nfs_wait_on_requests(inode, file, idx_start, npages);
if (error == 0)
- error = nfs_flush_file(inode, file, start, count, how);
+ error = nfs_flush_file(inode, file, idx_start, npages, how);
#ifdef CONFIG_NFS_V3
if (error == 0)
- error = nfs_commit_file(inode, file, start, count, how);
+ error = nfs_commit_file(inode, file, idx_start, npages, how);
#endif
} while (error > 0);
return error;
f->f_dentry = dentry;
f->f_pos = 0;
f->f_reada = 0;
- f->f_op = NULL;
- if (inode->i_op)
- f->f_op = inode->i_fop;
+ f->f_op = inode->i_fop;
if (inode->i_sb)
file_move(f, &inode->i_sb->s_files);
if (f->f_op && f->f_op->open) {
bool ' Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
bool ' Unixware slices support' CONFIG_UNIXWARE_DISKLABEL
fi
- bool 'SGI partition support' CONFIG_SGI_PARTITION
- bool 'Sun partition tables support' CONFIG_SUN_PARTITION
+ bool ' SGI partition support' CONFIG_SGI_PARTITION
+ bool ' Sun partition tables support' CONFIG_SUN_PARTITION
else
if [ "$ARCH" = "alpha" ]; then
define_bool CONFIG_OSF_PARTITION y
if [ "$CONFIG_AMIGA" = "y" ]; then
define_bool CONFIG_AMIGA_PARTITION y
fi
+ if [ "$CONFIG_MAC" = "y" ]; then
+ define_bool CONFIG_MAC_PARTITION y
+ fi
if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
define_bool CONFIG_ACORN_PARTITION y
define_bool CONFIG_ACORN_PARTITION_ADFS y
{
int retval = -EINVAL;
- lock_kernel();
switch (option) {
case 1:
retval = fs_index((const char *) arg1);
retval = fs_maxindex();
break;
}
- unlock_kernel();
return retval;
}
#endif
-#define arch_do_idle() do { } while (0)
+extern __inline__ void arch_idle(void)
+{
+ while (!current->need_resched && !hlt_counter);
+}
+
#define arch_power_off() do { } while (0)
extern __inline__ void arch_reset(char mode)
#include <asm/iomd.h>
-#define arch_do_idle() \
- outb(0, IOMD_SUSMODE)
+extern __inline__ void arch_idle(void)
+{
+ while (!current->need_resched && !hlt_counter)
+ outb(0, IOMD_SUSMODE);
+}
#define arch_reset(mode) \
do { \
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-cl7500/vmalloc.h
+ */
+
+/*
+ * Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts. That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+#define VMALLOC_END (PAGE_OFFSET + 0x10000000)
* RAM definitions
*/
#define FLUSH_BASE_PHYS 0x40000000
+#define UNCACHEABLE_ADDR 0xf3000000
#else /* __ASSEMBLY__ */
#ifndef __ASM_ARCH_SYSTEM_H
#define __ASM_ARCH_SYSTEM_H
-#define arch_do_idle() cpu_do_idle()
+/*
+ * This machine must never stop it MCLK. However, if we are
+ * idle for a long time, slow the processor clock to MCLK.
+ */
+extern __inline__ void arch_idle(void)
+{
+ unsigned long start_idle;
+
+ start_idle = jiffies;
+
+ do {
+ if (current->need_resched || hlt_counter)
+ goto slow_out;
+ } while (time_before(start_idle, jiffies + HZ/3));
+
+ cpu_do_idle(IDLE_CLOCK_SLOW);
+
+ while (!current->need_resched && !hlt_counter) {
+ /* do nothing slowly */
+ }
+
+ cpu_do_idle(IDLE_CLOCK_FAST);
+slow_out:
+}
+
#define arch_power_off() do { } while (0)
#define arch_reset(mode) cpu_reset(0x80000000)
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-ebsa110/vmalloc.h
+ */
+
+/*
+ * Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts. That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+#define VMALLOC_END (PAGE_OFFSET + 0x10000000)
#define FLASH_BASE 0xf8000000
#define PCIMEM_SIZE 0x01000000
-#define PCIMEM_BASE 0xe0000000
+#define PCIMEM_BASE 0xf0000000
#elif defined(CONFIG_ARCH_CO285)
#define PARAMS_BASE (PAGE_OFFSET + PARAMS_OFFSET)
#define FLUSH_BASE_PHYS 0x50000000
+#define UNCACHEABLE_ADDR (ARMCSR_BASE + 0x108)
/* PIC irq control */
#include <asm/hardware.h>
#include <asm/leds.h>
-#define arch_do_idle() cpu_do_idle()
+extern __inline__ void arch_idle(void)
+{
+ unsigned long start_idle;
+
+ start_idle = jiffies;
+
+ do {
+ if (current->need_resched || hlt_counter)
+ goto slow_out;
+ cpu_do_idle(IDLE_WAIT_FAST);
+ } while (time_before(start_idle, jiffies + HZ/3));
+
+ cpu_do_idle(IDLE_CLOCK_SLOW);
+
+ while (!current->need_resched && !hlt_counter) {
+ cpu_do_idle(IDLE_WAIT_SLOW);
+ }
+
+ cpu_do_idle(IDLE_CLOCK_FAST);
+slow_out:
+}
+
#define arch_power_off() do { } while (0)
extern __inline__ void arch_reset(char mode)
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-ebsa285/vmalloc.h
+ */
+
+/*
+ * Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts. That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+#define VMALLOC_END (PAGE_OFFSET + 0x20000000)
#ifndef __ASM_ARCH_SYSTEM_H
#define __ASM_ARCH_SYSTEM_H
-#define arch_do_idle() cpu_do_idle()
+extern __inline__ void arch_idle(void)
+{
+ while (!current->need_resched && !hlt_counter)
+ cpu_do_idle(IDLE_WAIT_SLOW);
+}
+
#define arch_reset(mode) do { } while (0)
#define arch_power_off() do { } while (0)
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-nexuspci/vmalloc.h
+ */
+
+/*
+ * Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts. That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+#define VMALLOC_END (PAGE_OFFSET + 0x20000000)
#define SCREEN1_BASE 0xd0000000
#define FLUSH_BASE 0xdf000000
+#define UNCACHEABLE_ADDR 0xdf010000
#ifndef __ASSEMBLY__
#include <asm/iomd.h>
#include <asm/io.h>
-#define arch_do_idle() cpu_do_idle()
+extern __inline__ void arch_idle(void)
+{
+ unsigned long start_idle;
+
+ start_idle = jiffies;
+
+ do {
+ if (current->need_resched || hlt_counter)
+ goto slow_out;
+ cpu_do_idle(IDLE_WAIT_FAST);
+ } while (time_before(start_idle, jiffies + HZ/3));
+
+ cpu_do_idle(IDLE_CLOCK_SLOW);
+
+ while (!current->need_resched && !hlt_counter) {
+ cpu_do_idle(IDLE_WAIT_SLOW);
+ }
+
+ cpu_do_idle(IDLE_CLOCK_FAST);
+slow_out:
+}
+
#define arch_power_off() do { } while (0)
extern __inline__ void arch_reset(char mode)
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-rpc/vmalloc.h
+ */
+
+/*
+ * Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts. That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+#define VMALLOC_END (PAGE_OFFSET + 0x10000000)
#define FLUSH_BASE_PHYS 0xe0000000 /* SA1100 zero bank */
#define FLUSH_BASE 0xdf000000
#define FLUSH_BASE_MINICACHE 0xdf800000
+#define UNCACHEABLE_ADDR 0xfa050000
/*
* PCMCIA IO is mapped to 0xe0000000. We are likely to use in*()/out*()
/*
- * linux/include/asm-arm/arch-sa1100/keyboard.h
- *
- * Keyboard driver definitions for SA1100 architecture
- *
- * This really has to be cleaned up somehow...
- *
+ * linux/include/asm-arm/arch-sa1100/keyboard.h
+ * Created 16 Dec 1999 by Nicolas Pitre <nico@cam.org>
+ * This file contains the SA1100 architecture specific keyboard definitions
*/
-#define KEYBOARD_IRQ
+#ifndef _SA1100_KEYBOARD_H
+#define _SA1100_KEYBOARD_H
-#define NR_SCANCODES 128
+#include <linux/config.h>
-#define kbd_setkeycode(sc,kc) (-EINVAL)
-#define kbd_getkeycode(sc) (-EINVAL)
-#define kbd_pretranslate(sc,kc) 1
-#define kbd_translate(sc, kcp, raw) kbd_drv_translate(sc, kcp, raw)
-#define kbd_init_hw() kbd_drv_init()
-#define kbd_unexpected_up
-#define kbd_leds(leds)
+#ifdef CONFIG_SA1100_BRUTUS
-#define kbd_sysrq_xlate
-#define kbd_disable_irq()
-#define kbd_enable_irq()
+extern int Brutus_kbd_translate(unsigned char scancode, unsigned char *keycode,
+ char raw_mode);
+extern void Brutus_kbd_leds(unsigned char leds);
+extern void Brutus_kbd_init_hw(void);
+extern void Brutus_kbd_enable_irq(void);
+extern void Brutus_kbd_disable_irq(void);
+extern unsigned char Brutus_kbd_sysrq_xlate[128];
+
+#define kbd_setkeycode(x...) (-ENOSYS)
+#define kbd_getkeycode(x...) (-ENOSYS)
+#define kbd_translate Brutus_kbd_translate
+#define kbd_unexpected_up(x...) (1)
+#define kbd_leds Brutus_kbd_leds
+#define kbd_init_hw Brutus_kbd_init_hw
+#define kbd_enable_irq Brutus_kbd_enable_irq
+#define kbd_disable_irq Brutus_kbd_disable_irq
+#define kbd_sysrq_xlate Brutus_kbd_sysrq_xlate
+
+#define SYSRQ_KEY 0x54
+
+#else
+
+/* dummy i.e. no real keyboard */
+#define kbd_setkeycode(x...) (-ENOSYS)
+#define kbd_getkeycode(x...) (-ENOSYS)
+#define kbd_translate(x...) (0)
+#define kbd_unexpected_up(x...) (1)
+#define kbd_leds(x...) (0)
+#define kbd_init_hw(x...) (0)
+#define kbd_enable_irq(x...) (0)
+#define kbd_disable_irq(x...) (0)
+
+#endif
+
+
+#endif /* _SA1100_KEYBOARD_H */
-#define SYSRQ_KEY 0x54
*/
#include <linux/config.h>
-#ifdef CONFIG_SA1100_VICTOR
-
-#define arch_reset( x ) { \
- /* switch off power supply */ \
- mdelay(2000); \
- GPCR = GPIO_GPIO23; \
- while(1); \
+extern __inline__ void arch_idle(void)
+{
+ while (!current->need_resched && !hlt_counter) {
+ cpu_do_idle(IDLE_CLOCK_SLOW);
+ cpu_do_idle(IDLE_WAIT_FAST);
+ cpu_do_idle(IDLE_CLOCK_FAST);
}
+}
-#else
-
-#define arch_reset(x) cpu_reset(0)
+#ifdef CONFIG_SA1100_VICTOR
-#endif
+extern inline void arch_power_off(void)
+{
+ /* switch off power supply */
+ mdelay(2000);
+ GPCR = GPIO_GPIO23;
+ while(1);
+}
+/* power off unconditionally */
+#define arch_reset(x) arch_power_off()
-#if 0
-#define arch_do_idle() cpu_do_idle()
#else
-/* Enter SA1100 idle mode (see data sheet sec 9.5).
- * It seems that the wait-on-interrupt just hang the CPU forever if it's
- * on the end of a cache line. Workaround: we force an explicit alignment
- * before it.
- */
-#define arch_do_idle() \
- do { \
- __asm__ __volatile__( \
-" mcr p15, 0, %0, c15, c2, 2 @ Disable clock switching \n" \
-" ldr %0, [%0] @ Must perform a non-cached access \n" \
-" b 1f @ Seems we must align the next \n" \
-" .align 5 @ instruction on a cache line \n" \
-"1: mcr p15, 0, %0, c15, c8, 2 @ Wait for interrupts \n" \
-" mov r0, r0 @ insert NOP to ensure SA1100 re-awakes\n" \
-" mcr p15, 0, %0, c15, c1, 2 @ Reenable clock switching \n" \
- : : "r" (&ICIP) : "cc" ); \
- } while (0)
-#endif
+
+extern inline void arch_reset(char mode)
+{
+ if (mode == 's') {
+ /* Jump into ROM at address 0 */
+ cpu_reset(0);
+ } else {
+ /* Activate SA1100 watchdog and wait for the trigger... */
+ OSMR3 = OSCR + 3686400/2; /* in 1/2 sec */
+ OWER |= OWER_WME;
+ OIER |= OIER_E3;
+ }
+}
#define arch_power_off() do { } while (0)
+#endif
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-sa1100/vmalloc.h
+ */
+
+/*
+ * Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts. That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+#define VMALLOC_END (PAGE_OFFSET + 0x10000000)
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/dma.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ */
+#ifndef __ASM_ARCH_DMA_H
+#define __ASM_ARCH_DMA_H
+
+/* Use only the lowest 4MB, nothing else works.
+ * The rest is not DMAable. See dev / .properties
+ * in OpenFirmware.
+ */
+#define MAX_DMA_ADDRESS 0xC0400000
+#define MAX_DMA_CHANNELS 8
+#define DMA_ISA_CASCADE 4
+
+#endif /* _ASM_ARCH_DMA_H */
+
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/hardware.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ *
+ * derived from:
+ * linux/include/asm-arm/arch-ebsa110/hardware.h
+ * Copyright (C) 1996-1999 Russell King.
+ */
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H
+
+#ifndef __ASSEMBLER__
+
+/*
+ * Mapping areas
+ */
+#define IO_BASE 0xe0000000
+
+/*
+ * RAM definitions
+ */
+#define FLUSH_BASE_PHYS 0x60000000
+
+#else
+
+#define IO_BASE 0
+
+#endif
+
+#define IO_SIZE 0x10000000
+#define IO_START 0x40000000
+
+#define FLUSH_BASE 0xdf000000
+#define PCIO_BASE 0xe0000000
+
+
+/* defines for the Framebuffer */
+#define FB_BASE 0xd0000000
+#define FB_START 0x06000000
+#define FB_SIZE 0x00200000
+
+/* Registers for Framebuffer */
+#define FBREG_BASE (FB_BASE + FB_SIZE)
+#define FBREG_START 0x06800000
+#define FBREG_SIZE 0x000c0000
+
+#endif
+
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/ide.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ *
+ * derived from:
+ * linux/include/asm-arm/arch-ebsa285/ide.h
+ * Copyright (c) 1998 Russell King
+ */
+
+#include <asm/irq.h>
+
+/*
+ * Set up a hw structure for a specified data port, control port and IRQ.
+ * This should follow whatever the default interface uses.
+ */
+static __inline__ void
+ide_init_hwif_ports(hw_regs_t *hw, int data_port, int ctrl_port, int irq)
+{
+ ide_ioreg_t reg = (ide_ioreg_t) data_port;
+ int i;
+
+ memset(hw, 0, sizeof(*hw));
+
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+ hw->io_ports[i] = reg;
+ reg += 1;
+ }
+ hw->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) ctrl_port;
+ hw->irq = irq;
+}
+
+/*
+ * This registers the standard ports for this architecture with the IDE
+ * driver.
+ */
+static __inline__ void
+ide_init_default_hwifs(void)
+{
+ hw_regs_t hw;
+
+ ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, 14);
+ ide_register_hw(&hw, NULL);
+}
+
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/io.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ *
+ * derived from:
+ * linux/include/asm-arm/arch-ebsa110/io.h
+ * Copyright (C) 1997,1998 Russell King
+ */
+
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffffffff
+
+/*
+ * We use two different types of addressing - PC style addresses, and ARM
+ * addresses. PC style accesses the PC hardware with the normal PC IO
+ * addresses, eg 0x3f8 for serial#1. ARM addresses are 0x80000000+
+ * and are translated to the start of IO.
+ */
+#define __PORT_PCIO(x) (!((x) & 0x80000000))
+
+/*
+ * Dynamic IO functions - let the compiler
+ * optimize the expressions
+ */
+#define DECLARE_DYN_OUT(fnsuffix,instr) \
+extern __inline__ void __out##fnsuffix (unsigned int value, unsigned int port) \
+{ \
+ unsigned long temp; \
+ __asm__ __volatile__( \
+ "tst %2, #0x80000000\n\t" \
+ "mov %0, %4\n\t" \
+ "addeq %0, %0, %3\n\t" \
+ "str" ##instr## " %1, [%0, %2] @ out"###fnsuffix \
+ : "=&r" (temp) \
+ : "r" (value), "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE) \
+ : "cc"); \
+}
+
+#define DECLARE_DYN_IN(sz,fnsuffix,instr) \
+extern __inline__ unsigned sz __in##fnsuffix (unsigned int port) \
+{ \
+ unsigned long temp, value; \
+ __asm__ __volatile__( \
+ "tst %2, #0x80000000\n\t" \
+ "mov %0, %4\n\t" \
+ "addeq %0, %0, %3\n\t" \
+ "ldr" ##instr## " %1, [%0, %2] @ in"###fnsuffix \
+ : "=&r" (temp), "=r" (value) \
+ : "r" (port), "Ir" (PCIO_BASE - IO_BASE), "Ir" (IO_BASE) \
+ : "cc"); \
+ return (unsigned sz)value; \
+}
+
+extern __inline__ unsigned int __ioaddr (unsigned int port) \
+{ \
+ if (__PORT_PCIO(port)) \
+ return (unsigned int)(PCIO_BASE + (port)); \
+ else \
+ return (unsigned int)(IO_BASE + (port)); \
+}
+
+#define DECLARE_IO(sz,fnsuffix,instr) \
+ DECLARE_DYN_OUT(fnsuffix,instr) \
+ DECLARE_DYN_IN(sz,fnsuffix,instr)
+
+DECLARE_IO(char,b,"b")
+DECLARE_IO(short,w,"h")
+DECLARE_IO(long,l,"")
+
+#undef DECLARE_IO
+#undef DECLARE_DYN_OUT
+#undef DECLARE_DYN_IN
+
+/*
+ * Constant address IO functions
+ *
+ * These have to be macros for the 'J' constraint to work -
+ * +/-4096 immediate operand.
+ */
+#define __outbc(value,port) \
+({ \
+ if (__PORT_PCIO((port))) \
+ __asm__ __volatile__( \
+ "strb %0, [%1, %2] @ outbc" \
+ : : "r" (value), "r" (PCIO_BASE), "Jr" (port)); \
+ else \
+ __asm__ __volatile__( \
+ "strb %0, [%1, %2] @ outbc" \
+ : : "r" (value), "r" (IO_BASE), "r" (port)); \
+})
+
+#define __inbc(port) \
+({ \
+ unsigned char result; \
+ if (__PORT_PCIO((port))) \
+ __asm__ __volatile__( \
+ "ldrb %0, [%1, %2] @ inbc" \
+ : "=r" (result) : "r" (PCIO_BASE), "Jr" (port)); \
+ else \
+ __asm__ __volatile__( \
+ "ldrb %0, [%1, %2] @ inbc" \
+ : "=r" (result) : "r" (IO_BASE), "r" (port)); \
+ result; \
+})
+
+#define __outwc(value,port) \
+({ \
+ unsigned long v = value; \
+ if (__PORT_PCIO((port))) \
+ __asm__ __volatile__( \
+ "strh %0, [%1, %2] @ outwc" \
+ : : "r" (v|v<<16), "r" (PCIO_BASE), "Jr" (port)); \
+ else \
+ __asm__ __volatile__( \
+ "strh %0, [%1, %2] @ outwc" \
+ : : "r" (v|v<<16), "r" (IO_BASE), "r" (port)); \
+})
+
+#define __inwc(port) \
+({ \
+ unsigned short result; \
+ if (__PORT_PCIO((port))) \
+ __asm__ __volatile__( \
+ "ldrh %0, [%1, %2] @ inwc" \
+ : "=r" (result) : "r" (PCIO_BASE), "Jr" (port)); \
+ else \
+ __asm__ __volatile__( \
+ "ldrh %0, [%1, %2] @ inwc" \
+ : "=r" (result) : "r" (IO_BASE), "r" (port)); \
+ result & 0xffff; \
+})
+
+#define __outlc(value,port) \
+({ \
+ unsigned long v = value; \
+ if (__PORT_PCIO((port))) \
+ __asm__ __volatile__( \
+ "str %0, [%1, %2] @ outlc" \
+ : : "r" (v), "r" (PCIO_BASE), "Jr" (port)); \
+ else \
+ __asm__ __volatile__( \
+ "str %0, [%1, %2] @ outlc" \
+ : : "r" (v), "r" (IO_BASE), "r" (port)); \
+})
+
+#define __inlc(port) \
+({ \
+ unsigned long result; \
+ if (__PORT_PCIO((port))) \
+ __asm__ __volatile__( \
+ "ldr %0, [%1, %2] @ inlc" \
+ : "=r" (result) : "r" (PCIO_BASE), "Jr" (port)); \
+ else \
+ __asm__ __volatile__( \
+ "ldr %0, [%1, %2] @ inlc" \
+ : "=r" (result) : "r" (IO_BASE), "r" (port)); \
+ result; \
+})
+
+#define __ioaddrc(port) \
+({ \
+ unsigned long addr; \
+ if (__PORT_PCIO((port))) \
+ addr = PCIO_BASE + (port); \
+ else \
+ addr = IO_BASE + (port); \
+ addr; \
+})
+
+#define __mem_pci(addr) addr
+
+#define inb(p) (__builtin_constant_p((p)) ? __inbc(p) : __inb(p))
+#define inw(p) (__builtin_constant_p((p)) ? __inwc(p) : __inw(p))
+#define inl(p) (__builtin_constant_p((p)) ? __inlc(p) : __inl(p))
+#define outb(v,p) (__builtin_constant_p((p)) ? __outbc(v,p) : __outb(v,p))
+#define outw(v,p) (__builtin_constant_p((p)) ? __outwc(v,p) : __outw(v,p))
+#define outl(v,p) (__builtin_constant_p((p)) ? __outlc(v,p) : __outl(v,p))
+
+#define __arch_getb(addr) (*(volatile unsigned char *)(addr))
+#define __arch_getw(addr) (*(volatile unsigned short *)(addr))
+#define __arch_getl(addr) (*(volatile unsigned long *)(addr))
+
+#define __arch_putb(b,addr) (*(volatile unsigned char *)(addr) = (b))
+#define __arch_putw(b,addr) (*(volatile unsigned short *)(addr) = (b))
+#define __arch_putl(b,addr) (*(volatile unsigned long *)(addr) = (b))
+
+/*
+ * Translated address IO functions
+ *
+ * IO address has already been translated to a virtual address
+ */
+#define outb_t(v,p) \
+ (*(volatile unsigned char *)(p) = (v))
+
+#define inb_t(p) \
+ (*(volatile unsigned char *)(p))
+
+#define outl_t(v,p) \
+ (*(volatile unsigned long *)(p) = (v))
+
+#define inl_t(p) \
+ (*(volatile unsigned long *)(p))
+
+#endif
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/irq.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ *
+ * derived from linux/arch/ppc/kernel/i8259.c and:
+ * include/asm-arm/arch-ebsa110/irq.h
+ * Copyright (C) 1996-1998 Russell King
+ */
+
+#define fixup_irq(x) (x)
+
+/*
+ * 8259A PIC functions to handle ISA devices:
+ */
+
+/*
+ * This contains the irq mask for both 8259A irq controllers,
+ * Let through the cascade-interrupt no. 2 (ff-(1<<2)==fb)
+ */
+static unsigned char cached_irq_mask[2] = { 0xfb, 0xff };
+
+/*
+ * These have to be protected by the irq controller spinlock
+ * before being called.
+ */
+static void shark_disable_8259A_irq(unsigned int irq)
+{
+ unsigned int mask;
+ if (irq<8) {
+ mask = 1 << irq;
+ cached_irq_mask[0] |= mask;
+ } else {
+ mask = 1 << (irq-8);
+ cached_irq_mask[1] |= mask;
+ }
+ outb(cached_irq_mask[1],0xA1);
+ outb(cached_irq_mask[0],0x21);
+}
+
+static void shark_enable_8259A_irq(unsigned int irq)
+{
+ unsigned int mask;
+ if (irq<8) {
+ mask = ~(1 << irq);
+ cached_irq_mask[0] &= mask;
+ } else {
+ mask = ~(1 << (irq-8));
+ cached_irq_mask[1] &= mask;
+ }
+ outb(cached_irq_mask[1],0xA1);
+ outb(cached_irq_mask[0],0x21);
+}
+
+/*
+ * Careful! The 8259A is a fragile beast, it pretty
+ * much _has_ to be done exactly like this (mask it
+ * first, _then_ send the EOI, and the order of EOI
+ * to the two 8259s is important!
+ */
+static void shark_mask_and_ack_8259A_irq(unsigned int irq)
+{
+ if (irq & 8) {
+ cached_irq_mask[1] |= 1 << (irq-8);
+ inb(0xA1); /* DUMMY */
+ outb(cached_irq_mask[1],0xA1);
+ } else {
+ cached_irq_mask[0] |= 1 << irq;
+ outb(cached_irq_mask[0],0x21);
+ }
+}
+
+static void bogus_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk("Got interrupt %i!\n",irq);
+}
+
+static struct irqaction cascade;
+
+static __inline__ void irq_init_irq(void)
+{
+ int irq;
+
+ for (irq = 0; irq < NR_IRQS; irq++) {
+ irq_desc[irq].valid = 1;
+ irq_desc[irq].probe_ok = 1;
+ irq_desc[irq].mask_ack = shark_mask_and_ack_8259A_irq;
+ irq_desc[irq].mask = shark_disable_8259A_irq;
+ irq_desc[irq].unmask = shark_enable_8259A_irq;
+ }
+
+ /* The PICs are initialized to level triggered and auto eoi!
+ * If they are set to edge triggered they lose some IRQs,
+ * if they are set to manual eoi they get locked up after
+ * a short time
+ */
+
+ /* init master interrupt controller */
+ outb(0x19, 0x20); /* Start init sequence, level triggered */
+ outb(0x00, 0x21); /* Vector base */
+ outb(0x04, 0x21); /* Cascade (slave) on IRQ2 */
+ outb(0x03, 0x21); /* Select 8086 mode , auto eoi*/
+ outb(0x0A, 0x20);
+ /* init slave interrupt controller */
+ outb(0x19, 0xA0); /* Start init sequence, level triggered */
+ outb(0x08, 0xA1); /* Vector base */
+ outb(0x02, 0xA1); /* Cascade (slave) on IRQ2 */
+ outb(0x03, 0xA1); /* Select 8086 mode, auto eoi */
+ outb(0x0A, 0xA0);
+ outb(cached_irq_mask[1],0xA1);
+ outb(cached_irq_mask[0],0x21);
+ //request_region(0x20,0x2,"pic1");
+ //request_region(0xA0,0x2,"pic2");
+
+ cascade.handler = bogus_int;
+ cascade.flags = 0;
+ cascade.mask = 0;
+ cascade.name = "cascade";
+ cascade.next = NULL;
+ cascade.dev_id = NULL;
+ setup_arm_irq(2,&cascade);
+
+}
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/irqs.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ */
+
+#define NR_IRQS 16
+
+#define IRQ_ISA_KEYBOARD 1
+#define AUX_IRQ 12
+#define IRQ_HARDDISK 14
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-ebsa285/keyboard.h
+ *
+ * Keyboard driver definitions for EBSA285 architecture
+ *
+ * (C) 1998 Russell King
+ * (C) 1998 Phil Blundell
+ */
+#include <linux/ioport.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+extern int have_isa_bridge;
+
+extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode);
+extern int pckbd_getkeycode(unsigned int scancode);
+extern int pckbd_translate(unsigned char scancode, unsigned char *keycode,
+ char raw_mode);
+extern char pckbd_unexpected_up(unsigned char keycode);
+extern void pckbd_leds(unsigned char leds);
+extern void pckbd_init_hw(void);
+extern unsigned char pckbd_sysrq_xlate[128];
+
+#define KEYBOARD_IRQ IRQ_ISA_KEYBOARD
+
+#define NR_SCANCODES 128
+
+#define kbd_setkeycode(sc,kc) \
+ ({ \
+ int __ret; \
+ if (have_isa_bridge) \
+ __ret = pckbd_setkeycode(sc,kc);\
+ else \
+ __ret = -EINVAL; \
+ __ret; \
+ })
+
+#define kbd_getkeycode(sc) \
+ ({ \
+ int __ret; \
+ if (have_isa_bridge) \
+ __ret = pckbd_getkeycode(sc); \
+ else \
+ __ret = -EINVAL; \
+ __ret; \
+ })
+
+#define kbd_translate(sc, kcp, rm) \
+ ({ \
+ pckbd_translate(sc, kcp, rm); \
+ })
+
+#define kbd_unexpected_up pckbd_unexpected_up
+
+#define kbd_leds(leds) \
+ do { \
+ if (have_isa_bridge) \
+ pckbd_leds(leds); \
+ } while (0)
+
+#define kbd_init_hw() \
+ do { \
+ if (have_isa_bridge) \
+ pckbd_init_hw(); \
+ } while (0)
+
+#define kbd_sysrq_xlate pckbd_sysrq_xlate
+
+#define kbd_disable_irq()
+#define kbd_enable_irq()
+
+#define SYSRQ_KEY 0x54
+
+/* resource allocation */
+#define kbd_request_region() request_region(0x60, 16, "keyboard")
+#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, 0, \
+ "keyboard", NULL)
+
+/* How to access the keyboard macros on this platform. */
+#define kbd_read_input() inb(KBD_DATA_REG)
+#define kbd_read_status() inb(KBD_STATUS_REG)
+#define kbd_write_output(val) outb(val, KBD_DATA_REG)
+#define kbd_write_command(val) outb(val, KBD_CNTL_REG)
+
+/* Some stoneage hardware needs delays after some operations. */
+#define kbd_pause() do { } while(0)
+
+/*
+ * Machine specific bits for the PS/2 driver
+ */
+#define aux_request_irq(hand, dev_id) \
+ request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id)
+
+#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id)
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/memory.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ *
+ * derived from:
+ * linux/include/asm-arm/arch-ebsa110/memory.h
+ * Copyright (c) 1996-1999 Russell King.
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Task size: 3GB
+ */
+#define TASK_SIZE (0xc0000000UL)
+#define TASK_SIZE_26 (0x04000000UL)
+
+/*
+ * Page offset: = 3GB
+ */
+#define PAGE_OFFSET (0xC0000000UL)
+#define PHYS_OFFSET (0x08000000UL)
+
+#define __virt_to_phys__is_a_macro
+#define __phys_to_virt__is_a_macro
+#define __virt_to_phys(vpage) (vpage - PAGE_OFFSET + PHYS_OFFSET)
+#define __phys_to_virt(ppage) (ppage - PHYS_OFFSET + PAGE_OFFSET)
+
+#define __virt_to_bus__is_a_macro
+#define __virt_to_bus(x) __virt_to_phys(x)
+#define __bus_to_virt__is_a_macro
+#define __bus_to_virt(x) __phys_to_virt(x)
+
+#endif
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/param.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ */
+
+/* This must be a power of 2 because the RTC
+ * can't use anything else.
+ */
+#define HZ 64
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-ebsa110/processor.h
+ *
+ * Copyright (C) 1996-1999 Russell King
+ *
+ * Changelog:
+ * 21-Mar-1999 RMK Added asm/arch/memory.h
+ */
+
+#ifndef __ASM_ARCH_PROCESSOR_H
+#define __ASM_ARCH_PROCESSOR_H
+
+#include <asm/arch/memory.h>
+
+/*
+ * Bus types
+ */
+#define EISA_bus 0
+#define EISA_bus__is_a_macro /* for versions in ksyms.c */
+#define MCA_bus 0
+#define MCA_bus__is_a_macro /* for versions in ksyms.c */
+
+/* This decides where the kernel will search for a free chunk of vm
+ * space during mmap's.
+ */
+#define TASK_UNMAPPED_BASE (TASK_SIZE / 3)
+
+#endif
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-ebsa110/serial.h
+ *
+ * Copyright (c) 1996,1997,1998 Russell King.
+ *
+ * Changelog:
+ * 15-10-1996 RMK Created
+ */
+#ifndef __ASM_ARCH_SERIAL_H
+#define __ASM_ARCH_SERIAL_H
+
+/*
+ * This assumes you have a 1.8432 MHz clock for your UART.
+ *
+ * It'd be nice if someone built a serial card with a 24.576 MHz
+ * clock, since the 16550A is capable of handling a top speed of 1.5
+ * megabits/second; but this requires the faster clock.
+ */
+#define BASE_BAUD (1843200 / 16)
+
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+#define RS_TABLE_SIZE 2
+
+ /* UART CLK PORT IRQ FLAGS */
+#define STD_SERIAL_PORT_DEFNS \
+ { 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS }, /* ttyS0 */ \
+ { 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS }, /* ttyS1 */
+
+#define EXTRA_SERIAL_PORT_DEFNS
+
+#endif
+
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-ebsa110/system.h
+ *
+ * Copyright (c) 1996-1998 Russell King.
+ */
+#ifndef __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H
+
+extern __inline__ void arch_reset(char mode)
+{
+ /*
+ * loop endlessly
+ */
+ cli();
+}
+
+#define arch_power_off() do { } while (0)
+#define arch_do_idle() do {} while (0)
+/*cpu_do_idle()*/
+
+#endif
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/time.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ *
+ * Uses the real time clock because you can't run
+ * the timer with level triggered interrupts and
+ * you can't run the shark with edge triggered
+ * inetrrupts (loses ints and hangs).
+ *
+ * derived from linux/drivers/char/rtc.c and:
+ * linux/include/asm-arm/arch-ebsa110/time.h
+ * Copyright (c) 1996,1997,1998 Russell King.
+ */
+
+#include <linux/config.h>
+#include <asm/leds.h>
+#include <linux/mc146818rtc.h>
+
+#define IRQ_TIMER 8
+
+extern void get_rtc_time(struct rtc_time *rtc_tm);
+extern void set_rtc_irq_bit(unsigned char bit);
+extern unsigned long epoch;
+
+static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+
+ CMOS_READ(RTC_INTR_FLAGS);
+
+#ifdef CONFIG_LEDS
+ {
+ static int count = 50;
+ if (--count == 0) {
+ count = 50;
+ leds_event(led_timer);
+ }
+ }
+#endif
+
+ {
+#ifdef DIVISOR
+ static unsigned int divisor;
+
+ if (divisor-- == 0) {
+ divisor = DIVISOR - 1;
+#else
+ {
+#endif
+ do_timer(regs);
+ }
+ }
+}
+
+static struct irqaction timerirq = {
+ timer_interrupt,
+ SA_INTERRUPT,
+ 0,
+ "timer",
+ NULL,
+ NULL
+};
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ */
+extern __inline__ void setup_timer(void)
+{
+ struct rtc_time r_time;
+ unsigned long flags;
+ int tmp = 0;
+ unsigned char val;
+
+ /*
+ * Set the clock to 128 Hz, we already have a valid
+ * vector now:
+ */
+
+ while (HZ > (1<<tmp))
+ tmp++;
+
+ /*
+ * Check that the input was really a power of 2.
+ */
+ if (HZ != (1<<tmp))
+ panic("Please set HZ to a power of 2!");
+
+ save_flags(flags);
+ cli();
+ val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
+ val |= (16 - tmp);
+ CMOS_WRITE(val, RTC_FREQ_SELECT);
+ restore_flags(flags);
+ set_rtc_irq_bit(RTC_PIE);
+
+ get_rtc_time(&r_time);
+ xtime.tv_sec = mktime(r_time.tm_year+epoch, r_time.tm_mon+1, r_time.tm_mday,
+ r_time.tm_hour, r_time.tm_min, r_time.tm_sec);
+
+ setup_arm_irq(IRQ_TIMER, &timerirq);
+}
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-shark/timex.h
+ *
+ * by Alexander.Schulz@stud.uni-karlsruhe.de
+ */
--- /dev/null
+/*
+ * linux/include/asm-arm/arch-ebsa110/uncompress.h
+ *
+ * Copyright (C) 1996,1997,1998 Russell King
+ */
+
+/*
+ * This does not append a newline
+ */
+static void puts(const char *s)
+{
+ __asm__ __volatile__("
+ ldrb %0, [%2], #1
+ teq %0, #0
+ beq 3f
+1: strb %0, [%3]
+2: ldrb %1, [%3, #0x14]
+ and %1, %1, #0x60
+ teq %1, #0x60
+ bne 2b
+ teq %0, #'\n'
+ moveq %0, #'\r'
+ beq 1b
+ ldrb %0, [%2], #1
+ teq %0, #0
+ bne 1b
+3: " : : "r" (0), "r" (0), "r" (s), "r" (0xf0000be0) : "cc");
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
adcs %0, %0, %3
adcs %0, %0, %4
adcs %0, %0, %5
+ adc %0, %0, #0
adds %0, %0, %0, lsl #16
- addcs %0, %0, #0x10000"
+ addcs %0, %0, #0x10000
+ mvn %0, %0"
: "=&r"(sum)
- : "r" (sum), "r" (daddr), "r" (saddr), "r" (ntohs(len) << 16), "Ir" (proto << 8)
+ : "r" (sum), "r" (daddr), "r" (saddr), "r" (ntohs(len)), "Ir" (proto << 8)
: "cc");
- return (~sum) >> 16;
+ return sum >> 16;
}
/*
* Idle the processor
*/
- int (*_do_idle)(void);
+ int (*_do_idle)(int mode);
/*
* flush I cache for a page
*/
#define cpu_check_bugs() processor._check_bugs()
#define cpu_proc_init() processor._proc_init()
#define cpu_proc_fin() processor._proc_fin()
-#define cpu_do_idle() processor._do_idle()
+#define cpu_do_idle(mode) processor._do_idle(mode)
#define cpu_flush_cache_all() processor._flush_cache_all()
#define cpu_flush_cache_area(start,end,flags) processor._flush_cache_area(start,end,flags)
extern void cpu_check_bugs(void);
extern void cpu_proc_init(void);
extern void cpu_proc_fin(void);
-extern int cpu_do_idle(void);
+extern int cpu_do_idle(int mode);
extern void cpu_flush_cache_all(void);
extern void cpu_flush_cache_area(unsigned long address, unsigned long end, int flags);
#define __ASM_PROC_PGTABLE_H
#include <asm/proc/domain.h>
+#include <asm/arch/vmalloc.h>
/*
* entries per page directory level: they are two-level, so
#define PTRS_PER_PMD 1
#define PTRS_PER_PGD 4096
-/*
- * Just any arbitrary offset to the start of the vmalloc VM area: the
- * current 8MB value just means that there will be a 8MB "hole" after the
- * physical memory until the kernel virtual memory starts. That means that
- * any out-of-bounds memory accesses will hopefully be caught.
- * The vmalloc() routines leaves a hole of 4kB between each vmalloced
- * area for the same reason. ;)
- */
-#define VMALLOC_OFFSET (8*1024*1024)
-#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
-#define VMALLOC_VMADDR(x) ((unsigned long)(x))
-#define VMALLOC_END (PAGE_OFFSET + 0x10000000)
-
/****************
* PMD functions *
****************/
#define PAGE_NONE __pgprot(_L_PTE_DEFAULT)
#define PAGE_COPY __pgprot(_L_PTE_DEFAULT | _L_PTE_READ | L_PTE_BUFFERABLE)
#define PAGE_SHARED __pgprot(_L_PTE_DEFAULT | _L_PTE_READ | L_PTE_BUFFERABLE | L_PTE_WRITE)
-#define PAGE_READONLY __pgprot(_L_PTE_DEFAULT | _L_PTE_READ)
+#define PAGE_READONLY __pgprot(_L_PTE_DEFAULT | _L_PTE_READ | L_PTE_BUFFERABLE)
#define PAGE_KERNEL __pgprot(_L_PTE_DEFAULT | L_PTE_CACHEABLE | L_PTE_BUFFERABLE | L_PTE_DIRTY | L_PTE_WRITE)
#define _PAGE_CHG_MASK (PAGE_MASK | L_PTE_DIRTY | L_PTE_YOUNG)
{
unsigned long result;
- __asm__("1:\n"
+ __asm__("1:\n\t"
"shlr %1\n\t"
"bt/s 1b\n\t"
" add #1, %0"
static __inline__ __const__ __u16 ___arch__swab16(__u16 x)
{
- __asm__("swap.b %0,%0"
+ __asm__("swap.b %0, %0"
: "=r" (x)
: "0" (x));
return x;
return csum_partial_copy_generic ( src, dst, len, sum, err_ptr, NULL);
}
-#if 0
-
-/* Not used at the moment. It is difficult to imagine for what purpose
- it can be used :-) Please, do not forget to verify_area before it --ANK
- */
-
-/*
- * This combination is currently not used, but possible:
- */
-
-extern __inline__
-unsigned int csum_partial_copy_to_user ( const char *src, char *dst,
- int len, int sum, int *err_ptr)
-{
- return csum_partial_copy_generic ( src, dst, len, sum, NULL, err_ptr);
-}
-#endif
-
/*
* These are the old (and unsafe) way of doing checksums, a warning message will be
* printed if they are used and an exeption occurs.
{
unsigned int __dummy;
__asm__("clrt\n\t"
- "mov %0,%1\n\t"
+ "mov %0, %1\n\t"
"shll16 %0\n\t"
- "addc %0,%1\n\t"
+ "addc %0, %1\n\t"
"movt %0\n\t"
"shlr16 %1\n\t"
- "add %1,%0"
+ "add %1, %0"
: "=r" (sum), "=&r" (__dummy)
: "0" (sum));
return ~sum;
unsigned int sum, __dummy;
__asm__ __volatile__(
- "mov.l @%1+,%0\n\t"
- "add #-4,%2\n\t"
+ "mov.l @%1+, %0\n\t"
+ "add #-4, %2\n\t"
"clrt\n\t"
- "mov.l @%1+,%3\n\t"
- "addc %3,%0\n\t"
- "mov.l @%1+,%3\n\t"
- "addc %3,%0\n\t"
- "mov.l @%1+,%3\n\t"
- "addc %3,%0\n"
+ "mov.l @%1+, %3\n\t"
+ "addc %3, %0\n\t"
+ "mov.l @%1+, %3\n\t"
+ "addc %3, %0\n\t"
+ "mov.l @%1+, %3\n\t"
+ "addc %3, %0\n"
"1:\t"
- "mov.l @%1+,%3\n\t"
- "addc %3,%0\n\t"
+ "mov.l @%1+, %3\n\t"
+ "addc %3, %0\n\t"
"movt %3\n\t"
"dt %2\n\t"
"bf/s 1b\n\t"
- " cmp/eq #1,%3\n\t"
- "mov #0,%3\n\t"
- "addc %3,%0\n\t"
+ " cmp/eq #1, %3\n\t"
+ "mov #0, %3\n\t"
+ "addc %3, %0\n\t"
/* Since the input registers which are loaded with iph and ihl
are modified, we must also specify them as outputs, or gcc
will assume they contain their original values. */
unsigned long len_proto = (proto<<16)+len;
#endif
__asm__("clrt\n\t"
- "addc %0,%1\n\t"
- "addc %2,%1\n\t"
- "addc %3,%1\n\t"
+ "addc %0, %1\n\t"
+ "addc %2, %1\n\t"
+ "addc %3, %1\n\t"
"movt %0\n\t"
- "add %1,%0"
+ "add %1, %0"
: "=r" (sum), "=r" (len_proto)
: "r" (daddr), "r" (saddr), "1" (len_proto), "0" (sum));
return sum;
{
unsigned int __dummy;
__asm__("clrt\n\t"
- "mov.l @(0,%2),%1\n\t"
- "addc %1,%0\n\t"
- "mov.l @(4,%2),%1\n\t"
- "addc %1,%0\n\t"
- "mov.l @(8,%2),%1\n\t"
- "addc %1,%0\n\t"
- "mov.l @(12,%2),%1\n\t"
- "addc %1,%0\n\t"
- "mov.l @(0,%3),%1\n\t"
- "addc %1,%0\n\t"
- "mov.l @(4,%3),%1\n\t"
- "addc %1,%0\n\t"
- "mov.l @(8,%3),%1\n\t"
- "addc %1,%0\n\t"
- "mov.l @(12,%3),%1\n\t"
- "addc %1,%0\n\t"
- "addc %4,%0\n\t"
- "addc %5,%0\n\t"
+ "mov.l @(0,%2), %1\n\t"
+ "addc %1, %0\n\t"
+ "mov.l @(4,%2), %1\n\t"
+ "addc %1, %0\n\t"
+ "mov.l @(8,%2), %1\n\t"
+ "addc %1, %0\n\t"
+ "mov.l @(12,%2), %1\n\t"
+ "addc %1, %0\n\t"
+ "mov.l @(0,%3), %1\n\t"
+ "addc %1, %0\n\t"
+ "mov.l @(4,%3), %1\n\t"
+ "addc %1, %0\n\t"
+ "mov.l @(8,%3), %1\n\t"
+ "addc %1, %0\n\t"
+ "mov.l @(12,%3), %1\n\t"
+ "addc %1, %0\n\t"
+ "addc %4, %0\n\t"
+ "addc %5, %0\n\t"
"movt %1\n\t"
- "add %1,%0\n"
+ "add %1, %0\n"
: "=r" (sum), "=&r" (__dummy)
: "r" (saddr), "r" (daddr),
"r" (htonl(len)), "r" (htonl(proto)), "0" (sum));
{
struct task_struct *current;
- __asm__("stc r4_bank,%0\n\t"
- "add %1,%0"
+ __asm__("stc $r4_bank, %0\n\t"
+ "add %1, %0"
:"=&r" (current)
:"r" (-8192));
return current;
extern __inline__ void __delay(unsigned long loops)
{
__asm__ __volatile__(
- "tst %0,%0\n\t"
+ "tst %0, %0\n\t"
"1:\t"
"bf/s 1b\n\t"
" dt %0"
extern __inline__ void __udelay(unsigned long usecs, unsigned long lps)
{
usecs *= 0x000010c6; /* 2**32 / 1000000 */
- __asm__("dmulu.l %0,%2\n\t"
- "sts mach,%0"
+ __asm__("dmulu.l %0, %2\n\t"
+ "sts $mach, %0"
: "=r" (usecs)
: "0" (usecs), "r" (lps)
: "macl", "mach");
#define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future mappings */
+#define MADV_NORMAL 0x0 /* default page-in behavior */
+#define MADV_RANDOM 0x1 /* page-in minimum required */
+#define MADV_SEQUENTIAL 0x2 /* read-ahead aggressively */
+#define MADV_WILLNEED 0x3 /* pre-fault pages */
+#define MADV_DONTNEED 0x4 /* discard these pages */
+
/* compatibility flags */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FILE 0
/*
* User space process size: 2GB.
+ *
+ * Since SH7709 and SH7750 have "area 7", we can't use 0x7c000000--0x7fffffff
*/
-#define TASK_SIZE 0x80000000
+#define TASK_SIZE 0x7c000000UL
/* This decides where the kernel will search for a free chunk of vm
* space during mmap's.
#define TASK_UNMAPPED_BASE (TASK_SIZE / 3)
/*
- * FPU structure and data
- */
-/* FD-bit of SR register.
- * When it's set, it means the processor doesn't have right to use FPU,
- * and it results exception when the floating operation is executed.
+ * Bit of SR register
+ *
+ * FD-bit:
+ * When it's set, it means the processor doesn't have right to use FPU,
+ * and it results exception when the floating operation is executed.
+ *
+ * IMASK-bit:
+ * Interrupt level mask
*/
-#define SR_FD 0x00008000
+#define SR_FD 0x00008000
+#define SR_IMASK 0x000000f0
-#define NUM_FPU_REGS 16
+/*
+ * FPU structure and data
+ */
struct sh_fpu_hard_struct {
- unsigned long fp_regs[NUM_FPU_REGS];
- unsigned long xf_regs[NUM_FPU_REGS];
+ unsigned long fp_regs[16];
+ unsigned long long xd_regs[8];
unsigned long fpscr;
unsigned long fpul;
/* Dummy fpu emulator */
struct sh_fpu_soft_struct {
- unsigned long fp_regs[NUM_FPU_REGS];
+ unsigned long fp_regs[16];
+ unsigned long long xd_regs[8];
unsigned long fpscr;
unsigned long fpul;
- unsigned long xf_regs[NUM_FPU_REGS];
- unsigned char lookahead;
- unsigned long entry_pc;
+ unsigned char lookahead;
+ unsigned long entry_pc;
};
union sh_fpu_union {
regs->pr = 0; \
regs->sr = 0; /* User mode. */ \
regs->pc = new_pc; \
- regs->sp = new_sp
+ regs->regs[15] = new_sp
/* Forward declaration, a strange C thing */
struct task_struct;
(tsk)->flags &= ~PF_USEDFPU; \
} while (0)
+/* Double presision, NANS as NANS, rounding to nearest, no exceptions */
+#define FPSCR_INIT 0x00080000
+
/*
* Return saved PC of a blocked thread.
*/
#define __ASM_SH_PTRACE_H
/*
- * Copyright (C) 1999 Niibe Yutaka
+ * Copyright (C) 1999, 2000 Niibe Yutaka
*
*/
+/*
+ * As GCC does:
+ * 0 - 15 are integer registers
+ * 17 - 22 are control/special registers
+ * 24 - 39 fp registers
+ * 40 - 47 xd registers
+ * 48 - fpscr register
+ * -----------------------------
+ * Not as GCC:
+ * 16 --- program counter PC
+ * 23 --- syscall #
+ */
+#define REG_REG0 0
+#define REG_REG15 15
+#define REG_PC 16
+
+#define REG_PR 17
+#define REG_SR 18
+#define REG_GBR 19
+#define REG_MACH 20
+#define REG_MACL 21
+#define REG_FPUL 22
+
+#define REG_SYSCALL 23
+
+#define REG_FPREG0 24
+#define REG_FPREG15 39
+#define REG_XDREG0 40
+#define REG_XDREG14 47
+#define REG_FPSCR 48
+
/*
* This struct defines the way the registers are stored on the
* kernel stack during a system call or other kernel entry.
struct pt_regs {
long syscall_nr;
unsigned long sr;
- unsigned long sp;
- unsigned long regs[15];
+ unsigned long regs[16];
unsigned long gbr;
unsigned long mach;
unsigned long macl;
#define user_mode(regs) (((regs)->sr & 0x40000000)==0)
#define instruction_pointer(regs) ((regs)->pc)
extern void show_regs(struct pt_regs *);
+
+/* User Break Controller */
+
+#if defined(__sh3__)
+/* The value is for sh4, please fix... */
+#define UBC_BARA 0xff200000
+#define UBC_BAMRA 0xff200004
+#define UBC_BBRA 0xff200008
+#define UBC_BASRA 0xff000014
+#define UBC_BARB 0xff20000c
+#define UBC_BAMRB 0xff200010
+#define UBC_BBRB 0xff200014
+#define UBC_BASRB 0xff000018
+#define UBC_BDRB 0xff200018
+#define UBC_BDMRB 0xff20001c
+#define UBC_BRCR 0xff200020
+#elif defined(__SH4__)
+#define UBC_BARA 0xff200000
+#define UBC_BAMRA 0xff200004
+#define UBC_BBRA 0xff200008
+#define UBC_BASRA 0xff000014
+#define UBC_BARB 0xff20000c
+#define UBC_BAMRB 0xff200010
+#define UBC_BBRB 0xff200014
+#define UBC_BASRB 0xff000018
+#define UBC_BDRB 0xff200018
+#define UBC_BDMRB 0xff20001c
+#define UBC_BRCR 0xff200020
+#endif
+
+#define BAMR_ASID (1 << 2)
+#define BAMR_NONE 0
+#define BAMR_10 0x1
+#define BAMR_12 0x2
+#define BAMR_ALL 0x3
+#define BAMR_16 0x8
+#define BAMR_20 0x9
+
+#define BBR_INST (1 << 4)
+#define BBR_DATA (2 << 4)
+#define BBR_READ (1 << 2)
+#define BBR_WRITE (2 << 4)
+#define BBR_BYTE 0x1
+#define BBR_HALF 0x2
+#define BBR_LONG 0x3
+#define BBR_QUAD (1 << 6)
+
+#define BRCR_CMFA (1 << 15)
+#define BRCR_CMFB (1 << 14)
+#define BRCR_PCBA (1 << 10) /* 1: after execution */
+#define BRCR_DBEB (1 << 7)
+#define BRCR_PCBB (1 << 6)
+#define BRCR_SEQ (1 << 3)
+#define BRCR_UBDE (1 << 0)
#endif
#endif /* __ASM_SH_PTRACE_H */
unsigned long oldmask;
/* CPU registers */
- unsigned long sc_regs[15];
+ unsigned long sc_regs[16];
unsigned long sc_gbr;
unsigned long sc_mach;
unsigned long sc_macl;
unsigned long sc_pr;
- unsigned long sc_sp;
unsigned long sc_sr;
unsigned long sc_pc;
+
+#if defined(__SH4__)
+ /* FPU registers */
+ unsigned long sc_fpregs[16];
+ unsigned long long sc_xdregs[8];
+ unsigned int sc_fpscr;
+ unsigned int sc_fpul;
+ unsigned int sc_ownedfp;
+#endif
};
#endif /* __ASM_SH_SIGCONTEXT_H */
unsigned long __dummy;
__asm__ __volatile__("1:\n\t"
- "mov.b @%1+,%2\n\t"
- "mov.b %2,@%0\n\t"
- "cmp/eq #0,%2\n\t"
+ "mov.b @%1+, %2\n\t"
+ "mov.b %2, @%0\n\t"
+ "cmp/eq #0, %2\n\t"
"bf/s 1b\n\t"
- " add #1,%0\n\t"
+ " add #1, %0\n\t"
: "=r" (__dest), "=r" (__src), "=&z" (__dummy)
: "0" (__dest), "1" (__src)
: "memory");
__asm__ __volatile__(
"1:\n"
- "mov.b @%1+,%2\n\t"
- "mov.b %2,@%0\n\t"
- "cmp/eq #0,%2\n\t"
+ "mov.b @%1+, %2\n\t"
+ "mov.b %2, @%0\n\t"
+ "cmp/eq #0, %2\n\t"
"bt/s 2f\n\t"
" cmp/eq %5,%1\n\t"
"bf/s 1b\n\t"
- " add #1,%0\n"
+ " add #1, %0\n"
"2:"
: "=r" (__dest), "=r" (__src), "=&z" (__dummy)
: "0" (__dest), "1" (__src), "r" (__src+__n)
unsigned long __dummy;
__asm__ __volatile__(
- "mov.b @%1+,%3\n"
+ "mov.b @%1+, %3\n"
"1:\n\t"
- "mov.b @%0+,%2\n\t"
- "cmp/eq #0,%3\n\t"
+ "mov.b @%0+, %2\n\t"
+ "cmp/eq #0, %3\n\t"
"bt 2f\n\t"
- "cmp/eq %2,%3\n\t"
+ "cmp/eq %2, %3\n\t"
"bt/s 1b\n\t"
- " mov.b @%1+,%3\n\t"
- "add #-2,%1\n\t"
- "mov.b @%1,%3\n\t"
- "sub %3,%2\n"
+ " mov.b @%1+, %3\n\t"
+ "add #-2, %1\n\t"
+ "mov.b @%1, %3\n\t"
+ "sub %3, %2\n"
"2:"
: "=r" (__cs), "=r" (__ct), "=&r" (__res), "=&z" (__dummy)
: "0" (__cs), "1" (__ct));
unsigned long __dummy;
__asm__ __volatile__(
- "mov.b @%1+,%3\n"
+ "mov.b @%1+, %3\n"
"1:\n\t"
- "mov.b @%0+,%2\n\t"
- "cmp/eq %6,%0\n\t"
+ "mov.b @%0+, %2\n\t"
+ "cmp/eq %6, %0\n\t"
"bt/s 2f\n\t"
- " cmp/eq #0,%3\n\t"
+ " cmp/eq #0, %3\n\t"
"bt/s 3f\n\t"
- " cmp/eq %3,%2\n\t"
+ " cmp/eq %3, %2\n\t"
"bt/s 1b\n\t"
- " mov.b @%1+,%3\n\t"
- "add #-2,%1\n\t"
- "mov.b @%1,%3\n"
+ " mov.b @%1+, %3\n\t"
+ "add #-2, %1\n\t"
+ "mov.b @%1, %3\n"
"2:\n\t"
- "sub %3,%2\n"
+ "sub %3, %2\n"
"3:"
:"=r" (__cs), "=r" (__ct), "=&r" (__res), "=&z" (__dummy)
: "0" (__cs), "1" (__ct), "r" (__cs+__n));
#define __ASM_SH_SYSTEM_H
/*
- * Copyright (C) 1999, 2000 Niibe Yutaka
+ * Copyright (C) 1999, 2000 Niibe Yutaka & Kaz Kojima
*/
/*
/* Interrupt Control */
extern __inline__ void __sti(void)
{
- unsigned long __dummy;
+ unsigned long __dummy0, __dummy1;
__asm__ __volatile__("stc $sr, %0\n\t"
"and %1, %0\n\t"
+ "stc $r5_bank, %1\n\t"
+ "or %1, %0\n\t"
"ldc %0, $sr"
- : "=&r" (__dummy)
- : "r" (0xefffffff)
+ : "=&r" (__dummy0), "=r" (__dummy1)
+ : "1" (~0xf0)
: "memory");
}
{
unsigned long __dummy;
__asm__ __volatile__("stc $sr, %0\n\t"
- "or %1, %0\n\t"
+ "or #0xf0, %0\n\t"
"ldc %0, $sr"
- : "=&r" (__dummy)
- : "r" (0x10000000)
+ : "=&z" (__dummy)
+ : /* no inputs */
: "memory");
}
x = (__extension__ ({ unsigned long __sr; \
__asm__ __volatile__( \
"stc $sr, %0" \
- : "=r" (__sr) \
+ : "=&r" (__sr) \
: /* no inputs */ \
: "memory"); \
- (__sr & 0xffff7f0f);}))
+ (__sr & 0x000000f0);}))
#define __save_and_cli(x) \
x = (__extension__ ({ unsigned long __dummy,__sr; \
__asm__ __volatile__( \
"stc $sr, %1\n\t" \
- "or %0, %1\n\t" \
- "stc $sr, %0\n\t" \
- "ldc %1, $sr" \
- : "=r" (__sr), "=&r" (__dummy) \
- : "0" (0x10000000) \
- : "memory"); (__sr & 0xffff7f0f); }))
+ "mov %1, %0\n\t" \
+ "or #0xf0, %0\n\t" \
+ "ldc %0, $sr" \
+ : "=&z" (__dummy), "=&r" (__sr) \
+ : /* no inputs */ \
+ : "memory"); (__sr & 0x000000f0); }))
#define __restore_flags(x) do { \
- unsigned long __dummy; \
- __asm__ __volatile__( \
+ unsigned long __dummy0, __dummy1; \
+ if (x != 0xf0) /* not CLI-ed? */ \
+ __asm__ __volatile__( \
"stc $sr, %0\n\t" \
"and %1, %0\n\t" \
- "or %2, %0\n\t" \
+ "stc $r5_bank, %1\n\t" \
+ "or %1, %0\n\t" \
"ldc %0, $sr" \
- : "=&r" (__dummy) \
- : "r" (0x000080f0), /* IMASK+FD */ \
- "r" (x) \
+ : "=&r" (__dummy0), "=r" (__dummy1) \
+ : "1" (0xffffff0f) \
: "memory"); \
} while (0)
+/*
+ * Jump to P2 area.
+ * When handling TLB or caches, we need to do it from P2 area.
+ */
+#define jump_to_P2() \
+do { \
+ unsigned long __dummy; \
+ __asm__ __volatile__( \
+ "mov.l 1f, %0\n\t" \
+ "or %1, %0\n\t" \
+ "jmp @%0\n\t" \
+ " nop\n\t" \
+ ".balign 4\n" \
+ "1: .long 2f\n" \
+ "2:" \
+ : "=&r" (__dummy) \
+ : "r" (0x20000000)); \
+} while (0)
+
+/*
+ * Back to P1 area.
+ */
+#define back_to_P1() \
+do { \
+ unsigned long __dummy; \
+ __asm__ __volatile__( \
+ "nop;nop;nop;nop;nop;nop\n\t" \
+ "mov.l 1f, %0\n\t" \
+ "jmp @%0\n\t" \
+ " nop\n\t" \
+ ".balign 4\n" \
+ "1: .long 2f\n" \
+ "2:" \
+ : "=&r" (__dummy)); \
+} while (0)
+
/* For spinlocks etc */
#define local_irq_save(x) __save_and_cli(x)
#define local_irq_restore(x) __restore_flags(x)
-/* $Id: uaccess.h,v 1.6 1999/10/29 13:10:44 gniibe Exp $
+/* $Id: uaccess.h,v 1.10 2000/03/24 13:53:45 gniibe Exp $
*
* User space memory access functions
*
*/
#define __range_ok(addr,size) ({ \
unsigned long flag,sum; \
- __asm__("clrt; addc %3,%1; movt %0; cmp/hi %4,%1; rotcl %0" \
+ __asm__("clrt; addc %3, %1; movt %0; cmp/hi %4, %1; rotcl %0" \
:"=&r" (flag), "=r" (sum) \
:"1" (addr), "r" ((int)(size)), "r" (current->addr_limit.seg)); \
flag; })
({ \
__asm__ __volatile__( \
"1:\n\t" \
- "mov." insn " %2,%1\n\t" \
- "mov #0,%0\n" \
- "2:\n\t" \
+ "mov." insn " %2, %1\n\t" \
+ "mov #0, %0\n" \
+ "2:\n" \
".section .fixup,\"ax\"\n" \
"3:\n\t" \
- "mov #0,%1\n\t" \
- "mov.l 4f,%0\n\t" \
+ "mov #0, %1\n\t" \
+ "mov.l 4f, %0\n\t" \
"jmp @%0\n\t" \
- " mov %3,%0\n" \
+ " mov %3, %0\n" \
"4: .long 2b\n\t" \
- ".previous\n\t" \
+ ".previous\n" \
".section __ex_table,\"a\"\n\t" \
".long 1b, 3b\n\t" \
".previous" \
({ \
__asm__ __volatile__( \
"1:\n\t" \
- "mov." insn " %1,%2\n\t" \
- "mov #0,%0\n" \
- "2:\n\t" \
+ "mov." insn " %1, %2\n\t" \
+ "mov #0, %0\n" \
+ "2:\n" \
".section .fixup,\"ax\"\n" \
"3:\n\t" \
"nop\n\t" \
- "mov.l 4f,%0\n\t" \
+ "mov.l 4f, %0\n\t" \
"jmp @%0\n\t" \
- "mov %3,%0\n" \
+ "mov %3, %0\n" \
"4: .long 2b\n\t" \
- ".previous\n\t" \
+ ".previous\n" \
".section __ex_table,\"a\"\n\t" \
".long 1b, 3b\n\t" \
".previous" \
__asm__ __volatile__(
"9:\n\t"
- "mov.b @%2+,%1\n\t"
+ "mov.b @%2+, %1\n\t"
"dt %0\n"
"1:\n\t"
- "mov.b %1,@%3\n\t"
+ "mov.b %1, @%3\n\t"
"bf/s 9b\n\t"
- " add #1,%3\n"
- "2:"
+ " add #1, %3\n"
+ "2:\n"
".section .fixup,\"ax\"\n"
"3:\n\t"
- "mov.l 5f,%1\n\t"
+ "mov.l 5f, %1\n\t"
"jmp @%1\n\t"
- " mov %7,%0\n\t"
+ " mov %7, %0\n\t"
".balign 4\n"
"5: .long 2b\n"
".previous\n"
" .long 9b,3b\n"
" .long 1b,2b\n"
".previous"
- : "=&r" (res), "=&z" (__dummy), "=&r" (_f), "=&r" (_t)
+ : "=r" (res), "=&z" (__dummy), "=r" (_f), "=r" (_t)
: "2" (__from), "3" (__to), "0" (__n), "i" (-EFAULT)
: "memory");
extern __inline__ __kernel_size_t
__clear_user(void *addr, __kernel_size_t size)
{
- __kernel_size_t res;
- unsigned long __a, __s;
+ unsigned long __a;
__asm__ __volatile__(
"9:\n\t"
- "dt %2\n"
+ "dt %0\n"
"1:\n\t"
- "mov.b %5,@%1\n\t"
+ "mov.b %4, @%1\n\t"
"bf/s 9b\n\t"
- " add #1,%1\n\t"
- "sub %2,%0\n"
+ " add #1, %1\n"
"2:\n"
".section .fixup,\"ax\"\n"
"3:\n\t"
- "mov.l 4f,%0\n\t"
- "jmp @%0\n\t"
- " mov %7,%0\n"
+ "mov.l 4f, %1\n\t"
+ "jmp @%1\n\t"
+ " nop\n"
".balign 4\n"
"4: .long 2b\n"
".previous\n"
" .balign 4\n"
" .long 1b,3b\n"
".previous"
- : "=&r" (res), "=&r" (__a), "=&r" (__s)
- : "1" (addr), "2" (size), "r" (0), "0" (size), "i" (-EFAULT));
+ : "=r" (size), "=r" (__a)
+ : "0" (size), "1" (addr), "r" (0));
- return res;
+ return size;
}
#define clear_user(addr,n) ({ \
__asm__ __volatile__(
"9:\n"
- "mov.b @%2+,%1\n\t"
- "cmp/eq #0,%1\n\t"
+ "mov.b @%2+, %1\n\t"
+ "cmp/eq #0, %1\n\t"
"bt/s 2f\n"
"1:\n"
- "mov.b %1,@%3\n\t"
- "dt %0\n\t"
+ "mov.b %1, @%3\n\t"
+ "dt %7\n\t"
"bf/s 9b\n\t"
- " add #1,%3\n\t"
- "sub %6,%0\n"
- "2:\n"
+ " add #1, %3\n\t"
+ "2:\n\t"
+ "sub %7, %0\n"
+ "3:\n"
".section .fixup,\"ax\"\n"
- "3:\n\t"
- "mov.l 4f,%1\n\t"
+ "4:\n\t"
+ "mov.l 5f, %1\n\t"
"jmp @%1\n\t"
- " mov %8,%0\n\t"
+ " mov %8, %0\n\t"
".balign 4\n"
- "4: .long 2b\n"
+ "5: .long 3b\n"
".previous\n"
".section __ex_table,\"a\"\n"
" .balign 4\n"
- " .long 9b,3b\n"
- " .long 1b,2b\n"
+ " .long 9b,4b\n"
".previous"
- : "=&r" (res), "=&z" (__dummy), "=&r" (_s), "=&r" (_d)
- : "2" (__src), "3" (__dest), "r" (__count), "0" (__count),
+ : "=r" (res), "=&z" (__dummy), "=r" (_s), "=r" (_d)
+ : "0" (__count), "2" (__src), "3" (__dest), "r" (__count),
"i" (-EFAULT)
: "memory");
__asm__ __volatile__(
"9:\n"
- "cmp/eq %4,%0\n\t"
+ "cmp/eq %4, %0\n\t"
"bt 2f\n"
"1:\t"
- "mov.b @(%0,%3),%1\n\t"
- "tst %1,%1\n\t"
+ "mov.b @(%0,%3), %1\n\t"
+ "tst %1, %1\n\t"
"bf/s 9b\n\t"
- " add #1,%0\n"
+ " add #1, %0\n"
"2:\n"
".section .fixup,\"ax\"\n"
"3:\n\t"
- "mov.l 4f,%1\n\t"
+ "mov.l 4f, %1\n\t"
"jmp @%1\n\t"
- " mov %5,%0\n"
+ " mov %5, %0\n"
".balign 4\n"
"4: .long 2b\n"
".previous\n"
#define __NR_setfsuid32 215
#define __NR_setfsgid32 216
#define __NR_pivot_root 217
+#define __NR_mincore 218
+#define __NR_madvise 219
/* user-visible error numbers are in the range -1 - -125: see <asm-sh/errno.h> */
*/
struct user_fpu_struct {
- unsigned long fp_regs[NUM_FPU_REGS];
- unsigned long xf_regs[NUM_FPU_REGS];
+ unsigned long fp_regs[16];
+ unsigned long long xd_regs[8];
unsigned long fpscr;
unsigned long fpul;
};
struct user {
struct pt_regs regs; /* entire machine state */
- struct user_fpu_struct fpu; /* Math Co-processor registers. */
- int u_fpvalid; /* True if math co-processor being used. */
+ struct user_fpu_struct fpu; /* Math Co-processor registers */
+ int u_fpvalid; /* True if math co-processor being used */
size_t u_tsize; /* text size (pages) */
size_t u_dsize; /* data size (pages) */
size_t u_ssize; /* stack size (pages) */
unsigned long start_stack; /* stack starting address */
long int signal; /* signal causing core dump */
struct regs * u_ar0; /* help gdb find registers */
- struct user_fpu_struct* u_fpstate; /* Math Co-processor pointer. */
+ struct user_fpu_struct* u_fpstate; /* Math Co-processor pointer */
unsigned long magic; /* identifies a core file */
char u_comm[32]; /* user command name */
};
#define ASIZ_thread_w_saved 0x00000001
#define AOFF_thread_fpdepth 0x0000000d
#define ASIZ_thread_fpdepth 0x00000001
-#define AOFF_thread_fpsaved 0x0000000e
+#define AOFF_thread_fault_code 0x0000000e
+#define ASIZ_thread_fault_code 0x00000001
+#define AOFF_thread___pad1 0x0000000f
+#define ASIZ_thread___pad1 0x00000001
+#define AOFF_thread_fault_address 0x00000010
+#define ASIZ_thread_fault_address 0x00000008
+#define AOFF_thread_fpsaved 0x00000018
#define ASIZ_thread_fpsaved 0x00000007
-#define AOFF_thread___pad1 0x00000015
-#define ASIZ_thread___pad1 0x00000003
-#define AOFF_thread_kregs 0x00000018
+#define AOFF_thread___pad2 0x0000001f
+#define ASIZ_thread___pad2 0x00000001
+#define AOFF_thread_kregs 0x00000020
#define ASIZ_thread_kregs 0x00000008
-#define AOFF_thread_utraps 0x00000020
+#define AOFF_thread_utraps 0x00000028
#define ASIZ_thread_utraps 0x00000008
-#define AOFF_thread_gsr 0x00000028
+#define AOFF_thread_gsr 0x00000030
#define ASIZ_thread_gsr 0x00000007
-#define AOFF_thread___pad2 0x0000002f
-#define ASIZ_thread___pad2 0x00000001
-#define AOFF_thread_xfsr 0x00000030
+#define AOFF_thread___pad3 0x00000037
+#define ASIZ_thread___pad3 0x00000001
+#define AOFF_thread_xfsr 0x00000038
#define ASIZ_thread_xfsr 0x00000038
-#define AOFF_thread_reg_window 0x00000068
+#define AOFF_thread_reg_window 0x00000070
#define ASIZ_thread_reg_window 0x00000380
-#define AOFF_thread_rwbuf_stkptrs 0x000003e8
+#define AOFF_thread_rwbuf_stkptrs 0x000003f0
#define ASIZ_thread_rwbuf_stkptrs 0x00000038
-#define AOFF_thread_user_cntd0 0x00000420
+#define AOFF_thread_user_cntd0 0x00000428
#define ASIZ_thread_user_cntd0 0x00000008
-#define AOFF_thread_user_cntd1 0x00000428
+#define AOFF_thread_user_cntd1 0x00000430
#define ASIZ_thread_user_cntd1 0x00000008
-#define AOFF_thread_kernel_cntd0 0x00000430
+#define AOFF_thread_kernel_cntd0 0x00000438
#define ASIZ_thread_kernel_cntd0 0x00000008
-#define AOFF_thread_kernel_cntd1 0x00000438
+#define AOFF_thread_kernel_cntd1 0x00000440
#define ASIZ_thread_kernel_cntd1 0x00000008
-#define AOFF_thread_pcr_reg 0x00000440
+#define AOFF_thread_pcr_reg 0x00000448
#define ASIZ_thread_pcr_reg 0x00000008
#define ASIZ_thread 0x00000450
#define ASIZ_thread_w_saved 0x00000001
#define AOFF_thread_fpdepth 0x0000000d
#define ASIZ_thread_fpdepth 0x00000001
-#define AOFF_thread_fpsaved 0x0000000e
+#define AOFF_thread_fault_code 0x0000000e
+#define ASIZ_thread_fault_code 0x00000001
+#define AOFF_thread___pad1 0x0000000f
+#define ASIZ_thread___pad1 0x00000001
+#define AOFF_thread_fault_address 0x00000010
+#define ASIZ_thread_fault_address 0x00000008
+#define AOFF_thread_fpsaved 0x00000018
#define ASIZ_thread_fpsaved 0x00000007
-#define AOFF_thread___pad1 0x00000015
-#define ASIZ_thread___pad1 0x00000003
-#define AOFF_thread_kregs 0x00000018
+#define AOFF_thread___pad2 0x0000001f
+#define ASIZ_thread___pad2 0x00000001
+#define AOFF_thread_kregs 0x00000020
#define ASIZ_thread_kregs 0x00000008
-#define AOFF_thread_utraps 0x00000020
+#define AOFF_thread_utraps 0x00000028
#define ASIZ_thread_utraps 0x00000008
-#define AOFF_thread_gsr 0x00000028
+#define AOFF_thread_gsr 0x00000030
#define ASIZ_thread_gsr 0x00000007
-#define AOFF_thread___pad2 0x0000002f
-#define ASIZ_thread___pad2 0x00000001
-#define AOFF_thread_xfsr 0x00000030
+#define AOFF_thread___pad3 0x00000037
+#define ASIZ_thread___pad3 0x00000001
+#define AOFF_thread_xfsr 0x00000038
#define ASIZ_thread_xfsr 0x00000038
-#define AOFF_thread_reg_window 0x00000068
+#define AOFF_thread_reg_window 0x00000070
#define ASIZ_thread_reg_window 0x00000380
-#define AOFF_thread_rwbuf_stkptrs 0x000003e8
+#define AOFF_thread_rwbuf_stkptrs 0x000003f0
#define ASIZ_thread_rwbuf_stkptrs 0x00000038
-#define AOFF_thread_user_cntd0 0x00000420
+#define AOFF_thread_user_cntd0 0x00000428
#define ASIZ_thread_user_cntd0 0x00000008
-#define AOFF_thread_user_cntd1 0x00000428
+#define AOFF_thread_user_cntd1 0x00000430
#define ASIZ_thread_user_cntd1 0x00000008
-#define AOFF_thread_kernel_cntd0 0x00000430
+#define AOFF_thread_kernel_cntd0 0x00000438
#define ASIZ_thread_kernel_cntd0 0x00000008
-#define AOFF_thread_kernel_cntd1 0x00000438
+#define AOFF_thread_kernel_cntd1 0x00000440
#define ASIZ_thread_kernel_cntd1 0x00000008
-#define AOFF_thread_pcr_reg 0x00000440
+#define AOFF_thread_pcr_reg 0x00000448
#define ASIZ_thread_pcr_reg 0x00000008
#define ASIZ_thread 0x00000450
#define ASIZ_thread_w_saved 0x00000001
#define AOFF_thread_fpdepth 0x0000000d
#define ASIZ_thread_fpdepth 0x00000001
-#define AOFF_thread_fpsaved 0x0000000e
+#define AOFF_thread_fault_code 0x0000000e
+#define ASIZ_thread_fault_code 0x00000001
+#define AOFF_thread___pad1 0x0000000f
+#define ASIZ_thread___pad1 0x00000001
+#define AOFF_thread_fault_address 0x00000010
+#define ASIZ_thread_fault_address 0x00000008
+#define AOFF_thread_fpsaved 0x00000018
#define ASIZ_thread_fpsaved 0x00000007
-#define AOFF_thread___pad1 0x00000015
-#define ASIZ_thread___pad1 0x00000003
-#define AOFF_thread_kregs 0x00000018
+#define AOFF_thread___pad2 0x0000001f
+#define ASIZ_thread___pad2 0x00000001
+#define AOFF_thread_kregs 0x00000020
#define ASIZ_thread_kregs 0x00000008
-#define AOFF_thread_utraps 0x00000020
+#define AOFF_thread_utraps 0x00000028
#define ASIZ_thread_utraps 0x00000008
-#define AOFF_thread_gsr 0x00000028
+#define AOFF_thread_gsr 0x00000030
#define ASIZ_thread_gsr 0x00000007
-#define AOFF_thread___pad2 0x0000002f
-#define ASIZ_thread___pad2 0x00000001
-#define AOFF_thread_xfsr 0x00000030
+#define AOFF_thread___pad3 0x00000037
+#define ASIZ_thread___pad3 0x00000001
+#define AOFF_thread_xfsr 0x00000038
#define ASIZ_thread_xfsr 0x00000038
-#define AOFF_thread_reg_window 0x00000068
+#define AOFF_thread_reg_window 0x00000070
#define ASIZ_thread_reg_window 0x00000380
-#define AOFF_thread_rwbuf_stkptrs 0x000003e8
+#define AOFF_thread_rwbuf_stkptrs 0x000003f0
#define ASIZ_thread_rwbuf_stkptrs 0x00000038
-#define AOFF_thread_user_cntd0 0x00000420
+#define AOFF_thread_user_cntd0 0x00000428
#define ASIZ_thread_user_cntd0 0x00000008
-#define AOFF_thread_user_cntd1 0x00000428
+#define AOFF_thread_user_cntd1 0x00000430
#define ASIZ_thread_user_cntd1 0x00000008
-#define AOFF_thread_kernel_cntd0 0x00000430
+#define AOFF_thread_kernel_cntd0 0x00000438
#define ASIZ_thread_kernel_cntd0 0x00000008
-#define AOFF_thread_kernel_cntd1 0x00000438
+#define AOFF_thread_kernel_cntd1 0x00000440
#define ASIZ_thread_kernel_cntd1 0x00000008
-#define AOFF_thread_pcr_reg 0x00000440
+#define AOFF_thread_pcr_reg 0x00000448
#define ASIZ_thread_pcr_reg 0x00000008
#define ASIZ_thread 0x00000450
#endif /* SPIN_LOCK_DEBUG */
-/* $Id: pbm.h,v 1.21 2000/03/10 02:42:17 davem Exp $
+/* $Id: pbm.h,v 1.22 2000/03/25 05:18:30 davem Exp $
* pbm.h: UltraSparc PCI controller software state.
*
* Copyright (C) 1997, 1998, 1999 David S. Miller (davem@redhat.com)
struct resource io_space;
struct resource mem_space;
+ /* State of 66MHz capabilities on this PBM. */
+ int is_66mhz_capable;
+ int all_devs_66mhz;
+
/* This PBM's streaming buffer. */
struct pci_strbuf stc;
-/* $Id: pgtable.h,v 1.121 2000/03/02 20:37:41 davem Exp $
+/* $Id: pgtable.h,v 1.123 2000/03/26 09:13:53 davem Exp $
* pgtable.h: SpitFire page table operations.
*
* Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
/* There used to be some funny code here which tried to guess which
* TLB wanted the mapping, that wasn't accurate enough to justify it's
- * existance. The real way to do that is to have each TLB miss handler
- * pass in a distinct code to do_sparc64_fault() and do it more accurately
- * there.
+ * existance. Instead we now have each TLB miss handler record a
+ * distinct code in the thread struct.
*
* What we do need to handle here is prevent I-cache corruption. The
* deal is that the I-cache snoops stores from other CPUs and all DMA
* 4) Splat.
*/
extern void __flush_icache_page(unsigned long phys_page);
+extern void __prefill_dtlb(unsigned long vaddr, unsigned long pteval);
+extern void __prefill_itlb(unsigned long vaddr, unsigned long pteval);
#define update_mmu_cache(__vma, __address, _pte) \
do { \
unsigned short __flags = ((__vma)->vm_flags); \
- if ((__flags & VM_EXEC) != 0 && \
- ((pte_val(_pte) & (_PAGE_PRESENT | _PAGE_WRITE | _PAGE_MODIFIED)) == \
- (_PAGE_PRESENT | _PAGE_WRITE | _PAGE_MODIFIED))) { \
+ unsigned long pteval = pte_val(_pte); \
+ if ((__flags & VM_EXEC) != 0 && ((__vma)->vm_file != NULL) && \
+ ((pteval & (_PAGE_PRESENT | _PAGE_WRITE | _PAGE_MODIFIED)) == \
+ (_PAGE_PRESENT | _PAGE_WRITE | _PAGE_MODIFIED))) \
__flush_icache_page(pte_pagenr(_pte) << PAGE_SHIFT); \
+ if ((__vma)->vm_mm == current->mm && \
+ (__flags = current->thread.fault_code) != 0) { \
+ unsigned long tag = (__address & PAGE_MASK); \
+ tag |= CTX_HWBITS(current->mm->context); \
+ if (__flags & FAULT_CODE_DTLB) \
+ __prefill_dtlb(tag, pteval); \
+ else \
+ __prefill_itlb(tag, pteval); \
} \
} while(0)
-/* $Id: processor.h,v 1.61 2000/01/21 11:39:22 jj Exp $
+/* $Id: processor.h,v 1.62 2000/03/26 09:13:53 davem Exp $
* include/asm-sparc64/processor.h
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
unsigned long ksp __attribute__ ((aligned(16)));
unsigned char wstate, cwp, flags;
mm_segment_t current_ds;
- unsigned char w_saved, fpdepth;
+ unsigned char w_saved, fpdepth, fault_code, __pad1;
+ unsigned long fault_address;
unsigned char fpsaved[7];
- unsigned char __pad1[3];
- struct pt_regs *kregs;
+ unsigned char __pad2;
/* D$ line 2, 3, 4 */
+ struct pt_regs *kregs;
unsigned long *utraps;
unsigned char gsr[7];
- unsigned char __pad2;
+ unsigned char __pad3;
unsigned long xfsr[7];
struct reg_window reg_window[NSWINS];
#endif /* !(__ASSEMBLY__) */
-#define SPARC_FLAG_UNALIGNED 0x01 /* is allowed to do unaligned accesses */
-#define SPARC_FLAG_NEWSIGNALS 0x02 /* task wants new-style signals */
-#define SPARC_FLAG_32BIT 0x04 /* task is older 32-bit binary */
-#define SPARC_FLAG_NEWCHILD 0x08 /* task is just-spawned child process */
-#define SPARC_FLAG_PERFCTR 0x10 /* task has performance counters active */
+#define SPARC_FLAG_UNALIGNED 0x01 /* is allowed to do unaligned accesses */
+#define SPARC_FLAG_NEWSIGNALS 0x02 /* task wants new-style signals */
+#define SPARC_FLAG_32BIT 0x04 /* task is older 32-bit binary */
+#define SPARC_FLAG_NEWCHILD 0x08 /* task is just-spawned child process */
+#define SPARC_FLAG_PERFCTR 0x10 /* task has performance counters active */
+
+#define FAULT_CODE_WRITE 0x01 /* Write access, implies D-TLB */
+#define FAULT_CODE_DTLB 0x02 /* Miss happened in D-TLB */
+#define FAULT_CODE_ITLB 0x04 /* Miss happened in I-TLB */
+#define FAULT_CODE_WINFIXUP 0x08 /* Miss happened during spill/fill */
#define INIT_MMAP { &init_mm, 0xfffff80000000000, 0xfffff80001000000, \
NULL, PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
#define INIT_THREAD { \
/* ksp, wstate, cwp, flags, current_ds, */ \
0, 0, 0, 0, KERNEL_DS, \
-/* w_saved, fpdepth, fpsaved, pad1, kregs, */ \
- 0, 0, { 0 }, { 0 }, 0, \
-/* utraps, gsr, pad2, xfsr, */ \
- 0, { 0 }, 0, { 0 }, \
+/* w_saved, fpdepth, fault_code, __pad1, */ \
+ 0, 0, 0, 0, \
+/* fault_address, fpsaved, __pad2, kregs, */ \
+ 0, { 0 }, 0, 0, \
+/* utraps, gsr, __pad3, xfsr, */ \
+ 0, { 0 }, 0, { 0 }, \
/* reg_window */ \
{ { { 0, }, { 0, } }, }, \
/* rwbuf_stkptrs */ \
extern int bpcd_init(void);
extern int ps2esdi_init(void);
+#if defined(CONFIG_ARCH_S390)
+extern int mdisk_init(void);
+extern int dasd_init(void);
+#endif /* CONFIG_ARCH_S390 */
+
extern void set_device_ro(kdev_t dev,int flag);
void add_blkdev_randomness(int major);
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MDISK_MAJOR)
+
+#define DEVICE_NAME "mdisk"
+#define DEVICE_REQUEST mdisk_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == DASD_MAJOR)
+
+#define DEVICE_NAME "dasd"
+#define DEVICE_REQUEST do_dasd_request
+#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
#elif (MAJOR_NR == I2O_MAJOR)
#define DEVICE_NAME "I2O block"
#define ERESTARTNOHAND 514 /* restart if no handler.. */
#define ENOIOCTLCMD 515 /* No ioctl command */
+/* Defined for the NFSv3 protocol */
+#define EBADHANDLE 521 /* Illegal NFS file handle */
+#define ENOTSYNC 522 /* Update synchronization mismatch */
+#define EBADCOOKIE 523 /* Cookie is stale */
+#define ENOTSUPP 524 /* Operation is not supported */
+#define ETOOSMALL 525 /* Buffer or request is too small */
+#define ESERVERFAULT 526 /* An untranslatable error occurred */
+#define EBADTYPE 527 /* Type not supported by server */
+#define EJUKEBOX 528 /* Request initiated, but will not complete before timeout */
+
#endif
#endif
#define FS_NO_PRELIM 4 /* prevent preloading of dentries, even if
* FS_NO_DCACHE is not set.
*/
-#define FS_IBASKET 8 /* FS does callback to free_ibasket() if space gets low. */
/*
* These are the fs-independent mount-flags: up to 16 flags are supported
struct dentry *s_root;
wait_queue_head_t s_wait;
- struct inode *s_ibasket;
- short int s_ibasket_count;
- short int s_ibasket_max;
struct list_head s_dirty; /* dirty inodes */
struct list_head s_files;
*/
/*
- * ==FILEVERSION 20000115==
+ * ==FILEVERSION 20000324==
*
* NOTE TO MAINTAINERS:
* If you modify this file at all, please set the above date.
#define PPPIOCCONNECT _IOW('t', 58, int) /* connect channel to unit */
#define PPPIOCDISCONN _IO('t', 57) /* disconnect channel */
#define PPPIOCATTCHAN _IOW('t', 56, int) /* attach to ppp channel */
+#define PPPIOCGCHAN _IOR('t', 55, int) /* get ppp channel number */
#define SIOCGPPPSTATS (SIOCDEVPRIVATE + 0)
#define SIOCGPPPVER (SIOCDEVPRIVATE + 1) /* NEVER change this!! */
extern void free_area_init(unsigned long * zones_size);
extern void free_area_init_node(int nid, pg_data_t *pgdat,
- unsigned long * zones_size, unsigned long zone_start_paddr);
+ unsigned long * zones_size, unsigned long zone_start_paddr,
+ unsigned long *zholes_size);
extern void mem_init(void);
extern void show_mem(void);
extern void si_meminfo(struct sysinfo * val);
*/
extern void show_free_areas_core(int);
extern void free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,
- unsigned long *zones_size, unsigned long paddr);
+ unsigned long *zones_size, unsigned long paddr, unsigned long *zholes_size);
#ifndef CONFIG_DISCONTIGMEM
SS_DISCONNECTING /* in process of disconnecting */
} socket_state;
-#define SO_ACCEPTCON (1<<16) /* performed a listen */
-#define SO_WAITDATA (1<<17) /* wait data to read */
-#define SO_NOSPACE (1<<18) /* no space to write */
+#define __SO_ACCEPTCON (1<<16) /* performed a listen */
#ifdef __KERNEL__
+#define SOCK_ASYNC_NOSPACE 0
+#define SOCK_ASYNC_WAITDATA 1
+#define SOCK_NOSPACE 2
+
struct socket
{
socket_state state;
struct ip_conntrack *expectant;
};
-#if defined(CONFIG_IP_NF_NAT) || defined(CONFIG_IP_NF_NAT_MODULE)
+#ifdef CONFIG_IP_NF_NAT_NEEDED
#include <linux/netfilter_ipv4/ip_nat.h>
#endif
#if defined(CONFIG_IP_NF_FTP) || defined(CONFIG_IP_NF_FTP_MODULE)
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
-#if defined(CONFIG_IP_NF_NAT) || defined(CONFIG_IP_NF_NAT_MODULE)
+#ifdef CONFIG_IP_NF_NAT_NEEDED
#include <linux/netfilter_ipv4/ip_nat_ftp.h>
#endif
#endif
#endif
} help;
-#if defined(CONFIG_IP_NF_NAT) || defined(CONFIG_IP_NF_NAT_MODULE)
+#ifdef CONFIG_IP_NF_NAT_NEEDED
struct {
struct ip_nat_info info;
union {
int masq_index;
#endif
} nat;
-#endif /* CONFIG_IP_NF_NAT || CONFIG_IP_NF_NAT_MODULE */
+#endif /* CONFIG_IP_NF_NAT_NEEDED */
};
IP_CT_FTP_PASV = IP_CT_DIR_REPLY
};
-/* Protected by ip_conntrack_lock */
/* We record seq number and length of ftp ip/port text here: all in
host order. */
struct ip_ct_ftp
IPT_ICMP_HOST_UNREACHABLE,
IPT_ICMP_PROT_UNREACHABLE,
IPT_ICMP_PORT_UNREACHABLE,
- IPT_ICMP_ECHOREPLY,
- IPT_TCP_RESET,
+ IPT_ICMP_ECHOREPLY
};
struct ipt_reject_info {
#endif /* __KERNEL__ */
-#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
+#if defined(__KERNEL__)
extern struct rpc_program nfs_program;
extern struct rpc_stat nfs_rpcstat;
-struct nfs_time {
- __u32 seconds;
- __u32 useconds;
-};
-
-struct nfs_fattr {
- enum nfs_ftype type;
- __u32 mode;
- __u32 nlink;
- __u32 uid;
- __u32 gid;
- __u32 size;
- __u32 blocksize;
- __u32 rdev;
- __u32 blocks;
- __u32 fsid;
- __u32 fileid;
- struct nfs_time atime;
- struct nfs_time mtime;
- struct nfs_time ctime;
-};
-
-struct nfs_fsinfo {
- __u32 tsize;
- __u32 bsize;
- __u32 blocks;
- __u32 bfree;
- __u32 bavail;
-};
-
-/* Arguments to the write call.
- * Note that NFS_WRITE_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
- */
-#define NFS_WRITE_MAXIOV 8
-
enum nfs3_stable_how {
NFS_UNSTABLE = 0,
NFS_DATA_SYNC = 1,
NFS_FILE_SYNC = 2
};
-
-struct nfs_writeargs {
- struct nfs_fh * fh;
- __u32 offset;
- __u32 count;
- enum nfs3_stable_how stable;
- unsigned int nriov;
- struct iovec iov[NFS_WRITE_MAXIOV];
-};
-
-struct nfs_writeverf {
- enum nfs3_stable_how committed;
- __u32 verifier[2];
-};
-
-struct nfs_writeres {
- struct nfs_fattr * fattr;
- struct nfs_writeverf * verf;
- __u32 count;
-};
-
-#ifdef NFS_NEED_XDR_TYPES
-
-struct nfs_sattrargs {
- struct nfs_fh * fh;
- struct iattr * sattr;
-};
-
-struct nfs_diropargs {
- struct nfs_fh * fh;
- const char * name;
-};
-
-struct nfs_readlinkargs {
- struct nfs_fh * fh;
- const void * buffer;
-};
-
-struct nfs_readargs {
- struct nfs_fh * fh;
- __u32 offset;
- __u32 count;
- void * buffer;
-};
-
-struct nfs_createargs {
- struct nfs_fh * fh;
- const char * name;
- struct iattr * sattr;
-};
-
-struct nfs_renameargs {
- struct nfs_fh * fromfh;
- const char * fromname;
- struct nfs_fh * tofh;
- const char * toname;
-};
-
-struct nfs_linkargs {
- struct nfs_fh * fromfh;
- struct nfs_fh * tofh;
- const char * toname;
-};
-
-struct nfs_symlinkargs {
- struct nfs_fh * fromfh;
- const char * fromname;
- const char * topath;
- struct iattr * sattr;
-};
-
-struct nfs_readdirargs {
- struct nfs_fh * fh;
- __u32 cookie;
- void * buffer;
- int bufsiz;
-};
-
-struct nfs_diropok {
- struct nfs_fh * fh;
- struct nfs_fattr * fattr;
-};
-
-struct nfs_readres {
- struct nfs_fattr * fattr;
- unsigned int count;
-};
-
-struct nfs_readdirres {
- void * buffer;
- int bufsiz;
- u32 cookie;
-};
-
-#endif /* NFS_NEED_XDR_TYPES */
#endif /* __KERNEL__ */
+
#endif
--- /dev/null
+/*
+ * NFS protocol definitions
+ *
+ * This file contains constants for Version 2 of the protocol.
+ */
+#ifndef _LINUX_NFS2_H
+#define _LINUX_NFS2_H
+
+#define NFS2_PORT 2049
+#define NFS2_MAXDATA 8192
+#define NFS2_MAXPATHLEN 1024
+#define NFS2_MAXNAMLEN 255
+#define NFS2_MAXGROUPS 16
+#define NFS2_FHSIZE 32
+#define NFS2_COOKIESIZE 4
+#define NFS2_FIFO_DEV (-1)
+#define NFS2MODE_FMT 0170000
+#define NFS2MODE_DIR 0040000
+#define NFS2MODE_CHR 0020000
+#define NFS2MODE_BLK 0060000
+#define NFS2MODE_REG 0100000
+#define NFS2MODE_LNK 0120000
+#define NFS2MODE_SOCK 0140000
+#define NFS2MODE_FIFO 0010000
+
+#endif
#ifndef _LINUX_NFS3_H
#define _LINUX_NFS3_H
-#include <linux/sunrpc/msg_prot.h>
-#include <linux/nfs.h>
-
#define NFS3_PORT 2049
-#define NFS3_MAXDATA 8192
+#define NFS3_MAXDATA 32768
#define NFS3_MAXPATHLEN PATH_MAX
#define NFS3_MAXNAMLEN NAME_MAX
#define NFS3_MAXGROUPS 16
+#define NFS3_FHSIZE 64
#define NFS3_COOKIESIZE 4
#define NFS3_FIFO_DEV (-1)
#define NFS3MODE_FMT 0170000
#define NFS3MODE_SOCK 0140000
#define NFS3MODE_FIFO 0010000
-
/* Flags for access() call */
#define NFS3_ACCESS_READ 0x0001
#define NFS3_ACCESS_LOOKUP 0x0002
#define NFS3_ACCESS_EXECUTE 0x0020
/* Flags for create mode */
-#define NFS3_CREATE_UNCHECKED 0
-#define NFS3_CREATE_GUARDED 1
-#define NFS3_CREATE_EXCLUSIVE 2
+enum nfs3_createmode {
+ NFS3_CREATE_UNCHECKED = 0,
+ NFS3_CREATE_GUARDED = 1,
+ NFS3_CREATE_EXCLUSIVE = 2
+};
/* NFSv3 file system properties */
#define NFS3_FSF_LINK 0x0001
};
#define NFS3_VERSION 3
-#define NFSPROC_NULL 0
-#define NFSPROC_GETATTR 1
-#define NFSPROC_SETATTR 2
-#define NFSPROC_ROOT 3
-#define NFSPROC_LOOKUP 4
-#define NFSPROC_READLINK 5
-#define NFSPROC_READ 6
-#define NFSPROC_WRITECACHE 7
-#define NFSPROC_WRITE 8
-#define NFSPROC_CREATE 9
-#define NFSPROC_REMOVE 10
-#define NFSPROC_RENAME 11
-#define NFSPROC_LINK 12
-#define NFSPROC_SYMLINK 13
-#define NFSPROC_MKDIR 14
-#define NFSPROC_RMDIR 15
-#define NFSPROC_READDIR 16
-#define NFSPROC_STATFS 17
+#define NFS3PROC_NULL 0
+#define NFS3PROC_GETATTR 1
+#define NFS3PROC_SETATTR 2
+#define NFS3PROC_LOOKUP 3
+#define NFS3PROC_ACCESS 4
+#define NFS3PROC_READLINK 5
+#define NFS3PROC_READ 6
+#define NFS3PROC_WRITE 7
+#define NFS3PROC_CREATE 8
+#define NFS3PROC_MKDIR 9
+#define NFS3PROC_SYMLINK 10
+#define NFS3PROC_MKNOD 11
+#define NFS3PROC_REMOVE 12
+#define NFS3PROC_RMDIR 13
+#define NFS3PROC_RENAME 14
+#define NFS3PROC_LINK 15
+#define NFS3PROC_READDIR 16
+#define NFS3PROC_READDIRPLUS 17
+#define NFS3PROC_FSSTAT 18
+#define NFS3PROC_FSINFO 19
+#define NFS3PROC_PATHCONF 20
+#define NFS3PROC_COMMIT 21
+
+#define NFS_MNT3_PROGRAM 100005
+#define NFS_MNT3_VERSION 3
+#define MOUNTPROC3_NULL 0
+#define MOUNTPROC3_MNT 1
+#define MOUNTPROC3_UMNT 3
+#define MOUNTPROC3_UMNTALL 4
+
#if defined(__KERNEL__) || defined(NFS_NEED_KERNEL_TYPES)
/* Number of 32bit words in post_op_attr */
#define NFS3_POST_OP_ATTR_WORDS 22
-struct nfs3_fattr {
- enum nfs3_ftype type;
- __u32 mode;
- __u32 nlink;
- __u32 uid;
- __u32 gid;
- __u64 size;
- __u64 used;
- __u32 rdev_maj;
- __u32 rdev_min;
- __u32 fsid;
- __u32 fileid;
- struct nfs_time atime;
- struct nfs_time mtime;
- struct nfs_time ctime;
-};
+#ifdef NFS_NEED_NFS3_XDR_TYPES
-struct nfs3_wcc_attr {
- __u64 size;
- struct nfs_time mtime;
- struct nfs_time ctime;
-};
-
-struct nfs3_wcc_data {
- struct nfs3_wcc_attr before;
- struct nfs3_wcc_attr after;
-};
-
-struct nfs3_sattr {
- __u32 valid;
- __u32 mode;
- __u32 uid;
- __u32 gid;
- __u64 size;
- struct nfs_time atime;
- struct nfs_time mtime;
-};
-
-struct nfs3_entry {
- __u32 fileid;
- char * name;
- unsigned int length;
- __u32 cookie;
- __u32 eof;
+struct nfs3_sattrargs {
+ struct nfs_fh * fh;
+ struct iattr * sattr;
+ unsigned int guard;
+ __u64 guardtime;
};
-struct nfs3_fsinfo {
- __u32 tsize;
- __u32 bsize;
- __u32 blocks;
- __u32 bfree;
- __u32 bavail;
+struct nfs3_diropargs {
+ struct nfs_fh * fh;
+ const char * name;
+ int len;
};
-#ifdef NFS_NEED_XDR_TYPES
-
-struct nfs3_sattrargs {
+struct nfs3_accessargs {
struct nfs_fh * fh;
- struct nfs_sattr * sattr;
+ __u32 access;
};
-struct nfs3_diropargs {
+struct nfs3_createargs {
struct nfs_fh * fh;
const char * name;
+ int len;
+ struct iattr * sattr;
+ enum nfs3_createmode createmode;
+ __u32 verifier[2];
};
-struct nfs3_readargs {
+struct nfs3_mkdirargs {
struct nfs_fh * fh;
- __u32 offset;
- __u32 count;
- void * buffer;
+ const char * name;
+ int len;
+ struct iattr * sattr;
};
-struct nfs3_writeargs {
- struct nfs_fh * fh;
- __u32 offset;
- __u32 count;
- const void * buffer;
+struct nfs3_symlinkargs {
+ struct nfs_fh * fromfh;
+ const char * fromname;
+ int fromlen;
+ const char * topath;
+ int tolen;
+ struct iattr * sattr;
};
-struct nfs3_createargs {
+struct nfs3_mknodargs {
struct nfs_fh * fh;
const char * name;
- struct nfs_sattr * sattr;
+ int len;
+ enum nfs3_ftype type;
+ struct iattr * sattr;
+ dev_t rdev;
};
struct nfs3_renameargs {
struct nfs_fh * fromfh;
const char * fromname;
+ int fromlen;
struct nfs_fh * tofh;
const char * toname;
+ int tolen;
};
struct nfs3_linkargs {
struct nfs_fh * fromfh;
struct nfs_fh * tofh;
const char * toname;
-};
-
-struct nfs3_symlinkargs {
- struct nfs_fh * fromfh;
- const char * fromname;
- const char * topath;
- struct nfs_sattr * sattr;
+ int tolen;
};
struct nfs3_readdirargs {
struct nfs_fh * fh;
- __u32 cookie;
+ __u64 cookie;
+ __u32 verf[2];
void * buffer;
unsigned int bufsiz;
+ int plus;
};
-struct nfs3_diropok {
+struct nfs3_diropres {
+ struct nfs_fattr * dir_attr;
struct nfs_fh * fh;
struct nfs_fattr * fattr;
};
-struct nfs3_readres {
+struct nfs3_accessres {
struct nfs_fattr * fattr;
- unsigned int count;
+ __u32 access;
};
-struct nfs3_readlinkres {
- char ** string;
- unsigned int * lenp;
- unsigned int maxlen;
+struct nfs3_readlinkargs {
+ struct nfs_fh * fh;
void * buffer;
+ unsigned int bufsiz;
};
-struct nfs3_readdirres {
+struct nfs3_readlinkres {
+ struct nfs_fattr * fattr;
void * buffer;
unsigned int bufsiz;
};
-/*
- * The following are for NFSv3
- */
-struct nfs3_fh {
- __u32 size;
- __u8 data[NFS3_FHSIZE]
+struct nfs3_renameres {
+ struct nfs_fattr * fromattr;
+ struct nfs_fattr * toattr;
+};
+
+struct nfs3_linkres {
+ struct nfs_fattr * dir_attr;
+ struct nfs_fattr * fattr;
};
-struct nfs3_wcc_attr {
- __u64 size;
- struct nfs_time mtime;
- struct nfs_time ctime;
+struct nfs3_readdirres {
+ struct nfs_fattr * dir_attr;
+ __u32 * verf;
+ void * buffer;
+ unsigned int bufsiz;
+ int plus;
};
#endif /* NFS_NEED_XDR_TYPES */
-#endif /* __KERNEL__ */
-#endif
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_NFS3_H */
#define _LINUX_NFS_FS_H
#include <linux/config.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/pagemap.h>
#include <linux/in.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+#include <linux/sunrpc/debug.h>
-#include <linux/sunrpc/sched.h>
#include <linux/nfs.h>
-#include <linux/nfs_mount.h>
+#include <linux/nfs2.h>
+#include <linux/nfs_xdr.h>
/*
* Enable debugging support for nfs client.
#define NFS_REQUESTLIST(inode) (NFS_SERVER(inode)->rw_requests)
#define NFS_ADDR(inode) (RPC_PEERADDR(NFS_CLIENT(inode)))
#define NFS_CONGESTED(inode) (RPC_CONGESTED(NFS_CLIENT(inode)))
-
+#define NFS_COOKIEVERF(inode) ((inode)->u.nfs_i.cookieverf)
#define NFS_READTIME(inode) ((inode)->u.nfs_i.read_cache_jiffies)
-#define NFS_OLDMTIME(inode) ((inode)->u.nfs_i.read_cache_mtime)
+#define NFS_CACHE_CTIME(inode) ((inode)->u.nfs_i.read_cache_ctime)
+#define NFS_CACHE_MTIME(inode) ((inode)->u.nfs_i.read_cache_mtime)
+#define NFS_CACHE_ATIME(inode) ((inode)->u.nfs_i.read_cache_atime)
+#define NFS_CACHE_ISIZE(inode) ((inode)->u.nfs_i.read_cache_isize)
#define NFS_NEXTSCAN(inode) ((inode)->u.nfs_i.nextscan)
#define NFS_CACHEINV(inode) \
do { \
- NFS_READTIME(inode) = jiffies - 1000000; \
- NFS_OLDMTIME(inode) = 0; \
+ NFS_READTIME(inode) = jiffies - NFS_MAXATTRTIMEO(inode) - 1; \
} while (0)
#define NFS_ATTRTIMEO(inode) ((inode)->u.nfs_i.attrtimeo)
#define NFS_MINATTRTIMEO(inode) \
#define NFS_MAXATTRTIMEO(inode) \
(S_ISDIR(inode->i_mode)? NFS_SERVER(inode)->acdirmax \
: NFS_SERVER(inode)->acregmax)
+#define NFS_ATTRTIMEO_UPDATE(inode) ((inode)->u.nfs_i.attrtimeo_timestamp)
#define NFS_FLAGS(inode) ((inode)->u.nfs_i.flags)
#define NFS_REVALIDATING(inode) (NFS_FLAGS(inode) & NFS_INO_REVALIDATING)
-#define NFS_COOKIES(inode) ((inode)->u.nfs_i.cookies)
-#define NFS_DIREOF(inode) ((inode)->u.nfs_i.direof)
#define NFS_FILEID(inode) ((inode)->u.nfs_i.fileid)
#define NFS_FSID(inode) ((inode)->u.nfs_i.fsid)
+/* Inode Flags */
+#define NFS_USE_READDIRPLUS(inode) ((NFS_FLAGS(inode) & NFS_INO_ADVISE_RDPLUS) ? 1 : 0)
+#define NFS_MONOTONE_COOKIES(inode) ((NFS_SERVER(inode)->flags & NFS_NONMONOTONE_COOKIES) ? 0 : 1)
+
/*
* These are the default flags for swap requests
*/
}
#ifdef __KERNEL__
-
-/*
- * linux/fs/nfs/proc.c
- */
-extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr);
-extern int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr, struct iattr *sattr);
-extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir,
- const char *name, struct nfs_fh *fhandle,
- struct nfs_fattr *fattr);
-extern int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
- int swap, unsigned long offset, unsigned int count,
- void *buffer, struct nfs_fattr *fattr);
-extern int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
- int swap, unsigned long offset, unsigned int count,
- const void *buffer, struct nfs_fattr *fattr);
-extern int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
- const char *name, struct iattr *sattr,
- struct nfs_fh *fhandle, struct nfs_fattr *fattr);
-extern int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir,
- const char *name);
-extern int nfs_proc_rename(struct nfs_server *server,
- struct nfs_fh *old_dir, const char *old_name,
- struct nfs_fh *new_dir, const char *new_name);
-extern int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fh *dir, const char *name);
-extern int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
- const char *name, const char *path,
- struct iattr *sattr);
-extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
- const char *name, struct iattr *sattr,
- struct nfs_fh *fhandle, struct nfs_fattr *fattr);
-extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
- const char *name);
-extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
- struct nfs_fsinfo *res);
-
-
/*
* linux/fs/nfs/inode.c
*/
extern struct inode_operations nfs_dir_inode_operations;
extern struct file_operations nfs_dir_operations;
extern struct dentry_operations nfs_dentry_operations;
-extern void nfs_flush_dircache(struct inode *);
-extern void nfs_free_dircache(struct inode *);
/*
* linux/fs/nfs/symlink.c
static inline int
nfs_wb_page(struct inode *inode, struct page* page)
{
- int error = nfs_sync_file(inode, 0, page_offset(page), PAGE_CACHE_SIZE, FLUSH_WAIT | FLUSH_STABLE);
+ int error = nfs_sync_file(inode, 0, page_index(page), 1, FLUSH_WAIT | FLUSH_STABLE);
return (error < 0) ? error : 0;
}
return __nfs_revalidate_inode(server, dentry);
}
+static inline loff_t
+nfs_size_to_loff_t(__u64 size)
+{
+ loff_t maxsz = (((loff_t) ULONG_MAX) << PAGE_CACHE_SHIFT) + PAGE_CACHE_SIZE - 1;
+ if (size > maxsz)
+ return maxsz;
+ return (loff_t) size;
+}
+
+static inline ino_t
+nfs_fileid_to_ino_t(u64 fileid)
+{
+ ino_t ino = (ino_t) fileid;
+ if (sizeof(ino_t) < sizeof(u64))
+ ino ^= fileid >> (sizeof(u64)-sizeof(ino_t)) * 8;
+ return ino;
+}
+
+static inline time_t
+nfs_time_to_secs(__u64 time)
+{
+ return (time_t)(time >> 32);
+}
+
/* NFS root */
extern int nfs_root_mount(struct super_block *sb);
#ifndef _NFS_FS_I
#define _NFS_FS_I
-#include <linux/nfs.h>
-#include <linux/pipe_fs_i.h>
+#include <asm/types.h>
+#include <linux/list.h>
/*
* nfs fs inode data in memory
/*
* The 64bit 'inode number'
*/
- __u32 fsid;
- __u32 fileid;
+ __u64 fsid;
+ __u64 fileid;
/*
* Various flags
* mtime != read_cache_mtime
*/
unsigned long read_cache_jiffies;
- unsigned long read_cache_mtime;
+ __u64 read_cache_ctime;
+ __u64 read_cache_mtime;
+ __u64 read_cache_atime;
+ __u64 read_cache_isize;
unsigned long attrtimeo;
+ unsigned long attrtimeo_timestamp;
+
+ /*
+ * This is the cookie verifier used for NFSv3 readdir
+ * operations
+ */
+ __u32 cookieverf[2];
/*
* This is the list of dirty unwritten pages.
struct inode *hash_next,
*hash_prev;
unsigned long nextscan;
-
- /* Readdir caching information. */
- void *cookies;
- u32 direof;
};
/*
* Legal inode flag values
*/
+#define NFS_INO_ADVISE_RDPLUS 0x0002 /* advise readdirplus */
#define NFS_INO_REVALIDATING 0x0004 /* revalidating attrs */
#define NFS_IS_SNAPSHOT 0x0010 /* a snapshot file */
#define NFS_INO_FLUSH 0x0020 /* inode is due for flushing */
#ifndef _NFS_FS_SB
#define _NFS_FS_SB
-#include <linux/nfs.h>
-#include <linux/in.h>
-
/*
* NFS client parameters stored in the superblock.
*/
struct nfs_server {
struct rpc_clnt * client; /* RPC client handle */
int flags; /* various flags */
- int rsize; /* read size */
- int wsize; /* write size */
+ unsigned int rsize; /* read size */
+ unsigned int wsize; /* write size */
+ unsigned int dtsize; /* readdir size */
unsigned int bsize; /* server block size */
unsigned int acregmin; /* attr cache timeouts */
unsigned int acregmax;
*/
struct nfs_sb_info {
struct nfs_server s_server;
- struct nfs_fh s_root;
};
#endif
*
* structure passed from user-space to kernel-space during an nfs mount
*/
+#include <linux/in.h>
+#include <linux/nfs.h>
/*
* WARNING! Do not delete or change the order of these fields. If
#define NFS_MOUNT_VER3 0x0080 /* 3 */
#define NFS_MOUNT_KERBEROS 0x0100 /* 3 */
#define NFS_MOUNT_NONLM 0x0200 /* 3 */
+#define NFS_MOUNT_FLAGMASK 0xFFFF
+
+/*
+ * Private flags - not to be set by mount program
+ */
+#ifdef __KERNEL__
+#define NFS_NONMONOTONE_COOKIES 0x00010000
+#endif /* __KERNEL__ */
#endif
--- /dev/null
+#ifndef _LINUX_NFS_XDR_H
+#define _LINUX_NFS_XDR_H
+
+extern struct rpc_program nfs_program;
+extern struct rpc_stat nfs_rpcstat;
+
+struct nfs_fattr {
+ unsigned short valid; /* which fields are valid */
+ __u64 pre_size; /* pre_op_attr.size */
+ __u64 pre_mtime; /* pre_op_attr.mtime */
+ __u64 pre_ctime; /* pre_op_attr.ctime */
+ enum nfs_ftype type; /* always use NFSv2 types */
+ __u32 mode;
+ __u32 nlink;
+ __u32 uid;
+ __u32 gid;
+ __u64 size;
+ union {
+ struct {
+ __u32 blocksize;
+ __u32 blocks;
+ } nfs2;
+ struct {
+ __u64 used;
+ } nfs3;
+ } du;
+ __u32 rdev;
+ __u64 fsid;
+ __u64 fileid;
+ __u64 atime;
+ __u64 mtime;
+ __u64 ctime;
+};
+
+#define NFS_ATTR_WCC 0x0001 /* pre-op WCC data */
+#define NFS_ATTR_FATTR 0x0002 /* post-op attributes */
+#define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */
+
+struct nfs_fsinfo {
+ __u32 tsize;
+ __u32 bsize;
+ __u32 blocks;
+ __u32 bfree;
+ __u32 bavail;
+};
+
+/* Arguments to the write call.
+ * Note that NFS_WRITE_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
+ */
+#define NFS_WRITE_MAXIOV 8
+struct nfs_writeargs {
+ struct nfs_fh * fh;
+ __u32 offset;
+ __u32 count;
+ enum nfs3_stable_how stable;
+ unsigned int nriov;
+ struct iovec iov[NFS_WRITE_MAXIOV];
+};
+
+struct nfs_writeverf {
+ enum nfs3_stable_how committed;
+ __u32 verifier[2];
+};
+
+struct nfs_writeres {
+ struct nfs_fattr * fattr;
+ struct nfs_writeverf * verf;
+ __u32 count;
+};
+
+struct nfs_readargs {
+ struct nfs_fh * fh;
+ __u32 offset;
+ __u32 count;
+ void * buffer;
+};
+
+struct nfs_readres {
+ struct nfs_fattr * fattr;
+ unsigned int count;
+};
+
+/*
+ * Argument struct for decode_entry function
+ */
+struct nfs_entry {
+ struct page * page;
+ __u64 ino;
+ __u64 cookie,
+ prev_cookie;
+ const char * name;
+ unsigned int len;
+ int eof;
+ struct nfs_fh fh;
+ struct nfs_fattr fattr;
+ unsigned long offset,
+ prev;
+};
+
+/*
+ * The following types are for NFSv2 only.
+ */
+struct nfs_sattrargs {
+ struct nfs_fh * fh;
+ struct iattr * sattr;
+};
+
+struct nfs_diropargs {
+ struct nfs_fh * fh;
+ const char * name;
+};
+
+struct nfs_createargs {
+ struct nfs_fh * fh;
+ const char * name;
+ struct iattr * sattr;
+};
+
+struct nfs_renameargs {
+ struct nfs_fh * fromfh;
+ const char * fromname;
+ struct nfs_fh * tofh;
+ const char * toname;
+};
+
+struct nfs_linkargs {
+ struct nfs_fh * fromfh;
+ struct nfs_fh * tofh;
+ const char * toname;
+};
+
+struct nfs_symlinkargs {
+ struct nfs_fh * fromfh;
+ const char * fromname;
+ const char * topath;
+ struct iattr * sattr;
+};
+
+struct nfs_readdirargs {
+ struct nfs_fh * fh;
+ __u32 cookie;
+ void * buffer;
+ unsigned int bufsiz;
+};
+
+struct nfs_diropok {
+ struct nfs_fh * fh;
+ struct nfs_fattr * fattr;
+};
+
+struct nfs_readlinkargs {
+ struct nfs_fh * fh;
+ const void * buffer;
+};
+
+struct nfs_readdirres {
+ void * buffer;
+ unsigned int bufsiz;
+};
+
+/*
+ * linux/fs/nfs/proc.c
+ */
+extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr);
+extern int nfs_proc_setattr(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr, struct iattr *sattr);
+extern int nfs_proc_lookup(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name, struct nfs_fh *fhandle,
+ struct nfs_fattr *fattr);
+extern int nfs_proc_read(struct nfs_server *server, struct nfs_fh *fhandle,
+ int swap, unsigned long offset, unsigned int count,
+ void *buffer, struct nfs_fattr *fattr);
+extern int nfs_proc_write(struct nfs_server *server, struct nfs_fh *fhandle,
+ int swap, unsigned long offset, unsigned int count,
+ const void *buffer, struct nfs_fattr *fattr);
+extern int nfs_proc_create(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name, struct iattr *sattr,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+extern int nfs_proc_remove(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name);
+extern int nfs_proc_rename(struct nfs_server *server,
+ struct nfs_fh *old_dir, const char *old_name,
+ struct nfs_fh *new_dir, const char *new_name);
+extern int nfs_proc_link(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fh *dir, const char *name);
+extern int nfs_proc_symlink(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name, const char *path,
+ struct iattr *sattr);
+extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name, struct iattr *sattr,
+ struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir,
+ const char *name);
+extern int nfs_proc_readdir(struct dentry *, struct nfs_fattr *,
+ u64 cookie, void *, unsigned int size, int plus);
+extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
+ struct nfs_fsinfo *res);
+extern u32 * nfs_decode_dirent(u32 *p, struct nfs_entry *entry, int plus);
+
+#endif
#define _LINUX_NFSD_CONST_H
#include <linux/nfs.h>
+#include <linux/nfs2.h>
/*
* Maximum protocol version supported by knfsd
*/
#define NFSSVC_MAXBLKSIZE 8192
-#define NFS2_MAXPATHLEN 1024
-#define NFS2_MAXNAMLEN 255
-#define NFS2_FHSIZE NFS_FHSIZE
-#define NFS2_COOKIESIZE 4
-
#define NFS3_MAXPATHLEN PATH_MAX
#define NFS3_MAXNAMLEN NAME_MAX
#define NFS3_FHSIZE 64
struct resource dma_resource[DEVICE_COUNT_DMA];
struct resource irq_resource[DEVICE_COUNT_IRQ];
- char name[48]; /* device name */
+ char name[80]; /* device name */
char slot_name[8]; /* slot name */
int active; /* ISAPnP: device is active */
int ro; /* ISAPnP: read only */
int pci_claim_resource(struct pci_dev *, int);
void pci_assign_unassigned_resources(void);
+void pdev_assign_unassigned_resources(struct pci_dev *dev);
void pci_set_bus_ranges(void);
void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),
int (*)(struct pci_dev *, u8, u8));
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * ==FILEVERSION 20000225==
+ * ==FILEVERSION 20000322==
*/
#include <linux/list.h>
/* Detach a channel from its PPP unit (e.g. on hangup). */
extern void ppp_unregister_channel(struct ppp_channel *);
-/* Get the unit number associated with a channel */
+/* Get the channel number for a channel */
+extern int ppp_channel_index(struct ppp_channel *);
+
+/* Get the unit number associated with a channel, or -1 if none */
extern int ppp_unit_number(struct ppp_channel *);
/*
return (atomic_t *)(skb->end);
}
+/**
+ * skb_queue_empty - check if a queue is empty
+ * @list: queue head
+ *
+ * Returns true if the queue is empty, false otherwise
+ */
+
extern __inline__ int skb_queue_empty(struct sk_buff_head *list)
{
return (list->next == (struct sk_buff *) list);
}
+/**
+ * skb_get - reference buffer
+ * @skb: buffer to reference
+ *
+ * Makes another reference to a socket buffer and returns a pointer
+ * to the buffer.
+ */
+
extern __inline__ struct sk_buff *skb_get(struct sk_buff *skb)
{
atomic_inc(&skb->users);
return skb;
}
-/* If users==1, we are the only owner and are can avoid redundant
+/*
+ * If users==1, we are the only owner and are can avoid redundant
* atomic change.
*/
+
+/**
+ * kfree_skb - free an sk_buff
+ * @skb: The buffer to free
+ *
+ * Drop a reference to the buffer and free it if the usage count has
+ * hit zero.
+ */
+
extern __inline__ void kfree_skb(struct sk_buff *skb)
{
if (atomic_read(&skb->users) == 1 || atomic_dec_and_test(&skb->users))
kfree_skbmem(skb);
}
+/**
+ * skb_cloned - is the buffer a clone
+ * @skb: Buffer to check
+ *
+ * Returns true if the buffer was generated with skb_clone and is
+ * one of multiple shared copies of the buffer. Cloned buffers are
+ * shared data so must not be written to under normal circumstances.
+ */
+
extern __inline__ int skb_cloned(struct sk_buff *skb)
{
return skb->cloned && atomic_read(skb_datarefp(skb)) != 1;
}
+/**
+ * skb_shared - is the buffer shared
+ * @skb: buffer to check
+ *
+ * Returns true if more than one person has a reference to this
+ * buffer.
+ */
+
extern __inline__ int skb_shared(struct sk_buff *skb)
{
return (atomic_read(&skb->users) != 1);
}
+/**
+ * skb_share_check - check if buffer is shared and if so clone it
+ * @skb: buffer to check
+ * @pri: priority for memory allocation
+ *
+ * If the buffer is shared the buffer is cloned and the old copy
+ * drops a reference. A new clone with a single reference is returned.
+ * If the buffer is not shared the original buffer is returned. When
+ * being called from interrupt status or with spinlocks held pri must
+ * be GFP_ATOMIC.
+ *
+ * NULL is returned on a memory allocation failure.
+ */
+
extern __inline__ struct sk_buff *skb_share_check(struct sk_buff *skb, int pri)
{
if (skb_shared(skb)) {
* a packet thats being forwarded.
*/
+/**
+ * skb_unshare - make a copy of a shared buffer
+ * @skb: buffer to check
+ * @pri: priority for memory allocation
+ *
+ * If the socket buffer is a clone then this function creates a new
+ * copy of the data, drops a reference count on the old copy and returns
+ * the new copy with the reference count at 1. If the buffer is not a clone
+ * the original buffer is returned. When called with a spinlock held or
+ * from interrupt state pri must be GFP_ATOMIC
+ *
+ * NULL is returned on a memory allocation failure.
+ */
+
extern __inline__ struct sk_buff *skb_unshare(struct sk_buff *skb, int pri)
{
struct sk_buff *nskb;
return nskb;
}
-/*
+/**
+ * skb_peek
+ * @list_: list to peek at
+ *
* Peek an sk_buff. Unlike most other operations you _MUST_
* be careful with this one. A peek leaves the buffer on the
- * list and someone else may run off with it. For an interrupt
- * type system cli() peek the buffer copy the data and sti();
+ * list and someone else may run off with it. You must hold
+ * the appropriate locks or have a private queue to do this.
+ *
+ * Returns NULL for an empty list or a pointer to the head element.
+ * The reference count is not incremented and the reference is therefore
+ * volatile. Use with caution.
*/
extern __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
return list;
}
+/**
+ * skb_peek_tail
+ * @list_: list to peek at
+ *
+ * Peek an sk_buff. Unlike most other operations you _MUST_
+ * be careful with this one. A peek leaves the buffer on the
+ * list and someone else may run off with it. You must hold
+ * the appropriate locks or have a private queue to do this.
+ *
+ * Returns NULL for an empty list or a pointer to the tail element.
+ * The reference count is not incremented and the reference is therefore
+ * volatile. Use with caution.
+ */
+
extern __inline__ struct sk_buff *skb_peek_tail(struct sk_buff_head *list_)
{
struct sk_buff *list = ((struct sk_buff *)list_)->prev;
return list;
}
-/*
- * Return the length of an sk_buff queue
+/**
+ * skb_queue_len - get queue length
+ * @list_: list to measure
+ *
+ * Return the length of an sk_buff queue.
*/
extern __inline__ __u32 skb_queue_len(struct sk_buff_head *list_)
* can only be called with interrupts disabled.
*/
+/**
+ * __skb_queue_head - queue a buffer at the list head
+ * @list: list to use
+ * @newsk: buffer to queue
+ *
+ * Queue a buffer at the start of a list. This function takes no locks
+ * and you must therefore hold required locks before calling it.
+ *
+ * A buffer cannot be placed on two lists at the same time.
+ */
+
extern __inline__ void __skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
{
struct sk_buff *prev, *next;
prev->next = newsk;
}
+
+/**
+ * skb_queue_head - queue a buffer at the list head
+ * @list: list to use
+ * @newsk: buffer to queue
+ *
+ * Queue a buffer at the start of the list. This function takes the
+ * list lock and can be used safely with other locking sk_buff functions
+ * safely.
+ *
+ * A buffer cannot be placed on two lists at the same time.
+ */
+
extern __inline__ void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
{
unsigned long flags;
spin_unlock_irqrestore(&list->lock, flags);
}
-/*
- * Insert an sk_buff at the end of a list.
- */
+/**
+ * __skb_queue_tail - queue a buffer at the list tail
+ * @list: list to use
+ * @newsk: buffer to queue
+ *
+ * Queue a buffer at the end of a list. This function takes no locks
+ * and you must therefore hold required locks before calling it.
+ *
+ * A buffer cannot be placed on two lists at the same time.
+ */
+
extern __inline__ void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
{
prev->next = newsk;
}
+/**
+ * skb_queue_tail - queue a buffer at the list tail
+ * @list: list to use
+ * @newsk: buffer to queue
+ *
+ * Queue a buffer at the tail of the list. This function takes the
+ * list lock and can be used safely with other locking sk_buff functions
+ * safely.
+ *
+ * A buffer cannot be placed on two lists at the same time.
+ */
+
extern __inline__ void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
{
unsigned long flags;
spin_unlock_irqrestore(&list->lock, flags);
}
-/*
- * Remove an sk_buff from a list.
+/**
+ * __skb_dequeue - remove from the head of the queue
+ * @list: list to dequeue from
+ *
+ * Remove the head of the list. This function does not take any locks
+ * so must be used with appropriate locks held only. The head item is
+ * returned or NULL if the list is empty.
*/
extern __inline__ struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
return result;
}
+/**
+ * skb_dequeue - remove from the head of the queue
+ * @list: list to dequeue from
+ *
+ * Remove the head of the list. The list lock is taken so the function
+ * may be used safely with other locking list functions. The head item is
+ * returned or NULL if the list is empty.
+ */
+
extern __inline__ struct sk_buff *skb_dequeue(struct sk_buff_head *list)
{
long flags;
list->qlen++;
}
-/*
- * Place a packet before a given packet in a list
+/**
+ * skb_insert - insert a buffer
+ * @old: buffer to insert before
+ * @newsk: buffer to insert
+ *
+ * Place a packet before a given packet in a list. The list locks are taken
+ * and this function is atomic with respect to other list locked calls
+ * A buffer cannot be placed on two lists at the same time.
*/
+
extern __inline__ void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
__skb_insert(newsk, old, old->next, old->list);
}
+/**
+ * skb_append - append a buffer
+ * @old: buffer to insert after
+ * @newsk: buffer to insert
+ *
+ * Place a packet after a given packet in a list. The list locks are taken
+ * and this function is atomic with respect to other list locked calls.
+ * A buffer cannot be placed on two lists at the same time.
+ */
+
+
extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
* remove sk_buff from list. _Must_ be called atomically, and with
* the list known..
*/
+
extern __inline__ void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
{
struct sk_buff * next, * prev;
prev->next = next;
}
-/*
- * Remove an sk_buff from its list. Works even without knowing the list it
- * is sitting on, which can be handy at times. It also means that THE LIST
- * MUST EXIST when you unlink. Thus a list must have its contents unlinked
- * _FIRST_.
+/**
+ * skb_unlink - remove a buffer from a list
+ * @skb: buffer to remove
+ *
+ * Place a packet after a given packet in a list. The list locks are taken
+ * and this function is atomic with respect to other list locked calls
+ *
+ * Works even without knowing the list it is sitting on, which can be
+ * handy at times. It also means that THE LIST MUST EXIST when you
+ * unlink. Thus a list must have its contents unlinked before it is
+ * destroyed.
*/
extern __inline__ void skb_unlink(struct sk_buff *skb)
}
/* XXX: more streamlined implementation */
+
+/**
+ * __skb_dequeue_tail - remove from the tail of the queue
+ * @list: list to dequeue from
+ *
+ * Remove the tail of the list. This function does not take any locks
+ * so must be used with appropriate locks held only. The tail item is
+ * returned or NULL if the list is empty.
+ */
+
extern __inline__ struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list)
{
struct sk_buff *skb = skb_peek_tail(list);
return skb;
}
+/**
+ * skb_dequeue - remove from the head of the queue
+ * @list: list to dequeue from
+ *
+ * Remove the head of the list. The list lock is taken so the function
+ * may be used safely with other locking list functions. The tail item is
+ * returned or NULL if the list is empty.
+ */
+
extern __inline__ struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list)
{
long flags;
return tmp;
}
+/**
+ * skb_put - add data to a buffer
+ * @skb: buffer to use
+ * @len: amount of data to add
+ *
+ * This function extends the used data area of the buffer. If this would
+ * exceed the total buffer size the kernel will panic. A pointer to the
+ * first byte of the extra data is returned
+ */
+
extern __inline__ unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
unsigned char *tmp=skb->tail;
return skb->data;
}
+/**
+ * skb_push - add data to the start of a buffer
+ * @skb: buffer to use
+ * @len: amount of data to add
+ *
+ * This function extends the used data area of the buffer at the buffer
+ * start. If this would exceed the total buffer headroom the kernel will
+ * panic. A pointer to the first byte of the extra data is returned
+ */
+
extern __inline__ unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data-=len;
return skb->data+=len;
}
+/**
+ * skb_pull - remove data from the start of a buffer
+ * @skb: buffer to use
+ * @len: amount of data to remove
+ *
+ * This function removes data from the start of a buffer, returning
+ * the memory to the headroom. A pointer to the next data in the buffer
+ * is returned. Once the data has been pulled future pushes will overwrite
+ * the old data
+ */
+
extern __inline__ unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)
{
if (len > skb->len)
return __skb_pull(skb,len);
}
+/**
+ * skb_headroom - bytes at buffer head
+ * @skb: buffer to check
+ *
+ * Return the number of bytes of free space at the head of an sk_buff
+ */
+
extern __inline__ int skb_headroom(const struct sk_buff *skb)
{
return skb->data-skb->head;
}
+/**
+ * skb_tailroom - bytes at buffer end
+ * @skb: buffer to check
+ *
+ * Return the number of bytes of free space at the tail of an sk_buff
+ */
+
extern __inline__ int skb_tailroom(const struct sk_buff *skb)
{
return skb->end-skb->tail;
}
+/**
+ * skb_reserve - adjust headroom
+ * @skb: buffer to alter
+ * @len: bytes to move
+ *
+ * Increase the headroom of an empty sk_buff by reducing the tail
+ * room. This is only allowed for an empty buffer.
+ */
+
extern __inline__ void skb_reserve(struct sk_buff *skb, unsigned int len)
{
skb->data+=len;
skb->tail+=len;
}
+
extern __inline__ void __skb_trim(struct sk_buff *skb, unsigned int len)
{
skb->len = len;
skb->tail = skb->data+len;
}
+/**
+ * skb_trim - remove end from a buffer
+ * @skb: buffer to alter
+ * @len: new length
+ *
+ * Cut the length of a buffer down by removing data from the tail. If
+ * the buffer is already under the length specified it is not modified.
+ */
+
extern __inline__ void skb_trim(struct sk_buff *skb, unsigned int len)
{
if (skb->len > len) {
}
}
+/**
+ * skb_orphan - orphan a buffer
+ * @skb: buffer to orphan
+ *
+ * If a buffer currently has an owner then we call the owners
+ * destructor function and make the skb unowned. The buffer continues
+ * to exist but is no longer charged to its former owner.
+ */
+
+
extern __inline__ void skb_orphan(struct sk_buff *skb)
{
if (skb->destructor)
skb->sk = NULL;
}
+/**
+ * skb_purge - empty a list
+ * @list: list to empty
+ *
+ * Delete all buffers on an sk_buff list. Each buffer is removed from
+ * the list and one reference dropped. This function takes the list
+ * lock and is atomic with respect to other list locking functions.
+ */
+
+
extern __inline__ void skb_queue_purge(struct sk_buff_head *list)
{
struct sk_buff *skb;
kfree_skb(skb);
}
+/**
+ * __skb_purge - empty a list
+ * @list: list to empty
+ *
+ * Delete all buffers on an sk_buff list. Each buffer is removed from
+ * the list and one reference dropped. This function does not take the
+ * list lock and the caller must hold the relevant locks to use it.
+ */
+
+
extern __inline__ void __skb_queue_purge(struct sk_buff_head *list)
{
struct sk_buff *skb;
kfree_skb(skb);
}
+/**
+ * dev_alloc_skb - allocate an skbuff for sending
+ * @length: length to allocate
+ *
+ * Allocate a new sk_buff and assign it a usage count of one. The
+ * buffer has unspecified headroom built in. Users should allocate
+ * the headroom they think they need without accounting for the
+ * built in space. The built in space is used for optimisations.
+ *
+ * NULL is returned in there is no free memory. Although this function
+ * allocates memory it can be called from an interrupt.
+ */
+
extern __inline__ struct sk_buff *dev_alloc_skb(unsigned int length)
{
struct sk_buff *skb;
return skb;
}
+/**
+ * skb_cow - copy a buffer if need be
+ * @skb: buffer to copy
+ * @headroom: needed headroom
+ *
+ * If the buffer passed lacks sufficient headroom or is a clone then
+ * it is copied and the additional headroom made available. If there
+ * is no free memory NULL is returned. The new buffer is returned if
+ * a copy was made (and the old one dropped a reference). The existing
+ * buffer is returned otherwise.
+ *
+ * This function primarily exists to avoid making two copies when making
+ * a writable copy of a buffer and then growing the headroom.
+ */
+
+
extern __inline__ struct sk_buff *
skb_cow(struct sk_buff *skb, unsigned int headroom)
{
#ifndef SUNRPC_SVC_H
#define SUNRPC_SVC_H
+#include <linux/in.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcauth.h>
#ifndef __LINUX_USB_H
#define __LINUX_USB_H
-#include <linux/types.h>
-#include <linux/ioctl.h>
-#include <linux/version.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h> /* for in_interrupt() */
-
/* USB constants */
/*
#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h> /* for in_interrupt() */
#include <linux/config.h>
#include <linux/list.h>
*/
typedef int (*usb_device_irq)(int, void *, int, void *);
-/* -------------------------------------------------------------------------------------*
- * New USB Structures *
- * -------------------------------------------------------------------------------------*/
-
+/*----------------------------------------------------------------------------*
+ * New USB Structures *
+ *----------------------------------------------------------------------------*/
#define USB_DISABLE_SPD 0x0001
#define USB_ISO_ASAP 0x0002
usb_device_irq handler;
};
-/* ------------------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
struct usb_operations {
int (*allocate)(struct usb_device *);
__u8 dup_acks; /* Consequetive duplicate acks seen from other end */
__u8 retransmits;
- __u16 __empty1;
+ __u8 __empty1;
+ __u8 sorry;
__u8 defer_accept;
/* RTT measurement */
if ((__sk)->backlog.tail != NULL) \
__release_sock(__sk); \
(__sk)->lock.users = 0; \
- wake_up(&((__sk)->lock.wq)); \
+ if (waitqueue_active(&((__sk)->lock.wq))) wake_up(&((__sk)->lock.wq)); \
spin_unlock_bh(&((__sk)->lock.slock)); \
} while(0)
return amt;
}
+extern __inline__ void sk_wake_async(struct sock *sk, int how, int band)
+{
+ if (sk->socket && sk->socket->fasync_list)
+ sock_wake_async(sk->socket, how, band);
+}
+
#define SOCK_MIN_SNDBUF 2048
#define SOCK_MIN_RCVBUF 128
/* Must be less or equal SOCK_MIN_SNDBUF */
return waitall ? len : min(sk->rcvlowat, len);
}
+/* Alas, with timeout socket operations are not restartable.
+ * Compare this to poll().
+ */
+extern __inline__ int sock_intr_errno(long timeo)
+{
+ return timeo == MAX_SCHEDULE_TIMEOUT ? -ERESTARTSYS : -EINTR;
+}
+
/*
* Enable debug/info messages
*/
struct tcphdr *th, int len,
struct sk_buff *skb);
-extern void tcp_v4_send_reset(struct sk_buff *skb);
-
extern int tcp_v4_conn_request(struct sock *sk,
struct sk_buff *skb);
#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))
+/*
+ * Compute minimal free write space needed to queue new packets.
+ */
+#define tcp_min_write_space(__sk) \
+ (atomic_read(&(__sk)->wmem_alloc) / 2)
+
+
/* This determines how many packets are "in the network" to the best
* of our knowledge. In many cases it is conservative, but where
* detailed information is available from the receiver (via SACK
if (sk->rcvbuf < 3*rcvbuf)
sk->rcvbuf = min (3*rcvbuf, sysctl_rmem_max);
+
+ /* Reserve slack space to reduce jitter of advertised window. */
+ if (tp->window_clamp >= tcp_full_space(sk)) {
+ int nwin = tcp_full_space(sk) - tp->mss_clamp;
+
+ if (nwin >= MAX_TCP_WINDOW && nwin >= 2*tp->advmss)
+ tp->window_clamp = nwin;
+ }
+
if (sk->sndbuf < 3*sndbuf)
sk->sndbuf = min (3*sndbuf, sysctl_wmem_max);
}
Version 2 and 3 extensions to driver:
* Copyright (C) 1998 - 2000 Douglas Gilbert
- Version: 3.1.12 (20000222)
+ Version: 3.1.13 (20000323)
This version is for 2.3/2.4 series kernels.
+ 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
additional property than during indirect
IO the user buffer is copied into the
kernel buffers before the transfer */
+#define SG_DXFER_UNKNOWN -5 /* Unknown data direction */
/* following flag values can be "or"-ed together */
#define SG_FLAG_DIRECT_IO 1 /* default is indirect IO */
*/
static inline void __schedule_tail(struct task_struct *prev)
{
+ current->need_resched |= prev->need_resched;
#ifdef __SMP__
if ((prev->state == TASK_RUNNING) &&
(prev != idle_task(smp_processor_id()))) {
* Should be invoked with paramters (0, 0, unsigned long *[], start_paddr).
*/
void __init free_area_init_node(int nid, pg_data_t *pgdat,
- unsigned long *zones_size, unsigned long zone_start_paddr)
+ unsigned long *zones_size, unsigned long zone_start_paddr,
+ unsigned long *zholes_size)
{
free_area_init_core(0, NODE_DATA(0), &mem_map, zones_size,
- zone_start_paddr);
+ zone_start_paddr, zholes_size);
}
#endif /* !CONFIG_DISCONTIGMEM */
* Nodes can be initialized parallely, in no particular order.
*/
void __init free_area_init_node(int nid, pg_data_t *pgdat,
- unsigned long *zones_size, unsigned long zone_start_paddr)
+ unsigned long *zones_size, unsigned long zone_start_paddr,
+ unsigned long *zholes_size)
{
int i, size = 0;
struct page *discard;
if (mem_map == (mem_map_t *)NULL)
mem_map = (mem_map_t *)PAGE_OFFSET;
- free_area_init_core(nid, pgdat, &discard, zones_size, zone_start_paddr);
+ free_area_init_core(nid, pgdat, &discard, zones_size, zone_start_paddr,
+ zholes_size);
pgdat->node_id = nid;
/*
* - clear the memory bitmaps
*/
void __init free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,
- unsigned long *zones_size, unsigned long zone_start_paddr)
+ unsigned long *zones_size, unsigned long zone_start_paddr,
+ unsigned long *zholes_size)
{
struct page *p, *lmem_map;
unsigned long i, j;
unsigned long map_size;
- unsigned long totalpages, offset;
+ unsigned long totalpages, offset, realtotalpages;
unsigned int cumulative = 0;
pgdat->node_next = pgdat_list;
unsigned long size = zones_size[i];
totalpages += size;
}
- printk("On node %d totalpages: %lu\n", nid, totalpages);
+ realtotalpages = totalpages;
+ if (zholes_size)
+ for (i = 0; i < MAX_NR_ZONES; i++)
+ realtotalpages -= zholes_size[i];
+
+ printk("On node %d totalpages: %lu\n", nid, realtotalpages);
/*
* Select nr of pages we try to keep free for important stuff
* This is fairly arbitrary, but based on some behaviour
* analysis.
*/
- i = totalpages >> 7;
+ i = realtotalpages >> 7;
if (i < 10)
i = 10;
if (i > 256)
for (j = 0; j < MAX_NR_ZONES; j++) {
zone_t *zone = pgdat->node_zones + j;
unsigned long mask;
- unsigned long size;
+ unsigned long size, realsize;
- size = zones_size[j];
+ realsize = size = zones_size[j];
+ if (zholes_size)
+ realsize -= zholes_size[j];
printk("zone(%lu): %lu pages.\n", j, size);
zone->size = size;
zone->name = zone_names[j];
zone->lock = SPIN_LOCK_UNLOCKED;
zone->zone_pgdat = pgdat;
+ zone->free_pages = 0;
if (!size)
continue;
zone->offset = offset;
cumulative += size;
- mask = (size / zone_balance_ratio[j]);
+ mask = (realsize / zone_balance_ratio[j]);
if (mask < zone_balance_min[j])
mask = zone_balance_min[j];
else if (mask > zone_balance_max[j])
void __init free_area_init(unsigned long *zones_size)
{
- free_area_init_core(0, NODE_DATA(0), &mem_map, zones_size, 0);
+ free_area_init_core(0, NODE_DATA(0), &mem_map, zones_size, 0, 0);
}
static int __init setup_mem_frac(char *str)
* the processes needing more memory will wake us
* up on a more timely basis.
*/
- do {
- pgdat = pgdat_list;
- while (pgdat) {
- for (i = 0; i < MAX_NR_ZONES; i++) {
- zone = pgdat->node_zones + i;
- if ((!zone->size) || (!zone->zone_wake_kswapd))
- continue;
- do_try_to_free_pages(GFP_KSWAPD, zone);
- }
- pgdat = pgdat->node_next;
+ pgdat = pgdat_list;
+ while (pgdat) {
+ for (i = 0; i < MAX_NR_ZONES; i++) {
+ zone = pgdat->node_zones + i;
+ if (tsk->need_resched)
+ schedule();
+ if ((!zone->size) || (!zone->zone_wake_kswapd))
+ continue;
+ do_try_to_free_pages(GFP_KSWAPD, zone);
}
- run_task_queue(&tq_disk);
- } while (!tsk->need_resched);
+ pgdat = pgdat->node_next;
+ }
+ run_task_queue(&tq_disk);
tsk->state = TASK_INTERRUPTIBLE;
interruptible_sleep_on(&kswapd_wait);
}
tristate ' Netlink device emulation' CONFIG_NETLINK_DEV
fi
bool 'Network packet filtering (replaces ipchains)' CONFIG_NETFILTER
-#if [ "$CONFIG_NETFILTER" = "y" ]; then
-# bool ' Network packet filtering debugging' CONFIG_NETFILTER_DEBUG
-#fi
+if [ "$CONFIG_NETFILTER" = "y" ]; then
+ bool ' Network packet filtering debugging' CONFIG_NETFILTER_DEBUG
+fi
bool 'Socket Filtering' CONFIG_FILTER
tristate 'Unix domain sockets' CONFIG_UNIX
bool 'TCP/IP networking' CONFIG_INET
MOD_SUB_DIRS := ipv4
ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \
netrom rose lapb x25 wanrouter netlink sched packet sunrpc \
- econet irda decnet atm khttpd
+ econet irda decnet atm khttpd ipv4/netfilter
SUB_DIRS := core ethernet sched
MOD_LIST_NAME := NET_MISC_MODULES
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*
- * $Id: br.c,v 1.40 2000/03/21 21:08:47 davem Exp $
+ * $Id: br.c,v 1.41 2000/03/24 01:33:36 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
goto out;
/* handle signals */
- error = -ERESTARTSYS;
if (signal_pending(current))
- goto out;
+ goto interrupted;
*timeo_p = schedule_timeout(*timeo_p);
remove_wait_queue(sk->sleep, &wait);
return 0;
+interrupted:
+ error = sock_intr_errno(*timeo_p);
out:
current->state = TASK_RUNNING;
remove_wait_queue(sk->sleep, &wait);
if (sock_writeable(sk))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else
- sk->socket->flags |= SO_NOSPACE;
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
return mask;
}
* change it and subsequent readers will get broken packet.
* --ANK (980803)
*/
+
+/**
+ * dev_add_pack - add packet handler
+ * @pt: packet type declaration
+ *
+ * Add a protocol handler to the networking stack. The passed packet_type
+ * is linked into kernel lists and may not be freed until it has been
+ * removed from the kernel lists.
+ */
void dev_add_pack(struct packet_type *pt)
{
}
-/*
- * Remove a protocol ID from the list.
+/**
+ * dev_remove_pack - remove packet handler
+ * @pt: packet type declaration
+ *
+ * Remove a protocol handler that was previously added to the kernel
+ * protocol handlers by dev_add_pack. The passed packet_type is removed
+ * from the kernel lists and can be freed or reused once this function
+ * returns.
*/
void dev_remove_pack(struct packet_type *pt)
******************************************************************************************/
-/*
- * Find an interface by name. May be called under rtnl semaphore
- * or dev_base_lock.
+/**
+ * __dev_get_by_name - find a device by its name
+ * @name: name to find
+ *
+ * Find an interface by name. Must be called under rtnl semaphore
+ * or dev_base_lock. If the name is found a pointer to the device
+ * is returned. If the name is not found then NULL is returned. The
+ * reference counters are not incremented so the caller must be
+ * careful with locks.
*/
return NULL;
}
-/*
- * Find an interface by name. Any context, dev_put() to release.
+/**
+ * dev_get_by_name - find a device by its name
+ * @name: name to find
+ *
+ * Find an interface by name. This can be called from any
+ * context and does its own locking. The returned handle has
+ * the usage count incremented and the caller must use dev_put() to
+ * release it when it is no longer needed. NULL is returned if no
+ * matching device is found.
*/
struct net_device *dev_get_by_name(const char *name)
is meaningless, if it was not issued under rtnl semaphore.
*/
+/**
+ * dev_get - test if a device exists
+ * @name: name to test for
+ *
+ * Test if a name exists. Returns true if the name is found. In order
+ * to be sure the name is not allocated or removed during the test the
+ * caller must hold the rtnl semaphore.
+ *
+ * This function primarily exists for back compatibility with older
+ * drivers.
+ */
+
int dev_get(const char *name)
{
struct net_device *dev;
return dev != NULL;
}
-/*
- * Find an interface by index. May be called under rtnl semaphore
+/**
+ * __dev_get_by_index - find a device by its ifindex
+ * @ifindex: index of device
+ *
+ * Search for an interface by index. Returns NULL if the device
+ * is not found or a pointer to the device. The device has not
+ * had its reference counter increased so the caller must be careful
+ * about locking. The caller must hold either the rtnl semaphore
* or dev_base_lock.
*/
return NULL;
}
-/*
- * Find an interface by index. Any context, dev_put() to release.
+
+/**
+ * dev_get_by_index - find a device by its ifindex
+ * @ifindex: index of device
+ *
+ * Search for an interface by index. Returns NULL if the device
+ * is not found or a pointer to the device. The device returned has
+ * had a reference added and the pointer is safe until the user calls
+ * dev_put to indicate they have finished with it.
*/
struct net_device * dev_get_by_index(int ifindex)
return dev;
}
-/*
- * Find an interface by ll addr. May be called only under rtnl semaphore.
+/**
+ * dev_getbyhwaddr - find a device by its hardware addres
+ * @type: media type of device
+ * @ha: hardware address
+ *
+ * Search for an interface by MAC address. Returns NULL if the device
+ * is not found or a pointer to the device. The caller must hold the
+ * rtnl semaphore. The returned device has not had its ref count increased
+ * and the caller must therefore be careful about locking
+ *
+ * BUGS:
+ * If the API was consistent this would be __dev_get_by_hwaddr
*/
struct net_device *dev_getbyhwaddr(unsigned short type, char *ha)
return NULL;
}
-/*
+/**
+ * dev_alloc_name - allocate a name for a device
+ * @dev: device
+ * @name: name format string
+ *
* Passed a format string - eg "lt%d" it will try and find a suitable
- * id. Not efficient for many devices, not called a lot..
+ * id. Not efficient for many devices, not called a lot. The caller
+ * must hold the dev_base or rtnl lock while allocating the name and
+ * adding the device in order to avoid duplicates. Returns the number
+ * of the unit assigned or a negative errno code.
*/
int dev_alloc_name(struct net_device *dev, const char *name)
return -ENFILE; /* Over 100 of the things .. bail out! */
}
+/**
+ * dev_alloc - allocate a network device and name
+ * @name: name format string
+ * @err: error return pointer
+ *
+ * Passed a format string - eg "lt%d" it will allocate a network device
+ * and space for the name. NULL is returned if no memory is available.
+ * If the allocation succeeds then the name is assigned and the
+ * device pointer returned. NULL is returned if the name allocation failed.
+ * The cause of an error is returned as a negative errno code in the
+ * variable err points to.
+ *
+ * The claler must hold the dev_base or rtnl locks when doing this in order
+ * to avoid duplicate name allocations.
+ */
+
struct net_device *dev_alloc(const char *name, int *err)
{
struct net_device *dev=kmalloc(sizeof(struct net_device)+16, GFP_KERNEL);
return dev;
}
+/**
+ * netdev_state_change - device changes state
+ * @dev: device to cause notification
+ *
+ * Called to indicate a device has changed state. This function calls
+ * the notifier chains for netdev_chain and sends a NEWLINK message
+ * to the routing socket.
+ */
+
void netdev_state_change(struct net_device *dev)
{
if (dev->flags&IFF_UP) {
}
-/*
- * Find and possibly load an interface.
- */
-
#ifdef CONFIG_KMOD
+/**
+ * dev_load - load a network module
+ * @name: name of interface
+ *
+ * If a network interface is not present and the process has suitable
+ * privileges this function loads the module. If module loading is not
+ * available in this kernel then it becomes a nop.
+ */
+
void dev_load(const char *name)
{
if (!__dev_get_by_name(name) && capable(CAP_SYS_MODULE))
return 1;
}
-/*
- * Prepare an interface for use.
+/**
+ * dev_open - prepare an interface for use.
+ * @dev: device to open
+ *
+ * Takes a device from down to up state. The devices private open
+ * function is invoked and then the multicast lists are loaded. Finally
+ * the device is moved into the up state and a NETDEV_UP message is
+ * sent to the netdev notifier chain.
+ *
+ * Calling this function on an active interface is a nop. On a failure
+ * a negative errno code is returned.
*/
int dev_open(struct net_device *dev)
}
#endif
-/*
- * Completely shutdown an interface.
+/**
+ * dev_close - shutdown an interface.
+ * @dev: device to shutdown
+ *
+ * This function moves an active device into down state. A
+ * NETDEV_GOING_DOWN is sent to the netev notifier chain. The device
+ * is then deactivated and finally a NETDEV_DOWN is sent to the notifier
+ * chain.
*/
int dev_close(struct net_device *dev)
* Device change register/unregister. These are not inline or static
* as we export them to the world.
*/
+
+/**
+ * register_netdevice_notifier - register a network notifier block
+ * @nb: notifier
+ *
+ * Register a notifier to be called when network device events occur.
+ * The notifier passed is linked into the kernel structures and must
+ * not be reused until it has been unregistered. A negative errno code
+ * is returned on a failure.
+ */
int register_netdevice_notifier(struct notifier_block *nb)
{
return notifier_chain_register(&netdev_chain, nb);
}
+/**
+ * unregister_netdevice_notifier - unregister a network notifier block
+ * @nb: notifier
+ *
+ * Unregister a notifier previously registered by register_netdevice_notifier
+ * The notifier is unlinked into the kernel structures and may
+ * then be reused. A negative errno code is returned on a failure.
+ */
+
int unregister_netdevice_notifier(struct notifier_block *nb)
{
return notifier_chain_unregister(&netdev_chain,nb);
netif_rx(newskb);
}
+/**
+ * dev_queue_xmit - transmit a buffer
+ * @skb: buffer to transmit
+ *
+ * Queue a buffer for transmission to a network device. The caller must
+ * have set the device and priority and built the buffer before calling this
+ * function. The function can be called from an interrupt.
+ *
+ * A negative errno code is returned on a failure. A success does not
+ * guarantee the frame will be transmitted as it may be dropped due
+ * to congestion or traffic shaping.
+ */
+
int dev_queue_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
}
#endif
-/*
- * Receive a packet from a device driver and queue it for the upper
- * (protocol) levels. It always succeeds.
+/**
+ * netif_rx - post buffer to the network code
+ * @skb: buffer to post
+ *
+ * This function receives a packet from a device driver and queues it for
+ * the upper (protocol) levels to process. It always succeeds. The buffer
+ * may be dropped during processing for congestion control or by the
+ * protocol layers.
*/
void netif_rx(struct sk_buff *skb)
}
}
+/**
+ * net_call_rx_atomic
+ * @fn: function to call
+ *
+ * Make a function call that is atomic with respect to the protocol
+ * layers
+ */
+
void net_call_rx_atomic(void (*fn)(void))
{
br_write_lock_bh(BR_NETPROTO_LOCK);
return;
}
-/* Protocol dependent address dumping routines */
-
static gifconf_func_t * gifconf_list [NPROTO];
+/**
+ * register_gifconf - register a SIOCGIF handler
+ * @family: Address family
+ * @gifconf: Function handler
+ *
+ * Register protocol dependent address dumping routines. The handler
+ * that is passed must not be freed or reused until it has been replaced
+ * by another handler.
+ */
+
int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
{
if (family>=NPROTO)
#endif /* CONFIG_PROC_FS */
#endif /* WIRELESS_EXT */
+/**
+ * netdev_set_master - set up master/slave pair
+ * @slave: slave device
+ * @master: new master device
+ *
+ * Changes the master device of the slave. Pass NULL to break the
+ * bonding. The caller must hold the RTNL semaphore. On a failure
+ * a negative errno code is returned. On success the reference counts
+ * are adjusted, RTM_NEWLINK is sent to the routing socket and the
+ * function returns zero.
+ */
+
int netdev_set_master(struct net_device *slave, struct net_device *master)
{
struct net_device *old = slave->master;
return 0;
}
+/**
+ * dev_set_promiscuity - update promiscuity count on a device
+ * @dev: device
+ * @inc: modifier
+ *
+ * Add or remove promsicuity from a device. While the count in the device
+ * remains above zero the interface remains promiscuous. Once it hits zero
+ * the device reverts back to normal filtering operation. A negative inc
+ * value is used to drop promiscuity on the device.
+ */
+
void dev_set_promiscuity(struct net_device *dev, int inc)
{
unsigned short old_flags = dev->flags;
}
}
+/**
+ * dev_set_allmulti - update allmulti count on a device
+ * @dev: device
+ * @inc: modifier
+ *
+ * Add or remove reception of all multicast frames to a device. While the
+ * count in the device remains above zero the interface remains listening
+ * to all interfaces. Once it hits zero the device reverts back to normal
+ * filtering operation. A negative inc value is used to drop the counter
+ * when releasing a resource needing all multicasts.
+ */
+
void dev_set_allmulti(struct net_device *dev, int inc)
{
unsigned short old_flags = dev->flags;
return -EINVAL;
}
-
/*
* This function handles all "interface"-type I/O control requests. The actual
* 'doing' part of this is dev_ifsioc above.
*/
+/**
+ * dev_ioctl - network device ioctl
+ * @cmd: command to issue
+ * @arg: pointer to a struct ifreq in user space
+ *
+ * Issue ioctl functions to devices. This is normally called by the
+ * user space syscall interfaces but can sometimes be useful for
+ * other purposes. The return value is the return from the syscall if
+ * positive or a negative errno code on error.
+ */
+
int dev_ioctl(unsigned int cmd, void *arg)
{
struct ifreq ifr;
}
}
+
+/**
+ * dev_new_index - allocate an ifindex
+ *
+ * Returns a suitable unique value for a new device interface number.
+ * The caller must hold the rtnl semaphore to be sure it remains
+ * unique.
+ */
+
int dev_new_index(void)
{
static int ifindex;
static int dev_boot_phase = 1;
+/**
+ * register_netdevice - register a network device
+ * @dev: device to register
+ *
+ * Take a completed network device structure and add it to the kernel
+ * interfaces. A NETDEV_REGISTER message is sent to the netdev notifier
+ * chain. 0 is returned on success. A negative errno code is returned
+ * on a failure to set up the device, or if the name is a duplicate.
+ *
+ * BUGS:
+ * The locking appears insufficient to guarantee two parallel registers
+ * will not get the same name.
+ */
int register_netdevice(struct net_device *dev)
{
return 0;
}
+/**
+ * netdev_finish_unregister - complete unregistration
+ * @dev: device
+ *
+ * Destroy and free a dead device. A value of zero is returned on
+ * success.
+ */
+
int netdev_finish_unregister(struct net_device *dev)
{
BUG_TRAP(dev->ip_ptr==NULL);
BUG_TRAP(dev->dn_ptr==NULL);
if (!dev->deadbeaf) {
- printk("Freeing alive device %p, %s\n", dev, dev->name);
+ printk(KERN_ERR "Freeing alive device %p, %s\n", dev, dev->name);
return 0;
}
#ifdef NET_REFCNT_DEBUG
return 0;
}
+/**
+ * unregister_netdevice - remove device from the kernel
+ * @dev: device
+ *
+ * This function shuts down a device interface and removes it
+ * from the kernel tables. On success 0 is returned, on a failure
+ * a negative errno code is returned.
+ */
+
int unregister_netdevice(struct net_device *dev)
{
unsigned long now;
* reliable.
*/
+/**
+ * skb_over_panic - private function
+ * @skb: buffer
+ * @sz: size
+ * @here: address
+ *
+ * Out of line support code for skb_put. Not user callable
+ */
+
void skb_over_panic(struct sk_buff *skb, int sz, void *here)
{
printk("skput:over: %p:%d put:%d dev:%s",
*(int*)0 = 0;
}
+/**
+ * skb_under_panic - private function
+ * @skb: buffer
+ * @sz: size
+ * @here: address
+ *
+ * Out of line support code for skb_push. Not user callable
+ */
+
+
void skb_under_panic(struct sk_buff *skb, int sz, void *here)
{
printk("skput:under: %p:%d put:%d dev:%s",
*
*/
+/**
+ * alloc_skb - allocate a network buffer
+ * @size: size to allocate
+ * @gfp_mask: allocation mask
+ *
+ * Allocate a new sk_buff. The returned buffer has no headroom and a
+ * tail room of size bytes. The object has a reference count of one.
+ * The return is the buffer. On a failure the return is NULL.
+ *
+ * Buffers may only be allocated from interrupts using a gfp_mask of
+ * GFP_ATOMIC.
+ */
+
struct sk_buff *alloc_skb(unsigned int size,int gfp_mask)
{
struct sk_buff *skb;
skb_head_to_pool(skb);
}
-/*
- * Free an sk_buff. Release anything attached to the buffer. Clean the state.
+/**
+ * __kfree_skb - private function
+ * @skb: buffer
+ *
+ * Free an sk_buff. Release anything attached to the buffer.
+ * Clean the state. This is an internal helper function. Users should
+ * always call kfree_skb
*/
void __kfree_skb(struct sk_buff *skb)
kfree_skbmem(skb);
}
-/*
- * Duplicate an sk_buff. The new one is not owned by a socket.
+/**
+ * skb_clone - duplicate an sk_buff
+ * @skb: buffer to clone
+ * @gfp_mask: allocation priority
+ *
+ * Duplicate an sk_buff. The new one is not owned by a socket. Both
+ * copies share the same packet data but not structure. The new
+ * buffer has a reference count of 1. If the allocation fails the
+ * function returns NULL otherwise the new buffer is returned.
+ *
+ * If this function is called from an interrupt gfp_mask must be
+ * GFP_ATOMIC.
*/
struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask)
#endif
}
-/*
- * This is slower, and copies the whole data area
+/**
+ * skb_copy - copy an sk_buff
+ * @skb: buffer to copy
+ * @gfp_mask: allocation priority
+ *
+ * Make a copy of both an sk_buff and its data. This is used when the
+ * caller wishes to modify the data and needs a private copy of the
+ * data to alter. Returns NULL on failure or the pointer to the buffer
+ * on success. The returned buffer has a reference count of 1.
+ *
+ * You must pass GFP_ATOMIC as the allocation priority if this function
+ * is called from an interrupt.
*/
struct sk_buff *skb_copy(const struct sk_buff *skb, int gfp_mask)
return n;
}
+/**
+ * skb_copy - copy and expand sk_buff
+ * @skb: buffer to copy
+ * @newheadroom: new free bytes at head
+ * @newtailroom: new free bytes at tail
+ * @gfp_mask: allocation priority
+ *
+ * Make a copy of both an sk_buff and its data and while doing so
+ * allocate additional space.
+ *
+ * This is used when the caller wishes to modify the data and needs a
+ * private copy of the data to alter as well as more space for new fields.
+ * Returns NULL on failure or the pointer to the buffer
+ * on success. The returned buffer has a reference count of 1.
+ *
+ * You must pass GFP_ATOMIC as the allocation priority if this function
+ * is called from an interrupt.
+ */
+
+
struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
int newheadroom,
int newtailroom,
* handler for protocols to use and generic option handler.
*
*
- * Version: $Id: sock.c,v 1.90 2000/02/27 19:48:11 davem Exp $
+ * Version: $Id: sock.c,v 1.91 2000/03/25 01:55:03 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
{
DECLARE_WAITQUEUE(wait, current);
- sk->socket->flags &= ~SO_NOSPACE;
+ clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
add_wait_queue(sk->sleep, &wait);
for (;;) {
if (signal_pending(current))
break;
+ set_bit(SOCK_NOSPACE, &sk->socket->flags);
set_current_state(TASK_INTERRUPTIBLE);
if (atomic_read(&sk->wmem_alloc) < sk->sndbuf)
break;
* This means we have too many buffers for this socket already.
*/
- sk->socket->flags |= SO_NOSPACE;
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+ set_bit(SOCK_NOSPACE, &sk->socket->flags);
err = -EAGAIN;
if (!timeo)
goto failure;
- err = -ERESTARTSYS;
if (signal_pending(current))
- goto failure;
+ goto interrupted;
timeo = sock_wait_for_wmem(sk, timeo);
}
return skb;
+interrupted:
+ err = sock_intr_errno(timeo);
failure:
*errcode = err;
return NULL;
void sock_def_wakeup(struct sock *sk)
{
read_lock(&sk->callback_lock);
- if(!sk->dead)
+ if (sk->sleep && waitqueue_active(sk->sleep))
wake_up_interruptible_all(sk->sleep);
read_unlock(&sk->callback_lock);
}
void sock_def_error_report(struct sock *sk)
{
read_lock(&sk->callback_lock);
- if (!sk->dead) {
+ if (sk->sleep && waitqueue_active(sk->sleep))
wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket,0,POLL_ERR);
- }
+ sk_wake_async(sk,0,POLL_ERR);
read_unlock(&sk->callback_lock);
}
void sock_def_readable(struct sock *sk, int len)
{
read_lock(&sk->callback_lock);
- if(!sk->dead) {
+ if (sk->sleep && waitqueue_active(sk->sleep))
wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket,1,POLL_IN);
- }
+ sk_wake_async(sk,1,POLL_IN);
read_unlock(&sk->callback_lock);
}
/* Do not wake up a writer until he can make "significant"
* progress. --DaveM
*/
- if(!sk->dead &&
- ((atomic_read(&sk->wmem_alloc) << 1) <= sk->sndbuf)) {
- wake_up_interruptible(sk->sleep);
+ if((atomic_read(&sk->wmem_alloc) << 1) <= sk->sndbuf) {
+ if (sk->sleep && waitqueue_active(sk->sleep))
+ wake_up_interruptible(sk->sleep);
/* Should agree with poll, otherwise some programs break */
if (sock_writeable(sk))
- sock_wake_async(sk->socket, 2, POLL_OUT);
+ sk_wake_async(sk, 2, POLL_OUT);
}
+
read_unlock(&sk->callback_lock);
}
goto out;
}
- sock->flags |= SO_WAITDATA;
+ set_bit(SOCK_ASYNC_WAITDATA, &sock->flags);
SOCK_SLEEP_PRE(sk)
if (!dn_data_ready(sk, queue, flags, target))
schedule();
SOCK_SLEEP_POST(sk)
- sock->flags &= ~SO_WAITDATA;
+ clear_bit(SOCK_ASYNC_WAITDATA, &sock->flags);
}
for(skb = queue->next; skb != (struct sk_buff *)queue; skb = nskb) {
if (!sk->dead) {
struct socket *sock = sk->socket;
wake_up_interruptible(sk->sleep);
- if (!(sock->flags & SO_WAITDATA) && sock->fasync_list)
+ if (sock && sock->fasync_list &&
+ !test_bit(SOCK_ASYNC_WAITDATA, &sock->flags))
kill_fasync(sock->fasync_list, sig,
(sig == SIGURG) ? POLL_PRI : POLL_IN);
}
}
if (space < len) {
- sk->socket->flags |= SO_NOSPACE;
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
if (noblock) {
*err = EWOULDBLOCK;
break;
}
- sk->socket->flags &= ~SO_NOSPACE;
+ clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
SOCK_SLEEP_PRE(sk)
if ((sk->sndbuf - atomic_read(&sk->wmem_alloc)) < len)
*
* PF_INET protocol family socket handler.
*
- * Version: $Id: af_inet.c,v 1.108 2000/02/21 16:25:59 davem Exp $
+ * Version: $Id: af_inet.c,v 1.109 2000/03/25 01:55:10 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
if (!timeo || !inet_wait_for_connect(sk, timeo))
goto out;
- err = -ERESTARTSYS;
+ err = sock_intr_errno(timeo);
if (signal_pending(current))
goto out;
}
*
* Alan Cox, <alan@redhat.com>
*
- * Version: $Id: icmp.c,v 1.66 2000/03/17 14:41:50 davem Exp $
+ * Version: $Id: icmp.c,v 1.67 2000/03/25 01:55:11 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
if ((err=ops->create(icmp_socket, IPPROTO_ICMP))<0)
panic("Failed to create the ICMP control socket.\n");
icmp_socket->sk->allocation=GFP_ATOMIC;
+ icmp_socket->sk->sndbuf = SK_WMEM_MAX*2;
icmp_socket->sk->protinfo.af_inet.ttl = MAXTTL;
/* Unhash it so that IP input processing does not even
*
* The Internet Protocol (IP) output module.
*
- * Version: $Id: ip_output.c,v 1.82 2000/03/17 14:41:50 davem Exp $
+ * Version: $Id: ip_output.c,v 1.83 2000/03/25 01:52:08 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
/* OK, we know where to send it, allocate and build IP header. */
iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
- iph->version = 4;
- iph->ihl = 5;
- iph->tos = sk->protinfo.af_inet.tos;
+ *((__u16 *)iph) = htons((4 << 12) | (5 << 8) | (sk->protinfo.af_inet.tos & 0xff));
+ iph->tot_len = htons(skb->len);
iph->frag_off = 0;
iph->ttl = sk->protinfo.af_inet.ttl;
- iph->daddr = rt->rt_dst;
- iph->saddr = rt->rt_src;
iph->protocol = sk->protocol;
+ iph->saddr = rt->rt_src;
+ iph->daddr = rt->rt_dst;
skb->nh.iph = iph;
/* Transport layer set skb->h.foo itself. */
ip_options_build(skb, opt, sk->daddr, rt, 0);
}
- iph->tot_len = htons(skb->len);
-
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
ip_queue_xmit2);
if [ "$CONFIG_IP_NF_CONNTRACK" != "n" ]; then
dep_tristate ' Full NAT' CONFIG_IP_NF_NAT $CONFIG_IP_NF_IPTABLES
if [ "$CONFIG_IP_NF_NAT" != "n" ]; then
+ define_bool CONFIG_IP_NF_NAT_NEEDED y
dep_tristate ' MASQUERADE target support' CONFIG_IP_NF_TARGET_MASQUERADE $CONFIG_IP_NF_NAT
dep_tristate ' REDIRECT target support' CONFIG_IP_NF_TARGET_REDIRECT $CONFIG_IP_NF_NAT
fi
if [ "$CONFIG_IP_NF_CONNTRACK" != "y" ]; then
if [ "$CONFIG_IP_NF_IPTABLES" != "y" ]; then
tristate 'ipchains (2.2-style) support' CONFIG_IP_NF_COMPAT_IPCHAINS
+ if [ "$CONFIG_IP_NF_COMPAT_IPCHAINS" != "n" ]; then
+ define_bool CONFIG_IP_NF_NAT_NEEDED y
+ fi
if [ "$CONFIG_IP_NF_COMPAT_IPCHAINS" != "y" ]; then
tristate 'ipfwadm (2.0-style) support' CONFIG_IP_NF_COMPAT_IPFWADM
+ if [ "$CONFIG_IP_NF_COMPAT_IPFWADM" != "n" ]; then
+ define_bool CONFIG_IP_NF_NAT_NEEDED y
+ fi
fi
fi
fi
IP_NF_NAT_OBJ:=ip_nat_core.o ip_nat_proto_unknown.o ip_nat_proto_tcp.o ip_nat_proto_udp.o ip_nat_proto_icmp.o
+# All the parts of conntrack and NAT required for compatibility layer.
+IP_NF_COMPAT_LAYER:=ip_fw_compat.o ip_fw_compat_redir.o ip_fw_compat_masq.o $(IP_NF_CONNTRACK_OBJ) $(IP_NF_NAT_OBJ)
+
# Link order matters here.
ifeq ($(CONFIG_IP_NF_CONNTRACK),y)
-OX_OBJS += ip_conntrack_standalone.o
-O_OBJS += $(IP_NF_CONNTRACK_OBJ)
+O_OBJS += ip_conntrack_standalone.o $(IP_NF_CONNTRACK_OBJ)
else
ifeq ($(CONFIG_IP_NF_CONNTRACK),m)
MI_OBJS += $(IP_NF_CONNTRACK_OBJ)
endif
endif
-ifeq ($(CONFIG_IP_NF_QUEUE),y)
-O_OBJS += ip_queue.o
-else
- ifeq ($(CONFIG_IP_NF_QUEUE),m)
- M_OBJS += ip_queue.o
- endif
-endif
-
ifeq ($(CONFIG_IP_NF_FTP),y)
-OX_OBJS += ip_conntrack_ftp.o
+O_OBJS += ip_conntrack_ftp.o
else
ifeq ($(CONFIG_IP_NF_FTP),m)
MX_OBJS += ip_conntrack_ftp.o
O_OBJS += ip_tables.o
else
ifeq ($(CONFIG_IP_NF_IPTABLES),m)
- M_OBJS += ip_tables.o
+ MX_OBJS += ip_tables.o
endif
endif
endif
endif
-ifeq ($(CONFIG_IP_NF_FILTER),y)
-O_OBJS += iptable_filter.o
-else
- ifeq ($(CONFIG_IP_NF_FILTER),m)
- M_OBJS += iptable_filter.o
- endif
-endif
-
ifeq ($(CONFIG_IP_NF_NAT),y)
-OX_OBJS += ip_nat_standalone.o
-O_OBJS += ip_nat_rule.o $(IP_NF_NAT_OBJ)
+O_OBJS += ip_nat_standalone.o ip_nat_rule.o $(IP_NF_NAT_OBJ)
ifeq ($(CONFIG_IP_NF_FTP),y)
O_OBJS += ip_nat_ftp.o
endif
endif
endif
+ifeq ($(CONFIG_IP_NF_FILTER),y)
+O_OBJS += iptable_filter.o
+else
+ ifeq ($(CONFIG_IP_NF_FILTER),m)
+ M_OBJS += iptable_filter.o
+ endif
+endif
+
ifeq ($(CONFIG_IP_NF_MANGLE),y)
O_OBJS += iptable_mangle.o
else
endif
ifeq ($(CONFIG_IP_NF_COMPAT_IPCHAINS),y)
-O_OBJS += ipchains.o
+O_OBJS += ipchains_core.o $(IP_NF_COMPAT_LAYER)
else
ifeq ($(CONFIG_IP_NF_COMPAT_IPCHAINS),m)
M_OBJS += ipchains.o
endif
ifeq ($(CONFIG_IP_NF_COMPAT_IPFWADM),y)
-O_OBJS += ipfwadm.o
+O_OBJS += ipfwadm_core.o $(IP_NF_COMPAT_LAYER)
else
ifeq ($(CONFIG_IP_NF_COMPAT_IPFWADM),m)
M_OBJS += ipfwadm.o
endif
endif
+ifeq ($(CONFIG_IP_NF_QUEUE),y)
+O_OBJS += ip_queue.o
+else
+ ifeq ($(CONFIG_IP_NF_QUEUE),m)
+ M_OBJS += ip_queue.o
+ endif
+endif
+
include $(TOPDIR)/Rules.make
ip_conntrack.o: ip_conntrack_standalone.o $(IP_NF_CONNTRACK_OBJ)
iptable_nat.o: ip_nat_standalone.o ip_nat_rule.o $(IP_NF_NAT_OBJ)
$(LD) -r -o $@ ip_nat_standalone.o ip_nat_rule.o $(IP_NF_NAT_OBJ)
-# All the parts of conntrack and NAT required for compatibility layer.
-IP_NF_COMPAT_LAYER:=ip_fw_compat.o ip_fw_compat_redir.o ip_fw_compat_masq.o $(IP_NF_CONNTRACK_OBJ) $(IP_NF_NAT_OBJ)
-
ipfwadm.o: ipfwadm_core.o $(IP_NF_COMPAT_LAYER)
$(LD) -r -o $@ ipfwadm_core.o $(IP_NF_COMPAT_LAYER)
-ipchains.o: ipchains_core.o $(IP_NF_COMPAT_LAYER)
+ipchains.o: ipchains_core.o $(IP_NF_COMPAT_LAYER)
$(LD) -r -o $@ ipchains_core.o $(IP_NF_COMPAT_LAYER)
#include <net/checksum.h>
#include <linux/stddef.h>
#include <linux/sysctl.h>
+#include <linux/slab.h>
/* This rwlock protects the main hash table, protocol/helper/expected
registrations, conntrack timers*/
DECLARE_RWLOCK(ip_conntrack_lock);
void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack) = NULL;
-static LIST_HEAD(expect_list);
-static LIST_HEAD(protocol_list);
+LIST_HEAD(expect_list);
+LIST_HEAD(protocol_list);
static LIST_HEAD(helpers);
unsigned int ip_conntrack_htable_size = 0;
static int ip_conntrack_max = 0;
static atomic_t ip_conntrack_count = ATOMIC_INIT(0);
struct list_head *ip_conntrack_hash;
+static kmem_cache_t *ip_conntrack_cachep;
extern struct ip_conntrack_protocol ip_conntrack_generic_protocol;
if (ip_conntrack_destroyed)
ip_conntrack_destroyed(ct);
- kfree(ct);
+ kmem_cache_free(ip_conntrack_cachep, ct);
atomic_dec(&ip_conntrack_count);
}
return 1;
}
- conntrack = kmalloc(sizeof(struct ip_conntrack), GFP_ATOMIC);
+ conntrack = kmem_cache_alloc(ip_conntrack_cachep, GFP_ATOMIC);
if (!conntrack) {
DEBUGP("Can't allocate conntrack.\n");
return 1;
conntrack->infos[i].master = &conntrack->ct_general;
if (!protocol->new(conntrack, skb->nh.iph, skb->len)) {
- kfree(conntrack);
+ kmem_cache_free(ip_conntrack_cachep, conntrack);
return 1;
}
if (__ip_conntrack_find(tuple, NULL)) {
WRITE_UNLOCK(&ip_conntrack_lock);
printk("ip_conntrack: Wow someone raced us!\n");
- kfree(conntrack);
+ kmem_cache_free(ip_conntrack_cachep, conntrack);
return 0;
}
conntrack->helper = LIST_FIND(&helpers, helper_cmp,
#define NET_IP_CONNTRACK_MAX 2089
#define NET_IP_CONNTRACK_MAX_NAME "ip_conntrack_max"
+#ifdef CONFIG_SYSCTL
static struct ctl_table_header *ip_conntrack_sysctl_header;
static ctl_table ip_conntrack_table[] = {
{CTL_NET, "net", NULL, 0, 0555, ip_conntrack_dir_table, 0, 0, 0, 0, 0},
{ 0 }
};
+#endif /*CONFIG_SYSCTL*/
static int kill_all(const struct ip_conntrack *i, void *data)
{
supposed to kill the mall. */
void ip_conntrack_cleanup(void)
{
+#ifdef CONFIG_SYSCTL
unregister_sysctl_table(ip_conntrack_sysctl_header);
+#endif
ip_ct_selective_cleanup(kill_all, NULL);
+ kmem_cache_destroy(ip_conntrack_cachep);
vfree(ip_conntrack_hash);
nf_unregister_sockopt(&so_getorigdst);
}
return -ENOMEM;
}
+ ip_conntrack_cachep = kmem_cache_create("ip_conntrack",
+ sizeof(struct ip_conntrack), 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (!ip_conntrack_cachep) {
+ printk(KERN_ERR "Unable to create ip_conntrack slab cache\n");
+ vfree(ip_conntrack_hash);
+ nf_unregister_sockopt(&so_getorigdst);
+ return -ENOMEM;
+ }
+
/* Don't NEED lock here, but good form anyway. */
WRITE_LOCK(&ip_conntrack_lock);
/* Sew in builtin protocols. */
ip_conntrack_sysctl_header
= register_sysctl_table(ip_conntrack_root_table, 0);
if (ip_conntrack_sysctl_header == NULL) {
+ kmem_cache_destroy(ip_conntrack_cachep);
vfree(ip_conntrack_hash);
nf_unregister_sockopt(&so_getorigdst);
return -ENOMEM;
}
#endif /*CONFIG_SYSCTL*/
- ret = ip_conntrack_protocol_tcp_init();
- if (ret != 0) {
- unregister_sysctl_table(ip_conntrack_sysctl_header);
- vfree(ip_conntrack_hash);
- nf_unregister_sockopt(&so_getorigdst);
- }
-
return ret;
}
-
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
DECLARE_LOCK(ip_ftp_lock);
+struct module *ip_conntrack_ftp = THIS_MODULE;
#define SERVER_STRING "227 Entering Passive Mode ("
#define CLIENT_STRING "PORT "
ip_conntrack_helper_unregister(&ftp);
}
-struct module *ip_conntrack_ftp = THIS_MODULE;
-EXPORT_SYMBOL(ip_conntrack_ftp);
-EXPORT_SYMBOL(ip_ftp_lock);
-
module_init(init);
module_exit(fini);
= { { NULL, NULL }, IPPROTO_TCP, "tcp",
tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack,
tcp_packet, tcp_new, NULL };
-
-int __init ip_conntrack_protocol_tcp_init(void)
-{
- return 0;
-}
module_init(init);
module_exit(fini);
+#ifdef MODULE
EXPORT_SYMBOL(ip_conntrack_protocol_register);
EXPORT_SYMBOL(invert_tuplepr);
EXPORT_SYMBOL(ip_conntrack_alter_reply);
EXPORT_SYMBOL(ip_conntrack_module);
EXPORT_SYMBOL(ip_conntrack_helper_register);
EXPORT_SYMBOL(ip_conntrack_helper_unregister);
-EXPORT_SYMBOL(ip_conntrack_lock);
-EXPORT_SYMBOL(find_proto);
-EXPORT_SYMBOL(get_tuple);
EXPORT_SYMBOL(ip_ct_selective_cleanup);
EXPORT_SYMBOL(ip_ct_refresh);
EXPORT_SYMBOL(ip_conntrack_expect_related);
EXPORT_SYMBOL(ip_conntrack_tuple_taken);
EXPORT_SYMBOL(ip_ct_gather_frags);
+#endif
#include <linux/netfilter_ipv4/compat_firewall.h>
#include <linux/netfilter_ipv4/ip_conntrack.h>
-EXPORT_NO_SYMBOLS;
-
static struct firewall_ops *fwops;
/* From ip_fw_compat_redir.c */
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
-EXPORT_NO_SYMBOLS;
-
#if 0
#define DEBUGP printk
#else
static struct ip_nat_expect ftp_expect
= { { NULL, NULL }, ftp_nat_expected };
-extern struct module *ip_conntrack_ftp;
-
static int __init init(void)
{
int ret;
if (ret == 0) {
ret = ip_nat_helper_register(&ftp);
- if (ret == 0)
- __MOD_INC_USE_COUNT(ip_conntrack_ftp);
- else
+ if (ret != 0)
ip_nat_expect_unregister(&ftp_expect);
}
return ret;
static void __exit fini(void)
{
- __MOD_DEC_USE_COUNT(ip_conntrack_ftp);
ip_nat_helper_unregister(&ftp);
ip_nat_expect_unregister(&ftp_expect);
}
printk("ip_nat_init: can't register local out hook.\n");
goto cleanup_outops;
}
- __MOD_INC_USE_COUNT(ip_conntrack_module);
+ if (ip_conntrack_module)
+ __MOD_INC_USE_COUNT(ip_conntrack_module);
return ret;
cleanup:
- __MOD_DEC_USE_COUNT(ip_conntrack_module);
+ if (ip_conntrack_module)
+ __MOD_DEC_USE_COUNT(ip_conntrack_module);
nf_unregister_hook(&ip_nat_local_out_ops);
cleanup_outops:
nf_unregister_hook(&ip_nat_out_ops);
module_init(init);
module_exit(fini);
+#ifdef MODULE
EXPORT_SYMBOL(ip_nat_setup_info);
EXPORT_SYMBOL(ip_nat_helper_register);
EXPORT_SYMBOL(ip_nat_helper_unregister);
EXPORT_SYMBOL(ip_nat_expect_register);
EXPORT_SYMBOL(ip_nat_expect_unregister);
EXPORT_SYMBOL(ip_nat_cheat_check);
+#endif
* This is a module which is used for queueing IPv4 packets and
* communicating with userspace via netlink.
*
- * (C) 2000 James Morris
+ * Copyright (C) 2000 James Morris
*/
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4/ip_queue.h>
-EXPORT_NO_SYMBOLS;
-
#define IPQ_THR_NAME "kipq"
#define IPQ_NAME "ip_queue"
#define IPQ_QMAX_DEFAULT 1024
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
+#include <net/ip.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
+#include <linux/proc_fs.h>
#include <linux/netfilter_ipv4/ip_tables.h>
-#ifndef IP_OFFSET
-#define IP_OFFSET 0x1FFF
-#endif
-
/*#define DEBUG_IP_FIREWALL*/
/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
/*#define DEBUG_IP_FIREWALL_USER*/
+ TABLE_OFFSET(table->private, smp_processor_id());
e = get_entry(table_base, table->private->hook_entry[hook]);
- /* Check noone else using our table */
- IP_NF_ASSERT(((struct ipt_entry *)table_base)->comefrom == 0xdead57ac);
#ifdef CONFIG_NETFILTER_DEBUG
+ /* Check noone else using our table */
+ if (((struct ipt_entry *)table_base)->comefrom != 0xdead57ac
+ && ((struct ipt_entry *)table_base)->comefrom != 0xeeeeeeec) {
+ printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
+ smp_processor_id(),
+ table->name,
+ &((struct ipt_entry *)table_base)->comefrom,
+ ((struct ipt_entry *)table_base)->comefrom);
+ }
((struct ipt_entry *)table_base)->comefrom = 0x57acc001;
#endif
e = get_entry(table_base, v);
} else {
+ /* Targets which reenter must return
+ abs. verdicts */
+#ifdef CONFIG_NETFILTER_DEBUG
+ ((struct ipt_entry *)table_base)->comefrom
+ = 0xeeeeeeec;
+#endif
verdict = t->u.target->target(pskb, hook,
in, out,
t->data,
userdata);
+#ifdef CONFIG_NETFILTER_DEBUG
+ if (((struct ipt_entry *)table_base)->comefrom
+ != 0xeeeeeeec
+ && verdict == IPT_CONTINUE) {
+ printk("Target %s reentered!\n",
+ t->u.target->name);
+ verdict = NF_DROP;
+ }
+ ((struct ipt_entry *)table_base)->comefrom
+ = 0x57acc001;
+#endif
/* Target might have changed stuff. */
ip = (*pskb)->nh.iph;
protohdr = (u_int32_t *)ip + ip->ihl;
static struct ipt_match icmp_matchstruct
= { { NULL, NULL }, "icmp", &icmp_match, &icmp_checkentry, NULL };
+#ifdef CONFIG_PROC_FS
+static inline int print_name(const struct ipt_table *t,
+ off_t start_offset, char *buffer, int length,
+ off_t *pos, unsigned int *count)
+{
+ if ((*count)++ >= start_offset) {
+ unsigned int namelen;
+
+ namelen = sprintf(buffer + *pos, "%s\n", t->name);
+ if (*pos + namelen > length) {
+ /* Stop iterating */
+ return 1;
+ }
+ *pos += namelen;
+ }
+ return 0;
+}
+
+static int ipt_get_tables(char *buffer, char **start, off_t offset, int length)
+{
+ off_t pos = 0;
+ unsigned int count = 0;
+
+ if (down_interruptible(&ipt_mutex) != 0)
+ return 0;
+
+ LIST_FIND(&ipt_tables, print_name, struct ipt_table *,
+ offset, buffer, length, &pos, &count);
+
+ up(&ipt_mutex);
+
+ /* `start' hack - see fs/proc/generic.c line ~105 */
+ *start=(char *)((unsigned long)count-offset);
+ return pos;
+}
+#endif /*CONFIG_PROC_FS*/
+
static int __init init(void)
{
int ret;
return ret;
}
- printk("iptables: (c)2000 Netfilter core team\n");
+#ifdef CONFIG_PROC_FS
+ if (!proc_net_create("ip_tables_names", 0, ipt_get_tables)) {
+ nf_unregister_sockopt(&ipt_sockopts);
+ return -ENOMEM;
+ }
+#endif
+
+ printk("ip_tables: (c)2000 Netfilter core team\n");
return 0;
}
static void __exit fini(void)
{
nf_unregister_sockopt(&ipt_sockopts);
+#ifdef CONFIG_PROC_FS
+ proc_net_remove("ip_tables_names");
+#endif
}
module_init(init);
/*#define DEBUG_IP_FIREWALL_USER*/
/*#define DEBUG_IP_FIREWALL_LOCKING*/
+#if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
static struct sock *ipfwsk;
+#endif
#ifdef CONFIG_SMP
#define SLOT_NUMBER() (cpu_number_map(smp_processor_id())*2 + !in_interrupt())
__u32 spi;
}; /* FIXME evil kludge */
-/* Make init and cleanup non-static, so gcc doesn't warn about unused,
- but don't export the symbols */
-EXPORT_NO_SYMBOLS;
-
/* Use lock to serialize, so printks don't overlap */
static spinlock_t log_lock = SPIN_LOCK_UNLOCKED;
static int __init init(void)
{
- if (ipt_register_target(&ipt_log_reg))
- return -EINVAL;
+ if (ipt_register_target(&ipt_log_reg))
+ return -EINVAL;
- return 0;
+ return 0;
}
static void __exit fini(void)
{
- ipt_unregister_target(&ipt_log_reg);
+ ipt_unregister_target(&ipt_log_reg);
}
module_init(init);
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_MARK.h>
-EXPORT_NO_SYMBOLS;
-
static unsigned int
target(struct sk_buff **pskb,
unsigned int hooknum,
static int __init init(void)
{
- if (ipt_register_target(&ipt_mark_reg))
- return -EINVAL;
+ if (ipt_register_target(&ipt_mark_reg))
+ return -EINVAL;
- return 0;
+ return 0;
}
static void __exit fini(void)
{
- ipt_unregister_target(&ipt_mark_reg);
+ ipt_unregister_target(&ipt_mark_reg);
}
module_init(init);
#include <linux/netfilter_ipv4/ip_nat_rule.h>
#include <linux/netfilter_ipv4/ip_tables.h>
-EXPORT_NO_SYMBOLS;
-
#if 0
#define DEBUGP printk
#else
#include <linux/route.h>
struct in_device;
#include <net/route.h>
-EXPORT_NO_SYMBOLS;
#if 0
#define DEBUGP printk
}
/* check if the interface we are living by is the same as the one we arrived on */
- if (skb->rx_dev != rt->u.dst.dev) {
+ if (skb->rx_dev == rt->u.dst.dev) {
/* Drop old route. */
dst_release(skb->dst);
skb->dst = &rt->u.dst;
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv4/ip_nat_rule.h>
-EXPORT_NO_SYMBOLS;
-
#if 0
#define DEBUGP printk
#else
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <net/icmp.h>
-#include <net/tcp.h>
+#include <net/ip.h>
struct in_device;
#include <net/route.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_REJECT.h>
-EXPORT_NO_SYMBOLS;
#if 0
#define DEBUGP printk
{
const struct ipt_reject_info *reject = targinfo;
+ /* WARNING: This code has causes reentry within iptables.
+ This means that the iptables jump stack is now crap. We
+ must return an absolute verdict. --RR */
switch (reject->with) {
case IPT_ICMP_NET_UNREACHABLE:
icmp_send(*pskb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0);
}
}
break;
- case IPT_TCP_RESET:
- tcp_v4_send_reset(*pskb);
- break;
}
return NF_DROP;
DEBUGP("REJECT: ECHOREPLY illegal for non-ping\n");
return 0;
}
- } else if (rejinfo->with == IPT_TCP_RESET) {
- if (e->ip.proto != IPPROTO_TCP
- || (e->ip.invflags & IPT_INV_PROTO)) {
- DEBUGP("REJECT: TCP_RESET illegal for non-tcp\n");
- return 0;
- }
}
return 1;
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_TOS.h>
-EXPORT_NO_SYMBOLS;
-
static unsigned int
target(struct sk_buff **pskb,
unsigned int hooknum,
static int __init init(void)
{
- if (ipt_register_target(&ipt_tos_reg))
- return -EINVAL;
+ if (ipt_register_target(&ipt_tos_reg))
+ return -EINVAL;
- return 0;
+ return 0;
}
static void __exit fini(void)
{
- ipt_unregister_target(&ipt_tos_reg);
+ ipt_unregister_target(&ipt_tos_reg);
}
module_init(init);
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_limit.h>
-EXPORT_NO_SYMBOLS;
#define IP_PARTS_NATIVE(n) \
(unsigned int)((n)>>24)&0xFF, \
#include <linux/netfilter_ipv4/ipt_mac.h>
#include <linux/netfilter_ipv4/ip_tables.h>
-EXPORT_NO_SYMBOLS;
static int
match(const struct sk_buff *skb,
#include <linux/netfilter_ipv4/ipt_mark.h>
#include <linux/netfilter_ipv4/ip_tables.h>
-EXPORT_NO_SYMBOLS;
-
static int
match(const struct sk_buff *skb,
const struct net_device *in,
#define duprintf(format, args...)
#endif
-EXPORT_NO_SYMBOLS;
-
/* Returns 1 if the port is matched by the test, 0 otherwise. */
static inline int
ports_match(const u_int16_t *portlist, enum ipt_multiport_flags flags,
/* Kernel module to match various things tied to sockets associated with
locally generated outgoing packets.
- (C)2000 Marc Boucher
+ Copyright (C) 2000 Marc Boucher
*/
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4/ipt_owner.h>
#include <linux/netfilter_ipv4/ip_tables.h>
-EXPORT_NO_SYMBOLS;
-
static int
match_pid(const struct sk_buff *skb, pid_t pid)
{
#include <linux/netfilter_ipv4/ip_conntrack.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_state.h>
-EXPORT_NO_SYMBOLS;
static int
match(const struct sk_buff *skb,
static int __init init(void)
{
- __MOD_INC_USE_COUNT(ip_conntrack_module);
+ /* NULL if ip_conntrack not a module */
+ if (ip_conntrack_module)
+ __MOD_INC_USE_COUNT(ip_conntrack_module);
return ipt_register_match(&state_match);
}
static void __exit fini(void)
{
ipt_unregister_match(&state_match);
- __MOD_DEC_USE_COUNT(ip_conntrack_module);
+ if (ip_conntrack_module)
+ __MOD_DEC_USE_COUNT(ip_conntrack_module);
}
module_init(init);
#include <linux/netfilter_ipv4/ipt_tos.h>
#include <linux/netfilter_ipv4/ip_tables.h>
-EXPORT_NO_SYMBOLS;
-
static int
match(const struct sk_buff *skb,
const struct net_device *in,
#include <linux/netfilter_ipv4/ip_tables.h>
-EXPORT_NO_SYMBOLS;
-
#define limpk(format, args...) \
do { \
if (net_ratelimit()) \
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp.c,v 1.165 2000/03/23 05:30:32 davem Exp $
+ * Version: $Id: tcp.c,v 1.166 2000/03/25 01:55:11 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
return sk->tp_pinfo.af_tcp.accept_queue ? (POLLIN | POLLRDNORM) : 0;
}
-/*
- * Compute minimal free write space needed to queue new packets.
- */
-#define tcp_min_write_space(__sk) \
- (atomic_read(&(__sk)->wmem_alloc) / 2)
-
/*
* Wait for a TCP event.
*
if (sock_wspace(sk) >= tcp_min_write_space(sk)) {
mask |= POLLOUT | POLLWRNORM;
} else { /* send SIGIO later */
- sk->socket->flags |= SO_NOSPACE;
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+ set_bit(SOCK_NOSPACE, &sk->socket->flags);
+
+ /* Race breaker. If space is freed after
+ * wspace test but before the flags are set,
+ * IO signal will be lost.
+ */
+ if (sock_wspace(sk) >= tcp_min_write_space(sk))
+ mask |= POLLOUT | POLLWRNORM;
}
}
* Socket write_space callback.
* This (or rather the sock_wake_async) should agree with poll.
*
- * WARNING. This callback is called from any context (process,
- * bh or irq). Do not make anything more smart from it.
+ * WARNING. This callback is called, when socket is not locked.
+ *
+ * This wakeup is used by TCP only as dead-lock breaker, real
+ * wakeup occurs when incoming ack frees some space in buffer.
*/
void tcp_write_space(struct sock *sk)
{
+ struct socket *sock;
+
read_lock(&sk->callback_lock);
- if (!sk->dead) {
- /* Why??!! Does it really not overshedule? --ANK */
- wake_up_interruptible(sk->sleep);
+ if ((sock = sk->socket) != NULL && atomic_read(&sk->wmem_alloc) == 0) {
+ if (test_bit(SOCK_NOSPACE, &sock->flags)) {
+ if (sk->sleep && waitqueue_active(sk->sleep)) {
+ clear_bit(SOCK_NOSPACE, &sock->flags);
+ wake_up_interruptible(sk->sleep);
+ }
+ }
- if (sock_wspace(sk) >= tcp_min_write_space(sk))
- sock_wake_async(sk->socket, 2, POLL_OUT);
+ if (sock->fasync_list)
+ sock_wake_async(sock, 2, POLL_OUT);
}
read_unlock(&sk->callback_lock);
}
sk->write_space = tcp_listen_write_space;
sk_dst_reset(sk);
sk->prot->hash(sk);
- sk->socket->flags |= SO_ACCEPTCON;
return 0;
}
if(!*timeo_p)
return -EAGAIN;
if(signal_pending(tsk))
- return -ERESTARTSYS;
+ return sock_intr_errno(*timeo_p);
__set_task_state(tsk, TASK_INTERRUPTIBLE);
add_wait_queue(sk->sleep, &wait);
if (!tcp_memory_free(sk)) {
DECLARE_WAITQUEUE(wait, current);
- sk->socket->flags &= ~SO_NOSPACE;
+ clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+
add_wait_queue(sk->sleep, &wait);
for (;;) {
+ set_bit(SOCK_NOSPACE, &sk->socket->flags);
+
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current))
goto out_unlock;
/* This should be in poll */
- sk->socket->flags &= ~SO_NOSPACE; /* clear SIGIO XXX */
+ clear_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
mss_now = tcp_current_mss(sk);
/* If we didn't get any memory, we need to sleep. */
if (skb == NULL) {
- sk->socket->flags |= SO_NOSPACE;
+ set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+ set_bit(SOCK_NOSPACE, &sk->socket->flags);
+
if (!timeo) {
err = -EAGAIN;
goto do_interrupted;
}
if (signal_pending(current)) {
- err = -ERESTARTSYS;
+ err = sock_intr_errno(timeo);
goto do_interrupted;
}
__tcp_push_pending_frames(sk, tp, mss_now);
msg->msg_flags|=MSG_OOB;
if(len>0) {
- err = memcpy_toiovec(msg->msg_iov, &c, 1);
+ if (!(flags & MSG_PEEK))
+ err = memcpy_toiovec(msg->msg_iov, &c, 1);
len = 1;
} else
msg->msg_flags|=MSG_TRUNC;
__set_current_state(TASK_INTERRUPTIBLE);
- sk->socket->flags |= SO_WAITDATA;
+ set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
release_sock(sk);
if (skb_queue_empty(&sk->receive_queue))
timeo = schedule_timeout(timeo);
lock_sock(sk);
- sk->socket->flags &= ~SO_WAITDATA;
+ clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
remove_wait_queue(sk->sleep, &wait);
__set_current_state(TASK_RUNNING);
if (signal_pending(current)) {
if (copied)
break;
- copied = -ERESTARTSYS;
- if (!timeo)
- copied = -EAGAIN;
+ copied = timeo ? sock_intr_errno(timeo) : -EAGAIN;
break;
}
if (tp->ucopy.task == user_recv) {
/* Install new reader */
- if (user_recv == NULL && !(flags&MSG_PEEK)) {
+ if (user_recv == NULL && !(flags&(MSG_TRUNC|MSG_PEEK))) {
user_recv = current;
tp->ucopy.task = user_recv;
tp->ucopy.iov = msg->msg_iov;
tp->ucopy.len = len;
- BUG_TRAP(tp->copied_seq == tp->rcv_nxt || (flags&MSG_PEEK));
+ BUG_TRAP(tp->copied_seq == tp->rcv_nxt || (flags&(MSG_PEEK|MSG_TRUNC)));
/* Ugly... If prequeue is not empty, we have to
* process it before releasing socket, otherwise
}
}
- err = memcpy_toiovec(msg->msg_iov, ((unsigned char *)skb->h.th) + skb->h.th->doff*4 + offset, used);
- if (err) {
- /* Exception. Bailout! */
- if (!copied)
- copied = -EFAULT;
- break;
+ err = 0;
+ if (!(flags&MSG_TRUNC)) {
+ err = memcpy_toiovec(msg->msg_iov, ((unsigned char *)skb->h.th) + skb->h.th->doff*4 + offset, used);
+ if (err) {
+ /* Exception. Bailout! */
+ if (!copied)
+ copied = -EFAULT;
+ break;
+ }
}
*seq += used;
err = -EINVAL;
if (sk->state != TCP_LISTEN)
break;
- err = -ERESTARTSYS;
+ err = sock_intr_errno(timeo);
if (signal_pending(current))
break;
err = -EAGAIN;
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_input.c,v 1.190 2000/03/21 19:34:23 davem Exp $
+ * Version: $Id: tcp_input.c,v 1.191 2000/03/25 01:55:13 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
if (ack != tp->snd_una || (flag == 0 && !th->fin))
dst_confirm(sk->dst_cache);
+ if (ack != tp->snd_una)
+ tp->sorry = 1;
+
/* Remember the highest ack received. */
tp->snd_una = ack;
return 1;
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
tp->fin_seq = TCP_SKB_CB(skb)->end_seq;
- tcp_send_ack(sk);
+ tp->ack.pending = 1;
sk->shutdown |= RCV_SHUTDOWN;
break;
case TCP_FIN_WAIT2:
/* Received a FIN -- send ACK and enter TIME_WAIT. */
+ tcp_send_ack(sk);
tcp_time_wait(sk, TCP_TIME_WAIT, 0);
break;
default:
if (eaten) {
kfree_skb(skb);
- } else
+ } else if (!sk->dead)
sk->data_ready(sk, 0);
return;
}
kfree_skb(skb);
}
+/* When incoming ACK allowed to free some skb from write_queue,
+ * we remember this in flag tp->sorry and wake up socket on the exit
+ * from tcp input handler. Probably, handler has already eat this space
+ * sending ACK and cloned frames from tcp_write_xmit().
+ */
+static __inline__ void tcp_new_space(struct sock *sk)
+{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct socket *sock;
+
+ tp->sorry = 0;
+
+ if (sock_wspace(sk) >= tcp_min_write_space(sk) &&
+ (sock = sk->socket) != NULL) {
+ clear_bit(SOCK_NOSPACE, &sock->flags);
+
+ if (sk->sleep && waitqueue_active(sk->sleep))
+ wake_up_interruptible(sk->sleep);
+
+ if (sock->fasync_list)
+ sock_wake_async(sock, 2, POLL_OUT);
+ }
+}
+
static void __tcp_data_snd_check(struct sock *sk, struct sk_buff *skb)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
*/
/* More than one full frame received or... */
- if (((tp->rcv_nxt - tp->rcv_wup) > tp->ack.rcv_mss) ||
+ if (((tp->rcv_nxt - tp->rcv_wup) > tp->ack.rcv_mss
+#ifdef TCP_MORE_COARSE_ACKS
+ /* Avoid to send immediate ACK from input path, if it
+ * does not advance window far enough. tcp_recvmsg() will do this.
+ */
+ && (!sysctl_tcp_retrans_collapse || __tcp_select_window(sk) >= tp->rcv_wnd)
+#endif
+ ) ||
/* We ACK each frame or... */
tcp_in_quickack_mode(tp) ||
/* We have out of order data or */
TCP_SKB_CB(skb)->ack_seq, len);
kfree_skb(skb);
tcp_data_snd_check(sk);
+ if (tp->sorry)
+ tcp_new_space(sk);
return 0;
} else { /* Header too small */
TCP_INC_STATS_BH(TcpInErrs);
if(sk->state != TCP_CLOSE) {
tcp_data_snd_check(sk);
tcp_ack_snd_check(sk);
+ if (tp->sorry)
+ tcp_new_space(sk);
}
return 0;
newtp->saw_tstamp = 0;
newtp->probes_out = 0;
+ newtp->num_sacks = 0;
newtp->syn_seq = req->rcv_isn;
newtp->fin_seq = req->rcv_isn;
newtp->urg_data = 0;
tcp_sync_mss(sk, tp->pmtu_cookie);
tcp_initialize_rcv_mss(sk);
tcp_init_metrics(sk);
+ tcp_init_buffer_space(sk);
if (sk->keepopen)
tcp_reset_keepalive_timer(sk, keepalive_time_when(tp));
if (sk->state != TCP_CLOSE) {
tcp_data_snd_check(sk);
tcp_ack_snd_check(sk);
+ if (tp->sorry)
+ tcp_new_space(sk);
}
if (!queued) {
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_ipv4.c,v 1.203 2000/03/22 17:55:03 davem Exp $
+ * Version: $Id: tcp_ipv4.c,v 1.205 2000/03/26 09:16:08 davem Exp $
*
* IPv4 specific functions
*
void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
struct sk_buff *skb)
{
- th->check = 0;
th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
csum_partial((char *)th, th->doff<<2, skb->csum));
}
* Exception: precedence violation. We do not implement it in any case.
*/
-void tcp_v4_send_reset(struct sk_buff *skb)
+static void tcp_v4_send_reset(struct sk_buff *skb)
{
struct tcphdr *th = skb->h.th;
struct tcphdr rth;
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_output.c,v 1.122 2000/02/21 15:51:41 davem Exp $
+ * Version: $Id: tcp_output.c,v 1.123 2000/03/25 01:52:05 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
#define SYSCTL_FLAG_SACK 0x4
sysctl_flags = 0;
- if(tcb->flags & TCPCB_FLAG_SYN) {
+ if (tcb->flags & TCPCB_FLAG_SYN) {
tcp_header_size = sizeof(struct tcphdr) + TCPOLEN_MSS;
if(sysctl_tcp_timestamps) {
tcp_header_size += TCPOLEN_TSTAMP_ALIGNED;
if(!(sysctl_flags & SYSCTL_FLAG_TSTAMPS))
tcp_header_size += TCPOLEN_SACKPERM_ALIGNED;
}
- } else if(tp->sack_ok && tp->num_sacks) {
+ } else if (tp->num_sacks) {
/* A SACK is 2 pad bytes, a 2 byte header, plus
* 2 32-bit sequence numbers for each SACK block.
*/
th->dest = sk->dport;
th->seq = htonl(TCP_SKB_CB(skb)->seq);
th->ack_seq = htonl(tp->rcv_nxt);
- th->doff = (tcp_header_size >> 2);
- th->res1 = 0;
- *(((__u8 *)th) + 13) = tcb->flags;
- th->check = 0;
- th->urg_ptr = ntohs(tcb->urg_ptr);
- if(tcb->flags & TCPCB_FLAG_SYN) {
+ *(((__u16 *)th) + 6) = htons(((tcp_header_size >> 2) << 12) | tcb->flags);
+ if (tcb->flags & TCPCB_FLAG_SYN) {
/* RFC1323: The window in SYN & SYN/ACK segments
* is never scaled.
*/
th->window = htons(tp->rcv_wnd);
+ } else {
+ th->window = htons(tcp_select_window(sk));
+ }
+ th->check = 0;
+ th->urg_ptr = ntohs(tcb->urg_ptr);
+
+ if (tcb->flags & TCPCB_FLAG_SYN) {
tcp_syn_build_options((__u32 *)(th + 1),
tcp_advertise_mss(sk),
(sysctl_flags & SYSCTL_FLAG_TSTAMPS),
TCP_SKB_CB(skb)->when,
tp->ts_recent);
} else {
- th->window = htons(tcp_select_window(sk));
tcp_build_and_update_options((__u32 *)(th + 1),
tp, TCP_SKB_CB(skb)->when);
}
tp->af_specific->send_check(sk, th, skb->len, skb);
- if (th->ack)
+ if (tcb->flags & TCPCB_FLAG_ACK)
tcp_event_ack_sent(sk);
if (skb->len != tcp_header_size)
void tcp_send_delayed_ack(struct sock *sk)
{
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ long ato = tp->ack.ato;
unsigned long timeout;
+ if (ato > TCP_DELACK_MIN) {
+ int max_ato;
+
+ /* If some rtt estimate is known, use it to bound delayed ack.
+ * Do not use tp->rto here, use results of rtt measurements
+ * directly.
+ */
+ if (tp->srtt)
+ max_ato = (tp->srtt >> 3) + tp->mdev;
+ else
+ max_ato = TCP_DELACK_MAX;
+
+ ato = min(ato, max_ato);
+ }
+
/* Stay within the limit we were given */
- timeout = jiffies + tp->ack.ato;
+ timeout = jiffies + ato;
/* Use new timeout only if there wasn't a older one earlier. */
spin_lock_bh(&sk->timer_lock);
/* If delack timer was blocked or is about to expire,
* send ACK now.
*/
- if (tp->ack.blocked || time_before_eq(tp->delack_timer.expires, jiffies+(tp->ack.ato>>2))) {
+ if (tp->ack.blocked || time_before_eq(tp->delack_timer.expires, jiffies+(ato>>2))) {
spin_unlock_bh(&sk->timer_lock);
tcp_send_ack(sk);
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: icmp.c,v 1.27 2000/02/22 23:54:28 davem Exp $
+ * $Id: icmp.c,v 1.28 2000/03/25 01:55:20 davem Exp $
*
* Based on net/ipv4/icmp.c
*
sk = icmpv6_socket->sk;
sk->allocation = GFP_ATOMIC;
+ sk->sndbuf = SK_WMEM_MAX*2;
sk->prot->unhash(sk);
inet6_add_protocol(&icmpv6_protocol);
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: tcp_ipv6.c,v 1.121 2000/03/08 19:36:47 davem Exp $
+ * $Id: tcp_ipv6.c,v 1.122 2000/03/25 01:52:11 davem Exp $
*
* Based on:
* linux/net/ipv4/tcp.c
struct sk_buff *skb)
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
- th->check = 0;
th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
csum_partial((char *)th, th->doff<<2,
error=sock->ops->listen(sock,48);
if (error!=0)
(void)printk(KERN_ERR "kHTTPd: Error listening on socket \n");
- sock->flags |= SO_ACCEPTCON;
MainSocket = sock;
if (signal_pending(current)) {
kfree_skb(skb);
- return -ERESTARTSYS;
+ return sock_intr_errno(timeo);
}
goto retry;
}
EXPORT_SYMBOL(tcp_v4_rebuild_header);
EXPORT_SYMBOL(tcp_v4_send_check);
EXPORT_SYMBOL(tcp_v4_conn_request);
-EXPORT_SYMBOL(tcp_v4_send_reset);
EXPORT_SYMBOL(tcp_create_openreq_child);
EXPORT_SYMBOL(tcp_bucket_create);
EXPORT_SYMBOL(__tcp_put_port);
EXPORT_SYMBOL(nf_getsockopt);
#endif
+#ifdef CONFIG_IP_NF_CONNTRACK
+#include <linux/netfilter_ipv4/ip_conntrack_protocol.h>
+#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
+#include <linux/netfilter_ipv4/ip_conntrack_core.h>
+EXPORT_SYMBOL(ip_conntrack_protocol_register);
+EXPORT_SYMBOL(invert_tuplepr);
+EXPORT_SYMBOL(ip_conntrack_alter_reply);
+EXPORT_SYMBOL(ip_conntrack_destroyed);
+EXPORT_SYMBOL(ip_conntrack_get);
+EXPORT_SYMBOL(ip_conntrack_module);
+EXPORT_SYMBOL(ip_conntrack_helper_register);
+EXPORT_SYMBOL(ip_conntrack_helper_unregister);
+EXPORT_SYMBOL(ip_ct_selective_cleanup);
+EXPORT_SYMBOL(ip_ct_refresh);
+EXPORT_SYMBOL(ip_conntrack_expect_related);
+EXPORT_SYMBOL(ip_conntrack_tuple_taken);
+EXPORT_SYMBOL(ip_ct_gather_frags);
+#ifdef CONFIG_IP_NF_FTP
+#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
+EXPORT_SYMBOL(ip_ftp_lock);
+#endif
+#endif /*CONFIG_IP_NF_CONNTRACK*/
+
+#ifdef CONFIG_IP_NF_NAT
+#include <linux/netfilter_ipv4/ip_nat.h>
+#include <linux/netfilter_ipv4/ip_nat_helper.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+EXPORT_SYMBOL(ip_nat_setup_info);
+EXPORT_SYMBOL(ip_nat_helper_register);
+EXPORT_SYMBOL(ip_nat_helper_unregister);
+EXPORT_SYMBOL(ip_nat_expect_register);
+EXPORT_SYMBOL(ip_nat_expect_unregister);
+EXPORT_SYMBOL(ip_nat_cheat_check);
+#endif
+
+#ifdef CONFIG_IP_NF_IPTABLES
+#include <linux/netfilter_ipv4/ip_tables.h>
+EXPORT_SYMBOL(ipt_register_table);
+EXPORT_SYMBOL(ipt_unregister_table);
+EXPORT_SYMBOL(ipt_register_target);
+EXPORT_SYMBOL(ipt_unregister_target);
+EXPORT_SYMBOL(ipt_register_match);
+EXPORT_SYMBOL(ipt_unregister_match);
+#endif
+
EXPORT_SYMBOL(register_gifconf);
EXPORT_SYMBOL(net_call_rx_atomic);
switch (how)
{
case 1:
- if (sock->flags & SO_WAITDATA)
+
+ if (test_bit(SOCK_ASYNC_WAITDATA, &sock->flags))
break;
goto call_kill;
case 2:
- if (!(sock->flags & SO_NOSPACE))
+ if (!test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags))
break;
- sock->flags &= ~SO_NOSPACE;
/* fall through */
case 0:
call_kill:
- /* read_lock(&sock->sk->callback_lock); */
- if(sock->fasync_list != NULL)
- kill_fasync(sock->fasync_list, SIGIO, band);
- /* read_unlock(&sock->sk->callback_lock); */
+ kill_fasync(sock->fasync_list, SIGIO, band);
break;
case 3:
kill_fasync(sock->fasync_list, SIGURG, band);
#include <linux/sunrpc/clnt.h>
+#include <linux/nfs.h>
+
#define RPC_SLACK_SPACE 1024 /* total overkill */
* The following is an NFS-specific hack to cater for setuid
* processes whose uid is mapped to nobody on the server.
*/
- if (task->tk_client->cl_prog == 100003 &&
+ if (task->tk_client->cl_prog == NFS_PROGRAM &&
(ntohl(*p) == NFSERR_ACCES || ntohl(*p) == NFSERR_PERM)) {
if (RPC_IS_SETUID(task) && task->tk_suid_retry) {
dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
if (protocol == IPPROTO_TCP) {
if ((error = sock->ops->listen(sock, 5)) < 0)
goto bummer;
- sock->flags |= SO_ACCEPTCON;
}
if ((svsk = svc_setup_socket(serv, sock, &error, 1)) != NULL)
*/
break;
case -EAGAIN:
- if (sock->flags & SO_NOSPACE)
+ if (test_bit(SOCK_NOSPACE, &sock->flags))
result = -ENOMEM;
break;
case -ENOTCONN:
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Version: $Id: af_unix.c,v 1.90 2000/03/16 20:38:45 davem Exp $
+ * Version: $Id: af_unix.c,v 1.91 2000/03/25 01:55:34 davem Exp $
*
* Fixes:
* Linus Torvalds : Assorted bug cures.
static void unix_write_space(struct sock *sk)
{
read_lock(&sk->callback_lock);
- if (!sk->dead && unix_writable(sk)) {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 2, POLL_OUT);
+ if (unix_writable(sk)) {
+ if (sk->sleep && waitqueue_active(sk->sleep))
+ wake_up_interruptible(sk->sleep);
+ sk_wake_async(sk, 2, POLL_OUT);
}
read_unlock(&sk->callback_lock);
}
if (!skb_queue_empty(&sk->receive_queue) || embrion)
skpair->err = ECONNRESET;
unix_state_wunlock(skpair);
- sk->state_change(skpair);
- sock_wake_async(sk->socket,1,POLL_HUP);
+ skpair->state_change(skpair);
+ read_lock(&skpair->callback_lock);
+ sk_wake_async(skpair,1,POLL_HUP);
+ read_unlock(&skpair->callback_lock);
}
sock_put(skpair); /* It may now die */
unix_peer(sk) = NULL;
wake_up_interruptible_all(&sk->protinfo.af_unix.peer_wait);
sk->max_ack_backlog=backlog;
sk->state=TCP_LISTEN;
- sock->flags |= SO_ACCEPTCON;
/* set credentials so connect can copy them */
sk->peercred.pid = current->pid;
sk->peercred.uid = current->euid;
timeo = unix_wait_for_peer(other, timeo);
- err = -ERESTARTSYS;
+ err = sock_intr_errno(timeo);
if (signal_pending(current))
goto out;
sock_put(other);
timeo = unix_wait_for_peer(other, timeo);
- err = -ERESTARTSYS;
+ err = sock_intr_errno(timeo);
if (signal_pending(current))
goto out_free;
* much.
*/
- if (size > 4096-16)
- limit = 4096-16; /* Fall back to a page if we can't grab a big buffer this instant */
+ if (size > PAGE_SIZE-16)
+ limit = PAGE_SIZE-16; /* Fall back to a page if we can't grab a big buffer this instant */
else
limit = 0; /* Otherwise just grab and wait */
!timeo)
break;
- sk->socket->flags |= SO_WAITDATA;
+ set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
unix_state_runlock(sk);
timeo = schedule_timeout(timeo);
unix_state_rlock(sk);
- sk->socket->flags &= ~SO_WAITDATA;
+ clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
}
__set_current_state(TASK_RUNNING);
timeo = unix_stream_data_wait(sk, timeo);
if (signal_pending(current)) {
- err = -ERESTARTSYS;
+ err = sock_intr_errno(timeo);
goto out;
}
down(&sk->protinfo.af_unix.readsem);
other->shutdown |= peer_mode;
unix_state_wunlock(other);
other->state_change(other);
+ read_lock(&other->callback_lock);
if (peer_mode == SHUTDOWN_MASK)
- sock_wake_async(other->socket,1,POLL_HUP);
+ sk_wake_async(other,1,POLL_HUP);
else if (peer_mode & RCV_SHUTDOWN)
- sock_wake_async(other->socket,1,POLL_IN);
+ sk_wake_async(other,1,POLL_IN);
+ read_unlock(&other->callback_lock);
}
if (other)
sock_put(other);
s,
atomic_read(&s->refcnt),
0,
- s->state == TCP_LISTEN ? SO_ACCEPTCON : 0,
+ s->state == TCP_LISTEN ? __SO_ACCEPTCON : 0,
s->type,
s->socket ?
(s->state == TCP_ESTABLISHED ? SS_CONNECTED : SS_UNCONNECTED) :
DIALOG="./scripts/lxdialog/lxdialog"
-kernel_version="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}"
+kernel_version="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}"
backtitle="Linux Kernel v$kernel_version Configuration"
int main(int argc, char *argv[])
{
char buf[1024];
- char *vec[1024];
+ char *vec[8192];
char type[64];
int i;
int vp=2;
*/
vec[0]="kernel-doc";
vec[1]="-docbook";
- for(i=1;vp<1021;i++)
+ for(i=1;vp<8189;i++)
{
if(argv[i]==NULL)
break;
exit(1);
case 0:
execvp("scripts/kernel-doc", vec);
- perror("exec");
+ perror("exec scripts/kernel-doc");
exit(1);
default:
waitpid(pid, NULL,0);
if ($line eq ""){
print $lineprefix, $blankline;
} else {
+ $line =~ s/\\\\\\/\&/g;
print $lineprefix, $line;
}
print "\n";
}
$type = join " ", @args;
+ if ($type eq "" && $param eq "...")
+ {
+ $type="...";
+ $param="...";
+ $parameters{"..."} = "variable arguments";
+ }
if ($type eq "")
{
$type="";
$newcontents = $2;
if ($contents ne "") {
+ $contents =~ s/\&/\\\\\\amp;/g;
+ $contents =~ s/\</\\\\\\lt;/g;
+ $contents =~ s/\>/\\\\\\gt;/g;
dump_section($section, $contents);
$section = $section_default;
}
} elsif (/$doc_end/) {
if ($contents ne "") {
+ $contents =~ s/\&/\\\\\\amp;/g;
+ $contents =~ s/\</\\\\\\lt;/g;
+ $contents =~ s/\>/\\\\\\gt;/g;
dump_section($section, $contents);
$section = $section_default;
$contents = "";
# miguel-style comment kludge, look for blank lines after
# @parameter line to signify start of description
if ($1 eq "" && $section =~ m/^@/) {
+ $contents =~ s/\&/\\\\\\amp;/g;
+ $contents =~ s/\</\\\\\\lt;/g;
+ $contents =~ s/\>/\\\\\\gt;/g;
dump_section($section, $contents);
$section = $section_default;
$contents = "";