Logical Volume Manager (LVM) support
CONFIG_BLK_DEV_LVM
- This driver lets you combine several hard disks, hard disk partitions,
- multiple devices or even loop devices (for evaluation purposes) into
- a volume group. Imagine a volume group as a kind of virtual disk.
- Logical volumes, which can be thought of as virtual partitions,
- can be created in the volume group. You can resize volume groups and
- logical volumes after creation time, corresponding to new capacity needs.
- Logical volumes are accessed as block devices named
- /dev/VolumeGroupName/LogicalVolumeName.
+ This driver lets you combine several hard disks, hard disk
+ partitions, multiple devices or even loop devices (for evaluation
+ purposes) into a volume group. Imagine a volume group as a kind of
+ virtual disk. Logical volumes, which can be thought of as virtual
+ partitions, can be created in the volume group. You can resize
+ volume groups and logical volumes after creation time, corresponding
+ to new capacity needs. Logical volumes are accessed as block
+ devices named /dev/VolumeGroupName/LogicalVolumeName.
For details see /usr/src/linux/Documentaion/LVM-HOWTO.
To get the newest software see <http://linux.msede.com/lvm>.
-Logical Volume Manager proc filesystem information
+Logical Volume Manager /proc file system information
CONFIG_LVM_PROC_FS
- If you say Y here, you are able to access overall Logical Volume Manager,
- Volume Group, Logical and Physical Volume information in /proc/lvm.
+ If you say Y here, you are able to access overall Logical Volume
+ Manager, Volume Group, Logical and Physical Volume information in
+ /proc/lvm.
- To use this option, you have to check, that the "proc filesystem support"
- (CONFIG_PROC_FS) is enabled too.
+ To use this option, you have to check, that the "/proc file system
+ support" (CONFIG_PROC_FS) is enabled too.
Multiple devices driver support
CONFIG_BLK_DEV_MD
them off.
If you say Y here, note that SYN cookies aren't enabled by default;
- you can enable them by saying Y to "/proc file-system support" and
+ you can enable them by saying Y to "/proc file system support" and
"Sysctl support" below and executing the command
echo 1 >/proc/sys/net/ipv4/tcp_syncookies
- at boot time after the proc file-system has been mounted.
+ at boot time after the /proc file system has been mounted.
If unsure, say Y.
Documentation/java.txt for information about how to include Java
support.
- You must say Y to "proc file system support" (CONFIG_PROC_FS) to
+ You must say Y to "/proc file system support" (CONFIG_PROC_FS) to
use this part of the kernel.
You may say M here for module support and later load the module when
small amount of overhead to each and every SCSI command the aic7xxx
driver handles, so if you aren't really interested in this
information, it is best to leave it disabled. This will only work if
- you also say Y to "/proc file systems support", below.
+ you also say Y to "/proc file system support", below.
If unsure, say N.
slave=1 Adhoc slave(btw, it is still forming own net
sometimes)
channel=1..? meaningful in adhoc mode
- all other parameters can be set via proc interface
+ all other parameters can be set via /proc interface
These parameters belong to .._card module, but alas, they are here
if you have problems with screwing up card, both_bap_lock=1 is conservative
value (performance hit 15%)
That package also contains some documentation; for more, check out
http://snafu.freedom.org/linux2.2/iproute-notes.html .
- If you say Y here and to "/proc file systems" below, you will be able
- to read status information about packet schedulers from the file
- /proc/net/psched.
+ If you say Y here and to "/proc file system support" below, you will
+ be able to read status information about packet schedulers from the
+ file /proc/net/psched.
The available schedulers are listed in the following questions; you
can say Y to as many as you like. If unsure, say N now.
Network code profiler
CONFIG_NET_PROFILE
- If you say Y here and to "/proc file systems support" below, some
+ If you say Y here and to "/proc file system support" below, some
obscure and undocumented information about the network code's
performance will be written to /proc/net/profile. If you don't know
what it is about, you don't need it: say N.
module, say M here and read Documentation/modules.txt as well as
Documentation/networking/net-modules.txt.
-RealTek 8129/8139 (not 8019/8029!) support
-CONFIG_RTL8139
+RealTek 8129 (not 8019/8029!) support (EXPERIMENTAL)
+CONFIG_RTL8129
This is a driver for the Fast Ethernet PCI network cards based on
- the RTL8129 and RTL8139 chips. If you have one of those, say Y and
+ the RTL8129 chip. If you have one of those, say Y and
read the Ethernet-HOWTO, available from
http://www.linuxdoc.org/docs.html#howto .
If you want to compile this driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want),
say M here and read Documentation/modules.txt. This is recommended.
- The module will be called rtl8139.o.
+ The module will be called rtl8129.o.
+
+RealTek RTL-8139 PCI Fast Ethernet Adapter support
+CONFIG_8139TOO
+ This is a driver for the Fast Ethernet PCI network cards based on
+ the RTL8139 chip. If you have one of those, say Y and
+ read the Ethernet-HOWTO, available from
+ http://www.linuxdoc.org/docs.html#howto .
+
+ If you want to compile this driver as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read Documentation/modules.txt. This is recommended.
+ The module will be called 8139too.o.
SiS 900 PCI Fast Ethernet Adapter support
CONFIG_SIS900
Documentation/networking/net-modules.txt.
EISA, VLB, PCI and on board controllers
-CONFIG_NET_EISA
+CONFIG_NET_PCI
This is another class of network cards which attach directly to the
bus. If you have one of those, say Y and read the Ethernet-HOWTO,
available from http://www.linuxdoc.org/docs.html#howto .
Documentation/networking/net-modules.txt.
EtherExpress PRO/100 support
-CONFIG_EEXPRESS_PRO100
+CONFIG_EEPRO100
If you have an Intel EtherExpress PRO/100 PCI network (Ethernet)
card, say Y and read the Ethernet-HOWTO, available from
http://www.linuxdoc.org/docs.html#howto .
compiled as a module, and so this could be dangerous. Most everyone
wants to say Y here.
-SCO UnixWare BFS Support
+BFS file system support (EXPERIMENTAL)
CONFIG_BFS_FS
- Boot Filesystem (BFS) is a file system used under SCO UnixWare to
+ Boot File System (BFS) is a file system used under SCO UnixWare to
allow bootloader access the kernel image and other important files
during the boot process. It is usually mounted under /stand and
corresponds to the slice marked as "STAND" in the UnixWare
ISO 9660 CDROM file system support
CONFIG_ISO9660_FS
This is the standard file system used on CDROMs. It was previously
- known as "High Sierra Filesystem" and is called "hsfs" on other Unix
+ known as "High Sierra File System" and is called "hsfs" on other Unix
systems. The so-called Rock-Ridge extensions which allow for long
Unix filenames and symbolic links are also supported by this driver.
If you have a CDROM drive and want to do more with it than just
http://www.unicode.org for more information). Say Y here if you want
to be able to read Joliet CDROMs under Linux.
-UDF Filesystem support (read only)
+UDF file system support (read only)
CONFIG_UDF_FS
This is the new file system used by some CDROMS and DVD drivers. Say
Y if you intend to mount DVD discs or CDRW's written in packet mode,
say M here and read Documentation/modules.txt. The module will be
called vfat.o.
-UMSDOS: Unix-like file system on top of standard MSDOS file system
+UMSDOS: Unix-like file system on top of standard MSDOS fs
CONFIG_UMSDOS_FS
Say Y here if you want to run Linux from within an existing DOS
partition of your hard drive. The advantage of this is that you can
often a source of trouble if two devices are mistakenly configured
to use the same IRQ).
- The /proc filesystem is explained in the file
+ The /proc file system is explained in the file
Documentation/filesystems/proc.txt and on the proc(5) manpage ("man
5 proc").
This option will enlarge your kernel by about 67 KB. Several
programs depend on this, so everyone should say Y here.
-/dev filesystem support (EXPERIMENTAL)
+/dev file system support (EXPERIMENTAL)
CONFIG_DEVFS_FS
- This is another virtual filesystem (like /proc) which provides the
- filesystem interface to device drivers, normally found in /dev.
+ This is another virtual file system (like /proc) which provides the
+ file system interface to device drivers, normally found in /dev.
Devfs does not depend on major and minor number allocations. Device
drivers register entries in /dev which appear automagically. Without
devfs you need to populate /dev with hundreds, even thousands of
inodes.
This is work in progress. If you want to use this you *must* read
Documentation/filesystems/devfs/README
+ In particular, make sure you install devfsd. If you don't, expect to
+ spend time patching broken code and updating configuration files.
-Enable devfs debugging output
+Debug devfs
CONFIG_DEVFS_DEBUG
This option appears if you have CONFIG_DEVFS_FS enabled. Setting
this to 'Y' enables devfs debugging output. See the file
Documentation/filesystems/devfs/boot-options for more details.
The default is 'N'.
-NFS filesystem support
+NFS file system support
CONFIG_NFS_FS
If you are connected to some other (usually local) Unix computer
(using SLIP, PLIP, PPP or Ethernet) and want to mount files residing
The module is called hpfs.o. If you want to compile it as a module,
say M here and read Documentation/modules.txt. If unsure, say N.
-Windows NT NTFS support (read only)
+NTFS file system support (read only)
CONFIG_NTFS_FS
NTFS is the file system of Microsoft Windows NT. Say Y if you want
to get read access to files on NTFS partitions of your hard drive.
If you want to use the newer version of autofs with more features,
say N here and select automounter v4.
-Kernel automounter v4 support
+Kernel automounter version 4 support (also supports v3)
CONFIG_AUTOFS4_FS
The automounter is a tool to automatically mount remote file systems
on demand. This implementation is partially kernel-based to reduce
partition (VTOC - Virtual Table of Contents). Its format is
incompatible with all other OSes. Saying Y here allows you to read
VTOC and further mount UnixWare partitions read-only from within
- Linux if you have also said Y to "UFS file system support" or "System
- V and Coherent file system support", above.
+ Linux if you have also said Y to "UFS file system support", "System
+ V and Coherent file system support" or "BFS file system support",
+ above.
This is mainly used to carry data from a UnixWare box to your
Linux box via a removable medium like magneto-optical, ZIP or
Procfs entry for ftape
CONFIG_FT_PROC_FS
Optional. Saying Y will result in creation of a directory
- `/proc/ftape' under the proc file system. The files can be viewed
+ `/proc/ftape' under the /proc file system. The files can be viewed
with your favorite pager (i.e. use "more /proc/ftape/history" or
"less /proc/ftape/history" or simply "cat /proc/ftape/history"). The
file will contain some status information about the inserted
by approximately 2 KB.
WARNING: When compiling ftape as a module (i.e. saying M to
- "Floppy tape drive") it is dangerous to use ftape's proc file system
- interface. Accessing `/proc/ftape' while the module is unloaded will
- result in a kernel Oops. This cannot be fixed from inside ftape.
+ "Floppy tape drive") it is dangerous to use ftape's /proc file
+ system interface. Accessing `/proc/ftape' while the module is
+ unloaded will result in a kernel Oops. This cannot be fixed from
+ inside ftape.
Controlling the amount of debugging output of ftape
CONFIG_FT_NORMAL_DEBUG
11) exchange RAM chips
12) exchange the motherboard.
+ To compile this driver as a module ( = code which can be inserted in
+ and removed from the running kernel whenever you want), say M here
+ and read Documentation/modules.txt. The module will be called
+ apm.o.
+
Ignore USER SUSPEND
CONFIG_APM_IGNORE_USER_SUSPEND
This option will ignore USER SUSPEND requests. On machines with a
many of the newer IBM Thinkpads. If you experience hangs when you
suspend, try setting this to Y. Otherwise, say N.
-Entry point offset fix (some Acer laptops)
-CONFIG_APM_BAD_ENTRY_OFFSET
- Some implementations of the APM BIOS provide the driver with a bad
- entry point offset. If you set this option to Y, then the upper
- sixteen bits of the offset will be set to zero. This is usually
- unnecessary but harmless. This is required for the Acer Travelmate
- 510DX, Travelmate 510T and Extensa 503T. For others, say N.
-
Use real mode APM BIOS call to power off
CONFIG_APM_REAL_MODE_POWER_OFF
Use real mode APM BIOS calls to switch off the computer. This is
Changes for patch v105
- Unregister SCSI host from <scsi_host_no_list> in <scsi_unregister>
- Thanks to Zoltan BOSZORMENYI <zboszor@mol.hu>
+ Thanks to Zoltán Böszörményi <zboszor@mail.externet.hu>
- Don't save /dev/log in rc.devfs
- Moved device registration from <lp_init> to <lp_register>
Thanks to Tim Waugh <twaugh@redhat.com>
+===============================================================================
+Changes for patch v160
+
+Work sponsored by SGI
+
+- Fixed drivers/char/joystick/joystick.c
+ Thanks to Vojtech Pavlik <vojtech@suse.cz>
+
+- Documentation updates
+
+- Fixed arch/i386/kernel/mtrr.c if procfs and devfs not enabled
+
+- Fixed drivers/char/stallion.c
will require this. Note that the kernel creates a compatibility entry
for the root device, so you don't need initrd.
+Note that you no longer need to mount devpts if you use Unix98 PTYs,
+as devfs can manage /dev/pts itself. This saves you some RAM, as you
+don't need to compile and install devpts. Note that some versions of
+glibc have a bug with Unix98 pty handling on devfs systems. Contact
+the glibc maintainers for a fix.
+
+Note also that apart from editing /etc/fstab, other things will need
+to be changed if you *don't* install devfsd. Some software (like the X
+server) hard-wire device names in their source. It really is much
+easier to install devfsd so that compatibility entries are created.
+You can then slowly migrate your system to using the new device names
+(for example, by starting with /etc/fstab), and then limiting the
+compatibility entries that devfsd creates.
+MAKE SURE YOU INSTALL DEVFSD BEFORE YOU BOOT A DEVFS-ENABLED KERNEL!
+
+Now that devfs has gone into the 2.3.46 kernel, I'm getting a lot of
+reports back. Many of these are because people are trying to run
+without devfsd, and hence some things break. Please just run devfsd if
+things break. I want to concentrate on real bugs rather than
+misconfiguration problems at the moment. If people are willing to fix
+bugs/false assumptions in other code (i.e. glibc, X server) and submit
+that to the respective maintainers, that would be great.
+
Fail-safe Approach with real /dev inodes <subsection>
----------------------------------------
This method involves more work, and is no longer recommended now that
- TTY devices (console, serial ports, terminals and pseudo-terminals)
Thanks to C. Scott Ananian <cananian@alumni.princeton.edu>
-- SCSI tapes (/dev/st*)
+- SCSI tapes (/dev/scsi and /dev/tapes)
-- SCSI CD-ROMs (/dev/sr*)
+- SCSI CD-ROMs (/dev/scsi and /dev/cdroms)
-- SCSI generic devices (/dev/sg*)
+- SCSI generic devices (/dev/scsi)
- RAMDISCS (/dev/ram?)
- Meta Devices (/dev/md*)
-- Floppy discs (/dev/fd?*)
+- Floppy discs (/dev/floppy)
-- Parallel port printers (/dev/lp*)
+- Parallel port printers (/dev/printers)
-- Sound devices
+- Sound devices (/dev/sound)
Thanks to Eric Dumas <dumas@linux.eu.org> and
C. Scott Ananian <cananian@alumni.princeton.edu>
-- Joysticks (/dev/js*)
+- Joysticks (/dev/joysticks)
- Sparc keyboard (/dev/kbd)
- Coda network file system (/dev/cfs*)
-- Virtual console capture devices (/dev/vcs*)
+- Virtual console capture devices (/dev/vcc)
Thanks to Dennis Hou <smilax@mindmeld.yi.org>
-- Frame buffer devices (/dev/fb*)
+- Frame buffer devices (/dev/fb)
-- Video capture devices (/dev/video? /dev/vbi?)
+- Video capture devices (/dev/v4l)
Naming Scheme <section>
/dev/tts/{0,1,...} /dev/ttyS{0,1,...} Serial ports
/dev/cua/{0,1,...} /dev/cua{0,1,...} Call out devices
/dev/vc/{0,1,...} /dev/tty{1...63} Virtual consoles
+ /dev/vcc/{0,1,...} /dev/vcs{1...63} Virtual consoles
/dev/pty/m{0,1,...} /dev/ptyp?? PTY masters
/dev/pty/s{0,1,...} /dev/ttyp?? PTY slaves
is limited to the range 36864-61439 (majors 144-239), in order to
avoid any possible conflicts with existing official allocations.
+Please note that using dynamically allocated block device numbers may
+break the NFS daemons (both user and kernel mode), which expect dev_t
+for a given device to be constant over reboots. A simple reboot, with
+no change in your hardware layout, would result in the same device
+numbers being allocated, and hence will not cause a problem for NFS
+daemons.
+
A final note on this scheme: since it doesn't increase the size of
device numbers, there are no compatibility issues with userspace.
Dependent statements:
dep_bool /prompt/ /symbol/ /dep/ ...
+ dep_mbool /prompt/ /symbol/ /dep/ ...
dep_hex /prompt/ /symbol/ /word/ /dep/ ...
dep_int /prompt/ /symbol/ /word/ /dep/ ...
dep_string /prompt/ /symbol/ /word/ /dep/ ...
Example:
- # Not from the corpus
- define_int CONFIG_UID_TORVALDS 2026
+ # drivers/char/ftape/Config.in
+ define_int CONFIG_FT_ALPHA_CLOCK 0
Example:
- # not from the corpus
- if [ "$CONFIG_ZFTAPE" != "n" ]; then
- comment 'The compressor will be built as a module only!'
- define_tristate CONFIG_ZFT_COMPRESSOR m
+ # drivers/video/Config.in
+ if [ "$CONFIG_FB_AMIGA" = "y" ]; then
+ define_tristate CONFIG_FBCON_AFB y
+ define_tristate CONFIG_FBCON_ILBM y
+ else
+ if [ "$CONFIG_FB_AMIGA" = "m" ]; then
+ define_tristate CONFIG_FBCON_AFB m
+ define_tristate CONFIG_FBCON_ILBM m
+ fi
fi
Any dependency which has a value of "y" does not restrict the input
range. Any dependency which has an empty value is ignored.
Any dependency which has a value of "n", or which has some other value,
+(like "m") restricts the input range to "n". Quoting dependencies is not
+allowed. Using dependencies with an empty value possible is not
+recommended. See also dep_mbool below.
+
+If the input range is restricted to the single choice "n", dep_bool
+silently assigns "n" to /symbol/. If the input range has more than
+one choice, dep_bool displays /prompt/ to the user, accepts a value
+from the user, and assigns that value to /symbol/.
+
+Configure: implemented
+Menuconfig: implemented
+XConfig: implemented
+mconfig: implemented
+
+Example:
+
+ # drivers/net/Config.in
+ dep_bool 'Aironet 4500/4800 PCI support 'CONFIG_AIRONET4500_PCI $CONFIG_PCI
+
+Known bugs:
+- Xconfig does not write "# foo is not set" to .config (as well as
+ "#unset foo" to autoconf.h) if command is disabled by its dependencies.
+
+
+=== dep_mbool /prompt/ /symbol/ /dep/ ...
+
+This verb evaluates all of the dependencies in the dependency list.
+Any dependency which has a value of "y" or "m" does not restrict the
+input range. Any dependency which has an empty value is ignored.
+Any dependency which has a value of "n", or which has some other value,
restricts the input range to "n". Quoting dependencies is not allowed.
Using dependencies with an empty value possible is not recommended.
one choice, dep_bool displays /prompt/ to the user, accepts a value
from the user, and assigns that value to /symbol/.
+Notice that the only difference between dep_bool and dep_mbool
+is in the way of treating the "m" value as a dependency.
+
Configure: implemented
Menuconfig: implemented
XConfig: implemented
-mconfig: implemented
+mconfig: not implemented
- # not from the corpus
- dep_bool 'RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_PCI
+Example:
+
+ # Not from the corpus
+ dep_mbool 'Packet socket: mmapped IO' CONFIG_PACKET_MMAP $CONFIG_PACKET
Known bugs:
- Xconfig does not write "# foo is not set" to .config (as well as
Xconfig: implemented
mconfig: implemented
+Example:
+
+ # drivers/char/Config.in
+ dep_tristate 'Parallel printer support' CONFIG_PRINTER $CONFIG_PARPORT
+
Known bugs:
- Xconfig does not write "# foo is not set" to .config (as well as
"#unset foo" to autoconf.h) if command is disabled by its dependencies.
Xconfig: implemented (with bugs)
mconfig: implemented
+Example:
+
+ # arch/mips/config.in
+ unset CONFIG_PCI
+ unset CONFIG_MIPS_JAZZ
+ unset CONFIG_VIDEO_G364
+
=== choice /prompt/ /word/ /word/
Xconfig: implemented
mconfig: implemented
+Example:
+
+ # arch/i386/config.in
+ choice ' PCI access mode' \
+ "BIOS CONFIG_PCI_GOBIOS \
+ Direct CONFIG_PCI_GODIRECT \
+ Any CONFIG_PCI_GOANY" Any
+
=== nchoice /prompt/ /symbol/ /prompt/ /symbol/ ...
- literals with an empty "" value are not properly handled.
-- tkparse gives the wrong precedence to -o, -a, and !. Don't use both
- -o and -a in an expression. Don't use ! at all.
-
=== mainmenu_option next_comment
PG_MAGIC 'P' pg_{read,write}_hdr include/linux/pg.h
MKISS_DRIVER_MAGIC 0x04bf mkiss_channel drivers/net/mkiss.h
RISCOM8_MAGIC 0x0907 riscom_port drivers/char/riscom8.h
-APM_BIOS_MAGIC 0x4101 apm_bios_struct include/linux/apm_bios.h
+APM_BIOS_MAGIC 0x4101 apm_user arch/i386/kernel/apm.c
CYCLADES_MAGIC 0x4359 cyclades_port include/linux/cyclades.h
FASYNC_MAGIC 0x4601 fasync_struct include/linux/fs.h
PTY_MAGIC 0x5001 (none at the moment)
$(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \
--start-group \
$(CORE_FILES) \
- $(NETWORKS) \
$(DRIVERS) \
+ $(NETWORKS) \
$(LIBS) \
--end-group \
-o vmlinux
movw $0xAA, (0x1ff) # device present
no_psmouse:
-#ifdef CONFIG_APM
+#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
# Then check for an APM BIOS...
# %ds points to the bootsector
movw $0, 0x40 # version = 0 means no APM BIOS
xorw %bx, %bx
int $0x15 # ignore return code
movw $0x05303, %ax # 32 bit connect
- xorw %bx, %bx
+ xorl %ebx, %ebx
+ xorw %cx, %cx # paranoia :-)
+ xorw %dx, %dx # ...
+ xorl %esi, %esi # ...
+ xorw %di, %di # ...
int $0x15
jc no_32_apm_bios # Ack, error.
movl %ebx, (68) # BIOS entry point offset
movw %cx, (72) # BIOS 16 bit code segment
movw %dx, (74) # BIOS data segment
- movl %esi, (78) # BIOS code segment length
+ movl %esi, (78) # BIOS code segment lengths
movw %di, (82) # BIOS data segment length
# Redo the installation check as the 32 bit connect
# modifies the flags returned on some BIOSs
movw $0x05300, %ax # APM BIOS installation check
xorw %bx, %bx
+ xorw %cx, %cx # paranoia
int $0x15
jc apm_disconnect # error -> shouldn't happen
fi
fi
-bool 'Advanced Power Management BIOS support' CONFIG_APM
+tristate 'Advanced Power Management BIOS support' CONFIG_APM
if [ "$CONFIG_APM" != "n" ]; then
bool ' Ignore USER SUSPEND' CONFIG_APM_IGNORE_USER_SUSPEND
bool ' Enable PM at boot time' CONFIG_APM_DO_ENABLE
bool ' Ignore multiple suspend/resume cycles' CONFIG_APM_IGNORE_SUSPEND_BOUNCE
bool ' RTC stores time in GMT' CONFIG_APM_RTC_IS_GMT
bool ' Allow interrupts during APM BIOS calls' CONFIG_APM_ALLOW_INTS
- bool ' Entry point offset fix (some Acer laptops)' CONFIG_APM_BAD_ENTRY_OFFSET
bool ' Use real mode APM BIOS call to power off' CONFIG_APM_REAL_MODE_POWER_OFF
fi
/* -*- linux-c -*-
* APM BIOS driver for Linux
- * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com)
+ * Copyright 1994-2000 Stephen Rothwell (sfr@linuxcare.com)
*
* Initial development of this driver was funded by NEC Australia P/L
* and NEC Corporation
* Jan 1999, Version 1.9
* Oct 1999, Version 1.10
* Nov 1999, Version 1.11
+ * Jan 2000, Version 1.12
+ * Feb 2000, Version 1.13
*
* History:
* 0.6b: first version in official kernel, Linux 1.3.46
* The new replacment for it is, but Linux doesn't yet support this.
* Alan Cox Linux 2.1.55
* 1.3: Set up a valid data descriptor 0x40 for buggy BIOS's
- * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
+ * 1.4: Upgraded to support APM 1.2. Integrated ThinkPad suspend patch by
* Dean Gaudet <dgaudet@arctic.org>.
* C. Scott Ananian <cananian@alumni.princeton.edu> Linux 2.1.87
* 1.5: Fix segment register reloading (in case of bad segments saved
* (reported by Panos Katsaloulis <teras@writeme.com>).
* Real mode power off patch (Walter Hofmann
* <Walter.Hofmann@physik.stud.uni-erlangen.de>).
+ * 1.12: Remove CONFIG_SMP as the compiler will optimize
+ * the code away anyway (smp_num_cpus == 1 in UP)
+ * noted by Artur Skawina <skawina@geocities.com>.
+ * Make power off under SMP work again.
+ * Fix thinko with initial engaging of BIOS.
+ * Make sure power off only happens on CPU 0
+ * (Paul "Rusty" Russell <rusty@linuxcare.com>).
+ * Do error notification to user mode if BIOS calls fail.
+ * Move entrypoint offset fix to ...boot/setup.S
+ * where it belongs (Cosmos <gis88564@cis.nctu.edu.tw>).
+ * Remove smp-power-off. SMP users must now specify
+ * "apm=power-off" on the kernel command line. Suggested
+ * by Jim Avera <jima@hal.com>, modified by Alan Cox
+ * <alan@lxorguk.ukuu.org.uk>.
+ * Register the /proc/apm entry even on SMP so that
+ * scripts that check for it before doing power off
+ * work (Jim Avera <jima@hal.com>).
+ * 1.13: Changes for new pm_ interfaces (Andy Henroid
+ * <andy_henroid@yahoo.com>).
+ * Modularize the code.
*
* APM 1.1 Reference:
*
#include <linux/apm_bios.h>
#include <linux/init.h>
#include <linux/sched.h>
+#include <linux/pm.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/desc.h>
-#include <linux/pm.h>
-
extern unsigned long get_cmos_time(void);
extern void machine_real_restart(unsigned char *, int);
* See Documentation/Config.help for the configuration options.
*
* Various options can be changed at boot time as follows:
+ * (We allow underscores for compatibility with the modules code)
* apm=on/off enable/disable APM
* [no-]debug log some debugging messages
- * [no-]power-off power off on shutdown
- * [no-]smp-power-off allow power off even for SMP
+ * [no-]power[-_]off power off on shutdown
*/
/* KNOWN PROBLEM MACHINES:
/*
* Define to re-initialize the interrupt 0 timer to 100 Hz after a suspend.
- * This patched by Chad Miller <cmiller@surfsouth.com>, orig code by David
- * Chen <chen@ctpa04.mit.edu>
+ * This patched by Chad Miller <cmiller@surfsouth.com>, original code by
+ * David Chen <chen@ctpa04.mit.edu>
*/
#undef INIT_TIMER_AFTER_SUSPEND
/*
* If CONFIG_APM_IGNORE_SUSPEND_BOUNCE is defined then
- * ignore suspend events for this amount of time
+ * ignore suspend events for this amount of time after a resume
*/
#define BOUNCE_INTERVAL (3 * HZ)
#define savesegment(seg, where) \
__asm__ __volatile__("movl %%" #seg ",%0" : "=m" (where))
+/*
+ * Maximum number of events stored
+ */
+#define APM_MAX_EVENTS 20
+
+/*
+ * The per-file APM data
+ */
+struct apm_user {
+ int magic;
+ struct apm_user * next;
+ int suser: 1;
+ int suspend_wait: 1;
+ int suspend_result;
+ int suspends_pending;
+ int standbys_pending;
+ int suspends_read;
+ int standbys_read;
+ int event_head;
+ int event_tail;
+ apm_event_t events[APM_MAX_EVENTS];
+};
+
+/*
+ * The magic number in apm_user
+ */
+#define APM_BIOS_MAGIC 0x4101
+
/*
* Local variables
*/
unsigned long offset;
unsigned short segment;
} apm_bios_entry;
-static int apm_enabled = 0;
-static int smp_hack = 0;
#ifdef CONFIG_APM_CPU_IDLE
static int clock_slowed = 0;
#endif
#endif
static int debug = 0;
static int apm_disabled = 0;
-static int power_off_enabled = 1;
+#ifdef CONFIG_SMP
+static int power_off = 0;
+#else
+static int power_off = 1;
+#endif
+static int exit_kapmd = 0;
+static int kapmd_running = 0;
static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
-static struct apm_bios_struct * user_list = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
+static struct apm_user * user_list = NULL;
-static char driver_version[] = "1.11"; /* no spaces */
+static char driver_version[] = "1.12"; /* no spaces */
static char * apm_event_name[] = {
"system standby",
__save_flags(flags);
APM_DO_CLI;
APM_DO_SAVE_SEGS;
+ /*
+ * N.B. We do NOT need a cld after the BIOS call
+ * because we always save and restore the flags.
+ */
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
{
int cx, dx, si;
+ /*
+ * N.B. We do NOT need a cld after the BIOS call
+ * because we always save and restore the flags.
+ */
__asm__ __volatile__(APM_DO_ZERO_SEGS
"pushl %%edi\n\t"
"pushl %%ebp\n\t"
- "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry)"\n\t"
+ "lcall %%cs:" SYMBOL_NAME_STR(apm_bios_entry) "\n\t"
"setc %%bl\n\t"
"popl %%ebp\n\t"
"popl %%edi\n\t"
}
}
+#if 0
extern int hlt_counter;
/*
}
}
#endif
+#endif
+
+#ifdef CONFIG_SMP
+static int apm_magic(void * unused)
+{
+ while (1)
+ schedule();
+}
+#endif
static void apm_power_off(void)
{
+#ifdef CONFIG_APM_REAL_MODE_POWER_OFF
+ unsigned char po_bios_call[] = {
+ 0xb8, 0x00, 0x10, /* movw $0x1000,ax */
+ 0x8e, 0xd0, /* movw ax,ss */
+ 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */
+ 0xb8, 0x07, 0x53, /* movw $0x5307,ax */
+ 0xbb, 0x01, 0x00, /* movw $0x0001,bx */
+ 0xb9, 0x03, 0x00, /* movw $0x0003,cx */
+ 0xcd, 0x15 /* int $0x15 */
+ };
+#endif
+
/*
- * smp_hack == 2 means that we would have enabled APM support
- * except there is more than one processor and so most of
- * the APM stuff is unsafe. We will still try power down
- * because is is useful to some people and they know what
- * they are doing because they booted with the smp-power-off
- * kernel option.
+ * This may be called on an SMP machine.
*/
- if (apm_enabled || (smp_hack == 2)) {
+#ifdef CONFIG_SMP
+ /* Some bioses don't like being called from CPU != 0 */
+ while (cpu_number_map[smp_processor_id()] != 0) {
+ kernel_thread(apm_magic, NULL,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
+ schedule();
+ }
+#endif
#ifdef CONFIG_APM_REAL_MODE_POWER_OFF
- unsigned char po_bios_call[] = {
- 0xb8, 0x00, 0x10, /* movw $0x1000,ax */
- 0x8e, 0xd0, /* movw ax,ss */
- 0xbc, 0x00, 0xf0, /* movw $0xf000,sp */
- 0xb8, 0x07, 0x53, /* movw $0x5307,ax */
- 0xbb, 0x01, 0x00, /* movw $0x0001,bx */
- 0xb9, 0x03, 0x00, /* movw $0x0003,cx */
- 0xcd, 0x15 /* int $0x15 */
- };
-
- machine_real_restart(po_bios_call, sizeof(po_bios_call));
+ machine_real_restart(po_bios_call, sizeof(po_bios_call));
#else
- (void) apm_set_power_state(APM_STATE_OFF);
+ (void) apm_set_power_state(APM_STATE_OFF);
#endif
- }
}
-#ifdef CONFIG_APM_DO_ENABLE
-static int __init apm_enable_power_management(void)
+static int apm_enable_power_management(int enable)
{
u32 eax;
+ if ((enable == 0) && (apm_bios_info.flags & APM_BIOS_DISENGAGED))
+ return APM_NOT_ENGAGED;
if (apm_bios_call_simple(APM_FUNC_ENABLE_PM, APM_DEVICE_BALL,
- 1, &eax))
+ enable, &eax))
return (eax >> 8) & 0xff;
- apm_bios_info.flags &= ~APM_BIOS_DISABLED;
+ if (enable)
+ apm_bios_info.flags &= ~APM_BIOS_DISABLED;
+ else
+ apm_bios_info.flags |= APM_BIOS_DISABLED;
return APM_SUCCESS;
}
-#endif
static int apm_get_power_status(u_short *status, u_short *bat, u_short *life)
{
}
#endif
-static int __init apm_engage_power_management(u_short device)
+static int apm_engage_power_management(u_short device, int enable)
{
u32 eax;
- if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, 1, &eax))
+ if ((enable == 0) && (device == APM_DEVICE_ALL)
+ && (apm_bios_info.flags & APM_BIOS_DISABLED))
+ return APM_DISABLED;
+ if (apm_bios_call_simple(APM_FUNC_ENGAGE_PM, device, enable, &eax))
return (eax >> 8) & 0xff;
+ if (device == APM_DEVICE_ALL) {
+ if (enable)
+ apm_bios_info.flags &= ~APM_BIOS_DISENGAGED;
+ else
+ apm_bios_info.flags |= APM_BIOS_DISENGAGED;
+ }
return APM_SUCCESS;
}
}
#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
-/* Called from console driver -- must make sure apm_enabled. */
static int apm_console_blank(int blank)
{
int error;
state = blank ? APM_STATE_STANDBY : APM_STATE_READY;
/* Blank the first display device */
error = set_power_state(0x100, state);
- if (error != APM_SUCCESS)
+ if ((error != APM_SUCCESS) && (error != APM_NO_ERROR)) {
/* try to blank them all instead */
error = set_power_state(0x1ff, state);
+ if ((error != APM_SUCCESS) && (error != APM_NO_ERROR))
+ /* try to blank device one instead */
+ error = set_power_state(0x101, state);
+ }
if ((error == APM_SUCCESS) || (error == APM_NO_ERROR))
return 1;
apm_error("set display", error);
}
#endif
-static int queue_empty(struct apm_bios_struct * as)
+static int queue_empty(struct apm_user *as)
{
return as->event_head == as->event_tail;
}
-static apm_event_t get_queued_event(struct apm_bios_struct * as)
+static apm_event_t get_queued_event(struct apm_user *as)
{
as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
return as->events[as->event_tail];
}
-static void queue_event(apm_event_t event, struct apm_bios_struct *sender)
+static void queue_event(apm_event_t event, struct apm_user *sender)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
if (user_list == NULL)
return;
#endif
}
-static void suspend(void)
+static void reinit_timer(void)
{
- int err;
#ifdef INIT_TIMER_AFTER_SUSPEND
unsigned long flags;
-#endif
- get_time_diff();
- err = apm_set_power_state(APM_STATE_SUSPEND);
- if ((err != APM_SUCCESS) && (err != APM_NO_ERROR))
- apm_error("suspend", err);
-#ifdef INIT_TIMER_AFTER_SUSPEND
save_flags(flags);
cli();
/* set the clock to 100 Hz */
udelay(10);
restore_flags(flags);
#endif
+}
+
+static int suspend(void)
+{
+ int err;
+ int ret;
+ struct apm_user *as;
+
+ get_time_diff();
+ err = apm_set_power_state(APM_STATE_SUSPEND);
+ reinit_timer();
set_time();
+ ret = (err == APM_SUCCESS) || (err == APM_NO_ERROR);
+ if (!ret)
+ apm_error("suspend", err);
+ for (as = user_list; as != NULL; as = as->next) {
+ as->suspend_wait = 0;
+ as->suspend_result = (ret ? 0 : -EIO);
+ }
+ wake_up_interruptible(&apm_suspend_waitqueue);
+ return ret;
}
static void standby(void)
return 0;
}
-static int send_event(apm_event_t event, apm_event_t undo,
- struct apm_bios_struct *sender)
+static int send_event(apm_event_t event, struct apm_user *sender)
{
switch (event) {
case APM_SYS_SUSPEND:
case APM_CRITICAL_SUSPEND:
case APM_USER_SUSPEND:
/* map all suspends to ACPI D3 */
- if (pm_send_request(PM_SUSPEND, (void*) 3)) {
+ if (pm_send_request(PM_SUSPEND, (void *)3)) {
if (apm_bios_info.version > 0x100)
apm_set_power_state(APM_STATE_REJECT);
return 0;
case APM_NORMAL_RESUME:
case APM_CRITICAL_RESUME:
/* map all resumes to ACPI D0 */
- if (pm_send_request(PM_RESUME, 0)) {
- if (apm_bios_info.version > 0x100)
- apm_set_power_state(APM_STATE_REJECT);
- return 0;
- }
+ (void) pm_send_request(PM_RESUME, (void *)0);
break;
}
if (waiting_for_resume)
break;
#endif
- if (send_event(event, APM_STANDBY_RESUME, NULL)) {
+ if (send_event(event, NULL)) {
#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
waiting_for_resume = 1;
#endif
if (waiting_for_resume)
break;
#endif
- if (send_event(event, APM_NORMAL_RESUME, NULL)) {
+ if (send_event(event, NULL)) {
#ifdef CONFIG_APM_IGNORE_MULTIPLE_SUSPEND
waiting_for_resume = 1;
#endif
if (suspends_pending <= 0)
- suspend();
+ (void) suspend();
}
break;
ignore_bounce = 1;
#endif
set_time();
- send_event(event, 0, NULL);
+ send_event(event, NULL);
break;
case APM_CAPABILITY_CHANGE:
case APM_LOW_BATTERY:
case APM_POWER_STATUS_CHANGE:
- send_event(event, 0, NULL);
+ send_event(event, NULL);
break;
case APM_UPDATE_TIME:
break;
case APM_CRITICAL_SUSPEND:
- suspend();
+ (void) suspend();
break;
}
}
static void apm_event_handler(void)
{
- static int pending_count = 0;
+ static int pending_count = 4;
int err;
if ((standbys_pending > 0) || (suspends_pending > 0)) {
- if ((apm_bios_info.version > 0x100) && (pending_count-- <= 0)) {
+ if ((apm_bios_info.version > 0x100) && (pending_count-- < 0)) {
pending_count = 4;
err = apm_set_power_state(APM_STATE_BUSY);
if (err)
apm_error("busy", err);
}
} else
- pending_count = 0;
+ pending_count = 4;
check_events();
}
static void apm_mainloop(void)
{
DECLARE_WAITQUEUE(wait, current);
- apm_enabled = 1;
+
+ if (smp_num_cpus > 1)
+ return;
add_wait_queue(&apm_waitqueue, &wait);
current->state = TASK_INTERRUPTIBLE;
for (;;) {
/* Nothing to do, just sleep for the timeout */
schedule_timeout(APM_CHECK_TIMEOUT);
+ if (exit_kapmd)
+ break;
/*
* Ok, check all events, check for idle (and mark us sleeping
continue;
if (apm_do_idle()) {
unsigned long start = jiffies;
- do {
+ while (system_idle()) {
apm_do_idle();
if (jiffies - start > APM_CHECK_TIMEOUT)
break;
- } while (system_idle());
+ }
apm_do_busy();
apm_event_handler();
}
}
}
-static int check_apm_bios_struct(struct apm_bios_struct *as, const char *func)
+static int check_apm_user(struct apm_user *as, const char *func)
{
if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
printk(KERN_ERR "apm: %s passed bad filp", func);
static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
int i;
apm_event_t event;
DECLARE_WAITQUEUE(wait, current);
as = fp->private_data;
- if (check_apm_bios_struct(as, "read"))
+ if (check_apm_user(as, "read"))
return -EIO;
if (count < sizeof(apm_event_t))
return -EINVAL;
static unsigned int do_poll(struct file *fp, poll_table * wait)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
as = fp->private_data;
- if (check_apm_bios_struct(as, "select"))
+ if (check_apm_user(as, "poll"))
return 0;
poll_wait(fp, &apm_waitqueue, wait);
if (!queue_empty(as))
static int do_ioctl(struct inode * inode, struct file *filp,
u_int cmd, u_long arg)
{
- struct apm_bios_struct * as;
- int send_ok = 1;
+ struct apm_user * as;
+ DECLARE_WAITQUEUE(wait, current);
as = filp->private_data;
- if (check_apm_bios_struct(as, "ioctl"))
+ if (check_apm_user(as, "ioctl"))
return -EIO;
if (!as->suser)
return -EPERM;
as->standbys_read--;
as->standbys_pending--;
standbys_pending--;
- }
- else
- send_ok = send_event(APM_USER_STANDBY,
- APM_STANDBY_RESUME, as);
- if (send_ok && (standbys_pending <= 0))
+ } else if (!send_event(APM_USER_STANDBY, as))
+ return -EAGAIN;
+ if (standbys_pending <= 0)
standby();
break;
case APM_IOC_SUSPEND:
as->suspends_read--;
as->suspends_pending--;
suspends_pending--;
+ } else if (!send_event(APM_USER_SUSPEND, as))
+ return -EAGAIN;
+ if (suspends_pending <= 0) {
+ if (!suspend())
+ return -EIO;
+ } else {
+ as->suspend_wait = 1;
+ add_wait_queue(&apm_suspend_waitqueue, &wait);
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if ((as->suspend_wait == 0)
+ || signal_pending(current))
+ break;
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&apm_suspend_waitqueue, &wait);
+ return as->suspend_result;
}
- else
- send_ok = send_event(APM_USER_SUSPEND,
- APM_NORMAL_RESUME, as);
- if (send_ok && (suspends_pending <= 0))
- suspend();
break;
default:
return -EINVAL;
static int do_release(struct inode * inode, struct file * filp)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
as = filp->private_data;
- if (check_apm_bios_struct(as, "release"))
+ if (check_apm_user(as, "release"))
return 0;
filp->private_data = NULL;
if (as->standbys_pending > 0) {
if (as->suspends_pending > 0) {
suspends_pending -= as->suspends_pending;
if (suspends_pending <= 0)
- suspend();
+ (void) suspend();
}
if (user_list == as)
user_list = as->next;
else {
- struct apm_bios_struct * as1;
+ struct apm_user * as1;
for (as1 = user_list;
(as1 != NULL) && (as1->next != as);
as1->next = as->next;
}
kfree_s(as, sizeof(*as));
+ MOD_DEC_USE_COUNT;
return 0;
}
static int do_open(struct inode * inode, struct file * filp)
{
- struct apm_bios_struct * as;
+ struct apm_user * as;
- as = (struct apm_bios_struct *)kmalloc(sizeof(*as), GFP_KERNEL);
+ MOD_INC_USE_COUNT;
+
+ as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
if (as == NULL) {
printk(KERN_ERR "apm: cannot allocate struct of size %d bytes",
sizeof(*as));
+ MOD_DEC_USE_COUNT;
return -ENOMEM;
}
as->magic = APM_BIOS_MAGIC;
int time_units = -1;
char *units = "?";
- if (!apm_enabled)
- return 0;
p = buf;
- if (!(error = apm_get_power_status(&bx, &cx, &dx))) {
+ if ((smp_num_cpus == 1) &&
+ !(error = apm_get_power_status(&bx, &cx, &dx))) {
ac_line_status = (bx >> 8) & 0xff;
battery_status = bx & 0xff;
if ((cx & 0xff) != 0xff)
char * power_stat;
char * bat_stat;
+ kapmd_running = 1;
+
+ exit_files(current); /* daemonize doesn't do exit_files */
+ daemonize();
+
strcpy(current->comm, "kapmd");
sigfillset(¤t->blocked);
apm_bios_info.version = 0x100;
}
}
- if (debug) {
+ if (debug && (smp_num_cpus == 1)) {
printk(KERN_INFO "apm: Connection version %d.%d\n",
(apm_bios_info.version >> 8) & 0xff,
apm_bios_info.version & 0xff);
* is booted with PM disabled but not in the docking station.
* Unfortunate ...
*/
- error = apm_enable_power_management();
+ error = apm_enable_power_management(1);
if (error) {
apm_error("enable power management", error);
return -1;
}
}
#endif
- if (((apm_bios_info.flags & APM_BIOS_DISENGAGED) == 0)
+ if ((apm_bios_info.flags & APM_BIOS_DISENGAGED)
&& (apm_bios_info.version > 0x0100)) {
- if (apm_engage_power_management(0x0001) == APM_SUCCESS)
- apm_bios_info.flags &= ~APM_BIOS_DISENGAGED;
+ error = apm_engage_power_management(APM_DEVICE_ALL, 1);
+ if (error) {
+ apm_error("engage power management", error);
+ return -1;
+ }
}
/* Install our power off handler.. */
- if (power_off_enabled)
+ if (power_off)
pm_power_off = apm_power_off;
#ifdef CONFIG_MAGIC_SYSRQ
sysrq_power_off = apm_power_off;
#endif
#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
- console_blank_hook = apm_console_blank;
+ if (smp_num_cpus == 1)
+ console_blank_hook = apm_console_blank;
#endif
pm_active = 1;
apm_mainloop();
+
+ pm_active = 0;
+
+#if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
+ if (smp_num_cpus == 1)
+ console_blank_hook = NULL;
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+ sysrq_power_off = NULL;
+#endif
+ if (power_off)
+ pm_power_off = NULL;
+
+ kapmd_running = 0;
+
return 0;
}
str += 3;
if (strncmp(str, "debug", 5) == 0)
debug = !invert;
- if (strncmp(str, "power-off", 9) == 0)
- power_off_enabled = !invert;
- if (strncmp(str, "smp-power-off", 13) == 0)
- smp_hack = !invert;
+ if ((strncmp(str, "power-off", 9) == 0) ||
+ (strncmp(str, "power_off", 9) == 0))
+ power_off = !invert;
str = strchr(str, ',');
if (str != NULL)
str += strspn(str, ", \t");
printk(KERN_NOTICE "apm: disabled on user request.\n");
APM_INIT_ERROR_RETURN;
}
-
+ if ((smp_num_cpus > 1) && !power_off) {
+ printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
+ APM_INIT_ERROR_RETURN;
+ }
if (PM_IS_ACTIVE()) {
printk(KERN_NOTICE "apm: overridden by ACPI.\n");
APM_INIT_ERROR_RETURN;
_set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4));
apm_bios_entry.offset = apm_bios_info.offset;
-#ifdef CONFIG_APM_BAD_ENTRY_OFFSET
- apm_bios_entry.offset &= 0xffff;
-#endif
apm_bios_entry.segment = APM_CS;
set_base(gdt[APM_CS >> 3],
__va((unsigned long)apm_bios_info.cseg << 4));
set_base(gdt[APM_DS >> 3],
__va((unsigned long)apm_bios_info.dseg << 4));
#ifndef APM_RELAX_SEGMENTS
- if (apm_bios_info.version == 0x100)
+ if (apm_bios_info.version == 0x100) {
#endif
- {
/* For ASUS motherboard, Award BIOS rev 110 (and others?) */
_set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1);
/* For some unknown machine. */
_set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1);
/* For the DEC Hinote Ultra CT475 (and others?) */
_set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1);
- }
#ifndef APM_RELAX_SEGMENTS
- else {
+ } else {
_set_limit((char *)&gdt[APM_CS >> 3],
(apm_bios_info.cseg_len - 1) & 0xffff);
_set_limit((char *)&gdt[APM_CS_16 >> 3],
}
#endif
-#ifdef CONFIG_SMP
+ create_proc_info_entry("apm", 0, NULL, apm_get_info);
+
+ kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
+
if (smp_num_cpus > 1) {
- printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
- if (smp_hack)
- smp_hack = 2;
+ printk(KERN_NOTICE
+ "apm: disabled - APM is not SMP safe (power off active).\n");
APM_INIT_ERROR_RETURN;
}
-#endif
-
- create_proc_info_entry("apm", 0, 0, apm_get_info);
misc_register(&apm_device);
- kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
return 0;
}
-__initcall(apm_init);
+static void __exit apm_exit(void)
+{
+ misc_deregister(&apm_device);
+ remove_proc_entry("apm", NULL);
+ exit_kapmd = 1;
+ while (kapmd_running)
+ schedule();
+}
+
+module_init(apm_init);
+module_exit(apm_exit);
+
+MODULE_AUTHOR("Stephen Rothwell");
+MODULE_DESCRIPTION("Advanced Power Management");
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Enable debug mode");
+
+EXPORT_NO_SYMBOLS;
#include <linux/smp_lock.h>
#include <linux/pm.h>
#include <linux/pci.h>
+#include <linux/apm_bios.h>
#include <asm/semaphore.h>
#include <asm/processor.h>
#include <asm/delay.h>
#include <asm/irq.h>
#include <asm/mmx.h>
+#include <asm/desc.h>
extern void dump_thread(struct pt_regs *, struct user *);
extern int dump_fpu(elf_fpregset_t *);
EXPORT_SYMBOL(drive_info);
#endif
+extern unsigned long get_cmos_time(void);
+
/* platform dependent support */
EXPORT_SYMBOL(boot_cpu_data);
EXPORT_SYMBOL(EISA_bus);
EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(pm_idle);
EXPORT_SYMBOL(pm_power_off);
+EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(apm_bios_info);
+EXPORT_SYMBOL(gdt);
EXPORT_SYMBOL_NOVERS(__down_failed);
EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
/* Generic MTRR (Memory Type Range Register) driver.
- Copyright (C) 1997-1999 Richard Gooch
+ Copyright (C) 1997-2000 Richard Gooch
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
success.
19991008 Manfred Spraul <manfreds@colorfullife.com>
replaced spin_lock_reschedule() with a normal semaphore.
+ v1.36
+ 20000221 Richard Gooch <rgooch@atnf.csiro.au>
+ Compile fix if procfs and devfs not enabled.
+ Formatting changes.
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <asm/hardirq.h>
#include <linux/irq.h>
-#define MTRR_VERSION "1.35 (19990512)"
+#define MTRR_VERSION "1.36 (20000221)"
#define TRUE 1
#define FALSE 0
unsigned long dummy, mask_lo, base_lo;
rdmsr (MTRRphysMask_MSR(reg), mask_lo, dummy);
- if ((mask_lo & 0x800) == 0) {
- /* Invalid (i.e. free) range. */
+ if ( (mask_lo & 0x800) == 0 )
+ {
+ /* Invalid (i.e. free) range */
*base = 0;
*size = 0;
*type = 0;
*size = 0;
/* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */
- if (reg < 7) {
- switch (rcr) {
- case 1: *type = MTRR_TYPE_UNCACHABLE; break;
- case 8: *type = MTRR_TYPE_WRBACK; break;
- case 9: *type = MTRR_TYPE_WRCOMB; break;
- case 24:
- default: *type = MTRR_TYPE_WRTHROUGH; break;
- }
- } else {
- switch (rcr) {
- case 0: *type = MTRR_TYPE_UNCACHABLE; break;
- case 8: *type = MTRR_TYPE_WRCOMB; break;
- case 9: *type = MTRR_TYPE_WRBACK; break;
- case 25:
- default: *type = MTRR_TYPE_WRTHROUGH; break;
- }
+ if (reg < 7)
+ {
+ switch (rcr)
+ {
+ case 1: *type = MTRR_TYPE_UNCACHABLE; break;
+ case 8: *type = MTRR_TYPE_WRBACK; break;
+ case 9: *type = MTRR_TYPE_WRCOMB; break;
+ case 24:
+ default: *type = MTRR_TYPE_WRTHROUGH; break;
+ }
+ } else
+ {
+ switch (rcr)
+ {
+ case 0: *type = MTRR_TYPE_UNCACHABLE; break;
+ case 8: *type = MTRR_TYPE_WRCOMB; break;
+ case 9: *type = MTRR_TYPE_WRBACK; break;
+ case 25:
+ default: *type = MTRR_TYPE_WRTHROUGH; break;
+ }
}
} /* End Function cyrix_get_arr */
size &= 0x7fff; /* make sure arr_size <= 14 */
for(arr_size = 0; size; arr_size++, size >>= 1);
- if (reg<7) {
- switch (type) {
- case MTRR_TYPE_UNCACHABLE: arr_type = 1; break;
- case MTRR_TYPE_WRCOMB: arr_type = 9; break;
- case MTRR_TYPE_WRTHROUGH: arr_type = 24; break;
- default: arr_type = 8; break;
- }
- } else {
- switch (type) {
- case MTRR_TYPE_UNCACHABLE: arr_type = 0; break;
- case MTRR_TYPE_WRCOMB: arr_type = 8; break;
- case MTRR_TYPE_WRTHROUGH: arr_type = 25; break;
- default: arr_type = 9; break;
- }
+ if (reg<7)
+ {
+ switch (type) {
+ case MTRR_TYPE_UNCACHABLE: arr_type = 1; break;
+ case MTRR_TYPE_WRCOMB: arr_type = 9; break;
+ case MTRR_TYPE_WRTHROUGH: arr_type = 24; break;
+ default: arr_type = 8; break;
+ }
+ }
+ else
+ {
+ switch (type)
+ {
+ case MTRR_TYPE_UNCACHABLE: arr_type = 0; break;
+ case MTRR_TYPE_WRCOMB: arr_type = 8; break;
+ case MTRR_TYPE_WRTHROUGH: arr_type = 25; break;
+ default: arr_type = 9; break;
+ }
}
if (do_safe) set_mtrr_prepare (&ctxt);
int changed = FALSE;
rdmsr(MTRRphysBase_MSR(index), lo, hi);
- if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
- || (vr->base_hi & 0xfUL) != (hi & 0xfUL)) {
- wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
+ if ( (vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL)
+ || (vr->base_hi & 0xfUL) != (hi & 0xfUL) )
+ {
+ wrmsr (MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi);
changed = TRUE;
}
- rdmsr(MTRRphysMask_MSR(index), lo, hi);
+ rdmsr (MTRRphysMask_MSR(index), lo, hi);
- if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
- || (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) {
+ if ( (vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL)
+ || (vr->mask_hi & 0xfUL) != (hi & 0xfUL) )
+ {
wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
changed = TRUE;
}
unsigned long lo, hi;
rdmsr(MTRRfix64K_00000_MSR, lo, hi);
- if (p[0] != lo || p[1] != hi) {
- wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]);
+ if (p[0] != lo || p[1] != hi)
+ {
+ wrmsr (MTRRfix64K_00000_MSR, p[0], p[1]);
changed = TRUE;
}
- for (i = 0; i < 2; i++) {
- rdmsr(MTRRfix16K_80000_MSR + i, lo, hi);
- if (p[2 + i*2] != lo || p[3 + i*2] != hi) {
- wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]);
+ for (i = 0; i < 2; i++)
+ {
+ rdmsr (MTRRfix16K_80000_MSR + i, lo, hi);
+ if (p[2 + i*2] != lo || p[3 + i*2] != hi)
+ {
+ wrmsr (MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]);
changed = TRUE;
}
}
- for (i = 0; i < 8; i++) {
- rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi);
- if (p[6 + i*2] != lo || p[7 + i*2] != hi) {
+ for (i = 0; i < 8; i++)
+ {
+ rdmsr (MTRRfix4K_C0000_MSR + i, lo, hi);
+ if (p[6 + i*2] != lo || p[7 + i*2] != hi)
+ {
wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]);
changed = TRUE;
}
change_mask |= MTRR_CHANGE_MASK_FIXED;
/* Set_mtrr_restore restores the old value of MTRRdefType,
so to set it we fiddle with the saved value */
- if ((ctxt->deftype_lo & 0xff) != state->def_type
- || ((ctxt->deftype_lo & 0xc00) >> 10) != state->enabled)
+ if ( (ctxt->deftype_lo & 0xff) != state->def_type
+ || ( (ctxt->deftype_lo & 0xc00) >> 10 ) != state->enabled)
{
ctxt->deftype_lo |= (state->def_type | state->enabled << 10);
change_mask |= MTRR_CHANGE_MASK_DEFTYPE;
unsigned long lbase, lsize;
/* If we are to set up a region >32M then look at ARR7 immediately */
- if (size > 0x2000000UL) {
+ if (size > 0x2000000UL)
+ {
cyrix_get_arr (7, &lbase, &lsize, <ype);
if (lsize < 1) return 7;
- /* else try ARR0-ARR6 first */
- } else {
+ /* Else try ARR0-ARR6 first */
+ }
+ else
+ {
for (i = 0; i < 7; i++)
{
cyrix_get_arr (i, &lbase, &lsize, <ype);
switch (boot_cpu_data.x86_vendor)
{
case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6) { /* pre-Athlon CPUs */
- /* Apply the K6 block alignment and size rules
+ if (boot_cpu_data.x86 < 6)
+ { /* pre-Athlon CPUs */
+ /* Apply the K6 block alignment and size rules
In order
o Uncached or gathering only
o 128K or bigger block
o Power of 2 block
o base suitably aligned to the power
*/
- if (type > MTRR_TYPE_WRCOMB || size < (1 << 17) ||
- (size & ~(size-1))-size || (base & (size-1)))
- return -EINVAL;
+ if ( type > MTRR_TYPE_WRCOMB || size < (1 << 17) ||
+ (size & ~(size-1))-size || ( base & (size-1) ) )
+ return -EINVAL;
break;
- } /* else fall through */
+ }
+ /* Else fall through */
case X86_VENDOR_INTEL:
- /* Double check for Intel, we may run on Athlon. */
- if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) {
- /* For Intel PPro stepping <= 7, must be 4 MiB aligned */
- if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
- (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) - 1 ) ) )
- {
- printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
- return -EINVAL;
- }
+ /* Double check for Intel, we may run on Athlon */
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
+ {
+ /* For Intel PPro stepping <= 7, must be 4 MiB aligned */
+ if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
+ (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) -1 ) ) )
+ {
+ printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
+ return -EINVAL;
+ }
}
/* Fall through */
case X86_VENDOR_CYRIX:
if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return -ENODEV;
max = get_num_var_ranges ();
- down(&main_lock);
+ down (&main_lock);
if (reg < 0)
{
/* Search for existing MTRR */
}
if (reg >= max)
{
- up(&main_lock);
+ up (&main_lock);
printk ("mtrr: register: %d too big\n", reg);
return -EINVAL;
}
if (boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX)
{
- if ((reg == 3) && arr3_protected)
+ if ( (reg == 3) && arr3_protected )
{
- up(&main_lock);
+ up (&main_lock);
printk ("mtrr: ARR3 cannot be changed\n");
return -EINVAL;
}
(*get_mtrr) (reg, &lbase, &lsize, <ype);
if (lsize < 1)
{
- up(&main_lock);
+ up (&main_lock);
printk ("mtrr: MTRR %d not used\n", reg);
return -EINVAL;
}
if (usage_table[reg] < 1)
{
- up(&main_lock);
+ up (&main_lock);
printk ("mtrr: reg: %d has count=0\n", reg);
return -EINVAL;
}
if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0);
compute_ascii ();
- up(&main_lock);
+ up (&main_lock);
return reg;
} /* End Function mtrr_del */
static struct file_operations mtrr_fops =
{
- read: mtrr_read,
- write: mtrr_write,
- ioctl: mtrr_ioctl,
- release: mtrr_close,
+ read: mtrr_read,
+ write: mtrr_write,
+ ioctl: mtrr_ioctl,
+ release: mtrr_close,
};
# ifdef CONFIG_PROC_FS
-static struct inode_operations proc_mtrr_inode_operations = {
- &mtrr_fops, /* default property file-ops */
+static struct inode_operations proc_mtrr_inode_operations =
+{
+ &mtrr_fops, /* default property file-ops */
};
static struct proc_dir_entry *proc_root_mtrr;
#ifdef __SMP__
-typedef struct {
- unsigned long base;
- unsigned long size;
- mtrr_type type;
+typedef struct
+{
+ unsigned long base;
+ unsigned long size;
+ mtrr_type type;
} arr_state_t;
-arr_state_t arr_state[8] __initdata = {
- {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL},
- {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}
+arr_state_t arr_state[8] __initdata =
+{
+ {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL},
+ {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}
};
unsigned char ccr_state[7] __initdata = { 0, 0, 0, 0, 0, 0, 0 };
ccr[5] = getCx86 (CX86_CCR5);
ccr[6] = getCx86 (CX86_CCR6);
- if (ccr[3] & 1) {
- ccrc[3] = 1;
- arr3_protected = 1;
- } else {
- /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and
- * access to SMM memory through ARR3 (bit 7).
- */
- if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; }
- if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; }
- if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; }
- arr3_protected = 0;
- if (ccr[6] & 0x02) {
- ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3. */
- setCx86 (CX86_CCR6, ccr[6]);
- }
- /* Disable ARR3. This is safe now that we disabled SMM. */
- /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */
+ if (ccr[3] & 1)
+ {
+ ccrc[3] = 1;
+ arr3_protected = 1;
+ }
+ else
+ {
+ /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and
+ * access to SMM memory through ARR3 (bit 7).
+ */
+ if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; }
+ if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; }
+ if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; }
+ arr3_protected = 0;
+ if (ccr[6] & 0x02) {
+ ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3 */
+ setCx86 (CX86_CCR6, ccr[6]);
+ }
+ /* Disable ARR3. This is safe now that we disabled SMM. */
+ /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */
}
/* If we changed CCR1 in memory, change it in the processor, too. */
if (ccrc[1]) setCx86 (CX86_CCR1, ccr[1]);
/* Enable ARR usage by the processor */
- if (!(ccr[5] & 0x20)) {
- ccr[5] |= 0x20; ccrc[5] = 1;
- setCx86 (CX86_CCR5, ccr[5]);
+ if (!(ccr[5] & 0x20))
+ {
+ ccr[5] |= 0x20; ccrc[5] = 1;
+ setCx86 (CX86_CCR5, ccr[5]);
}
#ifdef __SMP__
switch (boot_cpu_data.x86_vendor)
{
case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6) { /* pre-Athlon CPUs */
- get_mtrr = amd_get_mtrr;
- set_mtrr_up = amd_set_mtrr_up;
- break;
- } /* else fall through */
+ if (boot_cpu_data.x86 < 6)
+ {
+ /* pre-Athlon CPUs */
+ get_mtrr = amd_get_mtrr;
+ set_mtrr_up = amd_set_mtrr_up;
+ break;
+ }
+ /* Else fall through */
case X86_VENDOR_INTEL:
get_mtrr = intel_get_mtrr;
set_mtrr_up = intel_set_mtrr_up;
switch (boot_cpu_data.x86_vendor)
{
case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6) break; /* pre-Athlon CPUs */
+ if (boot_cpu_data.x86 < 6) break; /* Pre-Athlon CPUs */
case X86_VENDOR_INTEL:
get_mtrr_state (&smp_mtrr_state);
break;
switch (boot_cpu_data.x86_vendor)
{
case X86_VENDOR_AMD:
- /* Just for robustness: pre-Athlon CPUs cannot do SMP. */
+ /* Just for robustness: pre-Athlon CPUs cannot do SMP */
if (boot_cpu_data.x86 < 6) break;
case X86_VENDOR_INTEL:
intel_mtrr_init_secondary_cpu ();
int __init mtrr_init(void)
{
if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MTRR) ) return 0;
-# ifdef __SMP__
+#ifdef __SMP__
switch (boot_cpu_data.x86_vendor)
{
case X86_VENDOR_AMD:
- if (boot_cpu_data.x86 < 6) break; /* pre-Athlon CPUs */
+ if (boot_cpu_data.x86 < 6) break; /* Pre-Athlon CPUs */
case X86_VENDOR_INTEL:
finalize_mtrr_state (&smp_mtrr_state);
mtrr_state_warn (smp_changes_mask);
break;
}
-# else /* __SMP__ */
+#else /* __SMP__ */
mtrr_setup ();
switch (boot_cpu_data.x86_vendor)
{
centaur_mcr_init ();
break;
}
-# endif /* !__SMP__ */
+#endif /* !__SMP__ */
-# ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
proc_root_mtrr = create_proc_entry ("mtrr", S_IWUSR | S_IRUGO, &proc_root);
proc_root_mtrr->ops = &proc_mtrr_inode_operations;
-#endif
+#endif
+#ifdev CONFIG_DEVFS_FS
devfs_handle = devfs_register (NULL, "cpu/mtrr", 0, DEVFS_FL_DEFAULT, 0, 0,
S_IFREG | S_IRUGO | S_IWUSR, 0, 0,
&mtrr_fops, NULL);
+#endif
init_table ();
return 0;
} /* End Function mtrr_init */
int hlt_counter=0;
-void disable_hlt(void)
-{
- hlt_counter++;
-}
-
-void enable_hlt(void)
-{
- hlt_counter--;
-}
-
/*
* Powermanagement idle function, if any..
*/
*/
void (*pm_power_off)(void) = NULL;
+void disable_hlt(void)
+{
+ hlt_counter++;
+}
+
+void enable_hlt(void)
+{
+ hlt_counter--;
+}
+
/*
* We use this if we don't have any better
* idle routine..
void machine_restart(char * __unused)
{
-#if __SMP__
+#if CONFIG_SMP
/*
* Stop all CPUs and turn off local APICs and the IO-APIC, so
* other OSs see a clean IRQ state.
if [ "$CONFIG_PCI" = "y" ]; then
tristate 'Generic DECchip & DIGITAL EtherWORKS PCI/EISA' CONFIG_DE4X5
tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX
- tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139
+ tristate 'RealTek RTL-8139 support' CONFIG_8139TOO
tristate 'PCI NE2000 support' CONFIG_NE2K_PCI
- tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100
+ tristate 'EtherExpressPro/100 support' CONFIG_EEPRO100
tristate 'Adaptec Starfire support' CONFIG_ADAPTEC_STARFIRE
fi
endmenu
EXPORT_SYMBOL(blk_cleanup_queue);
EXPORT_SYMBOL(blk_queue_headactive);
EXPORT_SYMBOL(blk_queue_pluggable);
+EXPORT_SYMBOL(blk_queue_make_request);
EXPORT_SYMBOL(generic_make_request);
spin_lock_irq(&io_request_lock);
current_request->sector += current_request->current_nr_sectors;
current_request->nr_sectors -= current_request->current_nr_sectors;
- list_add(¤t_request->queue, ¤t_request->q->queue_head);
+ list_add(¤t_request->queue, &q->queue_head);
end_request(1);
goto repeat;
error_out_lock:
spin_lock_irq(&io_request_lock);
error_out:
- list_add(¤t_request->queue, ¤t_request->q->queue_head);
+ list_add(¤t_request->queue, &q->queue_head);
end_request(0);
goto repeat;
}
while (!( /* FIXME: now we are rather fault tolerant than nice */
r1_bh = kmalloc (sizeof (struct raid1_bh), GFP_KERNEL)
) )
+ {
printk ("raid1_make_request(#1): out of memory\n");
+ current->policy |= SCHED_YIELD;
+ schedule();
+ }
memset (r1_bh, 0, sizeof (struct raid1_bh));
/*
while (!( /* FIXME: now we are rather fault tolerant than nice */
mirror_bh[i] = kmalloc (sizeof (struct buffer_head), GFP_KERNEL)
) )
+ {
printk ("raid1_make_request(#2): out of memory\n");
+ current->policy |= SCHED_YIELD;
+ schedule();
+ }
memset (mirror_bh[i], 0, sizeof (struct buffer_head));
/*
while (!( /* FIXME: now we are rather fault tolerant than nice */
mddev->private = kmalloc (sizeof (struct raid1_data), GFP_KERNEL)
) )
+ {
printk ("raid1_run(): out of memory\n");
+ current->policy |= SCHED_YIELD;
+ schedule();
+ }
raid_conf = mddev->private;
memset(raid_conf, 0, sizeof(*raid_conf));
# All of the (potential) objects that export symbols.
# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
-export-objs := busmouse.o console.o i2c-old.o keyboard.o \
+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
obj-$(CONFIG_MIXCOMWD) += mixcomwd.o
obj-$(CONFIG_AMIGAMOUSE) += amigamouse.o
obj-$(CONFIG_ATARIMOUSE) += atarimouse.o
-obj-$(CONFIG_ADBMOUSE) += adbmouse.o
+obj-$(CONFIG_ADBMOUSE) += adbmouse.o busmouse.o
obj-$(CONFIG_PC110_PAD) += pc110pad.o
obj-$(CONFIG_WDT) += wdt.o
obj-$(CONFIG_RTC) += rtc.o
ifeq ($(CONFIG_PPC),)
obj-$(CONFIG_NVRAM) += nvram.o
endif
+obj-$(CONFIG_I810_RNG) += i810_rng.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o
EXPORT_SYMBOL(video_scan_lines);
EXPORT_SYMBOL(vc_resize);
EXPORT_SYMBOL(fg_console);
+EXPORT_SYMBOL(console_blank_hook);
#ifndef VT_SINGLE_DRIVER
EXPORT_SYMBOL(take_over_console);
return;
} else if (data_reg == H8_SYNC_BYTE) {
h8_state = H8_IDLE;
- if (!QUEUE_EMPTY(&h8_actq, link))
+ if (!QUEUE_IS_EMPTY(&h8_actq, link))
h8_send_next_cmd_byte();
} else {
Dprintk ("h8_intr: resync unknown data 0x%x \n", data_reg);
QUEUE_REMOVE(&h8_actq, qp, link);
h8_cmd_done (qp);
/* More commands to send over? */
- if (!QUEUE_EMPTY(&h8_cmdq, link))
+ if (!QUEUE_IS_EMPTY(&h8_cmdq, link))
h8_start_new_cmd();
}
return;
/* get cmd buf */
save_flags(flags); cli();
- while (QUEUE_EMPTY(&h8_freeq, link)) {
+ while (QUEUE_IS_EMPTY(&h8_freeq, link)) {
Dprintk("H8: need to allocate more cmd buffers\n");
restore_flags(flags);
h8_alloc_queues();
return;
}
- if (!QUEUE_EMPTY(&h8_actq, link)) {
+ if (!QUEUE_IS_EMPTY(&h8_actq, link)) {
Dprintk("h8_start_new_cmd: inconsistency: IDLE with non-empty active queue!\n");
restore_flags(flags);
return;
}
- if (QUEUE_EMPTY(&h8_cmdq, link)) {
+ if (QUEUE_IS_EMPTY(&h8_cmdq, link)) {
Dprintk("h8_start_new_cmd: no command to dequeue\n");
restore_flags(flags);
return;
curd->name = all += axes * sizeof(struct js_corr);
strcpy(curd->name, name);
- sprintf (devfs_name, "analogue%d", number);
- curd->devfs_handle = devfs_register (devfs_handle, devfs_name, 0,
- DEVFS_FL_DEFAULT,
- JOYSTICK_MAJOR, number,
- S_IFCHR | S_IRUGO | S_IWUSR, 0, 0,
- &js_fops, NULL);
port->devs[number] = curd;
port->axes[number] = curd->new.axes;
spin_unlock_irqrestore(&js_lock, flags);
+ sprintf(devfs_name, "js%d", i);
+ curd->devfs_handle = devfs_register(devfs_handle, devfs_name, 0,
+ DEVFS_FL_DEFAULT,
+ JOYSTICK_MAJOR, i,
+ S_IFCHR | S_IRUGO | S_IWUSR, 0, 0,
+ &js_fops, NULL);
+
return i;
}
spin_unlock_irqrestore(&js_lock, flags);
- devfs_unregister (dev->devfs_handle);
+ devfs_unregister(dev->devfs_handle);
kfree(dev);
}
printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR);
return -EBUSY;
}
- devfs_handle = devfs_mk_dir (NULL, "joysticks", 9, NULL);
+ devfs_handle = devfs_mk_dir(NULL, "joysticks", 9, NULL);
printk(KERN_INFO "js: Joystick driver v%d.%d.%d (c) 1999 Vojtech Pavlik <vojtech@suse.cz>\n",
JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff);
void cleanup_module(void)
{
del_timer(&js_timer);
- devfs_unregister (devfs_handle);
+ devfs_unregister(devfs_handle);
if (devfs_unregister_chrdev(JOYSTICK_MAJOR, "js"))
- printk(KERN_ERR "js: can't unregister device\n");
+ printk(KERN_ERR "js: can't unregister device\n");
}
#endif
/*****************************************************************************/
+static devfs_handle_t devfs_handle = NULL;
+
#ifdef MODULE
/*
/*****************************************************************************/
-static devfs_handle_t devfs_handle = NULL;
-
void cleanup_module()
{
stlbrd_t *brdp;
#include <linux/kbd_kern.h>
#include <linux/quotaops.h>
#include <linux/smp_lock.h>
+#include <linux/module.h>
#include <asm/ptrace.h>
/* Machine specific power off function */
void (*sysrq_power_off)(void) = NULL;
+EXPORT_SYMBOL(sysrq_power_off);
+
/* Send a signal to all user processes */
static void send_sig_all(int sig, int even_init)
#include <linux/console.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
+#include <asm/unaligned.h>
#undef attr
#undef org
con_buf0[1] = (char) video_num_columns;
getconsxy(currcons, con_buf0 + 2);
- tmp_count = HEADER_SIZE - p;
+ con_buf_start += p;
+ this_round += p;
+ if (this_round > CON_BUF_SIZE) {
+ this_round = CON_BUF_SIZE;
+ orig_count = this_round - p;
+ }
+
+ tmp_count = HEADER_SIZE;
if (tmp_count > this_round)
tmp_count = this_round;
/* Advance state pointers and move on. */
this_round -= tmp_count;
- con_buf_start += p;
- orig_count -= p;
- p += tmp_count;
- con_buf0 = con_buf + p;
+ p = HEADER_SIZE;
+ con_buf0 = con_buf + HEADER_SIZE;
+ /* If this_round >= 0, then p is even... */
+ } else if (p & 1) {
+ /* Skip first byte for output if start address is odd
+ * Update region sizes up/down depending on free
+ * space in buffer.
+ */
+ con_buf_start++;
+ if (this_round < CON_BUF_SIZE)
+ this_round++;
+ else
+ orig_count--;
}
- p -= HEADER_SIZE;
- col = (p/2) % maxcol;
if (this_round > 0) {
- char tmp_byte;
-
- org = screen_pos(currcons, p/2, viewed);
- if ((p & 1) && this_round > 0) {
-#ifdef __BIG_ENDIAN
- tmp_byte = vcs_scr_readw(currcons, org++) & 0xff;
-#else
- tmp_byte = vcs_scr_readw(currcons, org++) >> 8;
-#endif
-
- *con_buf0++ = tmp_byte;
+ unsigned short *tmp_buf = (unsigned short *)con_buf0;
- this_round--;
- p++;
- if (++col == maxcol) {
- org = screen_pos(currcons, p/2, viewed);
- col = 0;
- }
- }
+ p -= HEADER_SIZE;
p /= 2;
+ col = p % maxcol;
+
+ org = screen_pos(currcons, p, viewed);
p += maxcol - col;
- }
- if (this_round > 1) {
- size_t tmp_count = this_round;
- unsigned short *tmp_buf = (unsigned short *)con_buf0;
+ /* Buffer has even length, so we can always copy
+ * character + attribute. We do not copy last byte
+ * to userspace if this_round is odd.
+ */
+ this_round = (this_round + 1) >> 1;
- while (tmp_count > 1) {
+ while (this_round) {
*tmp_buf++ = vcs_scr_readw(currcons, org++);
- tmp_count -= 2;
+ this_round --;
if (++col == maxcol) {
org = screen_pos(currcons, p, viewed);
col = 0;
p += maxcol;
}
}
-
- /* Advance pointers, and move on. */
- this_round = tmp_count;
- con_buf0 = (char*)tmp_buf;
- }
- if (this_round > 0) {
- char tmp_byte;
-
-#ifdef __BIG_ENDIAN
- tmp_byte = vcs_scr_readw(currcons, org) >> 8;
-#else
- tmp_byte = vcs_scr_readw(currcons, org) & 0xff;
-#endif
-
- *con_buf0++ = tmp_byte;
}
}
while (this_round > 1) {
unsigned short w;
- w = *((const unsigned short *)con_buf0);
+ w = get_unaligned(((const unsigned short *)con_buf0));
vcs_scr_writew(currcons, w, org++);
con_buf0 += 2;
this_round -= 2;
static struct hpsb_host_template *templates = NULL;
spinlock_t templates_lock = SPIN_LOCK_UNLOCKED;
-
-/*
- * The following function is exported for module usage. It will
- * be called from high-level drivers such as the raw driver.
- */
-int hpsb_get_host_list(struct hpsb_host *list[], int list_size)
-{
- struct hpsb_host *host, **ptr;
- struct hpsb_host_template *tmpl;
- int count=0;
-
- ptr = list;
-
- for (tmpl = templates ; tmpl != NULL; tmpl = tmpl->next) {
- for (host = tmpl->hosts; (host != NULL) && (count < list_size);
- host = host->next) {
- *ptr = host;
- ptr++;
- count++;
- }
- }
-
- return count;
-}
-
/*
* This function calls the add_host/remove_host hooks for every host currently
* registered. Init == TRUE means add_host.
h->timeout_tq.data = h;
h->topology_map = h->csr.topology_map + 3;
- h->speed_map = h->csr.speed_map + 2;
+ h->speed_map = (u8 *)(h->csr.speed_map + 2);
h->template = tmpl;
if (hd_size) {
wait_queue_head_t tlabel_wait;
int reset_retries;
- quadlet_t *topology_map, *speed_map;
+ quadlet_t *topology_map;
+ u8 *speed_map;
struct csr_control csr;
unsigned char iso_listen_count[64];
struct hpsb_host *hpsb_get_host(struct hpsb_host_template *tmpl,
size_t hostdata_size);
-/*
- * Write pointers to all available hpsb_hosts into list.
- * Return number of host adapters (i.e. elements in list).
- *
- * DEPRECATED - register with highlevel instead.
- */
-int hpsb_get_host_list(struct hpsb_host *list[], int max_list_size);
-
/*
* Increase / decrease host usage counter. Increase function will return true
* only if successful (host still existed). Decrease function expects host to
* Core support: hpsb_packet management, packet handling and forwarding to
* csr or lowlevel code
*
- * Copyright (C) 1999 Andreas E. Bombe
+ * Copyright (C) 1999, 2000 Andreas E. Bombe
*/
#include <linux/kernel.h>
return nodeid + 1;
}
+static void build_speed_map(struct hpsb_host *host, int nodecount)
+{
+ char speedcap[nodecount];
+ char cldcnt[nodecount];
+ u8 *map = host->speed_map;
+ quadlet_t *sidp;
+ int i, j, n;
+
+ for (i = 0; i < (nodecount * 64); i += 64) {
+ for (j = 0; j < nodecount; j++) {
+ map[i+j] = SPEED_400;
+ }
+ }
+
+ for (i = 0; i < nodecount; i++) {
+ cldcnt[i] = 0;
+ }
+
+ /* find direct children count and speed */
+ for (sidp = &host->topology_map[host->selfid_count-1],
+ n = nodecount - 1;
+ sidp >= host->topology_map; sidp--) {
+ if (*sidp & 0x00800000 /* extended */) {
+ for (i = 2; i < 18; i += 2) {
+ if ((*sidp & (0x3 << i)) == (0x3 << i)) {
+ cldcnt[n]++;
+ }
+ }
+ } else {
+ for (i = 2; i < 8; i += 2) {
+ if ((*sidp & (0x3 << i)) == (0x3 << i)) {
+ cldcnt[n]++;
+ }
+ }
+ speedcap[n] = (*sidp >> 14) & 0x3;
+ n--;
+ }
+ }
+
+ /* set self mapping */
+ for (i = nodecount - 1; i; i--) {
+ map[64*i + i] = speedcap[i];
+ }
+
+ /* fix up direct children count to total children count;
+ * also fix up speedcaps for sibling and parent communication */
+ for (i = 1; i < nodecount; i++) {
+ for (j = cldcnt[i], n = i - 1; j > 0; j--) {
+ cldcnt[i] += cldcnt[n];
+ speedcap[n] = MIN(speedcap[n], speedcap[i]);
+ n -= cldcnt[n] + 1;
+ }
+ }
+
+ for (n = 0; n < nodecount; n++) {
+ for (i = n - cldcnt[n]; i <= n; i++) {
+ for (j = 0; j < (n - cldcnt[n]); j++) {
+ map[j*64 + i] = map[i*64 + j] =
+ MIN(map[i*64 + j], speedcap[n]);
+ }
+ for (j = n + 1; j < nodecount; j++) {
+ map[j*64 + i] = map[i*64 + j] =
+ MIN(map[i*64 + j], speedcap[n]);
+ }
+ }
+ }
+}
+
void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid)
{
if (host->in_bus_reset) {
void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot)
{
-
-
host->node_id = 0xffc0 | phyid;
host->in_bus_reset = 0;
host->is_root = isroot;
return;
} else {
HPSB_NOTICE("stopping out-of-control reset loop");
- HPSB_NOTICE("warning - topology map will therefore not "
- "be valid");
+ HPSB_NOTICE("warning - topology map and speed map will "
+ "therefore not be valid");
}
+ } else {
+ build_speed_map(host, host->node_count);
}
/* irm_id is kept up to date by check_selfids() */
}
packet->state = queued;
+ packet->speed_code = host->speed_map[(host->node_id & NODE_MASK) * 64
+ + (packet->node_id & NODE_MASK)];
- dump_packet("send packet:", packet->header, packet->header_size);
+ switch (packet->speed_code) {
+ case 2:
+ dump_packet("send packet 400:", packet->header,
+ packet->header_size);
+ break;
+ case 1:
+ dump_packet("send packet 200:", packet->header,
+ packet->header_size);
+ break;
+ default:
+ dump_packet("send packet 100:", packet->header,
+ packet->header_size);
+ }
return host->template->transmit_packet(host, packet);
}
EXPORT_SYMBOL(hpsb_register_lowlevel);
EXPORT_SYMBOL(hpsb_unregister_lowlevel);
EXPORT_SYMBOL(hpsb_get_host);
-EXPORT_SYMBOL(hpsb_get_host_list);
EXPORT_SYMBOL(hpsb_inc_host_usage);
EXPORT_SYMBOL(hpsb_dec_host_usage);
EXPORT_SYMBOL(highlevel_write);
EXPORT_SYMBOL(highlevel_lock);
EXPORT_SYMBOL(highlevel_lock64);
-
-/*
-EXPORT_SYMBOL(hpsb_dispatch_event);
-EXPORT_SYMBOL(hpsb_reg_event_handler);
-*/
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+/*
+ * Things known to be working:
+ * . Async Request Transmit
+ * . Async Response Receive
+ * . Async Request Receive
+ * . Async Response Transmit
+ * . Iso Receive
+ *
+ * Things not implemented:
+ * . Iso Transmit
+ * . DMA to user's space in iso receive mode
+ * . DMA error recovery
+ *
+ * Things to be fixed:
+ * . Config ROM
+ *
+ * Known bugs:
+ * . Self-id are not received properly if card
+ * is initialized with no other nodes on the
+ * bus.
+ */
+
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/tqueue.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/wrapper.h>
+#include <linux/vmalloc.h>
+
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394_core.h"
#include "ohci1394.h"
+#ifdef DBGMSG
+#undef DBGMSG
+#endif
+
+#if OHCI1394_DEBUG
+#define DBGMSG(card, fmt, args...) \
+printk(KERN_INFO "ohci1394_%d: " fmt "\n" , card , ## args)
+#else
+#define DBGMSG(card, fmt, args...)
+#endif
+
/* print general (card independent) information */
#define PRINT_G(level, fmt, args...) \
printk(level "ohci1394: " fmt "\n" , ## args)
#define PRINT(level, card, fmt, args...) \
printk(level "ohci1394_%d: " fmt "\n" , card , ## args)
+#define FAIL(fmt, args...) \
+ PRINT_G(KERN_ERR, fmt , ## args); \
+ num_of_cards--; \
+ remove_card(ohci); \
+ return 1;
+
int supported_chips[][2] = {
- { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394 },
- { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_2 },
- { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_OHCI1394 },
- { -1, -1 }
+ { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394 },
+ { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_OHCI1394_2 },
+ { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_OHCI1394 },
+ { PCI_VENDOR_ID_SONY, PCI_DEVICE_ID_SONY_CXD3222 },
+ { -1, -1 }
};
static struct ti_ohci cards[MAX_OHCI1394_CARDS];
static int add_card(struct pci_dev *dev);
static void remove_card(struct ti_ohci *ohci);
static int init_driver(void);
+static void dma_trm_bh(void *data);
+static void dma_trm_reset(struct dma_trm_ctx *d);
/***********************************
* IEEE-1394 functionality section *
/* wait */
while (!(reg_read(ohci, OHCI1394_PhyControl)&0x80000000) && timeout)
- timeout--;
+ timeout--;
if (!timeout) {
/* wait */
while (!(reg_read(ohci, OHCI1394_PhyControl)&0x80000000) && timeout)
- timeout--;
+ timeout--;
spin_unlock(&ohci->phy_reg_lock);
if ((self_id_count&0x80000000) ||
((self_id_count&0x00FF0000) != (q[0]&0x00FF0000))) {
PRINT(KERN_ERR, ohci->id,
- "Error in reception of self-id packets");
+ "Error in reception of self-id packets"
+ "Self-id count: %08x q[0]: %08x",
+ self_id_count, q[0]);
return -1;
- }
-
+ }
+
size = ((self_id_count&0x0000EFFC)>>2) - 1;
q++;
while (size > 0) {
if (q[0] == ~q[1]) {
- printk("-%d- selfid packet 0x%x rcvd\n",
- ohci->id, q[0]);
+ PRINT(KERN_INFO, ohci->id, "selfid packet 0x%x rcvd",
+ q[0]);
hpsb_selfid_received(host, q[0]);
if (((q[0]&0x3f000000)>>24)==phyid) {
lsid=q[0];
- printk("This node self-id is 0x%08x\n",lsid);
+ PRINT(KERN_INFO, ohci->id,
+ "This node self-id is 0x%08x", lsid);
}
} else {
- printk("-%d- inconsistent selfid 0x%x/0x%x\n", ohci->id,
- q[0], q[1]);
+ PRINT(KERN_ERR, ohci->id,
+ "inconsistent selfid 0x%x/0x%x", q[0], q[1]);
}
q += 2;
size -= 2;
}
- printk(" calling self-id complete\n");
+ PRINT(KERN_INFO, ohci->id, "calling self-id complete");
hpsb_selfid_complete(host, phyid, isroot);
return 0;
reg_write(ohci, OHCI1394_HCControlSet, 0x00010000);
while ((reg_read(ohci, OHCI1394_HCControlSet)&0x00010000) && timeout)
- timeout--;
+ timeout--;
if (!timeout) {
PRINT(KERN_ERR, ohci->id, "soft reset timeout !!!");
return -EFAULT;
return 0;
}
+static int run_context(struct ti_ohci *ohci, int reg, char *msg)
+{
+ u32 nodeId;
+
+ /* check that the node id is valid */
+ nodeId = reg_read(ohci, OHCI1394_NodeID);
+ if (!(nodeId&0x80000000)) {
+ PRINT(KERN_ERR, ohci->id,
+ "Running dma failed because Node ID not valid");
+ return -1;
+ }
+
+ /* check that the node number != 63 */
+ if ((nodeId&0x3f)==63) {
+ PRINT(KERN_ERR, ohci->id,
+ "Running dma failed because Node ID == 63");
+ return -1;
+ }
+
+ /* Run the dma context */
+ reg_write(ohci, reg, 0x8000);
+
+ if (msg) PRINT(KERN_INFO, ohci->id, "%s", msg);
+
+ return 0;
+}
+
+static void stop_context(struct ti_ohci *ohci, int reg, char *msg)
+{
+ int i=0;
+
+ /* stop the channel program if it's still running */
+ reg_write(ohci, reg, 0x8000);
+
+ /* Wait until it effectively stops */
+ while (reg_read(ohci, reg) & 0x400) {
+ i++;
+ if (i>5000) {
+ PRINT(KERN_ERR, ohci->id,
+ "runaway loop while stopping context...");
+ break;
+ }
+ }
+ if (msg) PRINT(KERN_ERR, ohci->id, "%s\n dma prg stopped\n", msg);
+}
+
+/* Generate the dma receive prgs and start the context */
+static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d)
+{
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ int i;
+
+ stop_context(ohci, d->ctrlClear, NULL);
+
+ for (i=0; i<d->num_desc; i++) {
+
+ /* end of descriptor list? */
+ if ((i+1) < d->num_desc) {
+ d->prg[i]->control = (0x283C << 16) | d->buf_size;
+ d->prg[i]->branchAddress =
+ (virt_to_bus(d->prg[i+1]) & 0xfffffff0) | 0x1;
+ } else {
+ d->prg[i]->control = (0x283C << 16) | d->buf_size;
+ d->prg[i]->branchAddress =
+ (virt_to_bus(d->prg[0]) & 0xfffffff0);
+ }
+
+ d->prg[i]->address = virt_to_bus(d->buf[i]);
+ d->prg[i]->status = d->buf_size;
+ }
+
+ d->buf_ind = 0;
+ d->buf_offset = 0;
+
+ /* Tell the controller where the first AR program is */
+ reg_write(ohci, d->cmdPtr, virt_to_bus(d->prg[0]) | 0x1);
+
+ /* Run AR context */
+ reg_write(ohci, d->ctrlSet, 0x00008000);
+
+ PRINT(KERN_INFO, ohci->id, "Receive DMA ctx=%d initialized", d->ctx);
+}
+
+/* Initialize the dma transmit context */
+static void initialize_dma_trm_ctx(struct dma_trm_ctx *d)
+{
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+
+ /* Stop the context */
+ stop_context(ohci, d->ctrlClear, NULL);
+
+ d->prg_ind = 0;
+ d->sent_ind = 0;
+ d->free_prgs = d->num_desc;
+ d->branchAddrPtr = NULL;
+ d->first = NULL;
+ d->last = NULL;
+
+ PRINT(KERN_INFO, ohci->id, "AT dma ctx=%d initialized", d->ctx);
+}
+
+/* Global initialization */
static int ohci_initialize(struct hpsb_host *host)
{
struct ti_ohci *ohci=host->hostdata;
reg_write(ohci, OHCI1394_ConfigROMmap,
virt_to_bus(ohci->csr_config_rom));
-#if 1 /* Why is this step necessary ? */
/* Write the config ROM header */
- reg_write(ohci, OHCI1394_ConfigROMhdr,0x04040000);
+ reg_write(ohci, OHCI1394_ConfigROMhdr, ohci->csr_config_rom[0]);
/* Set bus options */
- reg_write(ohci, OHCI1394_BusOptions, 0xf064A002);
-#endif
+ reg_write(ohci, OHCI1394_BusOptions, ohci->csr_config_rom[2]);
-#if 1
- /* Accept phy packets into AR request context */
- reg_write(ohci, OHCI1394_LinkControlSet, 0x00000400);
-#endif
+ /* Write the GUID into the csr config rom */
+ ohci->csr_config_rom[3] = reg_read(ohci, OHCI1394_GUIDHi);
+ ohci->csr_config_rom[4] = reg_read(ohci, OHCI1394_GUIDLo);
+
+ /* Don't accept phy packets into AR request context */
+ reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
/* Enable link */
reg_write(ohci, OHCI1394_HCControlSet, 0x00020000);
/* Initialize IR dma */
+ for (i=0;i<4;i++) { /* FIXME : how many contexts are available ? */
+ reg_write(ohci, OHCI1394_IrRcvContextControlClear+32*i,
+ 0xffffffff);
+ reg_write(ohci, OHCI1394_IrRcvContextMatch+32*i, 0);
+ reg_write(ohci, OHCI1394_IrRcvCommandPtr+32*i, 0);
+ }
- /* make sure the context isn't running, dead, or active */
- if (!(reg_read(ohci, OHCI1394_IrRcvContextControlSet) & 0x00008F00)) {
-
- /* initialize IR program */
- for (i= 0; i < IR_NUM_DESC; i++) {
-
- /* end of descriptor list? */
- if ((i + 1) < IR_NUM_DESC) {
- ohci->IR_recv_prg[i]->control=
- (0x283C << 16) | IR_RECV_BUF_SIZE;
- ohci->IR_recv_prg[i]->branchAddress=
- (virt_to_bus(ohci->IR_recv_prg[i + 1])
- & 0xfffffff0) | 0x1;
- } else {
- ohci->IR_recv_prg[i]->control=
- (0x283C << 16) | IR_RECV_BUF_SIZE;
- ohci->IR_recv_prg[i]->branchAddress=
- (virt_to_bus(ohci->IR_recv_prg[0])
- & 0xfffffff0) | 0x1;
- }
-
- ohci->IR_recv_prg[i]->address=
- virt_to_bus(ohci->IR_recv_buf[i]);
- ohci->IR_recv_prg[i]->status= IR_RECV_BUF_SIZE;
- }
-
- /* Tell the controller where the first IR program is */
- reg_write(ohci, OHCI1394_IrRcvCommandPtr,
- virt_to_bus(ohci->IR_recv_prg[0]) | 0x1 );
-
- /* Set bufferFill, isochHeader, multichannel for IR context */
- reg_write(ohci, OHCI1394_IrRcvContextControlSet, 0xd0000000);
+ /* Set bufferFill, isochHeader, multichannel for IR context */
+ reg_write(ohci, OHCI1394_IrRcvContextControlSet, 0xd0000000);
+
+ /* Set the context match register to match on all tags */
+ reg_write(ohci, OHCI1394_IrRcvContextMatch, 0xf0000000);
- /* Set the context match register to match on all tags */
- reg_write(ohci, OHCI1394_IrRcvContextMatch, 0xf0000000);
+ /* Clear the multi channel mask high and low registers */
+ reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff);
+ reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff);
- /* Clear the multi channel mask high and low registers */
- reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff);
- reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff);
+ /* Clear the interrupt mask */
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff);
- /* Set up isoRecvIntMask to generate interrupts for context 0
- (thanks to Michael Greger for seeing that I forgot this) */
- reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0x00000001);
+ /* Set up isoRecvIntMask to generate interrupts for context 0
+ (thanks to Michael Greger for seeing that I forgot this) */
+ reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0x00000001);
- /* Run IR context */
- reg_write(ohci, OHCI1394_IrRcvContextControlSet, 0x00008000);
- }
+ initialize_dma_rcv_ctx(ohci->ir_context);
/* Initialize AR dma */
- /* make sure the context isn't running, dead, or active */
- if (!(reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x00008F00)) {
-
- /* initialize AR program */
- for (i= 0; i < AR_RESP_NUM_DESC; i++) {
-
- /* end of descriptor list? */
- if ((i + 1) < AR_RESP_NUM_DESC) {
- ohci->AR_resp_prg[i]->control=
- (0x283C << 16) | AR_RESP_BUF_SIZE;
- ohci->AR_resp_prg[i]->branchAddress=
- (virt_to_bus(ohci->AR_resp_prg[i + 1])
- & 0xfffffff0) | 0x1;
- } else {
- ohci->AR_resp_prg[i]->control=
- (0x283C << 16) | AR_RESP_BUF_SIZE;
- ohci->AR_resp_prg[i]->branchAddress=
- (virt_to_bus(ohci->AR_resp_prg[0])
- & 0xfffffff0) | 0x1;
- }
-
- ohci->AR_resp_prg[i]->address=
- virt_to_bus(ohci->AR_resp_buf[i]);
- ohci->AR_resp_prg[i]->status= AR_RESP_BUF_SIZE;
- }
+ initialize_dma_rcv_ctx(ohci->ar_req_context);
+ initialize_dma_rcv_ctx(ohci->ar_resp_context);
- /* Tell the controller where the first AR program is */
- reg_write(ohci, OHCI1394_AsRspRcvCommandPtr,
- virt_to_bus(ohci->AR_resp_prg[0]) | 0x1 );
+ /* Initialize AT dma */
+ initialize_dma_trm_ctx(ohci->at_req_context);
+ initialize_dma_trm_ctx(ohci->at_resp_context);
- /* Accept phy packets into AR request context */
- reg_write(ohci, OHCI1394_LinkControlSet, 0x00000400);
-
- /* Run AR context */
- reg_write(ohci, OHCI1394_AsRspRcvContextControlSet, 0x00008000);
- }
+ /*
+ * Accept AT requests from all nodes. This probably
+ * will have to be controlled from the subsystem
+ * on a per node basis.
+ * (Tip by Emilie Chung <emilie.chung@axis.com>)
+ */
+ reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0x80000000);
/* Specify AT retries */
reg_write(ohci, OHCI1394_ATRetries,
OHCI1394_respTxComplete |
OHCI1394_reqTxComplete |
OHCI1394_isochRx
- );
+ );
return 1;
}
}
}
-
-/* This must be called with the async_queue_lock held. */
-static void send_next_async(struct ti_ohci *ohci)
+/* Insert a packet in the AT DMA fifo and generate the DMA prg */
+static void insert_packet(struct ti_ohci *ohci,
+ struct dma_trm_ctx *d, struct hpsb_packet *packet)
{
- int i=0;
- struct hpsb_packet *packet = ohci->async_queue;
- struct dma_cmd prg;
-#if 0
- quadlet_t *ptr = (quadlet_t *)ohci->AT_req_prg;
-#endif
- //HPSB_TRACE();
-
- /* stop the channel program if it's still running */
- reg_write(ohci, OHCI1394_AsReqTrContextControlClear, 0x8000);
-
- /* Wait until it effectively stops */
- while (reg_read(ohci, OHCI1394_AsReqTrContextControlSet)
- & 0x400) {
- i++;
- if (i>5000) {
- PRINT(KERN_ERR, ohci->id,
- "runaway loop in DmaAT. bailing out...");
- break;
- }
- };
-
- if (packet->type == async)
- {
-
- /* re-format packet header according to ohci specification */
- packet->header[1] = (packet->header[1] & 0xFFFF) |
- (packet->header[0] & 0xFFFF0000);
- packet->header[0] = DMA_SPEED_200 |
- (packet->header[0] & 0xFFFF);
-
- if (packet->data_size) { /* block transmit */
- prg.control = OUTPUT_MORE_IMMEDIATE | 0x10;
- prg.address = 0;
- prg.branchAddress = 0;
- prg.status = 0;
- memcpy(ohci->AT_req_prg, &prg, 16);
- memcpy(ohci->AT_req_prg + 1, packet->header, 16);
- prg.control = OUTPUT_LAST | packet->data_size;
- prg.address = virt_to_bus(packet->data);
- memcpy(ohci->AT_req_prg + 2, &prg, 16);
-
- reg_write(ohci, OHCI1394_AsReqTrCommandPtr,
- virt_to_bus(ohci->AT_req_prg)|0x3);
- }
- else { /* quadlet transmit */
- prg.control = OUTPUT_LAST_IMMEDIATE |
- packet->header_size;
- prg.address = 0;
- prg.branchAddress = 0;
- prg.status = 0;
- memcpy(ohci->AT_req_prg, &prg, 16);
- memcpy(ohci->AT_req_prg + 1, packet->header, 16);
-#if 0
- PRINT(KERN_INFO, ohci->id,
- "dma_cmd: %08x %08x %08x %08x",
- *ptr, *(ptr+1), *(ptr+2), *(ptr+3));
- PRINT(KERN_INFO, ohci->id,
- "header: %08x %08x %08x %08x",
- *(ptr+4), *(ptr+5), *(ptr+6), *(ptr+7));
-#endif
- reg_write(ohci, OHCI1394_AsReqTrCommandPtr,
- virt_to_bus(ohci->AT_req_prg)|0x2);
- }
-
- }
- else if (packet->type == raw)
- {
- prg.control = OUTPUT_LAST | packet->data_size;
- prg.address = virt_to_bus(packet->data);
- prg.branchAddress = 0;
- prg.status = 0;
- memcpy(ohci->AT_req_prg, &prg, 16);
-#if 0
- PRINT(KERN_INFO, ohci->id,
- "dma_cmd: %08x %08x %08x %08x",
- *ptr, *(ptr+1), *(ptr+2), *(ptr+3));
-#endif
- reg_write(ohci, OHCI1394_AsReqTrCommandPtr,
- virt_to_bus(ohci->AT_req_prg)|0x2);
- }
+ u32 cycleTimer;
+ int idx = d->prg_ind;
+
+ d->prg[idx].begin.address = 0;
+ d->prg[idx].begin.branchAddress = 0;
+ if (d->ctx==1) {
+ /*
+ * For response packets, we need to put a timeout value in
+ * the 16 lower bits of the status... let's try 1 sec timeout
+ */
+ cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
+ d->prg[idx].begin.status =
+ (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) |
+ ((cycleTimer&0x01fff000)>>12);
+
+ DBGMSG(ohci->id, "cycleTimer: %08x timeStamp: %08x",
+ cycleTimer, d->prg[idx].begin.status);
+ }
+ else
+ d->prg[idx].begin.status = 0;
+
+ d->prg[idx].data[0] = packet->speed_code<<16 |
+ (packet->header[0] & 0xFFFF);
+ d->prg[idx].data[1] = (packet->header[1] & 0xFFFF) |
+ (packet->header[0] & 0xFFFF0000);
+ d->prg[idx].data[2] = packet->header[2];
+ d->prg[idx].data[3] = packet->header[3];
+
+ if (packet->data_size) { /* block transmit */
+ d->prg[idx].begin.control = OUTPUT_MORE_IMMEDIATE | 0x10;
+ d->prg[idx].end.control = OUTPUT_LAST | packet->data_size;
+ d->prg[idx].end.address = virt_to_bus(packet->data);
+ d->prg[idx].end.branchAddress = 0;
+ d->prg[idx].end.status = 0x4000;
+ if (d->branchAddrPtr)
+ *(d->branchAddrPtr) = virt_to_bus(d->prg+idx) | 0x3;
+ d->branchAddrPtr = &(d->prg[idx].end.branchAddress);
+ }
+ else { /* quadlet transmit */
+ d->prg[idx].begin.control =
+ OUTPUT_LAST_IMMEDIATE | packet->header_size;
+ if (d->branchAddrPtr)
+ *(d->branchAddrPtr) = virt_to_bus(d->prg+idx) | 0x2;
+ d->branchAddrPtr = &(d->prg[idx].begin.branchAddress);
+ }
+ d->free_prgs--;
- /* run program */
- reg_write(ohci, OHCI1394_AsReqTrContextControlSet, 0x00008000);
+ /* queue the packet in the appropriate context queue */
+ if (d->last) {
+ d->last->xnext = packet;
+ d->last = packet;
+ }
+ else {
+ d->first = packet;
+ d->last = packet;
+ }
}
static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet)
{
struct ti_ohci *ohci = host->hostdata;
- struct hpsb_packet *p;
- unsigned long flags;
+ struct dma_trm_ctx *d;
+ unsigned char tcode;
+ int i=50;
if (packet->data_size >= 4096) {
PRINT(KERN_ERR, ohci->id, "transmit packet data too big (%d)",
packet->data_size);
return 0;
}
-
- //HPSB_TRACE();
packet->xnext = NULL;
-
- spin_lock_irqsave(&ohci->async_queue_lock, flags);
- if (ohci->async_queue == NULL) {
- ohci->async_queue = packet;
- send_next_async(ohci);
- } else {
- p = ohci->async_queue;
- while (p->xnext != NULL) {
- p = p->xnext;
+ /* Decide wether we have a request or a response packet */
+ tcode = (packet->header[0]>>4)&0xf;
+ if ((tcode==TCODE_READQ)||
+ (tcode==TCODE_WRITEQ)||
+ (tcode==TCODE_READB)||
+ (tcode==TCODE_WRITEB)||
+ (tcode==TCODE_LOCK_REQUEST))
+ d = ohci->at_req_context;
+
+ else if ((tcode==TCODE_WRITE_RESPONSE)||
+ (tcode==TCODE_READQ_RESPONSE)||
+ (tcode==TCODE_READB_RESPONSE)||
+ (tcode==TCODE_LOCK_RESPONSE))
+ d = ohci->at_resp_context;
+
+ else {
+ PRINT(KERN_ERR, ohci->id,
+ "Unexpected packet tcode=%d in AT DMA", tcode);
+ return 0;
+ }
+
+ spin_lock(&d->lock);
+
+ if (d->free_prgs<1) {
+ PRINT(KERN_INFO, ohci->id,
+ "AT DMA ctx=%d Running out of prgs... waiting",d->ctx);
+ }
+ while (d->free_prgs<1) {
+ spin_unlock(&d->lock);
+ schedule();
+ if (i-- <0) {
+ stop_context(ohci, d->ctrlClear,
+ "AT DMA runaway loop... bailing out");
+ return 0;
}
-
- p->xnext = packet;
+ spin_lock(&d->lock);
+ }
+
+ insert_packet(ohci, d, packet);
+
+ /* Is the context running ? (should be unless it is
+ the first packet to be sent in this context) */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) {
+ DBGMSG(ohci->id,"Starting AT DMA ctx=%d",d->ctx);
+ if (packet->data_size)
+ reg_write(ohci, d->cmdPtr,
+ virt_to_bus(&(d->prg[d->prg_ind])) | 0x3);
+ else
+ reg_write(ohci, d->cmdPtr,
+ virt_to_bus(&(d->prg[d->prg_ind])) | 0x2);
+
+ run_context(ohci, d->ctrlSet, NULL);
+ }
+ else {
+ DBGMSG(ohci->id,"Waking AT DMA ctx=%d",d->ctx);
+ /* wake up the dma context if necessary */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x400))
+ reg_write(ohci, d->ctrlSet, 0x1000);
}
-
- spin_unlock_irqrestore(&ohci->async_queue_lock, flags);
+
+ d->prg_ind = (d->prg_ind+1)%d->num_desc;
+ spin_unlock(&d->lock);
return 1;
}
struct ti_ohci *ohci = host->hostdata;
int retval = 0;
unsigned long flags;
- struct hpsb_packet *packet, *lastpacket;
- u32 r;
switch (cmd) {
- case RESET_BUS:
+ case RESET_BUS:
PRINT(KERN_INFO, ohci->id, "resetting bus on request%s",
(host->attempt_root ? " and attempting to become root"
: ""));
- r = (host->attempt_root) ? 0x000041ff : 0x0000417f;
- reg_write(ohci, OHCI1394_PhyControl, r);
+ reg_write(ohci, OHCI1394_PhyControl,
+ (host->attempt_root) ? 0x000041ff : 0x0000417f);
break;
- case GET_CYCLE_COUNTER:
+ case GET_CYCLE_COUNTER:
retval = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
break;
- case SET_CYCLE_COUNTER:
+ case SET_CYCLE_COUNTER:
reg_write(ohci, OHCI1394_IsochronousCycleTimer, arg);
break;
- case SET_BUS_ID:
+ case SET_BUS_ID:
PRINT(KERN_ERR, ohci->id, "devctl command SET_BUS_ID err");
break;
- case ACT_CYCLE_MASTER:
+ case ACT_CYCLE_MASTER:
#if 0
if (arg) {
/* enable cycleTimer, cycleMaster, cycleSource */
#endif
break;
- case CANCEL_REQUESTS:
- spin_lock_irqsave(&ohci->async_queue_lock, flags);
- /* stop any chip activity */
- reg_write(ohci, OHCI1394_HCControlClear, 0x00020000);
- packet = ohci->async_queue;
- ohci->async_queue = NULL;
- spin_unlock_irqrestore(&ohci->async_queue_lock, flags);
-
- while (packet != NULL) {
- lastpacket = packet;
- packet = packet->xnext;
- hpsb_packet_sent(host, lastpacket, ACKX_ABORTED);
- }
-
+ case CANCEL_REQUESTS:
+ DBGMSG(ohci->id, "Cancel request received");
+ dma_trm_reset(ohci->at_req_context);
+ dma_trm_reset(ohci->at_resp_context);
break;
- case MODIFY_USAGE:
+ case MODIFY_USAGE:
if (arg) {
MOD_INC_USE_COUNT;
} else {
}
break;
- case ISO_LISTEN_CHANNEL:
+ case ISO_LISTEN_CHANNEL:
spin_lock_irqsave(&ohci->IR_channel_lock, flags);
spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
break;
- case ISO_UNLISTEN_CHANNEL:
+ case ISO_UNLISTEN_CHANNEL:
spin_lock_irqsave(&ohci->IR_channel_lock, flags);
spin_unlock_irqrestore(&ohci->IR_channel_lock, flags);
break;
- default:
+ default:
PRINT_G(KERN_ERR, "ohci_devctl cmd %d not implemented yet\n",
cmd);
break;
* Global stuff (interrupt handler, init/shutdown code) *
********************************************************/
-static void stop_ar_resp_context(struct ti_ohci *ohci, char *msg)
+static void dma_trm_reset(struct dma_trm_ctx *d)
{
- int i=0;
+ struct ti_ohci *ohci;
- /* stop the channel program if it's still running */
- reg_write(ohci, OHCI1394_AsRspRcvContextControlClear, 0x8000);
-
- /* Wait until it effectively stops */
- while (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet)
- & 0x400) {
- i++;
- if (i>5000) {
- PRINT(KERN_ERR, ohci->id,
- "runaway loop in Dma Ar Resp. bailing out...");
- break;
- }
+ if (d==NULL) {
+ PRINT_G(KERN_ERR, "dma_trm_reset called with NULL arg");
+ return;
+ }
+ ohci = (struct ti_ohci *)(d->ohci);
+ stop_context(ohci, d->ctrlClear, NULL);
+
+ spin_lock(&d->lock);
+
+ /* is there still any packet pending ? */
+ while(d->first) {
+ PRINT(KERN_INFO, ohci->id,
+ "AT dma reset ctx=%d, aborting transmission",
+ d->ctx);
+ hpsb_packet_sent(ohci->host, d->first, ACKX_ABORTED);
+ d->first = d->first->xnext;
}
- PRINT(KERN_ERR, ohci->id, "%s\n async response receive dma stopped\n", msg);
+ d->first = d->last = NULL;
+ d->branchAddrPtr=NULL;
+ d->sent_ind = d->prg_ind;
+ d->free_prgs = d->num_desc;
+ spin_unlock(&d->lock);
}
static void ohci_irq_handler(int irq, void *dev_id,
struct pt_regs *regs_are_unused)
{
- //int i;
- static quadlet_t event,node_id;
+ quadlet_t event,node_id;
struct ti_ohci *ohci = (struct ti_ohci *)dev_id;
struct hpsb_host *host = ohci->host;
int phyid = -1, isroot = 0;
event=reg_read(ohci, OHCI1394_IntEventSet);
- /* Clear the interrupt register */
- reg_write(ohci, OHCI1394_IntEventClear, event);
-
- /* PRINT(KERN_INFO, ohci->id, "int event %08X mask %08X",
- event,reg_read(ohci, OHCI1394_IntMaskSet)); */
-
if (event & OHCI1394_busReset) {
-#if 0
- PRINT(KERN_INFO, ohci->id, "bus reset interrupt");
-#endif
if (!host->in_bus_reset) {
- hpsb_bus_reset(host);
+ PRINT(KERN_INFO, ohci->id, "Bus reset");
+
+ /* Wait for the AT fifo to be flushed */
+ dma_trm_reset(ohci->at_req_context);
+ dma_trm_reset(ohci->at_resp_context);
+
+ /* Subsystem call */
+ hpsb_bus_reset(ohci->host);
+
+ ohci->NumBusResets++;
}
- ohci->NumBusResets++;
}
- if (event & OHCI1394_RQPkt) {
- PRINT(KERN_INFO, ohci->id, "RQPkt int received");
+ /*
+ * Problem: How can I ensure that the AT bottom half will be
+ * executed before the AR bottom half (both events may have
+ * occured within a single irq event)
+ * Quick hack: just launch it within the IRQ handler
+ */
+ if (event & OHCI1394_reqTxComplete) {
+ struct dma_trm_ctx *d = ohci->at_req_context;
+ DBGMSG(ohci->id, "Got reqTxComplete interrupt status=0x%08X",
+ reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ stop_context(ohci, d->ctrlClear, "reqTxComplete");
+ else
+ dma_trm_bh((void *)d);
+ }
+ if (event & OHCI1394_respTxComplete) {
+ struct dma_trm_ctx *d = ohci->at_resp_context;
+ DBGMSG(ohci->id, "Got respTxComplete interrupt status=0x%08X",
+ reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ stop_context(ohci, d->ctrlClear, "respTxComplete");
+ else
+ dma_trm_bh((void *)d);
}
if (event & OHCI1394_RQPkt) {
- PRINT(KERN_INFO, ohci->id, "ControlContext: %08X",
- reg_read(ohci, OHCI1394_AsReqRcvContextControlSet));
+ struct dma_rcv_ctx *d = ohci->ar_req_context;
+ DBGMSG(ohci->id, "Got RQPkt interrupt status=0x%08X",
+ reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ stop_context(ohci, d->ctrlClear, "RQPkt");
+ else {
+ queue_task(&d->task, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
}
if (event & OHCI1394_RSPkt) {
- unsigned int idx,offset,rescount;
-
- spin_lock(&ohci->AR_resp_lock);
-
- idx = ohci->AR_resp_buf_th_ind;
- offset = ohci->AR_resp_buf_th_offset;
-
- rescount = ohci->AR_resp_prg[idx]->status&0xffff;
- ohci->AR_resp_bytes_left += AR_RESP_BUF_SIZE - rescount - offset;
- offset = AR_RESP_BUF_SIZE - rescount;
-
- if (!rescount) { /* We cross a buffer boundary */
- idx = (idx+1) % AR_RESP_NUM_DESC;
-
-#if 0
- /* This bit of code does not work */
- /* Let's see how many bytes were written in the async response
- receive buf since last interrupt. This is done by finding
- the next active context (See OHCI Spec p91) */
- while (ohci->AR_resp_bytes_left <= AR_RESP_TOTAL_BUF_SIZE) {
- if (ohci->AR_resp_prg[idx]->status&0x04000000) break;
- idx = (idx+1) % AR_RESP_NUM_DESC;
- PRINT(KERN_INFO,ohci->id,"crossing more than one buffer boundary !!!");
- ohci->AR_resp_bytes_left += AR_RESP_BUF_SIZE;
- }
-#endif
- /* ASSUMPTION: only one buffer boundary is crossed */
- rescount = ohci->AR_resp_prg[idx]->status&0xffff;
- offset = AR_RESP_BUF_SIZE - rescount;
- ohci->AR_resp_bytes_left += offset;
- }
- if (offset==AR_RESP_BUF_SIZE) {
- offset=0;
- idx = (idx+1) % AR_RESP_NUM_DESC;
- }
- ohci->AR_resp_buf_th_ind = idx;
- ohci->AR_resp_buf_th_offset = offset;
-
- /* is buffer processing too slow? (all buffers used) */
- if (ohci->AR_resp_bytes_left > AR_RESP_TOTAL_BUF_SIZE) {
- stop_ar_resp_context(ohci,"async response receive processing too slow");
- spin_unlock(&ohci->AR_resp_lock);
- return;
+ struct dma_rcv_ctx *d = ohci->ar_resp_context;
+ DBGMSG(ohci->id, "Got RSPkt interrupt status=0x%08X",
+ reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ stop_context(ohci, d->ctrlClear, "RSPkt");
+ else {
+ queue_task(&d->task, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
}
- spin_unlock(&ohci->AR_resp_lock);
-
- /* queue bottom half in immediate queue */
- queue_task(&ohci->AR_resp_pdl_task, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
}
if (event & OHCI1394_isochRx) {
quadlet_t isoRecvIntEvent;
-
- /* ASSUMPTION: We assume there is only one context for now. */
-
- spin_lock(&ohci->IR_recv_lock);
-
- /* Clear the isoRecvIntEvent register (very important!) */
- isoRecvIntEvent= reg_read(ohci, OHCI1394_IsoRecvIntEventSet);
+ struct dma_rcv_ctx *d = ohci->ir_context;
+ isoRecvIntEvent = reg_read(ohci, OHCI1394_IsoRecvIntEventSet);
reg_write(ohci, OHCI1394_IsoRecvIntEventClear,
isoRecvIntEvent);
-
- ohci->IR_buf_used++;
- ohci->IR_buf_next_ind=
- (ohci->IR_buf_next_ind + 1) % IR_NUM_DESC;
-
- /* is buffer processing too slow? (all buffers used) */
- if (ohci->IR_buf_next_ind == ohci->IR_buf_last_ind) {
- int i= 0;
-
- /* stop the context */
- reg_write(ohci,
- OHCI1394_IrRcvContextControlClear, 0x8000);
-
- while (reg_read(ohci, OHCI1394_IrRcvContextControlSet)
- & 0x400) {
- i++;
-
- if (i>5000) {
- PRINT(KERN_ERR, ohci->id, "runaway loop in DmaIR. bailing out...");
- break;
- }
-
- }
-
- spin_unlock(&ohci->IR_recv_lock);
- PRINT(KERN_ERR, ohci->id,
- "iso receive processing too slow... stopped");
- return;
+ DBGMSG(ohci->id, "Got reqTxComplete interrupt status=0x%08X",
+ reg_read(ohci, d->ctrlSet));
+ if (reg_read(ohci, d->ctrlSet) & 0x800)
+ stop_context(ohci, d->ctrlClear, "isochRx");
+ else {
+ queue_task(&d->task, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
}
-
- /* reset status field of next descriptor */
- ohci->IR_recv_prg[ohci->IR_buf_next_ind]->status=
- IR_RECV_BUF_SIZE;
-
- spin_unlock(&ohci->IR_recv_lock);
-
- /* queue bottom half in immediate queue */
- queue_task(&ohci->IR_pdl_task, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
}
if (event & OHCI1394_selfIDComplete) {
if (host->in_bus_reset) {
handle_selfid(ohci, host, phyid, isroot);
}
else
- PRINT(KERN_ERR, ohci->id,
- "SelfID process finished but NodeID"
- " not valid: %08X",node_id);
+ PRINT(KERN_ERR, ohci->id,
+ "SelfID process finished but NodeID"
+ " not valid: %08X",node_id);
+
+ /* Accept Physical requests from all nodes. */
+ reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0xffffffff);
+ reg_write(ohci,OHCI1394_AsReqFilterLoSet, 0xffffffff);
}
else PRINT(KERN_INFO, ohci->id,
"phy reg received without reset\n");
if (host->in_bus_reset) {
PRINT(KERN_INFO, ohci->id, "PhyControl: %08X",
reg_read(ohci, OHCI1394_PhyControl));
- } else printk("-%d- phy reg received without reset\n",
- ohci->id);
+ } else
+ PRINT(KERN_ERR, ohci->id,
+ "phy reg received without reset");
#endif
}
- if (event & OHCI1394_reqTxComplete) {
- /* async packet sent - transmitter ready */
- u32 ack;
- struct hpsb_packet *packet;
-
- if (ohci->async_queue) {
-
- spin_lock(&ohci->async_queue_lock);
-
- ack=reg_read(ohci, OHCI1394_AsReqTrContextControlSet)
- & 0xF;
-
- packet = ohci->async_queue;
- ohci->async_queue = packet->xnext;
-
- if (ohci->async_queue != NULL) {
- send_next_async(ohci);
- }
- spin_unlock(&ohci->async_queue_lock);
-#if 0
- PRINT(KERN_INFO,ohci->id,
- "packet sent with ack code %d",ack);
-#endif
- hpsb_packet_sent(host, packet, ack);
- } else
- PRINT(KERN_INFO,ohci->id,
- "packet sent without async_queue (self-id?)");
- ohci->TxRdy++;
+ /* clear the interrupt event register */
+ reg_write(ohci, OHCI1394_IntEventClear, event);
+}
+
+/* Put the buffer back into the dma context */
+static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx)
+{
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ DBGMSG(ohci->id, "Inserting dma buf ctx=%d idx=%d", d->ctx, idx);
+
+ d->prg[idx]->status = d->buf_size;
+ d->prg[idx]->branchAddress &= 0xfffffff0;
+ idx = (idx + d->num_desc - 1 ) % d->num_desc;
+ d->prg[idx]->branchAddress |= 0x1;
+
+ /* wake up the dma context if necessary */
+ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) {
+ PRINT(KERN_INFO, ohci->id,
+ "Waking dma cxt=%d ... processing is probably too slow",
+ d->ctx);
+ reg_write(ohci, d->ctrlSet, 0x1000);
}
+}
- ohci->NumInterrupts++;
+static int block_length(struct dma_rcv_ctx *d, int idx,
+ quadlet_t *buf_ptr, int offset)
+{
+ int length=0;
+
+ /* Where is the data length ? */
+ if (offset+12>=d->buf_size)
+ length = (d->buf[(idx+1)%d->num_desc]
+ [3-(d->buf_size-offset)/4]>>16);
+ else
+ length = (buf_ptr[3]>>16);
+ if (length % 4) length += 4 - (length % 4);
+ return length;
}
+static int packet_length(struct dma_rcv_ctx *d, int idx,
+ quadlet_t *buf_ptr, int offset)
+{
+ unsigned char tcode;
+ int length;
+
+ /* Let's see what kind of packet is in there */
+ tcode = (buf_ptr[0]>>4)&0xf;
+
+ if (d->ctx==0) { /* Async Receive Request */
+ if (tcode==TCODE_READQ) return 16;
+ else if (tcode==TCODE_WRITEQ ||
+ tcode==TCODE_READB) return 20;
+ else if (tcode==TCODE_WRITEB ||
+ tcode==TCODE_LOCK_REQUEST) {
+ return block_length(d, idx, buf_ptr, offset) + 20;
+ }
+ else if (tcode==0xE) { /* Phy packet */
+ return 16;
+ }
+ else return -1;
+ }
+ else if (d->ctx==1) { /* Async Receive Response */
+ if (tcode==TCODE_WRITE_RESPONSE) return 16;
+ else if (tcode==TCODE_READQ_RESPONSE) return 20;
+ else if (tcode==TCODE_READB_RESPONSE ||
+ tcode==TCODE_LOCK_RESPONSE) {
+ return block_length(d, idx, buf_ptr, offset) + 20;
+ }
+ else return -1;
+ }
+ else if (d->ctx==2) { /* Iso receive */
+ /* Assumption: buffer fill mode with header/trailer */
+ length = (buf_ptr[0]>>16);
+ if (length % 4) length += 4 - (length % 4);
+ return length+8;
+ }
+ return -1;
+}
-/* This is the bottom half that processes async response receive descriptor buffers. */
-static void ohci_ar_resp_proc_desc(void *data)
+/* Bottom half that processes dma receive buffers */
+static void dma_rcv_bh(void *data)
{
+ struct dma_rcv_ctx *d = (struct dma_rcv_ctx*)data;
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ unsigned int split_left, idx, offset, rescount;
+ unsigned char tcode;
+ int length, bytes_left;
quadlet_t *buf_ptr;
char *split_ptr;
- unsigned int split_left;
- struct ti_ohci *ohci= (struct ti_ohci*)data;
- unsigned int packet_length;
- unsigned int idx,offset,tcode;
- unsigned long flags;
char msg[256];
- spin_lock_irqsave(&ohci->AR_resp_lock, flags);
+ spin_lock(&d->lock);
- idx = ohci->AR_resp_buf_bh_ind;
- offset = ohci->AR_resp_buf_bh_offset;
+ idx = d->buf_ind;
+ offset = d->buf_offset;
+ buf_ptr = d->buf[idx] + offset/4;
- buf_ptr = ohci->AR_resp_buf[idx];
- buf_ptr += offset/4;
+ rescount = d->prg[idx]->status&0xffff;
+ bytes_left = d->buf_size - rescount - offset;
- while(ohci->AR_resp_bytes_left > 0) {
-
- /* check to see if a fatal error occurred */
- if ((ohci->AR_resp_prg[idx]->status >> 16) & 0x800) {
- sprintf(msg,"fatal async response receive error -- status is %d",
- ohci->AR_resp_prg[idx]->status & 0x1F);
- stop_ar_resp_context(ohci, msg);
- spin_unlock_irqrestore(&ohci->AR_resp_lock, flags);
- return;
- }
-
- spin_unlock_irqrestore(&ohci->AR_resp_lock, flags);
-
- /* Let's see what kind of packet is in there */
+ while (bytes_left>0) {
tcode = (buf_ptr[0]>>4)&0xf;
- if (tcode==2) /* no-data receive */
- packet_length=16;
- else if (tcode==6) /* quadlet receive */
- packet_length=20;
- else if (tcode==7) { /* block receive */
- /* Where is the data length ? */
- if (offset+12>=AR_RESP_BUF_SIZE)
- packet_length=(ohci->AR_resp_buf[(idx+1)%AR_RESP_NUM_DESC]
- [3-(AR_RESP_BUF_SIZE-offset)/4]>>16)+20;
- else
- packet_length=(buf_ptr[3]>>16)+20;
- if (packet_length % 4)
- packet_length += 4 - (packet_length % 4);
- }
- else /* something is wrong */ {
- sprintf(msg,"unexpected packet tcode %d in async response receive buffer",tcode);
- stop_ar_resp_context(ohci,msg);
+ length = packet_length(d, idx, buf_ptr, offset);
+
+ if (length<4) { /* something is wrong */
+ sprintf(msg,"unexpected tcode 0x%X in AR ctx=%d",
+ tcode, d->ctx);
+ stop_context(ohci, d->ctrlClear, msg);
+ spin_unlock(&d->lock);
return;
}
- if ((offset+packet_length)>AR_RESP_BUF_SIZE) {
- /* we have a split packet */
- if (packet_length>AR_RESP_SPLIT_PACKET_BUF_SIZE) {
- sprintf(msg,"packet size %d bytes exceed split packet buffer size %d bytes",
- packet_length,AR_RESP_SPLIT_PACKET_BUF_SIZE);
- stop_ar_resp_context(ohci, msg);
+
+ if ((offset+length)>d->buf_size) { /* Split packet */
+ if (length>d->split_buf_size) {
+ stop_context(ohci, d->ctrlClear,
+ "split packet size exceeded");
+ d->buf_ind = idx;
+ d->buf_offset = offset;
+ spin_unlock(&d->lock);
return;
}
- split_left = packet_length;
- split_ptr = (char *)ohci->AR_resp_spb;
- while (split_left>0) {
- memcpy(split_ptr,buf_ptr,AR_RESP_BUF_SIZE-offset);
- split_left -= AR_RESP_BUF_SIZE-offset;
- split_ptr += AR_RESP_BUF_SIZE-offset;
- ohci->AR_resp_prg[idx]->status = AR_RESP_BUF_SIZE;
- idx = (idx+1) % AR_RESP_NUM_DESC;
- buf_ptr = ohci->AR_resp_buf[idx];
- offset=0;
- while (split_left >= AR_RESP_BUF_SIZE) {
- memcpy(split_ptr,buf_ptr,AR_RESP_BUF_SIZE);
- split_ptr += AR_RESP_BUF_SIZE;
- split_left -= AR_RESP_BUF_SIZE;
- ohci->AR_resp_prg[idx]->status = AR_RESP_BUF_SIZE;
- idx = (idx+1) % AR_RESP_NUM_DESC;
- buf_ptr = ohci->AR_resp_buf[idx];
- }
- if (split_left>0) {
- memcpy(split_ptr,buf_ptr,split_left);
- offset = split_left;
- split_left=0;
- buf_ptr += split_left/4;
- }
+ if (d->prg[(idx+1)%d->num_desc]->status==d->buf_size) {
+ /* other part of packet not written yet */
+ /* this should never happen I think */
+ /* anyway we'll get it on the next call */
+ PRINT(KERN_INFO, ohci->id,
+ "Got only half a packet !!!");
+ d->buf_ind = idx;
+ d->buf_offset = offset;
+ spin_unlock(&d->lock);
+ return;
}
-#if 0
- PRINT(KERN_INFO,ohci->id,"AR resp: received split packet tcode=%d length=%d",
- tcode,packet_length);
-#endif
- hpsb_packet_received(ohci->host, ohci->AR_resp_spb, packet_length);
- ohci->AR_resp_bytes_left -= packet_length;
+ split_left = length;
+ split_ptr = (char *)d->spb;
+ memcpy(split_ptr,buf_ptr,d->buf_size-offset);
+ split_left -= d->buf_size-offset;
+ split_ptr += d->buf_size-offset;
+ insert_dma_buffer(d, idx);
+ idx = (idx+1) % d->num_desc;
+ buf_ptr = d->buf[idx];
+ offset=0;
+ while (split_left >= d->buf_size) {
+ memcpy(split_ptr,buf_ptr,d->buf_size);
+ split_ptr += d->buf_size;
+ split_left -= d->buf_size;
+ insert_dma_buffer(d, idx);
+ idx = (idx+1) % d->num_desc;
+ buf_ptr = d->buf[idx];
+ }
+ if (split_left>0) {
+ memcpy(split_ptr, buf_ptr, split_left);
+ offset = split_left;
+ buf_ptr += offset/4;
+ }
+
+ /*
+ * We get one phy packet for each bus reset.
+ * we know that from now on the bus topology may
+ * have changed. Just ignore it for the moment
+ */
+ if (tcode != 0xE) {
+ DBGMSG(ohci->id, "Split packet received from"
+ " node %d ack=0x%02X spd=%d tcode=0x%X"
+ " length=%d data=0x%08x ctx=%d",
+ (d->spb[1]>>16)&0x3f,
+ (d->spb[length/4-1]>>16)&0x1f,
+ (d->spb[length/4-1]>>21)&0x3,
+ tcode, length, d->spb[3], d->ctx);
+ hpsb_packet_received(ohci->host, d->spb,
+ length);
+ }
+ else
+ PRINT(KERN_INFO, ohci->id,
+ "Got phy packet ctx=%d ... discarded",
+ d->ctx);
}
else {
-#if 0
- PRINT(KERN_INFO,ohci->id,"AR resp: received packet tcode=%d length=%d",
- tcode,packet_length);
-#endif
- hpsb_packet_received(ohci->host, buf_ptr, packet_length);
- offset += packet_length;
- buf_ptr += packet_length/4;
- ohci->AR_resp_bytes_left -= packet_length;
- if (offset==AR_RESP_BUF_SIZE) {
- ohci->AR_resp_prg[idx]->status = AR_RESP_BUF_SIZE;
- idx = (idx+1) % AR_RESP_NUM_DESC;
- buf_ptr = ohci->AR_resp_buf[idx];
+ /*
+ * We get one phy packet for each bus reset.
+ * we know that from now on the bus topology may
+ * have changed. Just ignore it for the moment
+ */
+ if (tcode != 0xE) {
+ DBGMSG(ohci->id, "Packet received from node"
+ " %d ack=0x%02X spd=%d tcode=0x%X"
+ " length=%d data=0x%08x ctx=%d",
+ (buf_ptr[1]>>16)&0x3f,
+ (buf_ptr[length/4-1]>>16)&0x1f,
+ (buf_ptr[length/4-1]>>21)&0x3,
+ tcode, length, buf_ptr[3], d->ctx);
+ hpsb_packet_received(ohci->host, buf_ptr,
+ length);
+ }
+ else
+ PRINT(KERN_INFO, ohci->id,
+ "Got phy packet ctx=%d ... discarded",
+ d->ctx);
+ offset += length;
+ buf_ptr += length/4;
+ if (offset==d->buf_size) {
+ insert_dma_buffer(d, idx);
+ idx = (idx+1) % d->num_desc;
+ buf_ptr = d->buf[idx];
offset=0;
}
}
-
+ rescount = d->prg[idx]->status & 0xffff;
+ bytes_left = d->buf_size - rescount - offset;
+
}
+
+ d->buf_ind = idx;
+ d->buf_offset = offset;
+
+ spin_unlock(&d->lock);
+}
+
+/* Bottom half that processes sent packets */
+static void dma_trm_bh(void *data)
+{
+ struct dma_trm_ctx *d = (struct dma_trm_ctx*)data;
+ struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci);
+ struct hpsb_packet *packet;
+ u32 ack;
+
+ spin_lock(&d->lock);
+
+ if (d->first==NULL) {
+ stop_context(ohci, d->ctrlClear,
+ "Packet sent ack received but queue is empty");
+ spin_unlock(&d->lock);
+ return;
+ }
+ packet = d->first;
+ d->first = d->first->xnext;
+ if (d->first==NULL) d->last=NULL;
+ if (packet->data_size)
+ ack = d->prg[d->sent_ind].end.status>>16;
+ else
+ ack = d->prg[d->sent_ind].begin.status>>16;
+ d->sent_ind = (d->sent_ind+1)%d->num_desc;
+ d->free_prgs++;
+ spin_unlock(&d->lock);
- if (ohci->AR_resp_bytes_left<0)
- stop_ar_resp_context(ohci, "Sync problem in AR resp dma buffer");
+ DBGMSG(ohci->id, "Packet sent to node %d ack=0x%X spd=%d ctx=%d",
+ (packet->header[0]>>16)&0x3f, ack&0x1f, (ack>>5)&0x3, d->ctx);
+ hpsb_packet_sent(ohci->host, packet, ack&0xf);
+}
+
+static int free_dma_rcv_ctx(struct dma_rcv_ctx *d)
+{
+ int i;
- ohci->AR_resp_buf_bh_ind = idx;
- ohci->AR_resp_buf_bh_offset = offset;
+ if (d==NULL) return -1;
- spin_unlock_irqrestore(&ohci->AR_resp_lock, flags);
+ if (d->buf) {
+ for (i=0; i<d->num_desc; i++)
+ if (d->buf[i]) kfree(d->buf[i]);
+ kfree(d->buf);
+ }
+ if (d->prg) {
+ for (i=0; i<d->num_desc; i++)
+ if (d->prg[i]) kfree(d->prg[i]);
+ kfree(d->prg);
+ }
+ if (d->spb) kfree(d->spb);
+
+ kfree(d);
+
+ return 0;
}
-/* This is the bottom half that processes iso receive descriptor buffers. */
-static void ohci_ir_proc_desc(void *data)
+static struct dma_rcv_ctx *
+alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc,
+ int buf_size, int split_buf_size,
+ int ctrlSet, int ctrlClear, int cmdPtr)
{
- quadlet_t *buf_ptr;
- struct ti_ohci *ohci= (struct ti_ohci*)data;
- int bytes_left, data_length;
- unsigned int idx;
- unsigned long flags;
+ struct dma_rcv_ctx *d=NULL;
+ int i;
- spin_lock_irqsave(&ohci->IR_recv_lock, flags);
+ d = (struct dma_rcv_ctx *)kmalloc(sizeof(struct dma_rcv_ctx),
+ GFP_KERNEL);
- while(ohci->IR_buf_used > 0)
- {
- idx= ohci->IR_buf_last_ind;
+ if (d==NULL) {
+ PRINT(KERN_ERR, ohci->id, "failed to allocate dma_rcv_ctx");
+ return NULL;
+ }
- /* check to see if a fatal error occurred */
- if ((ohci->IR_recv_prg[idx]->status >> 16) & 0x800) {
- int i= 0;
+ d->ohci = (void *)ohci;
+ d->ctx = ctx;
- /* stop the context */
- reg_write(ohci, OHCI1394_IrRcvContextControlClear,
- 0x8000);
+ d->num_desc = num_desc;
+ d->buf_size = buf_size;
+ d->split_buf_size = split_buf_size;
+ d->ctrlSet = ctrlSet;
+ d->ctrlClear = ctrlClear;
+ d->cmdPtr = cmdPtr;
- while (reg_read(ohci, OHCI1394_IrRcvContextControlSet)
- & 0x400) {
- i++;
+ d->buf = NULL;
+ d->prg = NULL;
+ d->spb = NULL;
- if (i > 5000) {
- PRINT(KERN_ERR, ohci->id, "runaway loop in DmaIR. bailing out...");
- break;
- }
+ d->buf = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL);
- }
+ if (d->buf == NULL) {
+ PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer");
+ free_dma_rcv_ctx(d);
+ return NULL;
+ }
+ memset(d->buf, 0, d->num_desc * sizeof(quadlet_t*));
- spin_unlock_irqrestore(&ohci->IR_recv_lock, flags);
- PRINT(KERN_ERR, ohci->id,
- "fatal iso receive error -- status is %d",
- ohci->IR_recv_prg[idx]->status & 0x1F);
- return;
- }
+ d->prg = kmalloc(d->num_desc * sizeof(struct dma_cmd*), GFP_KERNEL);
- spin_unlock_irqrestore(&ohci->IR_recv_lock, flags);
+ if (d->prg == NULL) {
+ PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg");
+ free_dma_rcv_ctx(d);
+ return NULL;
+ }
+ memset(d->prg, 0, d->num_desc * sizeof(struct dma_cmd*));
- buf_ptr= bus_to_virt(ohci->IR_recv_prg[idx]->address);
- bytes_left= IR_RECV_BUF_SIZE;
+ d->spb = kmalloc(d->split_buf_size, GFP_KERNEL);
- /* are we processing a split packet from last buffer */
- if (ohci->IR_sp_bytes_left) {
+ if (d->spb == NULL) {
+ PRINT(KERN_ERR, ohci->id, "failed to allocate split buffer");
+ free_dma_rcv_ctx(d);
+ return NULL;
+ }
- if (!ohci->IR_spb_bytes_used) {
- /* packet is in process of being dropped */
- if (ohci->IR_sp_bytes_left > bytes_left) {
- ohci->IR_sp_bytes_left-= bytes_left;
- bytes_left= 0;
- } else {
- buf_ptr= bus_to_virt((unsigned long)
- &((quadlet_t*)ohci->IR_recv_prg
- [idx]->address)
- [ohci->IR_sp_bytes_left / 4]);
- bytes_left-= ohci->IR_sp_bytes_left;
- ohci->IR_sp_bytes_left= 0;
- }
+ for (i=0; i<d->num_desc; i++) {
+ d->buf[i] = kmalloc(d->buf_size, GFP_KERNEL);
+
+ if (d->buf[i] != NULL) {
+ memset(d->buf[i], 0, d->buf_size);
+ } else {
+ PRINT(KERN_ERR, ohci->id,
+ "failed to allocate dma buffer");
+ free_dma_rcv_ctx(d);
+ return NULL;
+ }
- } else {
- /* packet is being assembled */
- if (ohci->IR_sp_bytes_left > bytes_left) {
- memcpy(&ohci->IR_spb
- [ohci->IR_spb_bytes_used / 4],
- buf_ptr, bytes_left);
- ohci->IR_spb_bytes_used+= bytes_left;
- ohci->IR_sp_bytes_left-= bytes_left;
- bytes_left= 0;
- } else {
- memcpy(&ohci->IR_spb
- [ohci->IR_spb_bytes_used / 4],
- buf_ptr,
- ohci->IR_sp_bytes_left);
- ohci->IR_spb_bytes_used+=
- ohci->IR_sp_bytes_left;
- hpsb_packet_received(ohci->host,
- ohci->IR_spb,
- ohci->IR_spb_bytes_used);
- buf_ptr=
- bus_to_virt((unsigned long)
- &((quadlet_t*)ohci->IR_recv_prg
- [idx]->address)
- [ohci->IR_sp_bytes_left / 4]);
- bytes_left-= ohci->IR_sp_bytes_left;
- ohci->IR_sp_bytes_left= 0;
- ohci->IR_spb_bytes_used= 0;
- }
+ d->prg[i]= kmalloc(sizeof(struct dma_cmd), GFP_KERNEL);
- }
+ if (d->prg[i] != NULL) {
+ memset(d->prg[i], 0, sizeof(struct dma_cmd));
+ } else {
+ PRINT(KERN_ERR, ohci->id,
+ "failed to allocate dma prg");
+ free_dma_rcv_ctx(d);
+ return NULL;
+ }
+ }
- }
+ spin_lock_init(&d->lock);
- while(bytes_left > 0) {
- data_length= (int)((buf_ptr[0] >> 16) & 0xffff);
+ /* initialize bottom handler */
+ d->task.routine = dma_rcv_bh;
+ d->task.data = (void*)d;
- if (data_length % 4)
- data_length+= 4 - (data_length % 4);
+ return d;
+}
- /* is this a split packet? */
- if ( (bytes_left - (data_length + 8)) < 0 ) {
+static int free_dma_trm_ctx(struct dma_trm_ctx *d)
+{
+ if (d==NULL) return -1;
+ if (d->prg) kfree(d->prg);
+ kfree(d);
+ return 0;
+}
- if ( (data_length + 8) <=
- IR_SPLIT_PACKET_BUF_SIZE ) {
- memcpy(ohci->IR_spb, buf_ptr,
- bytes_left);
- ohci->IR_spb_bytes_used= bytes_left;
- } else {
- PRINT(KERN_ERR, ohci->id, "Packet too large for split packet buffer... dropping it");
- PRINT(KERN_DEBUG, ohci->id, "Header: %8.8x\n", buf_ptr[0]);
- ohci->IR_spb_bytes_used= 0;
- }
+static struct dma_trm_ctx *
+alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc,
+ int ctrlSet, int ctrlClear, int cmdPtr)
+{
+ struct dma_trm_ctx *d=NULL;
- ohci->IR_sp_bytes_left=
- (data_length + 8) - bytes_left;
- } else {
- hpsb_packet_received(ohci->host, buf_ptr,
- (data_length + 8));
- buf_ptr= bus_to_virt((unsigned long)
- &((quadlet_t*)ohci->IR_recv_prg
- [idx]->address)
- [(IR_RECV_BUF_SIZE - bytes_left
- + data_length + 8) / 4]);
- }
+ d = (struct dma_trm_ctx *)kmalloc(sizeof(struct dma_trm_ctx),
+ GFP_KERNEL);
- bytes_left-= (data_length + 8);
- }
+ if (d==NULL) {
+ PRINT(KERN_ERR, ohci->id, "failed to allocate dma_trm_ctx");
+ return NULL;
+ }
- spin_lock_irqsave(&ohci->IR_recv_lock, flags);
- ohci->IR_buf_last_ind= (idx + 1) % IR_NUM_DESC;
- ohci->IR_buf_used--;
- }
+ d->ohci = (void *)ohci;
+ d->ctx = ctx;
+ d->num_desc = num_desc;
+ d->ctrlSet = ctrlSet;
+ d->ctrlClear = ctrlClear;
+ d->cmdPtr = cmdPtr;
+ d->prg = NULL;
- spin_unlock_irqrestore(&ohci->IR_recv_lock, flags);
+ d->prg = kmalloc(d->num_desc * sizeof(struct at_dma_prg), GFP_KERNEL);
+
+ if (d->prg == NULL) {
+ PRINT(KERN_ERR, ohci->id, "failed to allocate at dma prg");
+ free_dma_trm_ctx(d);
+ return NULL;
+ }
+ memset(d->prg, 0, d->num_desc * sizeof(struct at_dma_prg));
+
+ spin_lock_init(&d->lock);
+
+ /* initialize bottom handler */
+ d->task.routine = dma_trm_bh;
+ d->task.data = (void*)d;
+
+ return d;
}
static int add_card(struct pci_dev *dev)
{
-#define FAIL(fmt, args...) \
- PRINT_G(KERN_ERR, fmt , ## args); \
- num_of_cards--; \
- remove_card(ohci); \
- return 1;
-
struct ti_ohci *ohci; /* shortcut to currently handled device */
- int i;
if (num_of_cards == MAX_OHCI1394_CARDS) {
PRINT_G(KERN_WARNING, "cannot handle more than %d cards. "
FAIL("failed to allocate DMA buffer for self-id packets");
}
- /* AR dma buffer and program allocation */
- ohci->AR_resp_buf=
- kmalloc(AR_RESP_NUM_DESC * sizeof(quadlet_t*),
- GFP_KERNEL);
-
- if (ohci->AR_resp_buf == NULL) {
- FAIL("failed to allocate AR response receive DMA buffer");
- }
-
- ohci->AR_resp_prg=
- kmalloc(AR_RESP_NUM_DESC * sizeof(struct dma_cmd*),
- GFP_KERNEL);
-
- if (ohci->AR_resp_prg == NULL) {
- FAIL("failed to allocate AR response receive DMA program");
- }
-
- ohci->AR_resp_spb= kmalloc(AR_RESP_SPLIT_PACKET_BUF_SIZE, GFP_KERNEL);
-
- if (ohci->AR_resp_spb == NULL) {
- FAIL("failed to allocate AR response split packet buffer");
- }
-
- for (i= 0; i < AR_RESP_NUM_DESC; i++) {
- ohci->AR_resp_buf[i]= kmalloc(AR_RESP_BUF_SIZE, GFP_KERNEL);
-
- if (ohci->AR_resp_buf[i] != NULL) {
- memset(ohci->AR_resp_buf[i], 0, AR_RESP_BUF_SIZE);
- } else {
- FAIL("failed to allocate AR response DMA buffer");
- }
-
- ohci->AR_resp_prg[i]= kmalloc(sizeof(struct dma_cmd),
- GFP_KERNEL);
-
- if (ohci->AR_resp_prg[i] != NULL) {
- memset(ohci->AR_resp_prg[i], 0,
- sizeof(struct dma_cmd));
- } else {
- FAIL("failed to allocate AR response DMA buffer");
- }
-
- }
-
- ohci->AR_resp_buf_th_ind = 0;
- ohci->AR_resp_buf_th_offset = 0;
- ohci->AR_resp_buf_bh_ind = 0;
- ohci->AR_resp_buf_bh_offset = 0;
- ohci->AR_resp_bytes_left = 0;
- spin_lock_init(&ohci->AR_resp_lock);
-
- /* initialize AR response receive task */
- ohci->AR_resp_pdl_task.routine= ohci_ar_resp_proc_desc;
- ohci->AR_resp_pdl_task.data= (void*)ohci;
-
- /* AT dma program allocation */
- ohci->AT_req_prg = (struct dma_cmd *) kmalloc(AT_REQ_PRG_SIZE,
- GFP_KERNEL);
- if (ohci->AT_req_prg != NULL) {
- memset(ohci->AT_req_prg, 0, AT_REQ_PRG_SIZE);
- } else {
- FAIL("failed to allocate AT request DMA program");
- }
-
- /* IR dma buffer and program allocation */
- ohci->IR_recv_buf=
- kmalloc(IR_NUM_DESC * sizeof(quadlet_t*),
- GFP_KERNEL);
-
- if (ohci->IR_recv_buf == NULL) {
- FAIL("failed to allocate IR receive DMA buffer");
- }
-
- ohci->IR_recv_prg=
- kmalloc(IR_NUM_DESC * sizeof(struct dma_cmd*),
- GFP_KERNEL);
-
- if (ohci->IR_recv_prg == NULL) {
- FAIL("failed to allocate IR receive DMA program");
- }
-
- ohci->IR_spb= kmalloc(IR_SPLIT_PACKET_BUF_SIZE, GFP_KERNEL);
-
- if (ohci->IR_spb == NULL) {
- FAIL("failed to allocate IR split packet buffer");
- }
-
- for (i= 0; i < IR_NUM_DESC; i++) {
- ohci->IR_recv_buf[i]= kmalloc(IR_RECV_BUF_SIZE, GFP_KERNEL);
-
- if (ohci->IR_recv_buf[i] != NULL) {
- memset(ohci->IR_recv_buf[i], 0, IR_RECV_BUF_SIZE);
- } else {
- FAIL("failed to allocate IR DMA buffer");
- }
+ ohci->ar_req_context =
+ alloc_dma_rcv_ctx(ohci, 0, AR_REQ_NUM_DESC,
+ AR_REQ_BUF_SIZE, AR_REQ_SPLIT_BUF_SIZE,
+ OHCI1394_AsReqRcvContextControlSet,
+ OHCI1394_AsReqRcvContextControlClear,
+ OHCI1394_AsReqRcvCommandPtr);
+
+ if (ohci->ar_req_context == NULL) return 1;
+
+ ohci->ar_resp_context =
+ alloc_dma_rcv_ctx(ohci, 1, AR_RESP_NUM_DESC,
+ AR_RESP_BUF_SIZE, AR_RESP_SPLIT_BUF_SIZE,
+ OHCI1394_AsRspRcvContextControlSet,
+ OHCI1394_AsRspRcvContextControlClear,
+ OHCI1394_AsRspRcvCommandPtr);
+
+ if (ohci->ar_resp_context == NULL) return 1;
- ohci->IR_recv_prg[i]= kmalloc(sizeof(struct dma_cmd),
- GFP_KERNEL);
+ ohci->at_req_context =
+ alloc_dma_trm_ctx(ohci, 0, AT_REQ_NUM_DESC,
+ OHCI1394_AsReqTrContextControlSet,
+ OHCI1394_AsReqTrContextControlClear,
+ OHCI1394_AsReqTrCommandPtr);
+
+ if (ohci->at_req_context == NULL) return 1;
- if (ohci->IR_recv_prg[i] != NULL) {
- memset(ohci->IR_recv_prg[i], 0,
- sizeof(struct dma_cmd));
- } else {
- FAIL("failed to allocate IR DMA buffer");
- }
+ ohci->at_resp_context =
+ alloc_dma_trm_ctx(ohci, 1, AT_RESP_NUM_DESC,
+ OHCI1394_AsRspTrContextControlSet,
+ OHCI1394_AsRspTrContextControlClear,
+ OHCI1394_AsRspTrCommandPtr);
+
+ if (ohci->at_resp_context == NULL) return 1;
+
+ ohci->ir_context =
+ alloc_dma_rcv_ctx(ohci, 2, IR_NUM_DESC,
+ IR_BUF_SIZE, IR_SPLIT_BUF_SIZE,
+ OHCI1394_IrRcvContextControlSet,
+ OHCI1394_IrRcvContextControlClear,
+ OHCI1394_IrRcvCommandPtr);
- }
+ if (ohci->ir_context == NULL) return 1;
- ohci->IR_buf_used= 0;
- ohci->IR_buf_last_ind= 0;
- ohci->IR_buf_next_ind= 0;
- spin_lock_init(&ohci->IR_recv_lock);
- spin_lock_init(&ohci->IR_channel_lock);
ohci->IR_channel_usage= 0x0000000000000000;
-
- /* initialize iso receive task */
- ohci->IR_pdl_task.routine= ohci_ir_proc_desc;
- ohci->IR_pdl_task.data= (void*)ohci;
+ spin_lock_init(&ohci->IR_channel_lock);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
ohci->registers = ioremap_nocache(dev->base_address[0],
//unsigned char phyreg;
//int i, nports;
int i;
+ struct dma_rcv_ctx *d=NULL;
+ struct dma_trm_ctx *dt=NULL;
p += sprintf(p,"IEEE-1394 OHCI Driver status report:\n");
p += sprintf(p," bus number: 0x%x Node ID: 0x%x\n",
host->is_busmgr ? "bus_mgr" : "");
p += sprintf(p,"\n---Iso Receive DMA---\n");
- for (i= 0; i < IR_NUM_DESC; i++) {
- p += sprintf(p, "IR_recv_buf[%d] : %p IR_recv_prg[%d]: %p\n",
- i, ohci->IR_recv_buf[i], i, ohci->IR_recv_prg[i]);
+ d = ohci->ir_context;
+#if 0
+ for (i=0; i<d->num_desc; i++) {
+ p += sprintf(p, "IR buf[%d] : %p prg[%d]: %p\n",
+ i, d->buf[i], i, d->prg[i]);
}
+#endif
+ p += sprintf(p, "Current buf: %d offset: %d\n",
+ d->buf_ind,d->buf_offset);
+
+ p += sprintf(p,"\n---Async Receive DMA---\n");
+ d = ohci->ar_req_context;
+#if 0
+ for (i=0; i<d->num_desc; i++) {
+ p += sprintf(p, "AR req buf[%d] : %p prg[%d]: %p\n",
+ i, d->buf[i], i, d->prg[i]);
+ }
+#endif
+ p += sprintf(p, "Ar req current buf: %d offset: %d\n",
+ d->buf_ind,d->buf_offset);
+
+ d = ohci->ar_resp_context;
+#if 0
+ for (i=0; i<d->num_desc; i++) {
+ p += sprintf(p, "AR resp buf[%d] : %p prg[%d]: %p\n",
+ i, d->buf[i], i, d->prg[i]);
+ }
+#endif
+ p += sprintf(p, "AR resp current buf: %d offset: %d\n",
+ d->buf_ind,d->buf_offset);
+
+ p += sprintf(p,"\n---Async Transmit DMA---\n");
+ dt = ohci->at_req_context;
+ p += sprintf(p, "AT req prg: %d sent: %d free: %d branchAddrPtr: %p\n",
+ dt->prg_ind, dt->sent_ind, dt->free_prgs,
+ dt->branchAddrPtr);
+ p += sprintf(p, "AT req queue: first: %p last: %p\n",
+ dt->first, dt->last);
+ dt = ohci->at_resp_context;
+#if 0
+ for (i=0; i<dt->num_desc; i++) {
+ p += sprintf(p, "------- AT resp prg[%02d] ------\n",i);
+ p += sprintf(p, "%p: control : %08x\n",
+ &(dt->prg[i].begin.control),
+ dt->prg[i].begin.control);
+ p += sprintf(p, "%p: address : %08x\n",
+ &(dt->prg[i].begin.address),
+ dt->prg[i].begin.address);
+ p += sprintf(p, "%p: brancAddr: %08x\n",
+ &(dt->prg[i].begin.branchAddress),
+ dt->prg[i].begin.branchAddress);
+ p += sprintf(p, "%p: status : %08x\n",
+ &(dt->prg[i].begin.status),
+ dt->prg[i].begin.status);
+ p += sprintf(p, "%p: header[0]: %08x\n",
+ &(dt->prg[i].data[0]),
+ dt->prg[i].data[0]);
+ p += sprintf(p, "%p: header[1]: %08x\n",
+ &(dt->prg[i].data[1]),
+ dt->prg[i].data[1]);
+ p += sprintf(p, "%p: header[2]: %08x\n",
+ &(dt->prg[i].data[2]),
+ dt->prg[i].data[2]);
+ p += sprintf(p, "%p: header[3]: %08x\n",
+ &(dt->prg[i].data[3]),
+ dt->prg[i].data[3]);
+ p += sprintf(p, "%p: control : %08x\n",
+ &(dt->prg[i].end.control),
+ dt->prg[i].end.control);
+ p += sprintf(p, "%p: address : %08x\n",
+ &(dt->prg[i].end.address),
+ dt->prg[i].end.address);
+ p += sprintf(p, "%p: brancAddr: %08x\n",
+ &(dt->prg[i].end.branchAddress),
+ dt->prg[i].end.branchAddress);
+ p += sprintf(p, "%p: status : %08x\n",
+ &(dt->prg[i].end.status),
+ dt->prg[i].end.status);
+ }
+#endif
+ p += sprintf(p, "AR resp prg: %d sent: %d free: %d"
+ " branchAddrPtr: %p\n",
+ dt->prg_ind, dt->sent_ind, dt->free_prgs,
+ dt->branchAddrPtr);
+ p += sprintf(p, "AT resp queue: first: %p last: %p\n",
+ dt->first, dt->last);
-
- p += sprintf(p,"\n---Async Reponse Receive DMA---\n");
- for (i= 0; i < AR_RESP_NUM_DESC; i++) {
- p += sprintf(p, "AR_resp_buf[%d] : %p AR_resp_prg[%d]: %p\n",
- i, ohci->AR_resp_buf[i], i, ohci->AR_resp_prg[i]);
- }
- p += sprintf(p, "Current AR resp buf in irq handler: %d offset: %d\n",
- ohci->AR_resp_buf_th_ind,ohci->AR_resp_buf_th_offset);
- p += sprintf(p, "Current AR resp buf in bottom half: %d offset: %d\n",
- ohci->AR_resp_buf_bh_ind,ohci->AR_resp_buf_bh_offset);
-
/* ----- Register Dump ----- */
p += sprintf(p,"\n### HC Register dump ###\n");
SR("Version : %08x GUID_ROM : %08x ATRetries : %08x\n",
SR("AsRsRvCtxCtl: %08x AsRsRvCmdPtr: %08x IntEvent : %08x\n",
OHCI1394_AsRspRcvContextControlSet, OHCI1394_AsRspRcvCommandPtr,
OHCI1394_IntEventSet);
-
+ for (i=0;i<4;i++) {
+ p += sprintf(p,"IsoRCtxCtl%02d: %08x IsoRCmdPtr%02d: %08x"
+ " IsoRCxtMch%02d: %08x\n", i,
+ reg_read(ohci,
+ OHCI1394_IrRcvContextControlSet+32*i),
+ i,reg_read(ohci, OHCI1394_IrRcvCommandPtr+32*i),
+ i,reg_read(ohci,
+ OHCI1394_IrRcvContextMatch+32*i));
+ }
+
#if 0
p += sprintf(p,"\n### Phy Register dump ###\n");
phyreg=get_phy_reg(ohci,1);
- p += sprintf(p,"offset: %d val: 0x%02x -> RHB: %d IBR: %d Gap_count: %d\n",
- 1,phyreg,(phyreg&0x80) != 0, (phyreg&0x40) !=0, phyreg&0x3f);
+ p += sprintf(p,"offset: %d val: 0x%02x -> RHB: %d"
+ "IBR: %d Gap_count: %d\n",
+ 1,phyreg,(phyreg&0x80) != 0,
+ (phyreg&0x40) !=0, phyreg&0x3f);
phyreg=get_phy_reg(ohci,2);
nports=phyreg&0x1f;
- p += sprintf(p,"offset: %d val: 0x%02x -> SPD: %d E : %d Ports : %2d\n",
- 2,phyreg,
- (phyreg&0xC0)>>6, (phyreg&0x20) !=0, nports);
+ p += sprintf(p,"offset: %d val: 0x%02x -> SPD: %d"
+ " E : %d Ports : %2d\n",
+ 2,phyreg, (phyreg&0xC0)>>6, (phyreg&0x20) !=0, nports);
for (i=0;i<nports;i++) {
phyreg=get_phy_reg(ohci,3+i);
- p += sprintf(p,"offset: %d val: 0x%02x -> [port %d] TPA: %d TPB: %d | %s %s\n",
+ p += sprintf(p,"offset: %d val: 0x%02x -> [port %d]"
+ " TPA: %d TPB: %d | %s %s\n",
3+i,phyreg,
i, (phyreg&0xC0)>>6, (phyreg&0x30)>>4,
(phyreg&0x08) ? "child" : "parent",
phyreg&0x3f);
#endif
-#if 0
- p += sprintf(p,"AR_resp_prg ctrl: %08x\n",ohci->AR_resp_prg->control);
- p += sprintf(p,"AR_resp_prg status: %08x\n",ohci->AR_resp_prg->status);
-#endif
-
return p - buf;
}
static void remove_card(struct ti_ohci *ohci)
{
if (ohci->registers)
- iounmap(ohci->registers);
+ iounmap(ohci->registers);
- /* Free AR response buffers and programs */
- if (ohci->AR_resp_buf) {
- int i;
- for (i= 0; i < AR_RESP_NUM_DESC; i++) {
- kfree(ohci->AR_resp_buf[i]);
- }
- kfree(ohci->AR_resp_buf);
- }
- if (ohci->AR_resp_prg) {
- int i;
- for (i= 0; i < AR_RESP_NUM_DESC; i++) {
- kfree(ohci->AR_resp_prg[i]);
- }
- kfree(ohci->AR_resp_prg);
- }
- kfree(ohci->AR_resp_spb);
+ /* Free AR dma */
+ free_dma_rcv_ctx(ohci->ar_req_context);
+ free_dma_rcv_ctx(ohci->ar_resp_context);
- /* Free AT request buffer and program */
- if (ohci->AT_req_prg)
- kfree(ohci->AT_req_prg);
+ /* Free AT dma */
+ free_dma_trm_ctx(ohci->at_req_context);
+ free_dma_trm_ctx(ohci->at_resp_context);
- /* Free Iso receive buffers and programs */
- if (ohci->IR_recv_buf) {
- int i;
- for (i= 0; i < IR_NUM_DESC; i++) {
- kfree(ohci->IR_recv_buf[i]);
- }
- kfree(ohci->IR_recv_buf);
- }
- if (ohci->IR_recv_prg) {
- int i;
- for (i= 0; i < IR_NUM_DESC; i++) {
- kfree(ohci->IR_recv_prg[i]);
- }
- kfree(ohci->IR_recv_prg);
- }
- kfree(ohci->IR_spb);
+ /* Free IR dma */
+ free_dma_rcv_ctx(ohci->ir_context);
/* Free self-id buffer */
if (ohci->self_id_buffer)
- kfree(ohci->self_id_buffer);
+ kfree(ohci->self_id_buffer);
/* Free config rom */
if (ohci->csr_config_rom)
- kfree(ohci->csr_config_rom);
+ kfree(ohci->csr_config_rom);
/* Free the IRQ */
free_irq(ohci->dev->irq, ohci);
PRINT_G(KERN_INFO, "looking for Ohci1394 cards");
for (i = 0; supported_chips[i][0] != -1; i++) {
- while ((dev = pci_find_device(supported_chips[i][0],
- supported_chips[i][1], dev))
- != NULL) {
- if (add_card(dev) == 0) {
- success = 1;
+ while ((dev = pci_find_device(supported_chips[i][0],
+ supported_chips[i][1], dev))
+ != NULL) {
+ if (add_card(dev) == 0) {
+ success = 1;
+ }
}
- }
}
if (success == 0) {
{
struct ti_ohci *ohci=host->hostdata;
-#if 0
- PRINT(KERN_INFO, ohci->id, "request csr_rom address: %08X",
- (u32)ohci->csr_config_rom);
-#endif
+ DBGMSG(ohci->id, "request csr_rom address: %08X",
+ (u32)ohci->csr_config_rom);
*ptr = ohci->csr_config_rom;
return sizeof(ohci_csr_rom);
#include "ieee1394_types.h"
+#define OHCI1394_DEBUG 1
+
#define OHCI1394_DRIVER_NAME "ohci1394"
#ifndef PCI_DEVICE_ID_TI_OHCI1394
#define PCI_DEVICE_ID_VIA_OHCI1394 0x3044
#endif
+#ifndef PCI_VENDOR_ID_SONY
+#define PCI_VENDOR_ID_SONY 0x104d
+#endif
+
+#ifndef PCI_DEVICE_ID_SONY_CXD3222
+#define PCI_DEVICE_ID_SONY_CXD3222 0x8039
+#endif
+
#define MAX_OHCI1394_CARDS 4
-#define OHCI1394_MAX_AT_REQ_RETRIES 1
-#define OHCI1394_MAX_AT_RESP_RETRIES 1
-#define OHCI1394_MAX_PHYS_RESP_RETRIES 4
+#define OHCI1394_MAX_AT_REQ_RETRIES 0x2
+#define OHCI1394_MAX_AT_RESP_RETRIES 0x2
+#define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8
+
+#define AR_REQ_NUM_DESC 4 /* number of AR req descriptors */
+#define AR_REQ_BUF_SIZE 4096 /* size of AR req buffers */
+#define AR_REQ_SPLIT_BUF_SIZE 4096 /* split packet buffer */
#define AR_RESP_NUM_DESC 4 /* number of AR resp descriptors */
#define AR_RESP_BUF_SIZE 4096 /* size of AR resp buffers */
-#define AR_RESP_SPLIT_PACKET_BUF_SIZE 256 /* split packet buffer */
-#define AR_RESP_TOTAL_BUF_SIZE (AR_RESP_BUF_SIZE * AR_RESP_NUM_DESC)
-#define AT_REQ_PRG_SIZE 256
+#define AR_RESP_SPLIT_BUF_SIZE 4096 /* split packet buffer */
-#define IR_RECV_BUF_SIZE 4096 /* 4096 bytes/buffer */
-#define IR_SPLIT_PACKET_BUF_SIZE 8192 /* size of buffer for split packets */
-#define IR_NUM_DESC 16 /* number of ISO recv descriptors */
+#define IR_NUM_DESC 16 /* number of IR descriptors */
+#define IR_BUF_SIZE 6480 /* 6480 bytes/buffer */
+#define IR_SPLIT_BUF_SIZE 8192 /* split packet buffer */
+
+#define AT_REQ_NUM_DESC 32 /* number of AT req descriptors */
+#define AT_RESP_NUM_DESC 32 /* number of AT resp descriptors */
struct dma_cmd {
u32 control;
u32 status;
};
+struct at_dma_prg {
+ struct dma_cmd begin;
+ quadlet_t data[4];
+ struct dma_cmd end;
+};
+
+/* DMA receive context */
+struct dma_rcv_ctx {
+ void *ohci;
+ int ctx;
+ unsigned int num_desc;
+ unsigned int buf_size;
+ unsigned int split_buf_size;
+ struct dma_cmd **prg;
+ quadlet_t **buf;
+ unsigned int buf_ind;
+ unsigned int buf_offset;
+ quadlet_t *spb;
+ spinlock_t lock;
+ struct tq_struct task;
+ int ctrlClear;
+ int ctrlSet;
+ int cmdPtr;
+};
+
+/* DMA transmit context */
+struct dma_trm_ctx {
+ void *ohci;
+ int ctx;
+ unsigned int num_desc;
+ struct at_dma_prg *prg;
+ unsigned int prg_ind;
+ unsigned int sent_ind;
+ int free_prgs;
+ quadlet_t *branchAddrPtr;
+ struct hpsb_packet *first;
+ struct hpsb_packet *last;
+ spinlock_t lock;
+ struct tq_struct task;
+ int ctrlClear;
+ int ctrlSet;
+ int cmdPtr;
+};
+
struct ti_ohci {
int id; /* sequential card number */
quadlet_t *self_id_buffer; /* dma buffer for self-id packets */
quadlet_t *csr_config_rom; /* buffer for csr config rom */
- /* asynchronous receive */
- struct dma_cmd **AR_resp_prg;
- quadlet_t **AR_resp_buf;
- unsigned int AR_resp_buf_bh_ind;
- unsigned int AR_resp_buf_bh_offset;
- unsigned int AR_resp_buf_th_ind;
- unsigned int AR_resp_buf_th_offset;
- int AR_resp_bytes_left;
- quadlet_t *AR_resp_spb;
- spinlock_t AR_resp_lock;
-
- /* async receive task */
- struct tq_struct AR_resp_pdl_task;
-
- /* asynchronous transmit */
- struct dma_cmd *AT_req_prg;
-
- /* isochronous receive */
- struct dma_cmd **IR_recv_prg;
- quadlet_t **IR_recv_buf;
- unsigned int IR_buf_used;
- unsigned int IR_buf_last_ind;
- unsigned int IR_buf_next_ind;
- spinlock_t IR_recv_lock;
-
- /* iso recv split packet handling */
- quadlet_t *IR_spb;
- unsigned int IR_sp_bytes_left;
- unsigned int IR_spb_bytes_used;
-
- /* iso receive channel usage */
- spinlock_t IR_channel_lock;
- u64 IR_channel_usage;
+ /* async receive */
+ struct dma_rcv_ctx *ar_resp_context;
+ struct dma_rcv_ctx *ar_req_context;
- /* iso receive task */
- struct tq_struct IR_pdl_task;
+ /* async transmit */
+ struct dma_trm_ctx *at_resp_context;
+ struct dma_trm_ctx *at_req_context;
+
+ /* iso receive */
+ struct dma_rcv_ctx *ir_context;
+ u64 IR_channel_usage;
+ spinlock_t IR_channel_lock;
/* IEEE-1394 part follows */
struct hpsb_host *host;
spinlock_t phy_reg_lock;
- struct hpsb_packet *async_queue;
- spinlock_t async_queue_lock;
-
- int AR_resp_active;
int NumBusResets;
- int TxRdy;
- int NumInterrupts;
};
-
/*
* Register read and write helper functions.
*/
/* bus info block */
0x04040000, /* info/CRC length, CRC */
0x31333934, /* 1394 magic number */
- 0xf064a000, /* misc. settings - FIXME */
- 0x08002856, /* vendor ID, chip ID high */
- 0x0000083E, /* chip ID low */
+ 0xf07da002, /* cyc_clk_acc = 125us, max_rec = 1024 */
+ 0x00000000, /* vendor ID, chip ID high (written from card info) */
+ 0x00000000, /* chip ID low (written from card info) */
/* root directory - FIXME */
0x00090000, /* CRC length, CRC */
0x03080028, /* vendor ID (Texas Instr.) */
struct {
u32 control;
u32 pointer;
- } buffer[13];
-};
+ } buffer[13] __attribute__ ((packed));
+} __attribute__ ((packed));
#include <linux/stddef.h>
#define pcloffs(MEMBER) (offsetof(struct ti_pcl, MEMBER))
inline static u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid)
{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+ return lynx->dev->base_address[1] + pclid * sizeof(struct ti_pcl);
+#else
return lynx->dev->resource[1].start + pclid * sizeof(struct ti_pcl);
+#endif
}
#else /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */
*
* Raw interface to the bus
*
- * Copyright (C) 1999 Andreas E. Bombe
+ * Copyright (C) 1999, 2000 Andreas E. Bombe
*/
#include <linux/kernel.h>
static struct hpsb_highlevel *hl_handle = NULL;
+static atomic_t iso_buffer_size;
+static const int iso_buffer_max = 4 * 1024 * 1024; /* 4 MB */
+
static void queue_complete_cb(struct pending_request *req);
static struct pending_request *__alloc_pending_request(int flags)
{
if (req->ibs) {
if (atomic_dec_and_test(&req->ibs->refcount)) {
+ atomic_sub((req->data[0] >> 16) + 4, &iso_buffer_size);
kfree(req->ibs);
}
} else if (req->free_data) {
struct iso_block_store *ibs = NULL;
LIST_HEAD(reqs);
+ if ((atomic_read(&iso_buffer_size) + length) > iso_buffer_max) {
+ return;
+ }
+
spin_lock_irqsave(&host_info_lock, flags);
hi = find_host_info(host);
+ length, SLAB_ATOMIC);
if (!ibs) break;
+ atomic_add(length, &iso_buffer_size);
atomic_set(&ibs->refcount, 0);
memcpy(ibs->data, data, length);
}
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate ' DM9102 PCI Fast Ethernet Adapter support (EXPERIMENTAL)' CONFIG_DM9102
fi
- tristate ' EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100
+ tristate ' EtherExpressPro/100 support' CONFIG_EEPRO100
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_EEPRO100" = "y" -o "$CONFIG_EEPRO100" = "m" ]; then
+ bool ' Enable Power Management (EXPERIMENTAL)' CONFIG_EEPRO100_PM
+ fi
tristate ' Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390
tristate ' Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210
fi
obj-$(CONFIG_VORTEX) += 3c59x.o
obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
obj-$(CONFIG_PCNET32) += pcnet32.o
-obj-$(CONFIG_EEXPRESS_PRO100) += eepro100.o
+obj-$(CONFIG_EEPRO100) += eepro100.o
obj-$(CONFIG_TLAN) += tlan.o
obj-$(CONFIG_TULIP) += tulip.o
obj-$(CONFIG_EPIC100) += epic100.o
struct net_device *dev = pdev->driver_data;
long ioaddr = dev->base_addr;
- netif_stop_queue (dev);
+ netif_device_detach(dev);
outl(PortPartialReset, ioaddr + SCBPort);
/* XXX call pci_set_power_state ()? */
struct net_device *dev = pdev->driver_data;
struct speedo_private *np = (struct speedo_private *)dev->priv;
+ netif_device_attach(dev);
speedo_resume(dev);
np->rx_mode = -1;
np->flow_ctrl = np->partner = 0;
pci_set_power_state (pdev, sp->acpi_pwr);
+ pci_free_consistent(pdev, TX_RING_SIZE * sizeof(struct TxFD)
+ + sizeof(struct speedo_stats),
+ sp->tx_ring, sp->tx_ring_dma);
+
kfree (dev);
}
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/unaligned.h>
-
+#include <asm/delay.h>
/* A few user-configurable values. */
enum tbl_flag {
HAS_MII=1, HAS_MEDIA_TABLE=2, CSR12_IN_SROM=4, ALWAYS_CHECK_MII=8,
- HAS_PWRDWN=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
+ HAS_ACPI=0x10, MC_HASH_ONLY=0x20, /* Hash-only multicast filter. */
HAS_PNICNWAY=0x80, HAS_NWAY143=0x40, /* Uses internal NWay xcvr. */
HAS_8023X=0x100,
};
{ "Digital DS21140 Tulip", 128, 0x0001ebef,
HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer },
{ "Digital DS21143 Tulip", 128, 0x0801fbff,
- HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143,
+ HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143,
t21142_timer },
{ "Lite-On 82c168 PNIC", 256, 0x0001ebef,
HAS_MII | HAS_PNICNWAY, pnic_timer },
{ "Compex 9881 PMAC", 128, 0x0001ebef,
HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, mxic_timer },
{ "Intel DS21145 Tulip", 128, 0x0801fbff,
- HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143,
+ HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_NWAY143,
t21142_timer },
{ "Xircom tulip work-alike", 128, 0x0801fbff,
- HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_PWRDWN | HAS_NWAY143,
+ HAS_MII | HAS_MEDIA_TABLE | ALWAYS_CHECK_MII | HAS_ACPI | HAS_NWAY143,
t21142_timer },
{0},
};
COMET,
COMPEX9881,
I21145,
- XIRCLONE,
+ X3201_3,
};
{ 0x1317, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
{ 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
{ 0x8086, 0x0039, PCI_ANY_ID, PCI_ANY_ID, 0, 0, I21145 },
- { 0x115d, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, XIRCLONE },
+ { 0x115d, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, X3201_3 },
{0},
};
MODULE_DEVICE_TABLE(pci,tulip_pci_tbl);
int ttimer;
int susp_rx;
unsigned long nir;
+ unsigned long base_addr;
int pad0, pad1; /* Used for 8-byte alignment */
};
static int mdio_read(struct net_device *dev, int phy_id, int location);
static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
static void select_media(struct net_device *dev, int startup);
-static int tulip_open(struct net_device *dev);
/* Chip-specific media selection (timer functions prototyped above). */
static void t21142_lnk_change(struct net_device *dev, int csr5);
static void t21142_start_nway(struct net_device *dev);
static int tulip_refill_rx(struct net_device *dev);
static int tulip_rx(struct net_device *dev);
static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
+static int tulip_open(struct net_device *dev);
static int tulip_close(struct net_device *dev);
+static void tulip_up(struct net_device *dev);
+static void tulip_down(struct net_device *dev);
static struct net_device_stats *tulip_get_stats(struct net_device *dev);
static int private_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static void set_rx_mode(struct net_device *dev);
#define EE_READ_CMD (6)
/* Note: this routine returns extra data bits for size detection. */
-static int read_eeprom(long ioaddr, int location, int addr_len)
+static int __devinit read_eeprom(long ioaddr, int location, int addr_len)
{
int i;
unsigned retval = 0;
return;
}
-\f
-static int
-tulip_open(struct net_device *dev)
+
+/* The Xircom cards are picky about when certain bits in CSR6 can be
+ manipulated. Keith Owens <kaos@ocs.com.au>. */
+
+static void outl_CSR6 (struct tulip_private *tp, u32 newcsr6)
+{
+ long ioaddr = tp->base_addr;
+ const int strict_bits = 0x0060e202;
+ int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;
+ long flags;
+
+ /* really a hw lock */
+ spin_lock_irqsave (&tp->tx_lock, flags);
+
+ if (tp->chip_id != X3201_3)
+ goto out_write;
+
+ newcsr6 &= 0x726cfeca; /* mask out the reserved CSR6 bits that always */
+ /* read 0 on the Xircom cards */
+ newcsr6 |= 0x320c0000; /* or in the reserved bits that always read 1 */
+ currcsr6 = inl (ioaddr + CSR6);
+ if (((newcsr6 & strict_bits) == (currcsr6 & strict_bits)) ||
+ ((currcsr6 & ~0x2002) == 0))
+ goto out_write;
+
+ /* make sure the transmitter and receiver are stopped first */
+ currcsr6 &= ~0x2002;
+ while (1) {
+ csr5 = inl (ioaddr + CSR5);
+ if (csr5 == 0xffffffff)
+ break; /* cannot read csr5, card removed? */
+ csr5_22_20 = csr5 & 0x700000;
+ csr5_19_17 = csr5 & 0x0e0000;
+ if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) &&
+ (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))
+ break; /* both are stopped or suspended */
+ if (!--attempts) {
+ printk (KERN_INFO "tulip.c: outl_CSR6 too many attempts,"
+ "csr5=0x%08x\n", csr5);
+ goto out_write;
+ }
+ outl (currcsr6, ioaddr + CSR6);
+ udelay (1);
+ }
+
+out_write:
+ /* now it is safe to change csr6 */
+ outl (newcsr6, ioaddr + CSR6);
+
+ spin_unlock_irqrestore (&tp->lock, flags);
+}
+
+
+static void tulip_up(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
int i;
/* Wake the chip from sleep/snooze mode. */
- if (tp->flags & HAS_PWRDWN)
+ if (tp->flags & HAS_ACPI)
pci_write_config_dword(tp->pdev, 0x40, 0);
/* On some chip revs we must set the MII/SYM port before the reset!? */
if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii))
- outl(0x00040000, ioaddr + CSR6);
+ outl_CSR6 (tp, 0x00040000);
/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
outl(0x00000001, ioaddr + CSR0);
- if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev))
- return -EAGAIN;
-
/* Deassert reset.
Wait the specified 50 PCI cycles after a reset by initializing
Tx and Rx queues and the address filter list. */
if (tulip_debug > 1)
printk(KERN_DEBUG "%s: tulip_open() irq %d.\n", dev->name, dev->irq);
- MOD_INC_USE_COUNT;
-
- spin_lock_init(&tp->tx_lock);
- tulip_init_ring(dev);
-
-#if 0
- if (tp->chip_id == PNIC2) {
- u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
- u32 addr_high = cpu_to_le16(get_unaligned((u16 *)(dev->dev_addr+4)));
- addr_high = (dev->dev_addr[4]<<8) + (dev->dev_addr[5]<<0);
- outl((dev->dev_addr[0]<<8) + dev->dev_addr[1] +
- (dev->dev_addr[2]<<24) + (dev->dev_addr[3]<<16),
- ioaddr + 0xB0);
- outl(addr_high + (addr_high<<16), ioaddr + 0xB8);
- }
-#endif
if (tp->flags & MC_HASH_ONLY) {
u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr));
u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4)));
printk(KERN_INFO "%s: Using MII transceiver %d, status "
"%4.4x.\n",
dev->name, tp->phys[0], mdio_read(dev, tp->phys[0], 1));
- outl(0x82020000, ioaddr + CSR6);
+ outl_CSR6(tp, 0x82020000);
tp->csr6 = 0x820E0000;
dev->if_port = 11;
outl(0x0000, ioaddr + CSR13);
select_media(dev, 1);
/* Start the chip's Tx to process setup frame. */
- outl(tp->csr6, ioaddr + CSR6);
- outl(tp->csr6 | 0x2000, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6);
+ outl_CSR6(tp, tp->csr6 | 0x2000);
/* Enable interrupts by setting the interrupt mask. */
outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5);
outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
outl(0, ioaddr + CSR2); /* Rx poll demand */
if (tulip_debug > 2) {
tp->timer.function = tulip_tbl[tp->chip_id].media_timer;
add_timer(&tp->timer);
- netif_start_queue(dev);
+ netif_device_attach(dev);
+}
+
+static int
+tulip_open(struct net_device *dev)
+{
+ MOD_INC_USE_COUNT;
+
+ if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) {
+ MOD_DEC_USE_COUNT;
+ return -EBUSY;
+ }
+
+ tulip_init_ring (dev);
+
+ tulip_up (dev);
+
return 0;
}
+
/* Set up the transceiver control registers for the selected media type. */
static void select_media(struct net_device *dev, int startup)
{
*/
static int check_duplex(struct net_device *dev)
{
- long ioaddr = dev->base_addr;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int mii_reg1, mii_reg5, negotiated, duplex;
tp->csr6 &= ~0x00400000;
if (tp->full_duplex) tp->csr6 |= 0x0200;
else tp->csr6 &= ~0x0200;
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x0002);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
if (tulip_debug > 0)
printk(KERN_INFO "%s: Setting %s-duplex based on MII"
"#%d link partner capability of %4.4x.\n",
medianame[tp->mtable->mleaf[tp->cur_index].media]);
select_media(dev, 0);
/* Restart the transmit process. */
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x0002);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
next_tick = (24*HZ)/10;
break;
}
add_timer(&tp->timer);
}
+
/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list
of available transceivers. */
static void t21142_timer(unsigned long data)
tp->csr6 &= 0x00D5;
tp->csr6 |= new_csr6;
outl(0x0301, ioaddr + CSR12);
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x0002);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
}
next_tick = 3*HZ;
}
add_timer(&tp->timer);
}
+
static void t21142_start_nway(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
outl(0x0001, ioaddr + CSR13);
outl(csr14, ioaddr + CSR14);
tp->csr6 = 0x82420000 | (tp->to_advertise & 0x0040 ? 0x0200 : 0);
- outl(tp->csr6, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6);
if (tp->mtable && tp->mtable->csr15dir) {
outl(tp->mtable->csr15dir, ioaddr + CSR15);
outl(tp->mtable->csr15val, ioaddr + CSR15);
outl(0x1301, ioaddr + CSR12); /* Trigger NWAY. */
}
+
static void t21142_lnk_change(struct net_device *dev, int csr5)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
outl(1, ioaddr + CSR13);
}
#if 0 /* Restart shouldn't be needed. */
- outl(tp->csr6 | 0x0000, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x0000);
if (debug > 2)
printk(KERN_DEBUG "%s: Restarting Tx and Rx, CSR5 is %8.8x.\n",
dev->name, inl(ioaddr + CSR5));
#endif
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
if (debug > 2)
printk(KERN_DEBUG "%s: Setting CSR6 %8.8x/%x CSR12 %8.8x.\n",
dev->name, tp->csr6, inl(ioaddr + CSR6),
tp->csr6 = 0x83860000;
outl(0x0003FF7F, ioaddr + CSR14);
outl(0x0301, ioaddr + CSR12);
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x0002);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
}
}
+
static void mxic_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
}
}
+
static void pnic_do_nway(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
dev->name, phy_reg, medianame[dev->if_port]);
if (tp->csr6 != new_csr6) {
tp->csr6 = new_csr6;
- outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ /* Restart Tx */
+ outl_CSR6(tp, tp->csr6 | 0x0002);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
dev->trans_start = jiffies;
}
}
}
+
+
static void pnic_lnk_change(struct net_device *dev, int csr5)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
outl((inl(ioaddr + CSR7) & ~TPLnkFail) | TPLnkPass, ioaddr + CSR7);
if (! tp->nwayset || jiffies - dev->trans_start > 1*HZ) {
tp->csr6 = 0x00420000 | (tp->csr6 & 0x0000fdff);
- outl(tp->csr6, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6);
outl(0x30, ioaddr + CSR12);
outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
dev->trans_start = jiffies;
outl((inl(ioaddr + CSR7) & ~TPLnkPass) | TPLnkFail, ioaddr + CSR7);
}
}
+
+
static void pnic_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
}
if (tp->csr6 != new_csr6) {
tp->csr6 = new_csr6;
- outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ /* Restart Tx */
+ outl_CSR6(tp, tp->csr6 | 0x0002);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
dev->trans_start = jiffies;
if (tulip_debug > 1)
printk(KERN_INFO "%s: Changing PNIC configuration to %s "
#endif
/* Stop and restart the chip's Tx processes . */
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x0002);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
/* Trigger an immediate transmit demand. */
outl(0, ioaddr + CSR1);
printk(KERN_WARNING "%s: The transmitter stopped."
" CSR5 is %x, CSR6 %x, new CSR6 %x.\n",
dev->name, csr5, inl(ioaddr + CSR6), tp->csr6);
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x0002);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
}
spin_unlock(&tp->tx_lock);
}
else
tp->csr6 |= 0x00200000; /* Store-n-forward. */
/* Restart the transmit process. */
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x0002);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
outl(0, ioaddr + CSR1);
}
if (csr5 & RxDied) { /* Missed a Rx frame. */
tp->stats.rx_errors++;
tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, tp->csr6 | 0x2002);
}
if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000)) {
if (tp->link_change)
return received;
}
-static int
-tulip_close(struct net_device *dev)
+
+static void tulip_down (struct net_device *dev)
{
long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int i;
+ struct tulip_private *tp = (struct tulip_private *) dev->priv;
- netif_stop_queue(dev);
+ netif_device_detach (dev);
- if (tulip_debug > 1)
- printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
- dev->name, inl(ioaddr + CSR5));
+ del_timer (&tp->timer);
/* Disable interrupts by clearing the interrupt mask. */
- outl(0x00000000, ioaddr + CSR7);
+ outl (0x00000000, ioaddr + CSR7);
+
/* Stop the Tx and Rx processes. */
- outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+ outl_CSR6 (tp, inl (ioaddr + CSR6) & ~0x2002);
+
/* 21040 -- Leave the card in 10baseT state. */
if (tp->chip_id == DC21040)
- outl(0x00000004, ioaddr + CSR13);
+ outl (0x00000004, ioaddr + CSR13);
- if (inl(ioaddr + CSR6) != 0xffffffff)
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+ if (inl (ioaddr + CSR6) != 0xffffffff)
+ tp->stats.rx_missed_errors += inl (ioaddr + CSR8) & 0xffff;
- del_timer(&tp->timer);
+ dev->if_port = tp->saved_if_port;
- free_irq(dev->irq, dev);
+ /* Leave the driver in snooze, not sleep, mode. */
+ if (tp->flags & HAS_ACPI)
+ pci_write_config_dword (tp->pdev, 0x40, 0x40000000);
+}
+
+
+static int tulip_close (struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct tulip_private *tp = (struct tulip_private *) dev->priv;
+ int i;
- dev->if_port = tp->saved_if_port;
+ tulip_down (dev);
+
+ if (tulip_debug > 1)
+ printk (KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inl (ioaddr + CSR5));
+
+ free_irq (dev->irq, dev);
/* Free all the skbuffs in the Rx queue. */
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb = tp->rx_skbuff[i];
tp->rx_skbuff[i] = 0;
- tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */
+ tp->rx_ring[i].status = 0; /* Not owned by Tulip chip. */
tp->rx_ring[i].length = 0;
- tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
+ tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
if (skb) {
- dev_kfree_skb(skb);
+ dev_kfree_skb (skb);
}
}
for (i = 0; i < TX_RING_SIZE; i++) {
if (tp->tx_skbuff[i])
- dev_kfree_skb(tp->tx_skbuff[i]);
+ dev_kfree_skb (tp->tx_skbuff[i]);
tp->tx_skbuff[i] = 0;
}
- /* Leave the driver in snooze, not sleep, mode. */
- if (tp->flags & HAS_PWRDWN)
- pci_write_config_dword(tp->pdev, 0x40, 0x40000000);
-
MOD_DEC_USE_COUNT;
return 0;
}
-static struct enet_statistics *
-tulip_get_stats(struct net_device *dev)
+static struct enet_statistics *tulip_get_stats(struct net_device *dev)
{
struct tulip_private *tp = (struct tulip_private *)dev->priv;
long ioaddr = dev->base_addr;
outl(0, ioaddr + CSR1);
}
}
- outl(csr6 | 0x0000, ioaddr + CSR6);
+ outl_CSR6(tp, csr6 | 0x0000);
}
board_idx++;
if (tulip_debug > 0 && did_version++ == 0)
- printk(KERN_INFO "%s", version);
+ printk (KERN_INFO "%s", version);
if( pdev->subsystem_vendor == 0x1376 ){
- printk(KERN_ERR "tulip: skipping LMC card.\n");
+ printk (KERN_ERR PFX "skipping LMC card.\n");
return -ENODEV;
}
/* Make certain the data structures are quadword aligned. */
dev = init_etherdev (NULL, sizeof (*tp));
- if (!dev)
+ if (!dev) {
+ printk (KERN_ERR PFX "unable to allocate ether device, aborting\n");
return -ENOMEM;
+ }
/* We do a request_region() only to register /proc/ioports info. */
/* Note that proper size is tulip_tbl[chip_idx].chip_name, but... */
- if (!request_region (ioaddr, tulip_tbl[chip_idx].io_size, dev->name))
+ if (!request_region (ioaddr, tulip_tbl[chip_idx].io_size, dev->name)) {
+ printk (KERN_ERR PFX "unable to allocate ether device, aborting\n");
goto err_out_free_netdev;
+ }
+
+ if (pci_enable_device(pdev)) {
+ printk (KERN_ERR PFX "cannot enable PCI device (id %04x:%04x, bus %d, devfn %d), aborting\n",
+ pdev->vendor, pdev->device,
+ pdev->bus->number, pdev->devfn);
+ goto err_out_free_netdev;
+ }
- pci_enable_device(pdev);
pci_set_master(pdev);
tp = dev->priv;
pci_read_config_byte (pdev, PCI_REVISION_ID, &chip_rev);
- /* Bring the 21041/21143 out of sleep mode.
- Caution: Snooze mode does not work with some boards! */
- if (tulip_tbl[chip_idx].flags & HAS_PWRDWN)
- pci_write_config_dword(pdev, 0x40, 0x00000000);
-
printk(KERN_INFO "%s: %s rev %d at %#3lx,",
dev->name, tulip_tbl[chip_idx].chip_name, chip_rev, ioaddr);
/* Stop the chip's Tx and Rx processes. */
- outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+ outl_CSR6(tp, inl(ioaddr + CSR6) & ~0x2002);
/* Clear the missed-packet counter. */
(volatile int)inl(ioaddr + CSR8);
tp->flags = tulip_tbl[chip_idx].flags;
tp->csr0 = csr0;
tp->pdev = pdev;
+ tp->base_addr = dev->base_addr;
/* BugFixes: The 21143-TD hangs with PCI Write-and-Invalidate cycles.
And the ASIX must have a burst limit or horrible things happen. */
#ifdef TULIP_NO_MEDIA_SWITCH
tp->medialock = 1;
#endif
+ tp->tx_lock = SPIN_LOCK_UNLOCKED;
/* The lower four bits are the media type. */
if (board_idx >= 0 && board_idx < MAX_UNITS) {
outl(0x00000000, ioaddr + CSR13);
outl(0xFFFFFFFF, ioaddr + CSR14);
outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
- outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6);
+ outl_CSR6(tp, inl(ioaddr + CSR6) | 0x0200);
outl(0x0000EF05, ioaddr + CSR13);
break;
case DC21040:
case DC21142:
case PNIC2:
if (tp->mii_cnt || media_cap[dev->if_port] & MediaIsMII) {
- outl(0x82020000, ioaddr + CSR6);
+ outl_CSR6(tp, 0x82020000);
outl(0x0000, ioaddr + CSR13);
outl(0x0000, ioaddr + CSR14);
- outl(0x820E0000, ioaddr + CSR6);
+ outl_CSR6(tp, 0x820E0000);
} else
t21142_start_nway(dev);
break;
if ( ! tp->mii_cnt) {
tp->nway = 1;
tp->nwayset = 0;
- outl(0x00420000, ioaddr + CSR6);
+ outl_CSR6(tp, 0x00420000);
outl(0x30, ioaddr + CSR12);
- outl(0x0001F078, ioaddr + 0xB8);
- outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */
+ outl_CSR6(tp, 0x0001F078);
+ outl_CSR6(tp, 0x0201F078); /* Turn on autonegotiation. */
}
break;
case MX98713: case COMPEX9881:
- outl(0x00000000, ioaddr + CSR6);
+ outl_CSR6(tp, 0x00000000);
outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
outl(0x00000001, ioaddr + CSR13);
break;
case MX98715: case MX98725:
- outl(0x01a80000, ioaddr + CSR6);
+ outl_CSR6(tp, 0x01a80000);
outl(0xFFFFFFFF, ioaddr + CSR14);
outl(0x00001000, ioaddr + CSR12);
break;
break;
}
- if (tulip_tbl[chip_idx].flags & HAS_PWRDWN)
+ /* put the chip in snooze mode until opened */
+ if (tulip_tbl[chip_idx].flags & HAS_ACPI)
pci_write_config_dword(pdev, 0x40, 0x40000000);
return 0;
{
struct net_device *dev = pdev->driver_data;
- if (dev) {
- long ioaddr = dev->base_addr;
- struct tulip_private *tp = (struct tulip_private *)dev->priv;
- int csr6 = inl(ioaddr + CSR6);
- /* Disable interrupts, stop the chip, gather stats. */
- if (csr6 != 0xffffffff) {
- outl(0x00000000, ioaddr + CSR7);
- outl(csr6 & ~0x2002, ioaddr + CSR6);
- tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
- }
- tulip_close(dev);
- /* Put the 21143 into sleep mode. */
- pci_write_config_dword(pdev, 0x40,0x80000000);
- }
+ if (dev && netif_device_present (dev))
+ tulip_down (dev);
}
{
struct net_device *dev = pdev->driver_data;
- if (dev) {
- pci_write_config_dword(pdev, 0x40, 0x0000);
- tulip_open(dev);
- }
+ if (dev && !netif_device_present (dev))
+ tulip_up (dev);
}
static int __init tulip_init (void)
{
- if (pci_register_driver (&tulip_driver) > 0)
- return 0;
-
- pci_unregister_driver (&tulip_driver);
- return -ENODEV;
+ return pci_module_init (&tulip_driver);
}
#else
#define CB_OPT ""
#endif
-#if defined(CONFIG_APM) || defined(CONFIG_ACPI)
+#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) || defined(CONFIG_ACPI)
#define APM_OPT " [apm]"
#else
#define APM_OPT ""
#endif
#if !defined(CONFIG_CARDBUS) && !defined(CONFIG_PCI) && \
- !defined(CONFIG_APM) && !defined(CONFIG_ACPI)
+ !defined(CONFIG_APM) && !defined(CONFIG_APM_MODULE) && \
+ !defined(CONFIG_ACPI)
#define OPTIONS " none"
#else
#define OPTIONS PCI_OPT CB_OPT APM_OPT
static int io_speed = 0; /* ns */
/* Optional features */
-#if defined(CONFIG_APM) || defined(CONFIG_ACPI)
+#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) || defined(CONFIG_ACPI)
static int do_apm = 1;
MODULE_PARM(do_apm, "i");
#else
Copyright 1992 - 2000 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Fri Feb 11 19:43:57 2000 by makisara@kai.makisara.local
+ Last modified: Sat Feb 19 17:22:34 2000 by makisara@kai.makisara.local
Some small formal changes - aeb, 950809
Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
\f
-
/* Convert the result to success code */
-static int st_chk_result(Scsi_Cmnd * SCpnt)
+static int st_chk_result(Scsi_Request * SRpnt)
{
- int dev = TAPE_NR(SCpnt->request.rq_dev);
- int result = SCpnt->result;
- unsigned char *sense = SCpnt->sense_buffer, scode;
+ int dev = TAPE_NR(SRpnt->sr_request.rq_dev);
+ int result = SRpnt->sr_result;
+ unsigned char *sense = SRpnt->sr_sense_buffer, scode;
DEB(const char *stp;)
- if (!result /* && SCpnt->sense_buffer[0] == 0 */ )
+ if (!result)
return 0;
if (driver_byte(result) & DRIVER_SENSE)
if (debugging) {
printk(ST_DEB_MSG "st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n",
dev, result,
- SCpnt->data_cmnd[0], SCpnt->data_cmnd[1], SCpnt->data_cmnd[2],
- SCpnt->data_cmnd[3], SCpnt->data_cmnd[4], SCpnt->data_cmnd[5],
- SCpnt->request_bufflen);
+ SRpnt->sr_cmnd[0], SRpnt->sr_cmnd[1], SRpnt->sr_cmnd[2],
+ SRpnt->sr_cmnd[3], SRpnt->sr_cmnd[4], SRpnt->sr_cmnd[5],
+ SRpnt->sr_bufflen);
if (driver_byte(result) & DRIVER_SENSE)
- print_sense("st", SCpnt);
+ print_req_sense("st", SRpnt);
} else ) /* end DEB */
if (!(driver_byte(result) & DRIVER_SENSE) ||
((sense[0] & 0x70) == 0x70 &&
/* scode != UNIT_ATTENTION && */
scode != BLANK_CHECK &&
scode != VOLUME_OVERFLOW &&
- SCpnt->data_cmnd[0] != MODE_SENSE &&
- SCpnt->data_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
+ SRpnt->sr_cmnd[0] != MODE_SENSE &&
+ SRpnt->sr_cmnd[0] != TEST_UNIT_READY)) { /* Abnormal conditions for tape */
if (driver_byte(result) & DRIVER_SENSE) {
printk(KERN_WARNING "st%d: Error with sense data: ", dev);
- print_sense("st", SCpnt);
+ print_req_sense("st", SRpnt);
} else
printk(KERN_WARNING
"st%d: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
if ((sense[0] & 0x70) == 0x70 &&
scode == RECOVERED_ERROR
#if ST_RECOVERED_WRITE_FATAL
- && SCpnt->data_cmnd[0] != WRITE_6
- && SCpnt->data_cmnd[0] != WRITE_FILEMARKS
+ && SRpnt->sr_cmnd[0] != WRITE_6
+ && SRpnt->sr_cmnd[0] != WRITE_FILEMARKS
#endif
) {
scsi_tapes[dev].recover_count++;
DEB(
if (debugging) {
- if (SCpnt->data_cmnd[0] == READ_6)
+ if (SRpnt->sr_cmnd[0] == READ_6)
stp = "read";
- else if (SCpnt->data_cmnd[0] == WRITE_6)
+ else if (SRpnt->sr_cmnd[0] == WRITE_6)
stp = "write";
else
stp = "ioctl";
remainder = 0;
if ((SCpnt->sense_buffer[2] & 0x0f) == VOLUME_OVERFLOW ||
remainder > 0)
- (STp->buffer)->last_result = SCpnt->result; /* Error */
+ (STp->buffer)->midlevel_result = SCpnt->result; /* Error */
else
- (STp->buffer)->last_result = INT_MAX; /* OK */
+ (STp->buffer)->midlevel_result = INT_MAX; /* OK */
} else
- (STp->buffer)->last_result = SCpnt->result;
+ (STp->buffer)->midlevel_result = SCpnt->result;
SCpnt->request.rq_status = RQ_SCSI_DONE;
- (STp->buffer)->last_SCpnt = SCpnt;
+ (STp->buffer)->last_SRpnt = SCpnt->sc_request;
DEB( STp->write_pending = 0; )
up(SCpnt->request.sem);
/* Do the scsi command. Waits until command performed if do_wait is true.
Otherwise write_behind_check() is used to check that the command
has finished. */
-static Scsi_Cmnd *
- st_do_scsi(Scsi_Cmnd * SCpnt, Scsi_Tape * STp, unsigned char *cmd, int bytes,
- int timeout, int retries, int do_wait)
+static Scsi_Request *
+ st_do_scsi(Scsi_Request * SRpnt, Scsi_Tape * STp, unsigned char *cmd, int bytes,
+ int direction, int timeout, int retries, int do_wait)
{
unsigned char *bp;
- if (SCpnt == NULL)
- SCpnt = scsi_allocate_device(STp->device, 1, TRUE);
- if (SCpnt == NULL) {
+ if (SRpnt == NULL)
+ SRpnt = scsi_allocate_request(STp->device);
+ if (SRpnt == NULL) {
DEBC( printk(KERN_ERR "st%d: Can't get SCSI request.\n",
TAPE_NR(STp->devt)); );
if (signal_pending(current))
- (STp->buffer)->last_result_fatal = (-EINTR);
+ (STp->buffer)->syscall_result = (-EINTR);
else
- (STp->buffer)->last_result_fatal = (-EBUSY);
+ (STp->buffer)->syscall_result = (-EBUSY);
return NULL;
}
- cmd[1] |= (SCpnt->lun << 5) & 0xe0;
+ cmd[1] |= (SRpnt->sr_device->lun << 5) & 0xe0;
init_MUTEX_LOCKED(&STp->sem);
- SCpnt->use_sg = (bytes > (STp->buffer)->sg[0].length) ?
+ SRpnt->sr_use_sg = (bytes > (STp->buffer)->sg[0].length) ?
(STp->buffer)->use_sg : 0;
- if (SCpnt->use_sg) {
+ if (SRpnt->sr_use_sg) {
bp = (char *) &((STp->buffer)->sg[0]);
- if ((STp->buffer)->sg_segs < SCpnt->use_sg)
- SCpnt->use_sg = (STp->buffer)->sg_segs;
+ if ((STp->buffer)->sg_segs < SRpnt->sr_use_sg)
+ SRpnt->sr_use_sg = (STp->buffer)->sg_segs;
} else
bp = (STp->buffer)->b_data;
- SCpnt->cmd_len = 0;
- SCpnt->request.sem = &(STp->sem);
- SCpnt->request.rq_status = RQ_SCSI_BUSY;
- SCpnt->request.rq_dev = STp->devt;
+ SRpnt->sr_data_direction = direction;
+ SRpnt->sr_cmd_len = 0;
+ SRpnt->sr_request.sem = &(STp->sem);
+ SRpnt->sr_request.rq_status = RQ_SCSI_BUSY;
+ SRpnt->sr_request.rq_dev = STp->devt;
- scsi_do_cmd(SCpnt, (void *) cmd, bp, bytes,
+ scsi_do_req(SRpnt, (void *) cmd, bp, bytes,
st_sleep_done, timeout, retries);
if (do_wait) {
- down(SCpnt->request.sem);
- (STp->buffer)->last_result_fatal = st_chk_result(SCpnt);
+ down(SRpnt->sr_request.sem);
+ SRpnt->sr_request.sem = NULL;
+ (STp->buffer)->syscall_result = st_chk_result(SRpnt);
}
- return SCpnt;
+ return SRpnt;
}
) /* end DEB */
down(&(STp->sem));
+ (STp->buffer)->last_SRpnt->sr_request.sem = NULL;
- (STp->buffer)->last_result_fatal = st_chk_result((STp->buffer)->last_SCpnt);
- scsi_release_command((STp->buffer)->last_SCpnt);
+ (STp->buffer)->syscall_result = st_chk_result((STp->buffer)->last_SRpnt);
+ scsi_release_request((STp->buffer)->last_SRpnt);
if (STbuffer->writing < STbuffer->buffer_bytes)
#if 0
it messes up the block number). */
static int cross_eof(Scsi_Tape * STp, int forward)
{
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
unsigned char cmd[MAX_COMMAND_SIZE];
cmd[0] = SPACE;
DEBC(printk(ST_DEB_MSG "st%d: Stepping over filemark %s.\n",
TAPE_NR(STp->devt), forward ? "forward" : "backward"));
- SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout, MAX_RETRIES, TRUE);
- if (!SCpnt)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE,
+ STp->timeout, MAX_RETRIES, TRUE);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
- if ((STp->buffer)->last_result != 0)
+ if ((STp->buffer)->midlevel_result != 0)
printk(KERN_ERR "st%d: Stepping over filemark %s failed.\n",
TAPE_NR(STp->devt), forward ? "forward" : "backward");
- return (STp->buffer)->last_result_fatal;
+ return (STp->buffer)->syscall_result;
}
int offset, transfer, blks;
int result;
unsigned char cmd[MAX_COMMAND_SIZE];
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
ST_partstat *STps;
if ((STp->buffer)->writing) {
write_behind_check(STp);
- if ((STp->buffer)->last_result_fatal) {
+ if ((STp->buffer)->syscall_result) {
DEBC(printk(ST_DEB_MSG
"st%d: Async write error (flush) %x.\n",
- TAPE_NR(STp->devt), (STp->buffer)->last_result))
- if ((STp->buffer)->last_result == INT_MAX)
+ TAPE_NR(STp->devt), (STp->buffer)->midlevel_result))
+ if ((STp->buffer)->midlevel_result == INT_MAX)
return (-ENOSPC);
return (-EIO);
}
cmd[3] = blks >> 8;
cmd[4] = blks;
- SCpnt = st_do_scsi(NULL, STp, cmd, transfer, STp->timeout,
- MAX_WRITE_RETRIES, TRUE);
- if (!SCpnt)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(NULL, STp, cmd, transfer, SCSI_DATA_WRITE,
+ STp->timeout, MAX_WRITE_RETRIES, TRUE);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
STps = &(STp->ps[STp->partition]);
- if ((STp->buffer)->last_result_fatal != 0) {
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x40) &&
- (SCpnt->sense_buffer[2] & 0x0f) == NO_SENSE) {
+ if ((STp->buffer)->syscall_result != 0) {
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x40) &&
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == NO_SENSE) {
STp->dirty = 0;
(STp->buffer)->buffer_bytes = 0;
result = (-ENOSPC);
STp->dirty = 0;
(STp->buffer)->buffer_bytes = 0;
}
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
}
return result;
}
int i, need_dma_buffer, new_session = FALSE;
int retval;
unsigned char cmd[MAX_COMMAND_SIZE];
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Tape *STp;
ST_mode *STm;
ST_partstat *STps;
STp->buffer = st_buffers[i];
(STp->buffer)->in_use = 1;
(STp->buffer)->writing = 0;
- (STp->buffer)->last_result_fatal = 0;
+ (STp->buffer)->syscall_result = 0;
(STp->buffer)->use_sg = STp->device->host->sg_tablesize;
/* Compute the usable buffer size for this SCSI adapter */
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = TEST_UNIT_READY;
- SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->long_timeout, MAX_READY_RETRIES,
- TRUE);
- if (!SCpnt) {
- retval = (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE, STp->long_timeout,
+ MAX_READY_RETRIES, TRUE);
+ if (!SRpnt) {
+ retval = (STp->buffer)->syscall_result;
goto err_out;
}
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
/* Flush the queued UNIT ATTENTION sense data */
for (i=0; i < 10; i++) {
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = TEST_UNIT_READY;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, STp->long_timeout,
- MAX_READY_RETRIES, TRUE);
- if ((SCpnt->sense_buffer[0] & 0x70) != 0x70 ||
- (SCpnt->sense_buffer[2] & 0x0f) != UNIT_ATTENTION)
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, SCSI_DATA_NONE,
+ STp->long_timeout, MAX_READY_RETRIES, TRUE);
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 ||
+ (SRpnt->sr_sense_buffer[2] & 0x0f) != UNIT_ATTENTION)
break;
}
new_session = TRUE;
}
- if ((STp->buffer)->last_result_fatal != 0) {
+ if ((STp->buffer)->syscall_result != 0) {
if ((STp->device)->scsi_level >= SCSI_2 &&
- (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x0f) == NOT_READY &&
- SCpnt->sense_buffer[12] == 0x3a) { /* Check ASC */
+ (SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == NOT_READY &&
+ SRpnt->sr_sense_buffer[12] == 0x3a) { /* Check ASC */
STp->ready = ST_NO_TAPE;
} else
STp->ready = ST_NOT_READY;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
STp->density = 0; /* Clear the erroneous "residue" */
STp->write_prot = 0;
STp->block_size = 0;
memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
cmd[0] = READ_BLOCK_LIMITS;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 6, STp->timeout, MAX_READY_RETRIES,
- TRUE);
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, SCSI_DATA_READ, STp->timeout,
+ MAX_READY_RETRIES, TRUE);
- if (!SCpnt->result && !SCpnt->sense_buffer[0]) {
+ if (!SRpnt->sr_result && !SRpnt->sr_sense_buffer[0]) {
STp->max_block = ((STp->buffer)->b_data[1] << 16) |
((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3];
STp->min_block = ((STp->buffer)->b_data[4] << 8) |
cmd[0] = MODE_SENSE;
cmd[4] = 12;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 12, STp->timeout, MAX_READY_RETRIES, TRUE);
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, SCSI_DATA_READ, STp->timeout,
+ MAX_READY_RETRIES, TRUE);
- if ((STp->buffer)->last_result_fatal != 0) {
+ if ((STp->buffer)->syscall_result != 0) {
DEBC(printk(ST_DEB_MSG "st%d: No Mode Sense.\n", dev));
STp->block_size = ST_DEFAULT_BLOCK; /* Educated guess (?) */
- (STp->buffer)->last_result_fatal = 0; /* Prevent error propagation */
+ (STp->buffer)->syscall_result = 0; /* Prevent error propagation */
STp->drv_write_prot = 0;
} else {
DEBC(printk(ST_DEB_MSG
!enlarge_buffer(STp->buffer, STp->block_size, STp->restr_dma)) {
printk(KERN_NOTICE "st%d: Blocksize %d too large for buffer.\n",
dev, STp->block_size);
- scsi_release_command(SCpnt);
+ scsi_release_request(SRpnt);
retval = (-EIO);
goto err_out;
}
STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
}
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
STp->inited = TRUE;
if (STp->block_size > 0)
{
int result = 0, result2;
static unsigned char cmd[MAX_COMMAND_SIZE];
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Tape *STp;
ST_mode *STm;
ST_partstat *STps;
cmd[0] = WRITE_FILEMARKS;
cmd[4] = 1 + STp->two_fm;
- SCpnt = st_do_scsi(NULL, STp, cmd, 0, STp->timeout,
- MAX_WRITE_RETRIES, TRUE);
- if (!SCpnt) {
- result = (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(NULL, STp, cmd, 0, SCSI_DATA_NONE,
+ STp->timeout, MAX_WRITE_RETRIES, TRUE);
+ if (!SRpnt) {
+ result = (STp->buffer)->syscall_result;
goto out;
}
- if ((STp->buffer)->last_result_fatal != 0 &&
- ((SCpnt->sense_buffer[0] & 0x70) != 0x70 ||
- (SCpnt->sense_buffer[2] & 0x4f) != 0x40 ||
- ((SCpnt->sense_buffer[0] & 0x80) != 0 &&
- (SCpnt->sense_buffer[3] | SCpnt->sense_buffer[4] |
- SCpnt->sense_buffer[5] |
- SCpnt->sense_buffer[6]) == 0))) {
+ if ((STp->buffer)->syscall_result != 0 &&
+ ((SRpnt->sr_sense_buffer[0] & 0x70) != 0x70 ||
+ (SRpnt->sr_sense_buffer[2] & 0x4f) != 0x40 ||
+ ((SRpnt->sr_sense_buffer[0] & 0x80) != 0 &&
+ (SRpnt->sr_sense_buffer[3] | SRpnt->sr_sense_buffer[4] |
+ SRpnt->sr_sense_buffer[5] |
+ SRpnt->sr_sense_buffer[6]) == 0))) {
/* Filter out successful write at EOM */
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
printk(KERN_ERR "st%d: Error on write filemark.\n", dev);
if (result == 0)
result = (-EIO);
} else {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
if (STps->drv_file >= 0)
STps->drv_file++;
STps->drv_block = 0;
int doing_write = 0;
static unsigned char cmd[MAX_COMMAND_SIZE];
const char *b_point;
- Scsi_Cmnd *SCpnt = NULL;
+ Scsi_Request *SRpnt = NULL;
Scsi_Tape *STp;
ST_mode *STm;
ST_partstat *STps;
if ((STp->buffer)->writing) {
write_behind_check(STp);
- if ((STp->buffer)->last_result_fatal) {
+ if ((STp->buffer)->syscall_result) {
DEBC(printk(ST_DEB_MSG "st%d: Async write error (write) %x.\n",
- dev, (STp->buffer)->last_result));
- if ((STp->buffer)->last_result == INT_MAX)
+ dev, (STp->buffer)->midlevel_result));
+ if ((STp->buffer)->midlevel_result == INT_MAX)
STps->eof = ST_EOM_OK;
else
STps->eof = ST_EOM_ERROR;
i = append_to_buffer(b_point, STp->buffer, do_count);
if (i) {
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ if (SRpnt != NULL) {
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
}
return i;
}
cmd[3] = blks >> 8;
cmd[4] = blks;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, transfer, STp->timeout,
- MAX_WRITE_RETRIES, TRUE);
- if (!SCpnt)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, SCSI_DATA_WRITE,
+ STp->timeout, MAX_WRITE_RETRIES, TRUE);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
- if ((STp->buffer)->last_result_fatal != 0) {
+ if ((STp->buffer)->syscall_result != 0) {
DEBC(printk(ST_DEB_MSG "st%d: Error on write:\n", dev));
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x40)) {
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x40)) {
if (STp->block_size != 0 &&
- (SCpnt->sense_buffer[0] & 0x80) != 0)
- transfer = (SCpnt->sense_buffer[3] << 24) |
- (SCpnt->sense_buffer[4] << 16) |
- (SCpnt->sense_buffer[5] << 8) |
- SCpnt->sense_buffer[6];
+ (SRpnt->sr_sense_buffer[0] & 0x80) != 0)
+ transfer = (SRpnt->sr_sense_buffer[3] << 24) |
+ (SRpnt->sr_sense_buffer[4] << 16) |
+ (SRpnt->sr_sense_buffer[5] << 8) |
+ SRpnt->sr_sense_buffer[6];
else if (STp->block_size == 0 &&
- (SCpnt->sense_buffer[2] & 0x0f) ==
+ (SRpnt->sr_sense_buffer[2] & 0x0f) ==
VOLUME_OVERFLOW)
transfer = do_count;
else
retval = (-EIO);
}
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
(STp->buffer)->buffer_bytes = 0;
STp->dirty = 0;
if (count < total)
STp->dirty = 1;
i = append_to_buffer(b_point, STp->buffer, count);
if (i) {
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ if (SRpnt != NULL) {
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
}
return i;
}
count = 0;
}
- if (doing_write && (STp->buffer)->last_result_fatal != 0) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
- return (STp->buffer)->last_result_fatal;
+ if (doing_write && (STp->buffer)->syscall_result != 0) {
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
+ return (STp->buffer)->syscall_result;
}
if (STm->do_async_writes &&
cmd[4] = blks;
DEB( STp->write_pending = 1; )
- SCpnt = st_do_scsi(SCpnt, STp, cmd, (STp->buffer)->writing, STp->timeout,
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, (STp->buffer)->writing,
+ SCSI_DATA_WRITE, STp->timeout,
MAX_WRITE_RETRIES, FALSE);
- if (SCpnt == NULL)
- return (STp->buffer)->last_result_fatal;
+ if (SRpnt == NULL)
+ return (STp->buffer)->syscall_result;
- } else if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ } else if (SRpnt != NULL) {
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
}
STps->at_sm &= (total == 0);
if (total > 0)
/* Read data from the tape. Returns zero in the normal case, one if the
eof status has changed, and the negative error code in case of a
fatal error. Otherwise updates the buffer and the eof state. */
-static long read_tape(struct inode *inode, long count, Scsi_Cmnd ** aSCpnt)
+static long read_tape(struct inode *inode, long count, Scsi_Request ** aSRpnt)
{
int transfer, blks, bytes;
static unsigned char cmd[MAX_COMMAND_SIZE];
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Tape *STp;
ST_mode *STm;
ST_partstat *STps;
cmd[3] = blks >> 8;
cmd[4] = blks;
- SCpnt = *aSCpnt;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, STp->timeout, MAX_RETRIES, TRUE);
- *aSCpnt = SCpnt;
- if (!SCpnt)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = *aSRpnt;
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, SCSI_DATA_READ,
+ STp->timeout, MAX_RETRIES, TRUE);
+ *aSRpnt = SRpnt;
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
(STp->buffer)->read_pointer = 0;
STps->at_sm = 0;
/* Something to check */
- if ((STp->buffer)->last_result_fatal) {
+ if ((STp->buffer)->syscall_result) {
retval = 1;
DEBC(printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
dev,
- SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
- SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
- SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
- SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]));
- if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
+ SRpnt->sr_sense_buffer[0], SRpnt->sr_sense_buffer[1],
+ SRpnt->sr_sense_buffer[2], SRpnt->sr_sense_buffer[3],
+ SRpnt->sr_sense_buffer[4], SRpnt->sr_sense_buffer[5],
+ SRpnt->sr_sense_buffer[6], SRpnt->sr_sense_buffer[7]));
+ if ((SRpnt->sr_sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
- if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
- SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */
+ if ((SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK)
+ SRpnt->sr_sense_buffer[2] &= 0xcf; /* No need for EOM in this case */
- if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */
+ if ((SRpnt->sr_sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */
/* Compute the residual count */
- if ((SCpnt->sense_buffer[0] & 0x80) != 0)
- transfer = (SCpnt->sense_buffer[3] << 24) |
- (SCpnt->sense_buffer[4] << 16) |
- (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6];
+ if ((SRpnt->sr_sense_buffer[0] & 0x80) != 0)
+ transfer = (SRpnt->sr_sense_buffer[3] << 24) |
+ (SRpnt->sr_sense_buffer[4] << 16) |
+ (SRpnt->sr_sense_buffer[5] << 8) |
+ SRpnt->sr_sense_buffer[6];
else
transfer = 0;
if (STp->block_size == 0 &&
- (SCpnt->sense_buffer[2] & 0x0f) == MEDIUM_ERROR)
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == MEDIUM_ERROR)
transfer = bytes;
- if (SCpnt->sense_buffer[2] & 0x20) { /* ILI */
+ if (SRpnt->sr_sense_buffer[2] & 0x20) { /* ILI */
if (STp->block_size == 0) {
if (transfer <= 0)
transfer = 0;
(STp->buffer)->buffer_bytes = bytes - transfer;
} else {
- scsi_release_command(SCpnt);
- SCpnt = *aSCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = *aSRpnt = NULL;
if (transfer == blks) { /* We did not get anything, error */
printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev);
if (STps->drv_block >= 0)
if (st_int_ioctl(inode, MTBSR, 1))
return (-EIO);
}
- } else if (SCpnt->sense_buffer[2] & 0x80) { /* FM overrides EOM */
+ } else if (SRpnt->sr_sense_buffer[2] & 0x80) { /* FM overrides EOM */
if (STps->eof != ST_FM_HIT)
STps->eof = ST_FM_HIT;
else
DEBC(printk(ST_DEB_MSG
"st%d: EOF detected (%d bytes read).\n",
dev, (STp->buffer)->buffer_bytes));
- } else if (SCpnt->sense_buffer[2] & 0x40) {
+ } else if (SRpnt->sr_sense_buffer[2] & 0x40) {
if (STps->eof == ST_FM)
STps->eof = ST_EOD_1;
else
"st%d: Tape error while reading.\n", dev));
STps->drv_block = (-1);
if (STps->eof == ST_FM &&
- (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) {
+ (SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK) {
DEBC(printk(ST_DEB_MSG
"st%d: Zero returned for first BLANK CHECK after EOF.\n",
dev));
}
/* End of extended sense test */
else { /* Non-extended sense */
- retval = (STp->buffer)->last_result_fatal;
+ retval = (STp->buffer)->syscall_result;
}
}
ssize_t total;
ssize_t i, transfer;
int special;
- Scsi_Cmnd *SCpnt = NULL;
+ Scsi_Request *SRpnt = NULL;
Scsi_Tape *STp;
ST_mode *STm;
ST_partstat *STps;
/* Get new data if the buffer is empty */
if ((STp->buffer)->buffer_bytes == 0) {
- special = read_tape(inode, count - total, &SCpnt);
+ special = read_tape(inode, count - total, &SRpnt);
if (special < 0) { /* No need to continue read */
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
+ if (SRpnt != NULL) {
+ scsi_release_request(SRpnt);
}
return special;
}
(STp->buffer)->buffer_bytes : count - total;
i = from_buffer(STp->buffer, buf, transfer);
if (i) {
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ if (SRpnt != NULL) {
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
}
return i;
}
} /* for (total = 0, special = 0;
total < count && !special; ) */
- if (SCpnt != NULL) {
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ if (SRpnt != NULL) {
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
}
/* Change the eof state if no data from tape or buffer */
{
int dev;
unsigned char cmd[MAX_COMMAND_SIZE];
- Scsi_Cmnd *SCpnt = NULL;
+ Scsi_Request *SRpnt = NULL;
if (STp->ready != ST_READY)
return (-EIO);
cmd[2] = COMPRESSION_PAGE;
cmd[4] = COMPRESSION_PAGE_LENGTH + MODE_HEADER_LENGTH;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
- if (SCpnt == NULL)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_READ,
+ STp->timeout, 0, TRUE);
+ if (SRpnt == NULL)
+ return (STp->buffer)->syscall_result;
- dev = TAPE_NR(SCpnt->request.rq_dev);
+ dev = TAPE_NR(SRpnt->sr_request.rq_dev);
- if ((STp->buffer)->last_result_fatal != 0) {
+ if ((STp->buffer)->syscall_result != 0) {
DEBC(printk(ST_DEB_MSG "st%d: Compression mode page not supported.\n",
dev));
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return (-EIO);
}
DEBC(printk(ST_DEB_MSG "st%d: Compression state is %d.\n", dev,
/* Check if compression can be changed */
if (((STp->buffer)->b_data[MODE_HEADER_LENGTH + 2] & DCC_MASK) == 0) {
DEBC(printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev));
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return (-EIO);
}
(STp->buffer)->b_data[0] = 0; /* Reserved data length */
(STp->buffer)->b_data[1] = 0; /* Reserved media type byte */
(STp->buffer)->b_data[MODE_HEADER_LENGTH] &= 0x3f;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->timeout, 0, TRUE);
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
+ STp->timeout, 0, TRUE);
- if ((STp->buffer)->last_result_fatal != 0) {
+ if ((STp->buffer)->syscall_result != 0) {
DEBC(printk(ST_DEB_MSG "st%d: Compression change failed.\n", dev));
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return (-EIO);
}
DEBC(printk(ST_DEB_MSG "st%d: Compression state changed to %d.\n",
dev, state));
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
STp->compression_changed = TRUE;
return 0;
}
int i, ioctl_result;
int chg_eof = TRUE;
unsigned char cmd[MAX_COMMAND_SIZE];
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
Scsi_Tape *STp;
ST_partstat *STps;
- int fileno, blkno, at_sm, undone, datalen;
+ int fileno, blkno, at_sm, undone;
+ int datalen = 0, direction = SCSI_DATA_NONE;
int dev = TAPE_NR(inode->i_rdev);
STp = &(scsi_tapes[dev]);
at_sm = STps->at_sm;
memset(cmd, 0, MAX_COMMAND_SIZE);
- datalen = 0;
switch (cmd_in) {
case MTFSFM:
chg_eof = FALSE; /* Changed from the FSF after this */
}
cmd[0] = MODE_SELECT;
cmd[4] = datalen = 12;
+ direction = SCSI_DATA_WRITE;
memset((STp->buffer)->b_data, 0, 12);
if (cmd_in == MTSETDRVBUFFER)
return (-ENOSYS);
}
- SCpnt = st_do_scsi(NULL, STp, cmd, datalen, timeout, MAX_RETRIES, TRUE);
- if (!SCpnt)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction,
+ timeout, MAX_RETRIES, TRUE);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
- ioctl_result = (STp->buffer)->last_result_fatal;
+ ioctl_result = (STp->buffer)->syscall_result;
if (!ioctl_result) { /* SCSI command successful */
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
STps->drv_block = blkno;
STps->drv_file = fileno;
STps->at_sm = at_sm;
} else { /* SCSI command was not completely successful. Don't return
from this block without releasing the SCSI command block! */
- if (SCpnt->sense_buffer[2] & 0x40) {
+ if (SRpnt->sr_sense_buffer[2] & 0x40) {
if (cmd_in != MTBSF && cmd_in != MTBSFM &&
cmd_in != MTBSR && cmd_in != MTBSS)
STps->eof = ST_EOM_OK;
}
undone = (
- (SCpnt->sense_buffer[3] << 24) +
- (SCpnt->sense_buffer[4] << 16) +
- (SCpnt->sense_buffer[5] << 8) +
- SCpnt->sense_buffer[6]);
+ (SRpnt->sr_sense_buffer[3] << 24) +
+ (SRpnt->sr_sense_buffer[4] << 16) +
+ (SRpnt->sr_sense_buffer[5] << 8) +
+ SRpnt->sr_sense_buffer[6]);
if (cmd_in == MTWEOF &&
- (SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
- (SCpnt->sense_buffer[2] & 0x4f) == 0x40 &&
- ((SCpnt->sense_buffer[0] & 0x80) == 0 || undone == 0)) {
+ (SRpnt->sr_sense_buffer[0] & 0x70) == 0x70 &&
+ (SRpnt->sr_sense_buffer[2] & 0x4f) == 0x40 &&
+ ((SRpnt->sr_sense_buffer[0] & 0x80) == 0 || undone == 0)) {
ioctl_result = 0; /* EOF written succesfully at EOM */
if (fileno >= 0)
fileno++;
STps->drv_block = 0;
STps->eof = ST_NOEOF;
} else if (cmd_in == MTFSR) {
- if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ if (SRpnt->sr_sense_buffer[2] & 0x80) { /* Hit filemark */
if (STps->drv_file >= 0)
STps->drv_file++;
STps->drv_block = 0;
STps->eof = ST_NOEOF;
}
} else if (cmd_in == MTBSR) {
- if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */
+ if (SRpnt->sr_sense_buffer[2] & 0x80) { /* Hit filemark */
STps->drv_file--;
STps->drv_block = (-1);
} else {
} else if (chg_eof)
STps->eof = ST_NOEOF;
- if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK)
+ if ((SRpnt->sr_sense_buffer[2] & 0x0f) == BLANK_CHECK)
STps->eof = ST_EOD;
if (cmd_in == MTLOCK)
STp->door_locked = ST_LOCK_FAILS;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
}
return ioctl_result;
int dev = TAPE_NR(inode->i_rdev);
int result;
unsigned char scmd[MAX_COMMAND_SIZE];
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY)
if (!logical && !STp->scsi2_logical)
scmd[1] = 1;
}
- SCpnt = st_do_scsi(NULL, STp, scmd, 20, STp->timeout, MAX_READY_RETRIES, TRUE);
- if (!SCpnt)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(NULL, STp, scmd, 20, SCSI_DATA_READ, STp->timeout,
+ MAX_READY_RETRIES, TRUE);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
- if ((STp->buffer)->last_result_fatal != 0 ||
+ if ((STp->buffer)->syscall_result != 0 ||
(STp->device->scsi_level >= SCSI_2 &&
((STp->buffer)->b_data[0] & 4) != 0)) {
*block = *partition = 0;
DEBC(printk(ST_DEB_MSG "st%d: Got tape pos. blk %d part %d.\n", dev,
*block, *partition));
}
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return result;
}
unsigned int blk;
int timeout;
unsigned char scmd[MAX_COMMAND_SIZE];
- Scsi_Cmnd *SCpnt;
+ Scsi_Request *SRpnt;
STp = &(scsi_tapes[dev]);
if (STp->ready != ST_READY)
timeout = STp->timeout;
#endif
- SCpnt = st_do_scsi(NULL, STp, scmd, 20, timeout, MAX_READY_RETRIES, TRUE);
- if (!SCpnt)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(NULL, STp, scmd, 0, SCSI_DATA_NONE,
+ timeout, MAX_READY_RETRIES, TRUE);
+ if (!SRpnt)
+ return (STp->buffer)->syscall_result;
STps->drv_block = STps->drv_file = (-1);
STps->eof = ST_NOEOF;
- if ((STp->buffer)->last_result_fatal != 0) {
+ if ((STp->buffer)->syscall_result != 0) {
result = (-EIO);
if (STp->can_partitions &&
(STp->device)->scsi_level >= SCSI_2 &&
result = 0;
}
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
return result;
}
{
int dev = TAPE_NR(inode->i_rdev), result;
Scsi_Tape *STp;
- Scsi_Cmnd *SCpnt = NULL;
+ Scsi_Request *SRpnt = NULL;
unsigned char cmd[MAX_COMMAND_SIZE];
STp = &(scsi_tapes[dev]);
cmd[2] = PART_PAGE;
cmd[4] = 200;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, 200, STp->timeout, MAX_READY_RETRIES, TRUE);
- if (SCpnt == NULL)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, 200, SCSI_DATA_READ, STp->timeout,
+ MAX_READY_RETRIES, TRUE);
+ if (SRpnt == NULL)
+ return (STp->buffer)->syscall_result;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
- if ((STp->buffer)->last_result_fatal != 0) {
+ if ((STp->buffer)->syscall_result != 0) {
DEBC(printk(ST_DEB_MSG "st%d: Can't read medium partition page.\n",
dev));
result = (-EIO);
int dev = TAPE_NR(inode->i_rdev), result;
int length;
Scsi_Tape *STp;
- Scsi_Cmnd *SCpnt = NULL;
+ Scsi_Request *SRpnt = NULL;
unsigned char cmd[MAX_COMMAND_SIZE], *bp;
if ((result = nbr_partitions(inode)) < 0)
cmd[1] = 0x10;
cmd[4] = length + MODE_HEADER_LENGTH;
- SCpnt = st_do_scsi(SCpnt, STp, cmd, cmd[4], STp->long_timeout,
- MAX_READY_RETRIES, TRUE);
- if (SCpnt == NULL)
- return (STp->buffer)->last_result_fatal;
+ SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], SCSI_DATA_WRITE,
+ STp->long_timeout, MAX_READY_RETRIES, TRUE);
+ if (SRpnt == NULL)
+ return (STp->buffer)->syscall_result;
- scsi_release_command(SCpnt);
- SCpnt = NULL;
+ scsi_release_request(SRpnt);
+ SRpnt = NULL;
- if ((STp->buffer)->last_result_fatal != 0) {
+ if ((STp->buffer)->syscall_result != 0) {
printk(KERN_INFO "st%d: Partitioning of tape failed.\n", dev);
result = (-EIO);
} else
int buffer_bytes;
int read_pointer;
int writing;
- int last_result;
- int last_result_fatal;
- Scsi_Cmnd *last_SCpnt;
+ int midlevel_result;
+ int syscall_result;
+ Scsi_Request *last_SRpnt;
unsigned char *b_data;
unsigned short use_sg; /* zero or number of segments for this adapter */
unsigned short sg_segs; /* total number of allocated segments */
return -1;
if (pcidev->irq == 0)
return -1;
+ if (!pci_dma_supported(pcidev, 0xffffffff)) {
+ printk(KERN_WARNING "es1370: architecture does not support 32bit PCI busmaster DMA\n");
+ return -1;
+ }
if (!(s = kmalloc(sizeof(struct es1370_state), GFP_KERNEL))) {
printk(KERN_WARNING "es1370: out of memory\n");
return -1;
dev->driver_data = NULL;
}
-static const struct pci_device_id id_table[] __devinitdata = {
+static struct pci_device_id id_table[] __devinitdata = {
{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
{ 0, 0, 0, 0, 0, 0 }
};
return -1;
if (pcidev->irq == 0)
return -1;
+ if (!pci_dma_supported(pcidev, 0xffffffff)) {
+ printk(KERN_WARNING "es1371: architecture does not support 32bit PCI busmaster DMA\n");
+ return -1;
+ }
if (!(s = kmalloc(sizeof(struct es1371_state), GFP_KERNEL))) {
printk(KERN_WARNING "es1371: out of memory\n");
return -1;
dev->driver_data = NULL;
}
-static const struct pci_device_id id_table[] __devinitdata = {
+static struct pci_device_id id_table[] __devinitdata = {
{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
{ PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_CT5880, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
{ PCI_VENDOR_ID_ECTIVA, PCI_DEVICE_ID_ECTIVA_EV1938, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
* Tim Janik's BSE (Bedevilled Sound Engine) found this
* Integrated (aka redid 8-)) APM support patch by Zach Brown
* 07.02.2000 0.13 Use pci_alloc_consistent and pci_register_driver
- *
+ * 19.02.2000 0.14 Use pci_dma_supported to determine if recording should be disabled
*/
/*****************************************************************************/
db->mapped = db->ready = 0;
}
-static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db, unsigned long dmamask)
+static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db)
{
int order;
unsigned bytespersec;
db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
if (!db->rawbuf) {
db->ready = db->mapped = 0;
- s->dev->dma_mask = dmamask;
for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)
if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr)))
break;
int c;
stop_adc(s);
- if ((c = prog_dmabuf(s, &s->dma_adc, 0xffffff)))
+ /* check if PCI implementation supports 24bit busmaster DMA */
+ if (s->dev->dma_mask > 0xffffff)
+ return -EIO;
+ if ((c = prog_dmabuf(s, &s->dma_adc)))
return c;
va = s->dma_adc.dmaaddr;
if ((va & ~((1<<24)-1)))
int c;
stop_dac(s);
- if ((c = prog_dmabuf(s, &s->dma_dac, 0xffffffff)))
+ if ((c = prog_dmabuf(s, &s->dma_dac)))
return c;
memset(s->dma_dac.rawbuf, (s->fmt & (AFMT_U8 | AFMT_U16_LE)) ? 0 : 0x80, s->dma_dac.dmasize); /* almost correct for U16 */
va = s->dma_dac.dmaaddr;
{
struct solo1_state *s;
struct pm_dev *pmdev;
+ dma_addr_t dma_mask;
if (!RSRCISIOREGION(pcidev, 0) ||
!RSRCISIOREGION(pcidev, 1) ||
return -1;
if (pcidev->irq == 0)
return -1;
+ if (pci_dma_supported(pcidev, 0x00ffffff)) {
+ dma_mask = 0x00ffffff; /* this enables playback and recording */
+ } else if (pci_dma_supported(pcidev, 0xffffffff)) {
+ dma_mask = 0xffffffff; /* this enables only playback, as the recording BMDMA can handle only 24bits */
+ } else {
+ printk(KERN_WARNING "solo1: architecture does not support 24bit or 32bit PCI busmaster DMA\n");
+ return -1;
+ }
if (!(s = kmalloc(sizeof(struct solo1_state), GFP_KERNEL))) {
printk(KERN_WARNING "solo1: out of memory\n");
return -1;
goto err;
/* store it in the driver field */
pcidev->driver_data = s;
- pcidev->dma_mask = 0xffffff; /* pessimistic; play can handle 32bit addrs */
+ pcidev->dma_mask = dma_mask;
/* put it into driver list */
list_add_tail(&s->devs, &devs);
dev->driver_data = NULL;
}
-static const struct pci_device_id id_table[] __devinitdata = {
+static struct pci_device_id id_table[] __devinitdata = {
{ PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_SOLO1, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
{ 0, 0, 0, 0, 0, 0 }
};
{
if (!pci_present()) /* No PCI bus in this machine! */
return -ENODEV;
- printk(KERN_INFO "solo1: version v0.13 time " __TIME__ " " __DATE__ "\n");
+ printk(KERN_INFO "solo1: version v0.14 time " __TIME__ " " __DATE__ "\n");
if (!pci_register_driver(&solo1_driver)) {
pci_unregister_driver(&solo1_driver);
return -ENODEV;
return -1;
if (pcidev->irq == 0)
return -1;
+ if (!pci_dma_supported(pcidev, 0x00ffffff)) {
+ printk(KERN_WARNING "sonicvibes: architecture does not support 24bit PCI busmaster DMA\n");
+ return -1;
+ }
/* try to allocate a DDMA resource if not already available */
if (!RSRCISIOREGION(pcidev, RESOURCE_DDMA)) {
pcidev->resource[RESOURCE_DDMA].start = 0;
dev->driver_data = NULL;
}
-static const struct pci_device_id id_table[] __devinitdata = {
+static struct pci_device_id id_table[] __devinitdata = {
{ PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SONICVIBES, PCI_ANY_ID, PCI_ANY_ID, 0, 0 },
{ 0, 0, 0, 0, 0, 0 }
};
list-multi := usbcore.o
usbcore-objs := usb.o usb-debug.o usb-core.o hub.o
-usb-storage-objs := usb_storage.o
# Optional parts of multipart objects.
ifeq ($(CONFIG_USB_DEVICEFS),y)
usbcore-objs += devio.o inode.o drivers.o devices.o
endif
-ifeq ($(CONFIG_USB_STORAGE_DEBUG),y)
- usb-storage-objs += usb_storage_debug.o
-endif
# Object file lists.
usbcore.o: $(usbcore-objs)
$(LD) -r -o $@ $(usbcore-objs)
-usb-storage.o: $(usb-storage-objs)
- $(LD) -r -o $@ $(usb-storage-objs)
-
* Init / cleanup.
*/
-#ifdef MODULE
-void cleanup_module(void)
+static void __exit usb_acm_cleanup(void)
{
usb_deregister(&acm_driver);
tty_unregister_driver(&acm_tty_driver);
}
-int init_module(void)
-#else
-int usb_acm_init(void)
-#endif
+static int __init usb_acm_init(void)
{
acm_tty_driver.init_termios = tty_std_termios;
acm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
return 0;
}
-__initcall(usb_acm_init);
+module_init(usb_acm_init);
+module_exit(usb_acm_cleanup);
dbg("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d",
usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum);
- if (usbdev->descriptor.idVendor != 0x067b || usbdev->descriptor.idProduct != 0x1)
+ if (usbdev->descriptor.idVendor != 0x067b || usbdev->descriptor.idProduct > 0x1)
return NULL;
/* We don't handle multiple configurations */
minor: USBLP_MINOR_BASE
};
-#ifdef MODULE
-void cleanup_module(void)
+static void __exit usb_printer_cleanup(void)
{
usb_deregister(&usblp_driver);
}
-int init_module(void)
-#else
-int usb_printer_init(void)
-#endif
+
+static int __init usb_printer_init(void)
{
if (usb_register(&usblp_driver))
return -1;
return 0;
}
-__initcall(usb_printer_init);
+module_init(usb_printer_init);
+module_exit(usb_printer_cleanup);
void uhci_show_queues(struct uhci *uhci)
{
- int i, isqh;
+ int i, isqh = 0;
struct uhci_qh *qh;
struct uhci_td *td;
int usb_cpia_init(void);
int usb_ibmcam_init(void);
int usb_ov511_init(void);
-int usb_dc2xx_init(void);
-int usb_scanner_init(void);
int usb_stor_init(void);
int dabusb_init(void);
int plusb_init(void);
usb_hub_init();
#ifndef CONFIG_USB_MODULE
-#ifdef CONFIG_USB_SCANNER
- usb_scanner_init();
-#endif
#ifdef CONFIG_USB_AUDIO
usb_audio_init();
#endif
#ifdef CONFIG_USB_OV511
usb_ov511_init();
#endif
-#ifdef CONFIG_USB_DC2XX
- usb_dc2xx_init();
-#endif
#ifdef CONFIG_USB_STORAGE
usb_stor_init();
#endif
--- /dev/null
+#ifdef CONFIG_USB_STORAGE_DEBUG
+
+/* Debug output for Driver for USB mass storage (scsi-like) devices
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ */
+
+void us_show_command(Scsi_Cmnd *srb)
+{
+ char *what = NULL;
+
+ switch (srb->cmnd[0]) {
+ case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
+ case REZERO_UNIT: what = "REZERO_UNIT"; break;
+ case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
+ case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
+ case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
+ case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
+ case READ_6: what = "READ_6"; break;
+ case WRITE_6: what = "WRITE_6"; break;
+ case SEEK_6: what = "SEEK_6"; break;
+ case READ_REVERSE: what = "READ_REVERSE"; break;
+ case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
+ case SPACE: what = "SPACE"; break;
+ case INQUIRY: what = "INQUIRY"; break;
+ case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
+ case MODE_SELECT: what = "MODE_SELECT"; break;
+ case RESERVE: what = "RESERVE"; break;
+ case RELEASE: what = "RELEASE"; break;
+ case COPY: what = "COPY"; break;
+ case ERASE: what = "ERASE"; break;
+ case MODE_SENSE: what = "MODE_SENSE"; break;
+ case START_STOP: what = "START_STOP"; break;
+ case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
+ case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
+ case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
+ case SET_WINDOW: what = "SET_WINDOW"; break;
+ case READ_CAPACITY: what = "READ_CAPACITY"; break;
+ case READ_10: what = "READ_10"; break;
+ case WRITE_10: what = "WRITE_10"; break;
+ case SEEK_10: what = "SEEK_10"; break;
+ case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
+ case VERIFY: what = "VERIFY"; break;
+ case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
+ case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
+ case SEARCH_LOW: what = "SEARCH_LOW"; break;
+ case SET_LIMITS: what = "SET_LIMITS"; break;
+ case READ_POSITION: what = "READ_POSITION"; break;
+ case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
+ case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
+ case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
+ case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
+ case COMPARE: what = "COMPARE"; break;
+ case COPY_VERIFY: what = "COPY_VERIFY"; break;
+ case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
+ case READ_BUFFER: what = "READ_BUFFER"; break;
+ case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
+ case READ_LONG: what = "READ_LONG"; break;
+ case WRITE_LONG: what = "WRITE_LONG"; break;
+ case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
+ case WRITE_SAME: what = "WRITE_SAME"; break;
+ case READ_TOC: what = "READ_TOC"; break;
+ case LOG_SELECT: what = "LOG_SELECT"; break;
+ case LOG_SENSE: what = "LOG_SENSE"; break;
+ case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+ case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
+ case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+ case READ_12: what = "READ_12"; break;
+ case WRITE_12: what = "WRITE_12"; break;
+ case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
+ case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
+ case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
+ case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
+ case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+ case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+ case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
+ default: break;
+ }
+ printk(KERN_DEBUG USB_STORAGE
+ "Command %s (%d bytes)\n", what, srb->cmd_len);
+ printk(KERN_DEBUG USB_STORAGE
+ " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
+ srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
+}
+
+#endif
--- /dev/null
+/* Driver for USB Mass Storage compliant devices
+ *
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ * Further reference:
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the protocol used to communicate with such
+ * devices. Clearly, the designers had SCSI commands in mind when they
+ * created this document. The commands are all similar to commands
+ * in the SCSI-II specification.
+ *
+ * It is important to note that in a number of cases this class exhibits
+ * class-specific exemptions from the USB specification. Notably the
+ * usage of NAK, STALL and ACK differs from the norm, in that they are
+ * used to communicate wait, failed and OK on commands.
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/spinlock.h>
+#include <linux/smp_lock.h>
+
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "usb.h"
+#include "usb-storage.h"
+#include "usb-storage-debug.h"
+
+
+/*
+ * This is the size of the structure Scsi_Host_Template. We create
+ * an instance of this structure in this file and this is a check
+ * to see if this structure may have changed within the SCSI module.
+ * This is by no means foolproof, but it does help us some.
+ */
+#define SCSI_HOST_TEMPLATE_SIZE (104)
+
+/* direction table -- this indicates the direction of the data
+ * transfer for each command code -- a 1 indicates input
+ */
+unsigned char us_direction[256/8] = {
+ 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+ 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*
+ * Per device data
+ */
+
+static int my_host_number;
+
+int usb_stor_debug = 1;
+
+struct us_data;
+
+typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
+typedef int (*trans_reset)(struct us_data*);
+typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
+
+struct us_data {
+ struct us_data *next; /* next device */
+ struct usb_device *pusb_dev; /* this usb_device */
+ unsigned int flags; /* from filter initially */
+ __u8 ifnum; /* interface number */
+ __u8 ep_in; /* in endpoint */
+ __u8 ep_out; /* out ....... */
+ __u8 ep_int; /* interrupt . */
+ __u8 subclass; /* as in overview */
+ __u8 protocol; /* .............. */
+ __u8 attention_done; /* force attn on first cmd */
+ trans_cmnd transport; /* protocol specific do cmd */
+ trans_reset transport_reset; /* .......... device reset */
+ proto_cmnd proto_handler; /* protocol handler */
+ GUID(guid); /* unique dev id */
+ struct Scsi_Host *host; /* our dummy host data */
+ Scsi_Host_Template *htmplt; /* own host template */
+ int host_number; /* to find us */
+ int host_no; /* allocated by scsi */
+ Scsi_Cmnd *srb; /* current srb */
+ int action; /* what to do */
+ wait_queue_head_t waitq; /* thread waits */
+ wait_queue_head_t ip_waitq; /* for CBI interrupts */
+ __u16 ip_data; /* interrupt data */
+ int ip_wanted; /* needed */
+ int pid; /* control thread */
+ struct semaphore *notify; /* wait for thread to begin */
+ void *irq_handle; /* for USB int requests */
+ unsigned int irqpipe; /* pipe for release_irq */
+};
+
+/*
+ * kernel thread actions
+ */
+
+#define US_ACT_COMMAND 1
+#define US_ACT_ABORT 2
+#define US_ACT_DEVICE_RESET 3
+#define US_ACT_BUS_RESET 4
+#define US_ACT_HOST_RESET 5
+
+static struct us_data *us_list;
+
+static void * storage_probe(struct usb_device *dev, unsigned int ifnum);
+static void storage_disconnect(struct usb_device *dev, void *ptr);
+static struct usb_driver storage_driver = {
+ "usb-storage",
+ storage_probe,
+ storage_disconnect,
+ { NULL, NULL }
+};
+
+/***********************************************************************
+ * Data transfer routines
+ ***********************************************************************/
+
+/* Transfer one buffer (breaking into packets if necessary)
+ * Note that this function is necessary because if the device NAKs, we
+ * need to know that information directly
+ *
+ * FIXME: is the above true? Or will the URB status show ETIMEDOUT after
+ * retrying several times allready? Perhaps this is the way we should
+ * be going anyway?
+ */
+static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+{
+ int max_size;
+ int this_xfer;
+ int result;
+ int partial;
+ int maxtry;
+
+ /* determine the maximum packet size for these transfers */
+ max_size = usb_maxpacket(us->pusb_dev,
+ pipe, usb_pipeout(pipe)) * 16;
+
+ /* while we have data left to transfer */
+ while (length) {
+
+ /* calculate how long this will be -- maximum or a remainder */
+ this_xfer = length > max_size ? max_size : length;
+ length -= this_xfer;
+
+ /* FIXME: this number is totally outrageous. We need to pick
+ * a better (smaller) number).
+ */
+
+ /* setup the retry counter */
+ maxtry = 100;
+
+ /* set up the transfer loop */
+ do {
+ /* transfer the data */
+ US_DEBUGP("Bulk xfer 0x%x(%d) try #%d\n",
+ (unsigned int)buf, this_xfer, 101 - maxtry);
+ result = usb_bulk_msg(us->pusb_dev, pipe, buf,
+ this_xfer, &partial, HZ*5);
+ US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
+ result, partial, this_xfer);
+
+ /* if we stall, we need to clear it before we go on */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+ }
+
+ /* update to show what data was transferred */
+ this_xfer -= partial;
+ buf += partial;
+
+ /* NAK - we retry a few times */
+ if (result == -ETIMEDOUT) {
+
+ US_DEBUGP("us_one_transfer: device NAKed\n");
+
+ /* if our try counter reaches 0, bail out */
+ if (!maxtry--)
+ return -ETIMEDOUT;
+
+ /* just continue the while loop */
+ continue;
+ }
+
+ /* other errors (besides NAK) -- we just bail out*/
+ if (result != 0) {
+ US_DEBUGP("us_one_transfer: device returned error %d\n", result);
+ return result;
+ }
+
+ /* continue until this transfer is done */
+ } while ( this_xfer );
+ }
+
+ /* if we get here, we're done and successful */
+ return 0;
+}
+
+static unsigned int us_transfer_length(Scsi_Cmnd *srb);
+
+/* transfer one SCSI command, using scatter-gather if requested */
+/* FIXME: what do the return codes here mean? */
+static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ int i;
+ int result = -1;
+ unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* FIXME: stop transferring data at us_transfer_length(), not
+ * bufflen */
+ if (srb->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+
+ for (i = 0; i < srb->use_sg; i++) {
+ result = us_one_transfer(us, pipe, sg[i].address, sg[i].length);
+ if (result)
+ break;
+ }
+ }
+ else
+ result = us_one_transfer(us, pipe, srb->request_buffer,
+ us_transfer_length(srb));
+
+ if (result < 0)
+ US_DEBUGP("us_transfer returning error %d\n", result);
+ return result;
+}
+
+/* calculate the length of the data transfer (not the command) for any
+ * given SCSI command
+ */
+static unsigned int us_transfer_length(Scsi_Cmnd *srb)
+{
+ int i;
+ unsigned int total = 0;
+
+ /* always zero for some commands */
+ switch (srb->cmnd[0]) {
+ case SEEK_6:
+ case SEEK_10:
+ case REZERO_UNIT:
+ case ALLOW_MEDIUM_REMOVAL:
+ case START_STOP:
+ case TEST_UNIT_READY:
+ return 0;
+
+ case REQUEST_SENSE:
+ case INQUIRY:
+ case MODE_SENSE:
+ return srb->cmnd[4];
+
+ case LOG_SENSE:
+ case MODE_SENSE_10:
+ return (srb->cmnd[7] << 8) + srb->cmnd[8];
+
+ default:
+ break;
+ }
+
+ if (srb->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+
+ for (i = 0; i < srb->use_sg; i++) {
+ total += sg[i].length;
+ }
+ return total;
+ }
+ else
+ return srb->request_bufflen;
+}
+
+/***********************************************************************
+ * Protocol routines
+ ***********************************************************************/
+
+static int CB_transport(Scsi_Cmnd *srb, struct us_data *us);
+static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us);
+
+static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int old_cmnd = 0;
+
+ /* fix some commands -- this is a form of mode translation
+ * UFI devices only accept 12 byte long commands
+ *
+ * NOTE: This only works because a Scsi_Cmnd struct field contains
+ * a unsigned char cmnd[12], so we know we have storage available
+ */
+
+ /* set command length to 12 bytes (this affects the transport layer) */
+ srb->cmd_len = 12;
+
+ /* determine the correct (or minimum) data length for these commands */
+ switch (us->srb->cmnd[0]) {
+
+ /* for INQUIRY, UFI devices only ever return 36 bytes */
+ case INQUIRY:
+ us->srb->cmnd[4] = 36;
+ break;
+
+ /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+ case MODE_SENSE:
+ case MODE_SELECT:
+ /* save the command so we can tell what it was */
+ old_cmnd = srb->cmnd[0];
+
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+
+ /* if we're sending data, we send all. If getting data,
+ * get the minimum */
+ if (srb->cmnd[0] == MODE_SELECT)
+ srb->cmnd[8] = srb->cmnd[4];
+ else
+ srb->cmnd[8] = 8;
+
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = 0;
+ srb->cmnd[4] = 0;
+ srb->cmnd[3] = 0;
+ srb->cmnd[2] = srb->cmnd[2];
+ srb->cmnd[1] = srb->cmnd[1];
+ srb->cmnd[0] = srb->cmnd[0] | 0x40;
+ break;
+
+ /* again, for MODE_SENSE_10, we get the minimum (8) */
+ case MODE_SENSE_10:
+ us->srb->cmnd[7] = 0;
+ us->srb->cmnd[8] = 8;
+ break;
+
+ /* for REQUEST_SENSE, UFI devices only ever return 18 bytes */
+ case REQUEST_SENSE:
+ us->srb->cmnd[4] = 18;
+ break;
+
+ /* change READ_6/WRITE_6 to READ_10/WRITE_10, which
+ * are UFI commands */
+ case WRITE_6:
+ case READ_6:
+ srb->cmnd[11] = 0;
+ srb->cmnd[10] = 0;
+ srb->cmnd[9] = 0;
+ srb->cmnd[8] = srb->cmnd[4];
+ srb->cmnd[7] = 0;
+ srb->cmnd[6] = 0;
+ srb->cmnd[5] = srb->cmnd[3];
+ srb->cmnd[4] = srb->cmnd[2];
+ srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+ srb->cmnd[2] = 0;
+ srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+ srb->cmnd[0] = srb->cmnd[0] | 0x20;
+ break;
+ } /* end switch on cmnd[0] */
+
+ /* send the command to the transport layer */
+ us->srb->result = us->transport(srb, us);
+
+ /* if we have an error, we're going to do a
+ * REQUEST_SENSE automatically */
+
+ /* FIXME: we should only do this for device
+ * errors, not system errors */
+ if (us->srb->result) {
+ int temp_result;
+ int count;
+ void* old_request_buffer;
+
+ US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
+
+ us->srb->cmnd[0] = REQUEST_SENSE;
+ us->srb->cmnd[1] = 0;
+ us->srb->cmnd[2] = 0;
+ us->srb->cmnd[3] = 0;
+ us->srb->cmnd[4] = 18;
+ us->srb->cmnd[5] = 0;
+
+ /* set the buffer length for transfer */
+ old_request_buffer = us->srb->request_buffer;
+ us->srb->request_bufflen = 18;
+ us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+
+ /* FIXME: what if this command fails? */
+ temp_result = us->transport(us->srb, us);
+ US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+
+ /* copy the data from the request buffer to the sense buffer */
+ for(count = 0; count < 18; count++)
+ us->srb->sense_buffer[count] =
+ ((unsigned char *)(us->srb->request_buffer))[count];
+
+ US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+ us->srb->sense_buffer[2] & 0xf,
+ us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+
+ /* we're done here */
+ kfree(us->srb->request_buffer);
+ us->srb->request_buffer = old_request_buffer;
+ return;
+ }
+
+ /* FIXME: if we need to send more data, or recieve data, we should
+ * do it here. Then, we can do status handling here also.
+ *
+ * This includes MODE_SENSE from above
+ */
+ if (old_cmnd == MODE_SENSE) {
+ unsigned char *dta = (unsigned char *)us->srb->request_buffer;
+
+ /* calculate the new length */
+ int length = (dta[0] << 8) + dta[1] + 2;
+
+ /* copy the available data length into the structure */
+ us->srb->cmnd[7] = length >> 8;
+ us->srb->cmnd[8] = length & 0xFF;
+
+ /* send the command to the transport layer */
+ us->srb->result = us->transport(srb, us);
+
+ /* FIXME: this assumes that the 2nd attempt is always
+ * successful convert MODE_SENSE_10 return data format
+ * to MODE_SENSE_6 format */
+ dta[0] = dta[1]; /* data len */
+ dta[1] = dta[2]; /* med type */
+ dta[2] = dta[3]; /* dev-spec prm */
+ dta[3] = dta[7]; /* block desc len */
+ printk (KERN_DEBUG USB_STORAGE
+ "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
+ dta[0], dta[1], dta[2], dta[3]);
+ }
+
+ /* FIXME: if this was a TEST_UNIT_READY, and we get a NOT READY/
+ * LOGICAL DRIVE NOT READY then we do a START_STOP, and retry
+ */
+
+ /* FIXME: here is where we need to fix-up the return data from
+ * an INQUIRY command to show ANSI SCSI rev 2
+ */
+
+ /* FIXME: The rest of this is bogus. usb_control_msg() will only
+ * return an error if we've really honked things up. If it just
+ * needs a START_STOP, then we'll get some data back via
+ * REQUEST_SENSE -- either way, this belongs at a higher level
+ */
+
+#if 0
+ /* For UFI, if this is the first time we've sent this TEST_UNIT_READY
+ * command, we can try again
+ */
+ if (!done_start && (us->subclass == US_SC_UFI)
+ && (cmd[0] == TEST_UNIT_READY) && (result < 0)) {
+
+ /* as per spec try a start command, wait and retry */
+ wait_ms(100);
+
+ done_start++;
+ memset(cmd, 0, sizeof(cmd));
+ cmd[0] = START_STOP;
+ cmd[4] = 1; /* start */
+
+ result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
+ US_CBI_ADSC,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, us->ifnum,
+ cmd, 12, HZ*5);
+ US_DEBUGP("Next usb_control_msg returns %d\n", result);
+
+ /* allow another retry */
+ retry++;
+ continue;
+ }
+#endif
+}
+
+static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+ unsigned int savelen = us->srb->request_bufflen;
+ unsigned int saveallocation = 0;
+
+#if 0
+ /* force attention on first command */
+ if (!us->attention_done) {
+ if (us->srb->cmnd[0] == REQUEST_SENSE) {
+ US_DEBUGP("forcing unit attention\n");
+ us->attention_done = 1;
+
+ if (us->srb->result == USB_STOR_TRANSPORT_GOOD) {
+ unsigned char *p = (unsigned char *)us->srb->request_buffer;
+
+ if ((p[2] & 0x0f) != UNIT_ATTENTION) {
+ p[2] = UNIT_ATTENTION;
+ p[12] = 0x29; /* power on, reset or bus-reset */
+ p[13] = 0;
+ } /* if ((p[2] & 0x0f) != UNIT_ATTENTION) */
+ } /* if (us->srb->result == USB_STORE_TRANSPORT_GOOD) */
+ }
+ } /* if (!us->attention_done) */
+#endif
+
+ /* If the command has a variable-length payload, then we do them
+ * in two steps -- first we do the minimum, then we recalculate
+ * then length, and re-issue the command
+ *
+ * we use savelen to remember how much buffer we really have
+ * we use savealloction to remember how much was really requested
+ */
+
+ /* FIXME: remove savelen based on mods to us_transfer_length() */
+ switch (us->srb->cmnd[0]) {
+ case REQUEST_SENSE:
+ if (us->srb->request_bufflen > 18)
+ us->srb->request_bufflen = 18;
+ else
+ break;
+ saveallocation = us->srb->cmnd[4];
+ us->srb->cmnd[4] = 18;
+ break;
+
+ case INQUIRY:
+ if (us->srb->request_bufflen > 36)
+ us->srb->request_bufflen = 36;
+ else
+ break;
+ saveallocation = us->srb->cmnd[4];
+ us->srb->cmnd[4] = 36;
+ break;
+
+ case MODE_SENSE:
+ if (us->srb->request_bufflen > 4)
+ us->srb->request_bufflen = 4;
+ else
+ break;
+ saveallocation = us->srb->cmnd[4];
+ us->srb->cmnd[4] = 4;
+ break;
+
+ case LOG_SENSE:
+ case MODE_SENSE_10:
+ if (us->srb->request_bufflen > 8)
+ us->srb->request_bufflen = 8;
+ else
+ break;
+ saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8];
+ us->srb->cmnd[7] = 0;
+ us->srb->cmnd[8] = 8;
+ break;
+
+ default:
+ break;
+ } /* end switch on cmnd[0] */
+
+ /* This code supports devices which do not support {READ|WRITE}_6
+ * Apparently, neither Windows or MacOS will use these commands,
+ * so some devices do not support them
+ */
+ if (us->flags & US_FL_MODE_XLATE) {
+
+ /* translate READ_6 to READ_10 */
+ if (us->srb->cmnd[0] == 0x08) {
+
+ /* get the control */
+ us->srb->cmnd[9] = us->srb->cmnd[5];
+
+ /* get the length */
+ us->srb->cmnd[8] = us->srb->cmnd[6];
+ us->srb->cmnd[7] = 0;
+
+ /* set the reserved area to 0 */
+ us->srb->cmnd[6] = 0;
+
+ /* get LBA */
+ us->srb->cmnd[5] = us->srb->cmnd[3];
+ us->srb->cmnd[4] = us->srb->cmnd[2];
+ us->srb->cmnd[3] = 0;
+ us->srb->cmnd[2] = 0;
+
+ /* LUN and other info in cmnd[1] can stay */
+
+ /* fix command code */
+ us->srb->cmnd[0] = 0x28;
+
+ US_DEBUGP("Changing READ_6 to READ_10\n");
+ US_DEBUG(us_show_command(us->srb));
+ }
+
+ /* translate WRITE_6 to WRITE_10 */
+ if (us->srb->cmnd[0] == 0x0A) {
+
+ /* get the control */
+ us->srb->cmnd[9] = us->srb->cmnd[5];
+
+ /* get the length */
+ us->srb->cmnd[8] = us->srb->cmnd[4];
+ us->srb->cmnd[7] = 0;
+
+ /* set the reserved area to 0 */
+ us->srb->cmnd[6] = 0;
+
+ /* get LBA */
+ us->srb->cmnd[5] = us->srb->cmnd[3];
+ us->srb->cmnd[4] = us->srb->cmnd[2];
+ us->srb->cmnd[3] = 0;
+ us->srb->cmnd[2] = 0;
+
+ /* LUN and other info in cmnd[1] can stay */
+
+ /* fix command code */
+ us->srb->cmnd[0] = 0x2A;
+
+ US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
+ US_DEBUG(us_show_command(us->srb));
+ }
+ } /* end if (us->flags & US_FL_MODE_XLATE) */
+
+ /* send the command to the transport layer */
+ us->srb->result = us->transport(us->srb, us);
+
+ /* if we have an error, we're going to do a REQUEST_SENSE
+ * automatically */
+ /* FIXME: we should only do this for device errors, not
+ * system errors */
+ if (us->srb->result) {
+ int temp_result;
+ int count;
+ void* old_request_buffer;
+
+ US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
+
+ /* set the result so the higher layers expect this data */
+ us->srb->result = CHECK_CONDITION;
+
+ us->srb->cmnd[0] = REQUEST_SENSE;
+ us->srb->cmnd[1] = 0;
+ us->srb->cmnd[2] = 0;
+ us->srb->cmnd[3] = 0;
+ us->srb->cmnd[4] = 18;
+ us->srb->cmnd[5] = 0;
+
+ /* set the buffer length for transfer */
+ old_request_buffer = us->srb->request_buffer;
+ us->srb->request_bufflen = 18;
+ us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+
+ /* FIXME: what if this command fails? */
+ temp_result = us->transport(us->srb, us);
+ US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+
+ /* copy the data from the request buffer to the sense buffer */
+ for(count = 0; count < 18; count++)
+ us->srb->sense_buffer[count] =
+ ((unsigned char *)(us->srb->request_buffer))[count];
+
+ US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+ us->srb->sense_buffer[2] & 0xf,
+ us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+
+ /* we're done here */
+ kfree(us->srb->request_buffer);
+ us->srb->request_buffer = old_request_buffer;
+ return;
+ }
+
+ if (savelen != us->srb->request_bufflen) {
+ unsigned char *p = (unsigned char *)us->srb->request_buffer;
+ unsigned int length = 0;
+
+ /* set correct length and retry */
+ switch (us->srb->cmnd[0]) {
+
+ /* FIXME: we should try to get all the sense data */
+ case REQUEST_SENSE:
+ /* simply return 18 bytes */
+ p[7] = 10;
+ length = us->srb->request_bufflen;
+ break;
+
+ case INQUIRY:
+ length = p[4] + 5 > savelen ? savelen : p[4] + 5;
+ us->srb->cmnd[4] = length;
+ break;
+
+ case MODE_SENSE:
+ US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]);
+ length = p[0] + 1 > savelen ? savelen : p[0] + 1;
+ us->srb->cmnd[4] = length;
+ break;
+
+ case LOG_SENSE:
+ length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4;
+ us->srb->cmnd[7] = length >> 8;
+ us->srb->cmnd[8] = length;
+ break;
+
+ case MODE_SENSE_10:
+ US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n",
+ (p[0] << 8) + p[1]);
+ length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6;
+ us->srb->cmnd[7] = length >> 8;
+ us->srb->cmnd[8] = length;
+ break;
+ } /* end switch on cmnd[0] */
+
+ US_DEBUGP("Old/New length = %d/%d\n",
+ savelen, length);
+
+ /* issue the new command */
+ /* FIXME: this assumes that the second attempt is
+ * always successful */
+ if (us->srb->request_bufflen != length) {
+ US_DEBUGP("redoing cmd with len=%d\n", length);
+ us->srb->request_bufflen = length;
+ us->srb->result = us->transport(us->srb, us);
+ }
+
+ /* reset back to original values */
+ us->srb->request_bufflen = savelen;
+
+ /* fix data as necessary */
+ switch (us->srb->cmnd[0]) {
+ case INQUIRY:
+ if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) {
+ US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
+ ((unsigned char*)us->srb->request_buffer)[2] |= 2;
+ }
+ /* FALL THROUGH */
+ case REQUEST_SENSE:
+ case MODE_SENSE:
+ if (us->srb->use_sg == 0 && length > 0) {
+ int i;
+ printk(KERN_DEBUG "Data is");
+ for (i = 0; i < 32 && i < length; ++i)
+ printk(" %.2x", ((unsigned char *)us->srb->request_buffer)[i]);
+ if (i < length)
+ printk(" ...");
+ printk("\n");
+ }
+
+ /* FIXME: is this really necessary? */
+ us->srb->cmnd[4] = saveallocation;
+ break;
+
+ case LOG_SENSE:
+ case MODE_SENSE_10:
+ /* FIXME: is this really necessary? */
+ us->srb->cmnd[7] = saveallocation >> 8;
+ us->srb->cmnd[8] = saveallocation;
+ break;
+ } /* end switch on cmnd[0] */
+ } /* if good command */
+}
+
+/***********************************************************************
+ * Transport routines
+ ***********************************************************************/
+
+static int CBI_irq(int state, void *buffer, int len, void *dev_id)
+{
+ struct us_data *us = (struct us_data *)dev_id;
+
+ US_DEBUGP("USB IRQ recieved for device on host %d\n", us->host_no);
+
+ /* save the data for interpretation later */
+ if (state != USB_ST_REMOVED) {
+ us->ip_data = le16_to_cpup((__u16 *)buffer);
+ US_DEBUGP("Interrupt Status 0x%x\n", us->ip_data);
+ }
+
+ /* was this a wanted interrupt? */
+ if (us->ip_wanted) {
+ us->ip_wanted = 0;
+ wake_up(&us->ip_waitq);
+ } else {
+ US_DEBUGP("ERROR: Unwanted interrupt received!\n");
+ }
+
+ /* This return code is truly meaningless -- and I mean truly. It gets
+ * ignored by other layers. It used to indicate if we wanted to get
+ * another interrupt or disable the interrupt callback
+ */
+ return 0;
+}
+
+/* FIXME: this reset function doesn't really reset the port, and it
+ * should. Actually it should probably do what it's doing here, and
+ * reset the port physically
+ */
+static int CB_reset(struct us_data *us)
+{
+ unsigned char cmd[12];
+ int result;
+
+ US_DEBUGP("CB_reset\n");
+
+ memset(cmd, 0xFF, sizeof(cmd));
+ cmd[0] = SEND_DIAGNOSTIC;
+ cmd[1] = 4;
+ result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
+ US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, us->ifnum, cmd, sizeof(cmd), HZ*5);
+
+ /* long wait for reset */
+ schedule_timeout(HZ*6);
+
+ US_DEBUGP("CB_reset: clearing endpoint halt\n");
+ usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+
+ US_DEBUGP("CB_reset done\n");
+ return 0;
+}
+
+static int pop_CB_status(Scsi_Cmnd *srb);
+
+/* FIXME: we also need a CBI_command which sets up the completion
+ * interrupt, and waits for it
+ */
+static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+ int result;
+
+ US_DEBUGP("CBI gets a command:\n");
+ US_DEBUG(us_show_command(srb));
+
+ /* FIXME: we aren't setting the ip_wanted indicator early enough, which
+ * causes some commands to never complete. This hangs the driver.
+ */
+
+ /* let's send the command via the control pipe */
+ result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
+ US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, us->ifnum,
+ srb->cmnd, srb->cmd_len, HZ*5);
+
+ /* check the return code for the command */
+ if (result < 0) {
+ US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
+
+ /* a stall is a fatal condition from the device */
+ if (result == -EPIPE) {
+ US_DEBUGP("-- Stall on control pipe detected. Clearing\n");
+
+ US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
+ usb_clear_halt(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev, 0)));
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* FIXME: we need to handle NAKs here */
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* transfer the data payload for this command, if one exists*/
+ if (us_transfer_length(srb)) {
+ result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+ US_DEBUGP("CBI attempted to transfer data, result is 0x%x\n", result);
+
+ /* FIXME: what do the return codes from us_transfer mean? */
+ if ((result < 0) &&
+ (result != USB_ST_DATAUNDERRUN) &&
+ (result != USB_ST_STALL)) {
+ return DID_ERROR << 16;
+ }
+ } /* if (us_transfer_length(srb)) */
+
+ /* get status and return it */
+ return pop_CB_status(srb);
+}
+
+/*
+ * Control/Bulk status handler
+ */
+
+static int pop_CB_status(Scsi_Cmnd *srb)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ int result = 0;
+ __u8 status[2];
+ int retry = 5;
+
+ US_DEBUGP("pop_CB_status, proto=0x%x\n", us->protocol);
+ switch (us->protocol) {
+ case US_PR_CB:
+ /* get from control */
+
+ while (retry--) {
+ result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0),
+ USB_REQ_GET_STATUS, USB_DIR_IN |
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ 0, us->ifnum, status, sizeof(status), HZ*5);
+ if (result != USB_ST_TIMEOUT)
+ break;
+ }
+ if (result) {
+ US_DEBUGP("Bad AP status request %d\n", result);
+ return DID_ABORT << 16;
+ }
+ US_DEBUGP("Got AP status 0x%x 0x%x\n", status[0], status[1]);
+ if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
+ ( (status[0] & ~3) || status[1]))
+ return (DID_OK << 16) | 2;
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+ break;
+
+ /* FIXME: this should be in a separate function */
+ case US_PR_CBI:
+ /* get from interrupt pipe */
+
+ /* add interrupt transfer, marked for removal */
+ us->ip_wanted = 1;
+
+ /* go to sleep until we get this interrup */
+ /* FIXME: this should be changed to use a timeout */
+ sleep_on(&us->ip_waitq);
+
+ if (us->ip_wanted) {
+ US_DEBUGP("Did not get interrupt on CBI\n");
+ us->ip_wanted = 0;
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
+
+ /* UFI gives us ASC and ASCQ, like a request sense */
+ /* FIXME: is this right? do REQUEST_SENSE and INQUIRY need special
+ * case handling?
+ */
+ if (us->subclass == US_SC_UFI) {
+ if (srb->cmnd[0] == REQUEST_SENSE ||
+ srb->cmnd[0] == INQUIRY)
+ return USB_STOR_TRANSPORT_GOOD;
+ else
+ if (us->ip_data)
+ return USB_STOR_TRANSPORT_FAILED;
+ else
+ return USB_STOR_TRANSPORT_GOOD;
+ }
+
+ /* otherwise, we interpret the data normally */
+ switch (us->ip_data) {
+ case 0x0001:
+ return USB_STOR_TRANSPORT_GOOD;
+ case 0x0002:
+ return USB_STOR_TRANSPORT_FAILED;
+ default:
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ }
+ US_DEBUGP("pop_CB_status, reached end of function\n");
+ return USB_STOR_TRANSPORT_ERROR;
+}
+
+static int Bulk_reset(struct us_data *us)
+{
+ int result;
+
+ result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
+ US_BULK_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ US_BULK_RESET_HARD, us->ifnum,
+ NULL, 0, HZ*5);
+ if (result)
+ US_DEBUGP("Bulk hard reset failed %d\n", result);
+ usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+ usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+ /* long wait for reset */
+ schedule_timeout(HZ*6);
+
+ return result;
+}
+
+/*
+ * The bulk only protocol handler.
+ * Uses the in and out endpoints to transfer commands and data
+ */
+static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
+{
+ struct bulk_cb_wrap bcb;
+ struct bulk_cs_wrap bcs;
+ int result;
+ int pipe;
+ int partial;
+
+ /* set up the command wrapper */
+ bcb.Signature = US_BULK_CB_SIGN;
+ bcb.DataTransferLength = us_transfer_length(srb);
+ bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7;
+ bcb.Tag = srb->serial_number;
+ bcb.Lun = 0;
+ bcb.Length = srb->cmd_len;
+
+ /* construct the pipe handle */
+ pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+ /* copy the command payload */
+ memset(bcb.CDB, 0, sizeof(bcb.CDB));
+ memcpy(bcb.CDB, srb->cmnd, bcb.Length);
+
+ /* send it to out endpoint */
+ US_DEBUGP("Bulk command S 0x%x T 0x%x L %d F %d CL %d\n",
+ bcb.Signature, bcb.Tag, bcb.DataTransferLength,
+ bcb.Flags, bcb.Length);
+ result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
+ US_BULK_CB_WRAP_LEN, &partial, HZ*5);
+ US_DEBUGP("Bulk command transfer result=%d\n", result);
+
+ /* if we stall, we need to clear it before we go on */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+ }
+
+ /* if the command transfered well, then we go to the data stage */
+ /* FIXME: Regardless of the status of the data stage, we go on to the
+ * status stage. Note that this implies that if a command is
+ * partially successful, we rely on the device reporting an error
+ * the CSW. The spec says that the device may just decide to short us.
+ */
+ if (result == 0) {
+ /* send/receive data payload, if there is any */
+ if (bcb.DataTransferLength) {
+ result = us_transfer(srb, bcb.Flags);
+ US_DEBUGP("Bulk data transfer result 0x%x\n", result);
+#if 0
+ if ((result < 0) && (result != USB_ST_DATAUNDERRUN)
+ && (result != USB_ST_STALL)) {
+ US_DEBUGP("Bulk data transfer result 0x%x\n", result);
+ return DID_ABORT << 16;
+ }
+#endif
+ }
+ }
+
+ /* See flow chart on pg 15 of the Bulk Only Transport spec for
+ * an explanation of how this code works.
+ */
+
+ /* construct the pipe handle */
+ pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+
+ /* get CSW for device status */
+ result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
+ US_BULK_CS_WRAP_LEN, &partial, HZ*5);
+
+ /* did the attempt to read the CSW fail? */
+ if (result == -EPIPE) {
+ US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+ usb_clear_halt(us->pusb_dev, pipe);
+
+ /* get the status again */
+ result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
+ US_BULK_CS_WRAP_LEN, &partial, HZ*5);
+
+ /* if it fails again, we need a reset and return an error*/
+ if (result == -EPIPE) {
+ Bulk_reset(us);
+ return (DID_ABORT << 16);
+ }
+ }
+
+ /* if we still have a failure at this point, we're in trouble */
+ if (result) {
+ US_DEBUGP("Bulk status result = 0x%x\n", result);
+ return DID_ABORT << 16;
+ }
+
+ /* check bulk status */
+ US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
+ bcs.Signature, bcs.Tag, bcs.Residue, bcs.Status);
+ if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
+ bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
+ US_DEBUGP("Bulk logical error\n");
+ return DID_ABORT << 16;
+ }
+
+ /* based on the status code, we report good or bad */
+ switch (bcs.Status) {
+ case US_BULK_STAT_OK:
+ /* if there is residue, we really didn't finish the command */
+ if (bcs.Residue)
+ return DID_ERROR << 16;
+ else
+ return DID_OK << 16;
+
+ case US_BULK_STAT_FAIL:
+ return DID_ERROR << 16;
+
+ case US_BULK_STAT_PHASE:
+ Bulk_reset(us);
+ return DID_ERROR << 16;
+ }
+
+ return DID_OK << 16; /* check sense required */
+}
+
+/***********************************************************************
+ * Host functions
+ ***********************************************************************/
+
+/* detect adapter (always true ) */
+static int us_detect(struct SHT *sht)
+{
+ /* FIXME - not nice at all, but how else ? */
+ struct us_data *us = (struct us_data *)sht->proc_dir;
+ char name[32];
+
+ /* set up our name */
+ sprintf(name, "usbscsi%d", us->host_number);
+ sht->name = sht->proc_name = kmalloc(strlen(name)+1, GFP_KERNEL);
+ if (!sht->proc_name)
+ return 0;
+ strcpy(sht->proc_name, name);
+
+ /* we start with no /proc directory entry */
+ sht->proc_dir = NULL;
+
+ /* register the host */
+ us->host = scsi_register(sht, sizeof(us));
+ if (us->host) {
+ us->host->hostdata[0] = (unsigned long)us;
+ us->host_no = us->host->host_no;
+ return 1;
+ }
+
+ /* odd... didn't register properly. Abort and free pointers */
+ kfree(sht->proc_name);
+ sht->proc_name = NULL;
+ sht->name = NULL;
+ return 0;
+}
+
+/* release - must be here to stop scsi
+ * from trying to release IRQ etc.
+ * Kill off our data
+ */
+static int us_release(struct Scsi_Host *psh)
+{
+ struct us_data *us = (struct us_data *)psh->hostdata[0];
+ struct us_data *prev = (struct us_data *)&us_list;
+
+ if (us->irq_handle) {
+ usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
+ us->irq_handle = NULL;
+ }
+ if (us->pusb_dev)
+ usb_deregister(&storage_driver);
+
+ /* FIXME - leaves hanging host template copy */
+ /* (because scsi layer uses it after removal !!!) */
+ while (prev->next != us)
+ prev = prev->next;
+ prev->next = us->next;
+ return 0;
+}
+
+/* run command */
+static int us_command( Scsi_Cmnd *srb )
+{
+ US_DEBUGP("Bad use of us_command\n");
+
+ return DID_BAD_TARGET << 16;
+}
+
+/* run command */
+static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
+{
+ struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+ US_DEBUGP("Command wakeup\n");
+ if (us->srb) {
+ /* busy */
+ }
+ srb->host_scribble = (unsigned char *)us;
+ us->srb = srb;
+ srb->scsi_done = done;
+ us->action = US_ACT_COMMAND;
+
+ /* wake up the process task */
+
+ wake_up_interruptible(&us->waitq);
+
+ return 0;
+}
+
+/* FIXME: This doesn't actually abort anything */
+static int us_abort( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+static int us_bus_reset( Scsi_Cmnd *srb )
+{
+ // struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+ US_DEBUGP("Bus reset requested\n");
+ // us->transport_reset(us);
+ return SUCCESS;
+}
+
+/* FIXME: This doesn't actually reset anything */
+static int us_host_reset( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+/***********************************************************************
+ * /proc/scsi/ functions
+ ***********************************************************************/
+
+/* we use this macro to help us write into the buffer */
+#undef SPRINTF
+#define SPRINTF(args...) do { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } while (0)
+
+int usb_stor_proc_info (char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ struct us_data *us = us_list;
+ char *pos = buffer;
+ char *tmp_ptr;
+
+ /* find our data from hostno */
+ while (us) {
+ if (us->host_no == hostno)
+ break;
+ us = us->next;
+ }
+
+ /* if we couldn't find it, we return an error */
+ if (!us)
+ return -ESRCH;
+
+ /* if someone is sending us data, just throw it away */
+ if (inout)
+ return length;
+
+ /* print the controler name */
+ SPRINTF ("Host scsi%d: usb-storage\n", hostno);
+
+ /* print product and vendor strings */
+ tmp_ptr = kmalloc(256, GFP_KERNEL);
+ if (!us->pusb_dev || !tmp_ptr) {
+ SPRINTF(" Vendor: Unknown Vendor\n");
+ SPRINTF(" Product: Unknown Product\n");
+ } else {
+ SPRINTF(" Vendor: ");
+ if (usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer, tmp_ptr, 256) > 0)
+ SPRINTF("%s\n", tmp_ptr);
+ else
+ SPRINTF("Unknown Vendor\n");
+
+ SPRINTF(" Product: ");
+ if (usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct, tmp_ptr, 256) > 0)
+ SPRINTF("%s\n", tmp_ptr);
+ else
+ SPRINTF("Unknown Product\n");
+ kfree(tmp_ptr);
+ }
+
+ SPRINTF(" Protocol: ");
+ switch (us->protocol) {
+ case US_PR_CB:
+ SPRINTF("Control/Bulk\n");
+ break;
+
+ case US_PR_CBI:
+ SPRINTF("Control/Bulk/Interrupt\n");
+ break;
+
+ case US_PR_BULK:
+ SPRINTF("Bulk only\n");
+ break;
+
+ default:
+ SPRINTF("Unknown Protocol\n");
+ break;
+ }
+
+ /* show the GUID of the device */
+ SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
+
+ /*
+ * Calculate start of next buffer, and return value.
+ */
+ *start = buffer + offset;
+
+ if ((pos - buffer) < offset)
+ return (0);
+ else if ((pos - buffer - offset) < length)
+ return (pos - buffer - offset);
+ else
+ return (length);
+}
+
+/*
+ * this defines our 'host'
+ */
+
+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 */
+ FALSE, /* use_clustering */
+ TRUE, /* use_new_eh_code */
+ TRUE /* emulated */
+};
+
+static unsigned char sense_notready[] = {
+ 0x70, /* current error */
+ 0x00,
+ 0x02, /* not ready */
+ 0x00,
+ 0x00,
+ 0x0a, /* additional length */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x04, /* not ready */
+ 0x03, /* manual intervention */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00
+};
+
+static int usb_stor_control_thread(void * __us)
+{
+ struct us_data *us = (struct us_data *)__us;
+ int action;
+
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources..
+ */
+ daemonize();
+
+ sprintf(current->comm, "usbscsi%d", us->host_number);
+
+ unlock_kernel();
+
+ up(us->notify);
+
+ for(;;) {
+ siginfo_t info;
+ int unsigned long signr;
+
+ interruptible_sleep_on(&us->waitq);
+
+ action = us->action;
+ us->action = 0;
+
+ /* FIXME: we need to examine placment of break; and
+ * scsi_done() calls */
+
+ switch (action) {
+ case US_ACT_COMMAND:
+ /* bad device */
+ if (us->srb->target || us->srb->lun) {
+ US_DEBUGP( "Bad device number (%d/%d) or dev 0x%x\n",
+ us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev);
+ us->srb->result = DID_BAD_TARGET << 16;
+
+ us->srb->scsi_done(us->srb);
+ us->srb = NULL;
+ break;
+ }
+
+ /* our device has gone - pretend not ready */
+ /* FIXME: we also need to handle INQUIRY here,
+ * probably */
+ if (!us->pusb_dev) {
+ if (us->srb->cmnd[0] == REQUEST_SENSE) {
+ memcpy(us->srb->request_buffer, sense_notready,
+ sizeof(sense_notready));
+ us->srb->result = DID_OK << 16;
+ } else {
+ us->srb->result = (DID_OK << 16) | 2;
+ }
+
+ us->srb->scsi_done(us->srb);
+ us->srb = NULL;
+ break;
+ }
+
+ /* we've got a command, let's do it! */
+ US_DEBUG(us_show_command(us->srb));
+
+ /* FIXME: this is to support Shuttle E-USB bridges, it
+ * appears */
+ if (us->srb->cmnd[0] == START_STOP &&
+ us->pusb_dev->descriptor.idProduct == 0x0001 &&
+ us->pusb_dev->descriptor.idVendor == 0x04e6)
+ us->srb->result = DID_OK << 16;
+ else {
+ us->proto_handler(us->srb, us);
+ }
+
+ US_DEBUGP("scsi cmd done, result=0x%x\n", us->srb->result);
+ us->srb->scsi_done(us->srb);
+ us->srb = NULL;
+ break;
+
+ case US_ACT_ABORT:
+ break;
+
+ case US_ACT_DEVICE_RESET:
+ break;
+
+ case US_ACT_BUS_RESET:
+ break;
+
+ case US_ACT_HOST_RESET:
+ break;
+
+ } /* end switch on action */
+
+ if (signal_pending(current)) {
+ /* sending SIGUSR1 makes us print out some info */
+ spin_lock_irq(¤t->sigmask_lock);
+ signr = dequeue_signal(¤t->blocked, &info);
+ spin_unlock_irq(¤t->sigmask_lock);
+
+ if (signr == SIGUSR2) {
+ usb_stor_debug = !usb_stor_debug;
+ printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug);
+ } else {
+ break; /* exit the loop on any other signal */
+ }
+ }
+ }
+
+ // MOD_DEC_USE_COUNT;
+
+ printk("usb_stor_control_thread exiting\n");
+
+ /* FIXME: this is a hack to allow for debugging */
+ // scsi_unregister_module(MODULE_SCSI_HA, us->htmplt);
+
+ return 0;
+}
+
+/* Probe to see if a new device is actually a SCSI device */
+static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
+{
+ struct usb_interface_descriptor *interface;
+ int i;
+ char mf[32]; /* manufacturer */
+ char prod[32]; /* product */
+ char serial[32]; /* serial number */
+ struct us_data *ss = NULL;
+ unsigned int flags = 0;
+ GUID(guid); /* Global Unique Identifier */
+ struct us_data *prev;
+ Scsi_Host_Template *htmplt;
+ int protocol = 0;
+ int subclass = 0;
+ struct usb_interface_descriptor *altsetting =
+ &(dev->actconfig->interface[ifnum].altsetting[0]);
+
+ /* clear the GUID and fetch the strings */
+ GUID_CLEAR(guid);
+ memset(mf, 0, sizeof(mf));
+ memset(prod, 0, sizeof(prod));
+ memset(serial, 0, sizeof(serial));
+ if (dev->descriptor.iManufacturer)
+ usb_string(dev, dev->descriptor.iManufacturer, mf, sizeof(mf));
+ if (dev->descriptor.iProduct)
+ usb_string(dev, dev->descriptor.iProduct, prod, sizeof(prod));
+ if (dev->descriptor.iSerialNumber)
+ usb_string(dev, dev->descriptor.iSerialNumber, serial, sizeof(serial));
+
+ /* let's examine the device now */
+
+ /* We make an exception for the shuttle E-USB */
+ if (dev->descriptor.idVendor == 0x04e6 &&
+ dev->descriptor.idProduct == 0x0001) {
+ protocol = US_PR_CB;
+ subclass = US_SC_8070; /* an assumption */
+ } else if (dev->descriptor.bDeviceClass != 0 ||
+ altsetting->bInterfaceClass != USB_CLASS_MASS_STORAGE ||
+ altsetting->bInterfaceSubClass < US_SC_MIN ||
+ altsetting->bInterfaceSubClass > US_SC_MAX) {
+ /* if it's not a mass storage, we go no further */
+ return NULL;
+ }
+
+ /* At this point, we know we've got a live one */
+ US_DEBUGP("USB Mass Storage device detected\n");
+
+ /* Create a GUID for this device */
+ if (dev->descriptor.iSerialNumber && serial[0]) {
+ /* If we have a serial number, and it's a non-NULL string */
+ make_guid(guid, dev->descriptor.idVendor,
+ dev->descriptor.idProduct, serial);
+ } else {
+ /* We don't have a serial number, so we use 0 */
+ make_guid(guid, dev->descriptor.idVendor,
+ dev->descriptor.idProduct, "0");
+ }
+
+ /* Now check if we have seen this GUID before, and restore
+ * the flags if we find it
+ */
+ for (ss = us_list; ss != NULL; ss = ss->next) {
+ if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) {
+ US_DEBUGP("Found existing GUID " GUID_FORMAT "\n",
+ GUID_ARGS(guid));
+ flags = ss->flags;
+ break;
+ }
+ }
+
+ /* If ss == NULL, then this is a new device. Allocate memory for it */
+ if (!ss) {
+ if ((ss = (struct us_data *)kmalloc(sizeof(*ss),
+ GFP_KERNEL)) == NULL) {
+ printk(KERN_WARNING USB_STORAGE "Out of memory\n");
+ return NULL;
+ }
+ memset(ss, 0, sizeof(struct us_data));
+ }
+
+ /* Initialize the us_data structure with some useful info */
+ interface = altsetting;
+ ss->flags = flags;
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
+ ss->attention_done = 0;
+
+ /* If the device has subclass and protocol, then use that. Otherwise,
+ * take data from the specific interface.
+ */
+ if (subclass) {
+ ss->subclass = subclass;
+ ss->protocol = protocol;
+ } else {
+ ss->subclass = interface->bInterfaceSubClass;
+ ss->protocol = interface->bInterfaceProtocol;
+ }
+
+ /* set the handler pointers based on the protocol */
+ US_DEBUGP("Transport: ");
+ switch (ss->protocol) {
+ case US_PR_CB:
+ US_DEBUGPX("Control/Bulk\n");
+ ss->transport = CB_transport;
+ ss->transport_reset = CB_reset;
+ break;
+
+ case US_PR_CBI:
+ US_DEBUGPX("Control/Bulk/Interrupt\n");
+ ss->transport = CB_transport;
+ ss->transport_reset = CB_reset;
+ break;
+
+ case US_PR_BULK:
+ US_DEBUGPX("Bulk\n");
+ ss->transport = Bulk_transport;
+ ss->transport_reset = Bulk_reset;
+ break;
+
+ default:
+ US_DEBUGPX("Unknown\n");
+ kfree(ss);
+ return NULL;
+ break;
+ }
+
+ /*
+ * We are expecting a minimum of 2 endpoints - in and out (bulk).
+ * An optional interrupt is OK (necessary for CBI protocol).
+ * We will ignore any others.
+ */
+ for (i = 0; i < interface->bNumEndpoints; i++) {
+ /* is it an BULK endpoint? */
+ if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK) {
+ if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN)
+ ss->ep_in = interface->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else
+ ss->ep_out = interface->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+
+ /* is it an interrupt endpoint? */
+ if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_INT) {
+ ss->ep_int = interface->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+ }
+ US_DEBUGP("Endpoints In %d Out %d Int %d\n",
+ ss->ep_in, ss->ep_out, ss->ep_int);
+
+ /* Do some basic sanity checks, and bail if we find a problem */
+ if (usb_set_interface(dev, interface->bInterfaceNumber, 0) ||
+ !ss->ep_in || !ss->ep_out ||
+ (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
+ US_DEBUGP("Problems with device\n");
+ if (ss->host) {
+ scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
+ kfree(ss->htmplt->name);
+ kfree(ss->htmplt);
+ }
+
+ kfree(ss);
+ return NULL;
+ }
+
+ /* If this is a new device (i.e. we haven't seen it before), we need to
+ * generate a scsi host definition, and register with scsi above us
+ */
+ if (!ss->host) {
+ /* copy the GUID we created before */
+ US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+ memcpy(ss->guid, guid, sizeof(guid));
+
+ /* set class specific stuff */
+ US_DEBUGP("Protocol: ");
+ switch (ss->subclass) {
+ case US_SC_RBC:
+ US_DEBUGPX("Reduced Block Commands\n");
+ break;
+
+ case US_SC_8020:
+ US_DEBUGPX("8020\n");
+ break;
+
+ case US_SC_QIC:
+ US_DEBUGPX("QIC157\n");
+ break;
+
+ case US_SC_8070:
+ US_DEBUGPX("8070\n");
+ break;
+
+ case US_SC_SCSI:
+ US_DEBUGPX("Transparent SCSI\n");
+ ss->proto_handler = transparent_scsi_command;
+ break;
+
+ case US_SC_UFI:
+ US_DEBUGPX("UFI\n");
+ ss->proto_handler = ufi_command;
+ break;
+
+ default:
+ US_DEBUGPX("Unknown\n");
+ break;
+ }
+
+ /* We only handle certain protocols. Currently, these are
+ *the only ones that devices use.
+ */
+ if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) {
+ US_DEBUGP("Sorry, we do not support that protocol yet.\n");
+ US_DEBUGP("If you have a device which uses one of the unsupported\n");
+ US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n");
+
+ kfree(ss);
+ return NULL;
+ }
+
+ /* Allocate memory for the SCSI Host Template */
+ if ((htmplt = (Scsi_Host_Template *)
+ kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
+
+ printk(KERN_WARNING USB_STORAGE "Out of memory\n");
+
+ kfree(ss);
+ return NULL;
+ }
+
+ /* Initialize the host template based on the default one */
+ memcpy(htmplt, &my_host_template, sizeof(my_host_template));
+
+ /* Grab the next host number */
+ ss->host_number = my_host_number++;
+
+ /* MDD: FIXME: this is bad. We abuse this pointer so we
+ * can pass the ss pointer to the host controler thread
+ * in us_detect
+ */
+ (struct us_data *)htmplt->proc_dir = ss;
+
+ /* shuttle E-USB */
+ if (dev->descriptor.idVendor == 0x04e6 &&
+ dev->descriptor.idProduct == 0x0001) {
+ __u8 qstat[2];
+ int result;
+
+ result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0),
+ 1, 0xC0,
+ 0, ss->ifnum,
+ qstat, 2, HZ*5);
+ US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
+ init_waitqueue_head(&ss->ip_waitq);
+ ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
+ 255, (void *)ss, &ss->irq_handle);
+ if (result)
+ return NULL;
+
+ interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6);
+ } else if (ss->protocol == US_PR_CBI)
+ {
+ int result;
+
+ init_waitqueue_head(&ss->ip_waitq);
+
+ /* set up the IRQ pipe and handler */
+ /* FIXME: This needs to get the period from the device */
+ ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
+ 255, (void *)ss, &ss->irq_handle);
+ if (result) {
+ US_DEBUGP("usb_request_irq failed (0x%x), No interrupt for CBI\n",
+ result);
+ }
+ }
+
+
+ /* start up our thread */
+ {
+ DECLARE_MUTEX_LOCKED(sem);
+
+ init_waitqueue_head(&ss->waitq);
+
+ ss->notify = &sem;
+ ss->pid = kernel_thread(usb_stor_control_thread, ss,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (ss->pid < 0) {
+ printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
+ kfree(htmplt);
+
+ kfree(ss);
+ return NULL;
+ }
+
+ /* wait for it to start */
+ down(&sem);
+ }
+
+ /* now register - our detect function will be called */
+ scsi_register_module(MODULE_SCSI_HA, htmplt);
+
+ /* put us in the list */
+ prev = (struct us_data *)&us_list;
+ while (prev->next)
+ prev = prev->next;
+ prev->next = ss;
+ }
+
+ printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n");
+ printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum);
+
+ return ss;
+}
+
+/* Handle a disconnect event from the USB core */
+static void storage_disconnect(struct usb_device *dev, void *ptr)
+{
+ struct us_data *ss = ptr;
+
+ if (!ss)
+ return;
+
+ ss->pusb_dev = NULL;
+ // MOD_DEC_USE_COUNT;
+}
+
+
+/***********************************************************************
+ * Initialization and registration
+ ***********************************************************************/
+
+int __init usb_stor_init(void)
+{
+ // MOD_INC_USE_COUNT;
+
+ if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
+ printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\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;
+
+ printk(KERN_INFO "USB Mass Storage support registered.\n");
+ return 0;
+}
+
+void __exit usb_stor_exit(void)
+{
+ usb_deregister(&storage_driver) ;
+}
+
+module_init(usb_stor_init) ;
+module_exit(usb_stor_exit) ;
--- /dev/null
+/* Driver for USB mass storage - include file
+ *
+ * (c) 1999 Michael Gee (michael@linuxspecific.com)
+ * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *
+ */
+
+#include <linux/config.h>
+
+#define USB_STORAGE "usb-storage: "
+
+extern int usb_stor_debug;
+
+#ifdef CONFIG_USB_STORAGE_DEBUG
+void us_show_command(Scsi_Cmnd *srb);
+#define US_DEBUGP(x...) { if(usb_stor_debug) printk( KERN_DEBUG USB_STORAGE ## x ); }
+#define US_DEBUGPX(x...) { if(usb_stor_debug) printk( ## x ); }
+#define US_DEBUG(x) { if(usb_stor_debug) x; }
+#else
+#define US_DEBUGP(x...)
+#define US_DEBUGPX(x...)
+#define US_DEBUG(x)
+#endif
+
+/* bit set if input */
+extern unsigned char us_direction[256/8];
+#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
+
+/* Sub Classes */
+
+#define US_SC_RBC 1 /* Typically, flash devices */
+#define US_SC_8020 2 /* CD-ROM */
+#define US_SC_QIC 3 /* QIC-157 Tapes */
+#define US_SC_UFI 4 /* Floppy */
+#define US_SC_8070 5 /* Removable media */
+#define US_SC_SCSI 6 /* Transparent */
+#define US_SC_MIN US_SC_RBC
+#define US_SC_MAX US_SC_SCSI
+
+/* Protocols */
+
+#define US_PR_CB 1 /* Control/Bulk w/o interrupt */
+#define US_PR_CBI 0 /* Control/Bulk/Interrupt */
+#define US_PR_BULK 0x50 /* bulk only */
+
+/*
+ * Bulk only data structures (Zip 100, for example)
+ */
+
+/* command block wrapper */
+struct bulk_cb_wrap {
+ __u32 Signature; /* contains 'USBC' */
+ __u32 Tag; /* unique per command id */
+ __u32 DataTransferLength; /* size of data */
+ __u8 Flags; /* direction in bit 0 */
+ __u8 Lun; /* LUN normally 0 */
+ __u8 Length; /* of of the CDB */
+ __u8 CDB[16]; /* max command */
+};
+
+#define US_BULK_CB_WRAP_LEN 31
+#define US_BULK_CB_SIGN 0x43425355
+#define US_BULK_FLAG_IN 1
+#define US_BULK_FLAG_OUT 0
+
+/* command status wrapper */
+struct bulk_cs_wrap {
+ __u32 Signature; /* should = 'USBS' */
+ __u32 Tag; /* same as original command */
+ __u32 Residue; /* amount not transferred */
+ __u8 Status; /* see below */
+ __u8 Filler[18];
+};
+
+#define US_BULK_CS_WRAP_LEN 13
+#define US_BULK_CS_SIGN 0x53425355
+#define US_BULK_STAT_OK 0
+#define US_BULK_STAT_FAIL 1
+#define US_BULK_STAT_PHASE 2
+
+#define US_BULK_RESET 0xff
+#define US_BULK_RESET_SOFT 1
+#define US_BULK_RESET_HARD 0
+
+/*
+ * Transport return codes
+ */
+
+#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
+#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
+#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead */
+
+/*
+ * CBI style
+ */
+
+#define US_CBI_ADSC 0
+
+/*
+ * GUID definitions
+ */
+
+#define GUID(x) __u32 x[3]
+#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2])
+#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0;
+#define GUID_NONE(x) (!x[0] && !x[1] && !x[2])
+#define GUID_FORMAT "%08x%08x%08x"
+#define GUID_ARGS(x) x[0], x[1], x[2]
+
+static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial)
+{
+ pg[0] = (vendor << 16) | product;
+ pg[1] = pg[2] = 0;
+ while (*serial) {
+ pg[1] <<= 4;
+ pg[1] |= pg[2] >> 28;
+ pg[2] <<= 4;
+ if (*serial >= 'a')
+ *serial -= 'a' - 'A';
+ pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0'
+ : *serial - 'A' + 10;
+ serial++;
+ }
+}
+
+/* Flag definitions */
+#define US_FL_IP_STATUS 0x00000001 /* status uses interrupt */
+#define US_FL_FIXED_COMMAND 0x00000002 /* expand commands to fixed size */
+#define US_FL_MODE_XLATE 0x00000004 /* translate _6 to _10 comands for
+ Win/MacOS compatibility */
+++ /dev/null
-/* Driver for USB Mass Storage compliant devices
- *
- * (c) 1999 Michael Gee (michael@linuxspecific.com)
- * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
- *
- * Further reference:
- * This driver is based on the 'USB Mass Storage Class' document. This
- * describes in detail the protocol used to communicate with such
- * devices. Clearly, the designers had SCSI commands in mind when they
- * created this document. The commands are all similar to commands
- * in the SCSI-II specification.
- *
- * It is important to note that in a number of cases this class exhibits
- * class-specific exemptions from the USB specification. Notably the
- * usage of NAK, STALL and ACK differs from the norm, in that they are
- * used to communicate wait, failed and OK on commands.
- * Also, for certain devices, the interrupt endpoint is used to convey
- * status of a command.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/random.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/malloc.h>
-#include <linux/spinlock.h>
-#include <linux/smp_lock.h>
-
-#include <linux/blk.h>
-#include "../scsi/scsi.h"
-#include "../scsi/hosts.h"
-#include "../scsi/sd.h"
-
-#include "usb.h"
-#include "usb_storage.h"
-
-/*
- * This is the size of the structure Scsi_Host_Template. We create
- * an instance of this structure in this file and this is a check
- * to see if this structure may have changed within the SCSI module.
- * This is by no means foolproof, but it does help us some.
- */
-#define SCSI_HOST_TEMPLATE_SIZE (104)
-
-/* direction table -- this indicates the direction of the data
- * transfer for each command code -- a 1 indicates input
- */
-unsigned char us_direction[256/8] = {
- 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
- 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-/*
- * Per device data
- */
-
-static int my_host_number;
-
-int usb_stor_debug = 1;
-
-struct us_data;
-
-typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
-typedef int (*trans_reset)(struct us_data*);
-typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
-
-struct us_data {
- struct us_data *next; /* next device */
- struct usb_device *pusb_dev; /* this usb_device */
- unsigned int flags; /* from filter initially */
- __u8 ifnum; /* interface number */
- __u8 ep_in; /* in endpoint */
- __u8 ep_out; /* out ....... */
- __u8 ep_int; /* interrupt . */
- __u8 subclass; /* as in overview */
- __u8 protocol; /* .............. */
- __u8 attention_done; /* force attn on first cmd */
- trans_cmnd transport; /* protocol specific do cmd */
- trans_reset transport_reset; /* .......... device reset */
- proto_cmnd proto_handler; /* protocol handler */
- GUID(guid); /* unique dev id */
- struct Scsi_Host *host; /* our dummy host data */
- Scsi_Host_Template *htmplt; /* own host template */
- int host_number; /* to find us */
- int host_no; /* allocated by scsi */
- Scsi_Cmnd *srb; /* current srb */
- int action; /* what to do */
- wait_queue_head_t waitq; /* thread waits */
- wait_queue_head_t ip_waitq; /* for CBI interrupts */
- __u16 ip_data; /* interrupt data */
- int ip_wanted; /* needed */
- int pid; /* control thread */
- struct semaphore *notify; /* wait for thread to begin */
- void *irq_handle; /* for USB int requests */
- unsigned int irqpipe; /* pipe for release_irq */
-};
-
-/*
- * kernel thread actions
- */
-
-#define US_ACT_COMMAND 1
-#define US_ACT_ABORT 2
-#define US_ACT_DEVICE_RESET 3
-#define US_ACT_BUS_RESET 4
-#define US_ACT_HOST_RESET 5
-
-static struct us_data *us_list;
-
-static void * storage_probe(struct usb_device *dev, unsigned int ifnum);
-static void storage_disconnect(struct usb_device *dev, void *ptr);
-static struct usb_driver storage_driver = {
- "usb-storage",
- storage_probe,
- storage_disconnect,
- { NULL, NULL }
-};
-
-/***********************************************************************
- * Data transfer routines
- ***********************************************************************/
-
-/* Transfer one buffer (breaking into packets if necessary)
- * Note that this function is necessary because if the device NAKs, we
- * need to know that information directly
- *
- * FIXME: is the above true? Or will the URB status show ETIMEDOUT after
- * retrying several times allready? Perhaps this is the way we should
- * be going anyway?
- */
-static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
-{
- int max_size;
- int this_xfer;
- int result;
- int partial;
- int maxtry;
-
- /* determine the maximum packet size for these transfers */
- max_size = usb_maxpacket(us->pusb_dev,
- pipe, usb_pipeout(pipe)) * 16;
-
- /* while we have data left to transfer */
- while (length) {
-
- /* calculate how long this will be -- maximum or a remainder */
- this_xfer = length > max_size ? max_size : length;
- length -= this_xfer;
-
- /* FIXME: this number is totally outrageous. We need to pick
- * a better (smaller) number).
- */
-
- /* setup the retry counter */
- maxtry = 100;
-
- /* set up the transfer loop */
- do {
- /* transfer the data */
- US_DEBUGP("Bulk xfer 0x%x(%d) try #%d\n",
- (unsigned int)buf, this_xfer, 101 - maxtry);
- result = usb_bulk_msg(us->pusb_dev, pipe, buf,
- this_xfer, &partial, HZ*5);
- US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
- result, partial, this_xfer);
-
- /* if we stall, we need to clear it before we go on */
- if (result == -EPIPE) {
- US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
- usb_clear_halt(us->pusb_dev, pipe);
- }
-
- /* update to show what data was transferred */
- this_xfer -= partial;
- buf += partial;
-
- /* NAK - we retry a few times */
- if (result == -ETIMEDOUT) {
-
- US_DEBUGP("us_one_transfer: device NAKed\n");
-
- /* if our try counter reaches 0, bail out */
- if (!maxtry--)
- return -ETIMEDOUT;
-
- /* just continue the while loop */
- continue;
- }
-
- /* other errors (besides NAK) -- we just bail out*/
- if (result != 0) {
- US_DEBUGP("us_one_transfer: device returned error %d\n", result);
- return result;
- }
-
- /* continue until this transfer is done */
- } while ( this_xfer );
- }
-
- /* if we get here, we're done and successful */
- return 0;
-}
-
-static unsigned int us_transfer_length(Scsi_Cmnd *srb);
-
-/* transfer one SCSI command, using scatter-gather if requested */
-/* FIXME: what do the return codes here mean? */
-static int us_transfer(Scsi_Cmnd *srb, int dir_in)
-{
- struct us_data *us = (struct us_data *)srb->host_scribble;
- int i;
- int result = -1;
- unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
- usb_sndbulkpipe(us->pusb_dev, us->ep_out);
-
- /* FIXME: stop transferring data at us_transfer_length(), not
- * bufflen */
- if (srb->use_sg) {
- struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
-
- for (i = 0; i < srb->use_sg; i++) {
- result = us_one_transfer(us, pipe, sg[i].address, sg[i].length);
- if (result)
- break;
- }
- }
- else
- result = us_one_transfer(us, pipe, srb->request_buffer,
- us_transfer_length(srb));
-
- if (result < 0)
- US_DEBUGP("us_transfer returning error %d\n", result);
- return result;
-}
-
-/* calculate the length of the data transfer (not the command) for any
- * given SCSI command
- */
-static unsigned int us_transfer_length(Scsi_Cmnd *srb)
-{
- int i;
- unsigned int total = 0;
-
- /* always zero for some commands */
- switch (srb->cmnd[0]) {
- case SEEK_6:
- case SEEK_10:
- case REZERO_UNIT:
- case ALLOW_MEDIUM_REMOVAL:
- case START_STOP:
- case TEST_UNIT_READY:
- return 0;
-
- case REQUEST_SENSE:
- case INQUIRY:
- case MODE_SENSE:
- return srb->cmnd[4];
-
- case LOG_SENSE:
- case MODE_SENSE_10:
- return (srb->cmnd[7] << 8) + srb->cmnd[8];
-
- default:
- break;
- }
-
- if (srb->use_sg) {
- struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
-
- for (i = 0; i < srb->use_sg; i++) {
- total += sg[i].length;
- }
- return total;
- }
- else
- return srb->request_bufflen;
-}
-
-/***********************************************************************
- * Protocol routines
- ***********************************************************************/
-
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us);
-static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us);
-
-static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
-{
- int old_cmnd = 0;
-
- /* fix some commands -- this is a form of mode translation
- * UFI devices only accept 12 byte long commands
- *
- * NOTE: This only works because a Scsi_Cmnd struct field contains
- * a unsigned char cmnd[12], so we know we have storage available
- */
-
- /* set command length to 12 bytes (this affects the transport layer) */
- srb->cmd_len = 12;
-
- /* determine the correct (or minimum) data length for these commands */
- switch (us->srb->cmnd[0]) {
-
- /* for INQUIRY, UFI devices only ever return 36 bytes */
- case INQUIRY:
- us->srb->cmnd[4] = 36;
- break;
-
- /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
- case MODE_SENSE:
- case MODE_SELECT:
- /* save the command so we can tell what it was */
- old_cmnd = srb->cmnd[0];
-
- srb->cmnd[11] = 0;
- srb->cmnd[10] = 0;
- srb->cmnd[9] = 0;
-
- /* if we're sending data, we send all. If getting data,
- * get the minimum */
- if (srb->cmnd[0] == MODE_SELECT)
- srb->cmnd[8] = srb->cmnd[4];
- else
- srb->cmnd[8] = 8;
-
- srb->cmnd[7] = 0;
- srb->cmnd[6] = 0;
- srb->cmnd[5] = 0;
- srb->cmnd[4] = 0;
- srb->cmnd[3] = 0;
- srb->cmnd[2] = srb->cmnd[2];
- srb->cmnd[1] = srb->cmnd[1];
- srb->cmnd[0] = srb->cmnd[0] | 0x40;
- break;
-
- /* again, for MODE_SENSE_10, we get the minimum (8) */
- case MODE_SENSE_10:
- us->srb->cmnd[7] = 0;
- us->srb->cmnd[8] = 8;
- break;
-
- /* for REQUEST_SENSE, UFI devices only ever return 18 bytes */
- case REQUEST_SENSE:
- us->srb->cmnd[4] = 18;
- break;
-
- /* change READ_6/WRITE_6 to READ_10/WRITE_10, which
- * are UFI commands */
- case WRITE_6:
- case READ_6:
- srb->cmnd[11] = 0;
- srb->cmnd[10] = 0;
- srb->cmnd[9] = 0;
- srb->cmnd[8] = srb->cmnd[4];
- srb->cmnd[7] = 0;
- srb->cmnd[6] = 0;
- srb->cmnd[5] = srb->cmnd[3];
- srb->cmnd[4] = srb->cmnd[2];
- srb->cmnd[3] = srb->cmnd[1] & 0x1F;
- srb->cmnd[2] = 0;
- srb->cmnd[1] = srb->cmnd[1] & 0xE0;
- srb->cmnd[0] = srb->cmnd[0] | 0x20;
- break;
- } /* end switch on cmnd[0] */
-
- /* send the command to the transport layer */
- us->srb->result = us->transport(srb, us);
-
- /* if we have an error, we're going to do a
- * REQUEST_SENSE automatically */
-
- /* FIXME: we should only do this for device
- * errors, not system errors */
- if (us->srb->result) {
- int temp_result;
- int count;
- void* old_request_buffer;
-
- US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
-
- /* set the result so the higher layers expect this data */
- us->srb->result = CHECK_CONDITION;
-
- us->srb->cmnd[0] = REQUEST_SENSE;
- us->srb->cmnd[1] = 0;
- us->srb->cmnd[2] = 0;
- us->srb->cmnd[3] = 0;
- us->srb->cmnd[4] = 18;
- us->srb->cmnd[5] = 0;
-
- /* set the buffer length for transfer */
- old_request_buffer = us->srb->request_buffer;
- us->srb->request_bufflen = 18;
- us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
-
- /* FIXME: what if this command fails? */
- temp_result = us->transport(us->srb, us);
- US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
- /* copy the data from the request buffer to the sense buffer */
- for(count = 0; count < 18; count++)
- us->srb->sense_buffer[count] =
- ((unsigned char *)(us->srb->request_buffer))[count];
-
- US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
- us->srb->sense_buffer[2] & 0xf,
- us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
-
- /* we're done here */
- kfree(us->srb->request_buffer);
- us->srb->request_buffer = old_request_buffer;
- return;
- }
-
- /* FIXME: if we need to send more data, or recieve data, we should
- * do it here. Then, we can do status handling here also.
- *
- * This includes MODE_SENSE from above
- */
- if (old_cmnd == MODE_SENSE) {
- unsigned char *dta = (unsigned char *)us->srb->request_buffer;
-
- /* calculate the new length */
- int length = (dta[0] << 8) + dta[1] + 2;
-
- /* copy the available data length into the structure */
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length & 0xFF;
-
- /* send the command to the transport layer */
- us->srb->result = us->transport(srb, us);
-
- /* FIXME: this assumes that the 2nd attempt is always
- * successful convert MODE_SENSE_10 return data format
- * to MODE_SENSE_6 format */
- dta[0] = dta[1]; /* data len */
- dta[1] = dta[2]; /* med type */
- dta[2] = dta[3]; /* dev-spec prm */
- dta[3] = dta[7]; /* block desc len */
- printk (KERN_DEBUG USB_STORAGE
- "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
- dta[0], dta[1], dta[2], dta[3]);
- }
-
- /* FIXME: if this was a TEST_UNIT_READY, and we get a NOT READY/
- * LOGICAL DRIVE NOT READY then we do a START_STOP, and retry
- */
-
- /* FIXME: here is where we need to fix-up the return data from
- * an INQUIRY command to show ANSI SCSI rev 2
- */
-
- /* FIXME: The rest of this is bogus. usb_control_msg() will only
- * return an error if we've really honked things up. If it just
- * needs a START_STOP, then we'll get some data back via
- * REQUEST_SENSE -- either way, this belongs at a higher level
- */
-
-#if 0
- /* For UFI, if this is the first time we've sent this TEST_UNIT_READY
- * command, we can try again
- */
- if (!done_start && (us->subclass == US_SC_UFI)
- && (cmd[0] == TEST_UNIT_READY) && (result < 0)) {
-
- /* as per spec try a start command, wait and retry */
- wait_ms(100);
-
- done_start++;
- memset(cmd, 0, sizeof(cmd));
- cmd[0] = START_STOP;
- cmd[4] = 1; /* start */
-
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, us->ifnum,
- cmd, 12, HZ*5);
- US_DEBUGP("Next usb_control_msg returns %d\n", result);
-
- /* allow another retry */
- retry++;
- continue;
- }
-#endif
-}
-
-static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
-{
- unsigned int savelen = us->srb->request_bufflen;
- unsigned int saveallocation = 0;
-
-#if 0
- /* force attention on first command */
- if (!us->attention_done) {
- if (us->srb->cmnd[0] == REQUEST_SENSE) {
- US_DEBUGP("forcing unit attention\n");
- us->attention_done = 1;
-
- if (us->srb->result == USB_STOR_TRANSPORT_GOOD) {
- unsigned char *p = (unsigned char *)us->srb->request_buffer;
-
- if ((p[2] & 0x0f) != UNIT_ATTENTION) {
- p[2] = UNIT_ATTENTION;
- p[12] = 0x29; /* power on, reset or bus-reset */
- p[13] = 0;
- } /* if ((p[2] & 0x0f) != UNIT_ATTENTION) */
- } /* if (us->srb->result == USB_STORE_TRANSPORT_GOOD) */
- }
- } /* if (!us->attention_done) */
-#endif
-
- /* If the command has a variable-length payload, then we do them
- * in two steps -- first we do the minimum, then we recalculate
- * then length, and re-issue the command
- *
- * we use savelen to remember how much buffer we really have
- * we use savealloction to remember how much was really requested
- */
-
- /* FIXME: remove savelen based on mods to us_transfer_length() */
- switch (us->srb->cmnd[0]) {
- case REQUEST_SENSE:
- if (us->srb->request_bufflen > 18)
- us->srb->request_bufflen = 18;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 18;
- break;
-
- case INQUIRY:
- if (us->srb->request_bufflen > 36)
- us->srb->request_bufflen = 36;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 36;
- break;
-
- case MODE_SENSE:
- if (us->srb->request_bufflen > 4)
- us->srb->request_bufflen = 4;
- else
- break;
- saveallocation = us->srb->cmnd[4];
- us->srb->cmnd[4] = 4;
- break;
-
- case LOG_SENSE:
- case MODE_SENSE_10:
- if (us->srb->request_bufflen > 8)
- us->srb->request_bufflen = 8;
- else
- break;
- saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8];
- us->srb->cmnd[7] = 0;
- us->srb->cmnd[8] = 8;
- break;
-
- default:
- break;
- } /* end switch on cmnd[0] */
-
- /* This code supports devices which do not support {READ|WRITE}_6
- * Apparently, neither Windows or MacOS will use these commands,
- * so some devices do not support them
- */
- if (us->flags & US_FL_MODE_XLATE) {
-
- /* translate READ_6 to READ_10 */
- if (us->srb->cmnd[0] == 0x08) {
-
- /* get the control */
- us->srb->cmnd[9] = us->srb->cmnd[5];
-
- /* get the length */
- us->srb->cmnd[8] = us->srb->cmnd[6];
- us->srb->cmnd[7] = 0;
-
- /* set the reserved area to 0 */
- us->srb->cmnd[6] = 0;
-
- /* get LBA */
- us->srb->cmnd[5] = us->srb->cmnd[3];
- us->srb->cmnd[4] = us->srb->cmnd[2];
- us->srb->cmnd[3] = 0;
- us->srb->cmnd[2] = 0;
-
- /* LUN and other info in cmnd[1] can stay */
-
- /* fix command code */
- us->srb->cmnd[0] = 0x28;
-
- US_DEBUGP("Changing READ_6 to READ_10\n");
- US_DEBUG(us_show_command(us->srb));
- }
-
- /* translate WRITE_6 to WRITE_10 */
- if (us->srb->cmnd[0] == 0x0A) {
-
- /* get the control */
- us->srb->cmnd[9] = us->srb->cmnd[5];
-
- /* get the length */
- us->srb->cmnd[8] = us->srb->cmnd[4];
- us->srb->cmnd[7] = 0;
-
- /* set the reserved area to 0 */
- us->srb->cmnd[6] = 0;
-
- /* get LBA */
- us->srb->cmnd[5] = us->srb->cmnd[3];
- us->srb->cmnd[4] = us->srb->cmnd[2];
- us->srb->cmnd[3] = 0;
- us->srb->cmnd[2] = 0;
-
- /* LUN and other info in cmnd[1] can stay */
-
- /* fix command code */
- us->srb->cmnd[0] = 0x2A;
-
- US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
- US_DEBUG(us_show_command(us->srb));
- }
- } /* end if (us->flags & US_FL_MODE_XLATE) */
-
- /* send the command to the transport layer */
- us->srb->result = us->transport(us->srb, us);
-
- /* if we have an error, we're going to do a REQUEST_SENSE
- * automatically */
- /* FIXME: we should only do this for device errors, not
- * system errors */
- if (us->srb->result) {
- int temp_result;
- int count;
- void* old_request_buffer;
-
- US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
-
- /* set the result so the higher layers expect this data */
- us->srb->result = CHECK_CONDITION;
-
- us->srb->cmnd[0] = REQUEST_SENSE;
- us->srb->cmnd[1] = 0;
- us->srb->cmnd[2] = 0;
- us->srb->cmnd[3] = 0;
- us->srb->cmnd[4] = 18;
- us->srb->cmnd[5] = 0;
-
- /* set the buffer length for transfer */
- old_request_buffer = us->srb->request_buffer;
- us->srb->request_bufflen = 18;
- us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
-
- /* FIXME: what if this command fails? */
- temp_result = us->transport(us->srb, us);
- US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
- /* copy the data from the request buffer to the sense buffer */
- for(count = 0; count < 18; count++)
- us->srb->sense_buffer[count] =
- ((unsigned char *)(us->srb->request_buffer))[count];
-
- US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
- us->srb->sense_buffer[2] & 0xf,
- us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
-
- /* we're done here */
- kfree(us->srb->request_buffer);
- us->srb->request_buffer = old_request_buffer;
- return;
- }
-
- if (savelen != us->srb->request_bufflen) {
- unsigned char *p = (unsigned char *)us->srb->request_buffer;
- unsigned int length = 0;
-
- /* set correct length and retry */
- switch (us->srb->cmnd[0]) {
-
- /* FIXME: we should try to get all the sense data */
- case REQUEST_SENSE:
- /* simply return 18 bytes */
- p[7] = 10;
- length = us->srb->request_bufflen;
- break;
-
- case INQUIRY:
- length = p[4] + 5 > savelen ? savelen : p[4] + 5;
- us->srb->cmnd[4] = length;
- break;
-
- case MODE_SENSE:
- US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]);
- length = p[0] + 1 > savelen ? savelen : p[0] + 1;
- us->srb->cmnd[4] = length;
- break;
-
- case LOG_SENSE:
- length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4;
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length;
- break;
-
- case MODE_SENSE_10:
- US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n",
- (p[0] << 8) + p[1]);
- length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6;
- us->srb->cmnd[7] = length >> 8;
- us->srb->cmnd[8] = length;
- break;
- } /* end switch on cmnd[0] */
-
- US_DEBUGP("Old/New length = %d/%d\n",
- savelen, length);
-
- /* issue the new command */
- /* FIXME: this assumes that the second attempt is
- * always successful */
- if (us->srb->request_bufflen != length) {
- US_DEBUGP("redoing cmd with len=%d\n", length);
- us->srb->request_bufflen = length;
- us->srb->result = us->transport(us->srb, us);
- }
-
- /* reset back to original values */
- us->srb->request_bufflen = savelen;
-
- /* fix data as necessary */
- switch (us->srb->cmnd[0]) {
- case INQUIRY:
- if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) {
- US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
- ((unsigned char*)us->srb->request_buffer)[2] |= 2;
- }
- /* FALL THROUGH */
- case REQUEST_SENSE:
- case MODE_SENSE:
- if (us->srb->use_sg == 0 && length > 0) {
- int i;
- printk(KERN_DEBUG "Data is");
- for (i = 0; i < 32 && i < length; ++i)
- printk(" %.2x", ((unsigned char *)us->srb->request_buffer)[i]);
- if (i < length)
- printk(" ...");
- printk("\n");
- }
-
- /* FIXME: is this really necessary? */
- us->srb->cmnd[4] = saveallocation;
- break;
-
- case LOG_SENSE:
- case MODE_SENSE_10:
- /* FIXME: is this really necessary? */
- us->srb->cmnd[7] = saveallocation >> 8;
- us->srb->cmnd[8] = saveallocation;
- break;
- } /* end switch on cmnd[0] */
- } /* if good command */
-}
-
-/***********************************************************************
- * Transport routines
- ***********************************************************************/
-
-static int CBI_irq(int state, void *buffer, int len, void *dev_id)
-{
- struct us_data *us = (struct us_data *)dev_id;
-
- US_DEBUGP("USB IRQ recieved for device on host %d\n", us->host_no);
-
- /* save the data for interpretation later */
- if (state != USB_ST_REMOVED) {
- us->ip_data = le16_to_cpup((__u16 *)buffer);
- US_DEBUGP("Interrupt Status 0x%x\n", us->ip_data);
- }
-
- /* was this a wanted interrupt? */
- if (us->ip_wanted) {
- us->ip_wanted = 0;
- wake_up(&us->ip_waitq);
- } else {
- US_DEBUGP("ERROR: Unwanted interrupt received!\n");
- }
-
- /* This return code is truly meaningless -- and I mean truly. It gets
- * ignored by other layers. It used to indicate if we wanted to get
- * another interrupt or disable the interrupt callback
- */
- return 0;
-}
-
-/* FIXME: this reset function doesn't really reset the port, and it
- * should. Actually it should probably do what it's doing here, and
- * reset the port physically
- */
-static int CB_reset(struct us_data *us)
-{
- unsigned char cmd[12];
- int result;
-
- US_DEBUGP("CB_reset\n");
-
- memset(cmd, 0xFF, sizeof(cmd));
- cmd[0] = SEND_DIAGNOSTIC;
- cmd[1] = 4;
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, us->ifnum, cmd, sizeof(cmd), HZ*5);
-
- /* long wait for reset */
- schedule_timeout(HZ*6);
-
- US_DEBUGP("CB_reset: clearing endpoint halt\n");
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
-
- US_DEBUGP("CB_reset done\n");
- return 0;
-}
-
-static int pop_CB_status(Scsi_Cmnd *srb);
-
-/* FIXME: we also need a CBI_command which sets up the completion
- * interrupt, and waits for it
- */
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
-{
- int result;
-
- US_DEBUGP("CBI gets a command:\n");
- US_DEBUG(us_show_command(srb));
-
- /* FIXME: we aren't setting the ip_wanted indicator early enough, which
- * causes some commands to never complete. This hangs the driver.
- */
-
- /* let's send the command via the control pipe */
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- 0, us->ifnum,
- srb->cmnd, srb->cmd_len, HZ*5);
-
- /* check the return code for the command */
- if (result < 0) {
- US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
-
- /* a stall is a fatal condition from the device */
- if (result == -EPIPE) {
- US_DEBUGP("-- Stall on control pipe detected. Clearing\n");
-
- US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
- usb_clear_halt(us->pusb_dev,
- usb_sndctrlpipe(us->pusb_dev, 0)));
- return USB_STOR_TRANSPORT_ERROR;
- }
-
- /* FIXME: we need to handle NAKs here */
- return USB_STOR_TRANSPORT_ERROR;
- }
-
- /* transfer the data payload for this command, if one exists*/
- if (us_transfer_length(srb)) {
- result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
- US_DEBUGP("CBI attempted to transfer data, result is 0x%x\n", result);
-
- /* FIXME: what do the return codes from us_transfer mean? */
- if ((result < 0) &&
- (result != USB_ST_DATAUNDERRUN) &&
- (result != USB_ST_STALL)) {
- return DID_ERROR << 16;
- }
- } /* if (us_transfer_length(srb)) */
-
- /* get status and return it */
- return pop_CB_status(srb);
-}
-
-/*
- * Control/Bulk status handler
- */
-
-static int pop_CB_status(Scsi_Cmnd *srb)
-{
- struct us_data *us = (struct us_data *)srb->host_scribble;
- int result;
- __u8 status[2];
- int retry = 5;
-
- US_DEBUGP("pop_CB_status, proto=0x%x\n", us->protocol);
- switch (us->protocol) {
- case US_PR_CB:
- /* get from control */
-
- while (retry--) {
- result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0),
- USB_REQ_GET_STATUS, USB_DIR_IN |
- USB_TYPE_STANDARD | USB_RECIP_DEVICE,
- 0, us->ifnum, status, sizeof(status), HZ*5);
- if (result != USB_ST_TIMEOUT)
- break;
- }
- if (result) {
- US_DEBUGP("Bad AP status request %d\n", result);
- return DID_ABORT << 16;
- }
- US_DEBUGP("Got AP status 0x%x 0x%x\n", status[0], status[1]);
- if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
- ( (status[0] & ~3) || status[1]))
- return (DID_OK << 16) | 2;
- else
- return USB_STOR_TRANSPORT_GOOD;
- break;
-
- /* FIXME: this should be in a separate function */
- case US_PR_CBI:
- /* get from interrupt pipe */
-
- /* add interrupt transfer, marked for removal */
- us->ip_wanted = 1;
-
- /* go to sleep until we get this interrup */
- /* FIXME: this should be changed to use a timeout */
- sleep_on(&us->ip_waitq);
-
- if (us->ip_wanted) {
- US_DEBUGP("Did not get interrupt on CBI\n");
- us->ip_wanted = 0;
- return USB_STOR_TRANSPORT_ERROR;
- }
-
- US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
-
- /* UFI gives us ASC and ASCQ, like a request sense */
- /* FIXME: is this right? do REQUEST_SENSE and INQUIRY need special
- * case handling?
- */
- if (us->subclass == US_SC_UFI) {
- if (srb->cmnd[0] == REQUEST_SENSE ||
- srb->cmnd[0] == INQUIRY)
- return USB_STOR_TRANSPORT_GOOD;
- else
- if (us->ip_data)
- return USB_STOR_TRANSPORT_FAILED;
- else
- return USB_STOR_TRANSPORT_GOOD;
- }
-
- /* otherwise, we interpret the data normally */
- switch (us->ip_data) {
- case 0x0001:
- return USB_STOR_TRANSPORT_GOOD;
- case 0x0002:
- return USB_STOR_TRANSPORT_FAILED;
- default:
- return USB_STOR_TRANSPORT_ERROR;
- }
- }
- US_DEBUGP("pop_CB_status, reached end of function\n");
- return USB_STOR_TRANSPORT_ERROR;
-}
-
-static int Bulk_reset(struct us_data *us)
-{
- int result;
-
- result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
- US_BULK_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- US_BULK_RESET_HARD, us->ifnum,
- NULL, 0, HZ*5);
- if (result)
- US_DEBUGP("Bulk hard reset failed %d\n", result);
- usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
- usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
-
- /* long wait for reset */
- schedule_timeout(HZ*6);
-
- return result;
-}
-
-/*
- * The bulk only protocol handler.
- * Uses the in and out endpoints to transfer commands and data
- */
-static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
-{
- struct bulk_cb_wrap bcb;
- struct bulk_cs_wrap bcs;
- int result;
- int pipe;
- int partial;
-
- /* set up the command wrapper */
- bcb.Signature = US_BULK_CB_SIGN;
- bcb.DataTransferLength = us_transfer_length(srb);
- bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7;
- bcb.Tag = srb->serial_number;
- bcb.Lun = 0;
- bcb.Length = srb->cmd_len;
-
- /* construct the pipe handle */
- pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
-
- /* copy the command payload */
- memset(bcb.CDB, 0, sizeof(bcb.CDB));
- memcpy(bcb.CDB, srb->cmnd, bcb.Length);
-
- /* send it to out endpoint */
- US_DEBUGP("Bulk command S 0x%x T 0x%x L %d F %d CL %d\n",
- bcb.Signature, bcb.Tag, bcb.DataTransferLength,
- bcb.Flags, bcb.Length);
- result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
- US_BULK_CB_WRAP_LEN, &partial, HZ*5);
- US_DEBUGP("Bulk command transfer result=%d\n", result);
-
- /* if we stall, we need to clear it before we go on */
- if (result == -EPIPE) {
- US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
- usb_clear_halt(us->pusb_dev, pipe);
- }
-
- /* if the command transfered well, then we go to the data stage */
- /* FIXME: Regardless of the status of the data stage, we go on to the
- * status stage. Note that this implies that if a command is
- * partially successful, we rely on the device reporting an error
- * the CSW. The spec says that the device may just decide to short us.
- */
- if (result == 0) {
- /* send/receive data payload, if there is any */
- if (bcb.DataTransferLength) {
- result = us_transfer(srb, bcb.Flags);
- US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-#if 0
- if ((result < 0) && (result != USB_ST_DATAUNDERRUN)
- && (result != USB_ST_STALL)) {
- US_DEBUGP("Bulk data transfer result 0x%x\n", result);
- return DID_ABORT << 16;
- }
-#endif
- }
- }
-
- /* See flow chart on pg 15 of the Bulk Only Transport spec for
- * an explanation of how this code works.
- */
-
- /* construct the pipe handle */
- pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
-
- /* get CSW for device status */
- result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
- US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
- /* did the attempt to read the CSW fail? */
- if (result == -EPIPE) {
- US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
- usb_clear_halt(us->pusb_dev, pipe);
-
- /* get the status again */
- result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
- US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
- /* if it fails again, we need a reset and return an error*/
- if (result == -EPIPE) {
- Bulk_reset(us);
- return (DID_ABORT << 16);
- }
- }
-
- /* if we still have a failure at this point, we're in trouble */
- if (result) {
- US_DEBUGP("Bulk status result = 0x%x\n", result);
- return DID_ABORT << 16;
- }
-
- /* check bulk status */
- US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
- bcs.Signature, bcs.Tag, bcs.Residue, bcs.Status);
- if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
- bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
- US_DEBUGP("Bulk logical error\n");
- return DID_ABORT << 16;
- }
-
- /* based on the status code, we report good or bad */
- switch (bcs.Status) {
- case US_BULK_STAT_OK:
- /* if there is residue, we really didn't finish the command */
- if (bcs.Residue)
- return DID_ERROR << 16;
- else
- return DID_OK << 16;
-
- case US_BULK_STAT_FAIL:
- return DID_ERROR << 16;
-
- case US_BULK_STAT_PHASE:
- Bulk_reset(us);
- return DID_ERROR << 16;
- }
-
- return DID_OK << 16; /* check sense required */
-}
-
-/***********************************************************************
- * Host functions
- ***********************************************************************/
-
-/* detect adapter (always true ) */
-static int us_detect(struct SHT *sht)
-{
- /* FIXME - not nice at all, but how else ? */
- struct us_data *us = (struct us_data *)sht->proc_dir;
- char name[32];
-
- /* set up our name */
- sprintf(name, "usbscsi%d", us->host_number);
- sht->name = sht->proc_name = kmalloc(strlen(name)+1, GFP_KERNEL);
- if (!sht->proc_name)
- return 0;
- strcpy(sht->proc_name, name);
-
- /* we start with no /proc directory entry */
- sht->proc_dir = NULL;
-
- /* register the host */
- us->host = scsi_register(sht, sizeof(us));
- if (us->host) {
- us->host->hostdata[0] = (unsigned long)us;
- us->host_no = us->host->host_no;
- return 1;
- }
-
- /* odd... didn't register properly. Abort and free pointers */
- kfree(sht->proc_name);
- sht->proc_name = NULL;
- sht->name = NULL;
- return 0;
-}
-
-/* release - must be here to stop scsi
- * from trying to release IRQ etc.
- * Kill off our data
- */
-static int us_release(struct Scsi_Host *psh)
-{
- struct us_data *us = (struct us_data *)psh->hostdata[0];
- struct us_data *prev = (struct us_data *)&us_list;
-
- if (us->irq_handle) {
- usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
- us->irq_handle = NULL;
- }
- if (us->pusb_dev)
- usb_deregister(&storage_driver);
-
- /* FIXME - leaves hanging host template copy */
- /* (because scsi layer uses it after removal !!!) */
- while (prev->next != us)
- prev = prev->next;
- prev->next = us->next;
- return 0;
-}
-
-/* run command */
-static int us_command( Scsi_Cmnd *srb )
-{
- US_DEBUGP("Bad use of us_command\n");
-
- return DID_BAD_TARGET << 16;
-}
-
-/* run command */
-static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
-{
- struct us_data *us = (struct us_data *)srb->host->hostdata[0];
-
- US_DEBUGP("Command wakeup\n");
- if (us->srb) {
- /* busy */
- }
- srb->host_scribble = (unsigned char *)us;
- us->srb = srb;
- srb->scsi_done = done;
- us->action = US_ACT_COMMAND;
-
- /* wake up the process task */
-
- wake_up_interruptible(&us->waitq);
-
- return 0;
-}
-
-/* FIXME: This doesn't actually abort anything */
-static int us_abort( Scsi_Cmnd *srb )
-{
- return 0;
-}
-
-static int us_bus_reset( Scsi_Cmnd *srb )
-{
- // struct us_data *us = (struct us_data *)srb->host->hostdata[0];
-
- US_DEBUGP("Bus reset requested\n");
- // us->transport_reset(us);
- return SUCCESS;
-}
-
-/* FIXME: This doesn't actually reset anything */
-static int us_host_reset( Scsi_Cmnd *srb )
-{
- return 0;
-}
-
-/***********************************************************************
- * /proc/scsi/ functions
- ***********************************************************************/
-
-/* we use this macro to help us write into the buffer */
-#undef SPRINTF
-#define SPRINTF(args...) do { if (pos < (buffer + length)) pos += sprintf (pos, ## args); } while (0)
-
-int usb_stor_proc_info (char *buffer, char **start, off_t offset,
- int length, int hostno, int inout)
-{
- struct us_data *us = us_list;
- char *pos = buffer;
- char *tmp_ptr;
-
- /* find our data from hostno */
- while (us) {
- if (us->host_no == hostno)
- break;
- us = us->next;
- }
-
- /* if we couldn't find it, we return an error */
- if (!us)
- return -ESRCH;
-
- /* if someone is sending us data, just throw it away */
- if (inout)
- return length;
-
- /* print the controler name */
- SPRINTF ("Host scsi%d: usb-storage\n", hostno);
-
- /* print product and vendor strings */
- tmp_ptr = kmalloc(256, GFP_KERNEL);
- if (!us->pusb_dev || !tmp_ptr) {
- SPRINTF(" Vendor: Unknown Vendor\n");
- SPRINTF(" Product: Unknown Product\n");
- } else {
- SPRINTF(" Vendor: ");
- if (usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer, tmp_ptr, 256) > 0)
- SPRINTF("%s\n", tmp_ptr);
- else
- SPRINTF("Unknown Vendor\n");
-
- SPRINTF(" Product: ");
- if (usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct, tmp_ptr, 256) > 0)
- SPRINTF("%s\n", tmp_ptr);
- else
- SPRINTF("Unknown Product\n");
- kfree(tmp_ptr);
- }
-
- SPRINTF(" Protocol: ");
- switch (us->protocol) {
- case US_PR_CB:
- SPRINTF("Control/Bulk\n");
- break;
-
- case US_PR_CBI:
- SPRINTF("Control/Bulk/Interrupt\n");
- break;
-
- case US_PR_BULK:
- SPRINTF("Bulk only\n");
- break;
-
- default:
- SPRINTF("Unknown Protocol\n");
- break;
- }
-
- /* show the GUID of the device */
- SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
-
- /*
- * Calculate start of next buffer, and return value.
- */
- *start = buffer + offset;
-
- if ((pos - buffer) < offset)
- return (0);
- else if ((pos - buffer - offset) < length)
- return (pos - buffer - offset);
- else
- return (length);
-}
-
-/*
- * this defines our 'host'
- */
-
-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 */
- FALSE, /* use_clustering */
- TRUE, /* use_new_eh_code */
- TRUE /* emulated */
-};
-
-static unsigned char sense_notready[] = {
- 0x70, /* current error */
- 0x00,
- 0x02, /* not ready */
- 0x00,
- 0x00,
- 0x0a, /* additional length */
- 0x00,
- 0x00,
- 0x00,
- 0x00,
- 0x04, /* not ready */
- 0x03, /* manual intervention */
- 0x00,
- 0x00,
- 0x00,
- 0x00
-};
-
-static int usb_stor_control_thread(void * __us)
-{
- struct us_data *us = (struct us_data *)__us;
- int action;
-
- lock_kernel();
-
- /*
- * This thread doesn't need any user-level access,
- * so get rid of all our resources..
- */
- daemonize();
-
- sprintf(current->comm, "usbscsi%d", us->host_number);
-
- unlock_kernel();
-
- up(us->notify);
-
- for(;;) {
- siginfo_t info;
- int unsigned long signr;
-
- interruptible_sleep_on(&us->waitq);
-
- action = us->action;
- us->action = 0;
-
- /* FIXME: we need to examine placment of break; and
- * scsi_done() calls */
-
- switch (action) {
- case US_ACT_COMMAND:
- /* bad device */
- if (us->srb->target || us->srb->lun) {
- US_DEBUGP( "Bad device number (%d/%d) or dev 0x%x\n",
- us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev);
- us->srb->result = DID_BAD_TARGET << 16;
-
- us->srb->scsi_done(us->srb);
- us->srb = NULL;
- break;
- }
-
- /* our device has gone - pretend not ready */
- /* FIXME: we also need to handle INQUIRY here,
- * probably */
- if (!us->pusb_dev) {
- if (us->srb->cmnd[0] == REQUEST_SENSE) {
- memcpy(us->srb->request_buffer, sense_notready,
- sizeof(sense_notready));
- us->srb->result = DID_OK << 16;
- } else {
- us->srb->result = (DID_OK << 16) | 2;
- }
-
- us->srb->scsi_done(us->srb);
- us->srb = NULL;
- break;
- }
-
- /* we've got a command, let's do it! */
- US_DEBUG(us_show_command(us->srb));
-
- /* FIXME: this is to support Shuttle E-USB bridges, it
- * appears */
- if (us->srb->cmnd[0] == START_STOP &&
- us->pusb_dev->descriptor.idProduct == 0x0001 &&
- us->pusb_dev->descriptor.idVendor == 0x04e6)
- us->srb->result = DID_OK << 16;
- else {
- us->proto_handler(us->srb, us);
- }
-
- US_DEBUGP("scsi cmd done, result=0x%x\n", us->srb->result);
- us->srb->scsi_done(us->srb);
- us->srb = NULL;
- break;
-
- case US_ACT_ABORT:
- break;
-
- case US_ACT_DEVICE_RESET:
- break;
-
- case US_ACT_BUS_RESET:
- break;
-
- case US_ACT_HOST_RESET:
- break;
-
- } /* end switch on action */
-
- if (signal_pending(current)) {
- /* sending SIGUSR1 makes us print out some info */
- spin_lock_irq(¤t->sigmask_lock);
- signr = dequeue_signal(¤t->blocked, &info);
- spin_unlock_irq(¤t->sigmask_lock);
-
- if (signr == SIGUSR2) {
- usb_stor_debug = !usb_stor_debug;
- printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug);
- } else {
- break; /* exit the loop on any other signal */
- }
- }
- }
-
- // MOD_DEC_USE_COUNT;
-
- printk("usb_stor_control_thread exiting\n");
-
- /* FIXME: this is a hack to allow for debugging */
- // scsi_unregister_module(MODULE_SCSI_HA, us->htmplt);
-
- return 0;
-}
-
-/* Probe to see if a new device is actually a SCSI device */
-static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
-{
- struct usb_interface_descriptor *interface;
- int i;
- char mf[32]; /* manufacturer */
- char prod[32]; /* product */
- char serial[32]; /* serial number */
- struct us_data *ss = NULL;
- unsigned int flags = 0;
- GUID(guid); /* Global Unique Identifier */
- struct us_data *prev;
- Scsi_Host_Template *htmplt;
- int protocol = 0;
- int subclass = 0;
- struct usb_interface_descriptor *altsetting =
- &(dev->actconfig->interface[ifnum].altsetting[0]);
-
- /* clear the GUID and fetch the strings */
- GUID_CLEAR(guid);
- memset(mf, 0, sizeof(mf));
- memset(prod, 0, sizeof(prod));
- memset(serial, 0, sizeof(serial));
- if (dev->descriptor.iManufacturer)
- usb_string(dev, dev->descriptor.iManufacturer, mf, sizeof(mf));
- if (dev->descriptor.iProduct)
- usb_string(dev, dev->descriptor.iProduct, prod, sizeof(prod));
- if (dev->descriptor.iSerialNumber)
- usb_string(dev, dev->descriptor.iSerialNumber, serial, sizeof(serial));
-
- /* let's examine the device now */
-
- /* We make an exception for the shuttle E-USB */
- if (dev->descriptor.idVendor == 0x04e6 &&
- dev->descriptor.idProduct == 0x0001) {
- protocol = US_PR_CB;
- subclass = US_SC_8070; /* an assumption */
- } else if (dev->descriptor.bDeviceClass != 0 ||
- altsetting->bInterfaceClass != USB_CLASS_MASS_STORAGE ||
- altsetting->bInterfaceSubClass < US_SC_MIN ||
- altsetting->bInterfaceSubClass > US_SC_MAX) {
- /* if it's not a mass storage, we go no further */
- return NULL;
- }
-
- /* At this point, we know we've got a live one */
- US_DEBUGP("USB Mass Storage device detected\n");
-
- /* Create a GUID for this device */
- if (dev->descriptor.iSerialNumber && serial[0]) {
- /* If we have a serial number, and it's a non-NULL string */
- make_guid(guid, dev->descriptor.idVendor,
- dev->descriptor.idProduct, serial);
- } else {
- /* We don't have a serial number, so we use 0 */
- make_guid(guid, dev->descriptor.idVendor,
- dev->descriptor.idProduct, "0");
- }
-
- /* Now check if we have seen this GUID before, and restore
- * the flags if we find it
- */
- for (ss = us_list; ss != NULL; ss = ss->next) {
- if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) {
- US_DEBUGP("Found existing GUID " GUID_FORMAT "\n",
- GUID_ARGS(guid));
- flags = ss->flags;
- break;
- }
- }
-
- /* If ss == NULL, then this is a new device. Allocate memory for it */
- if (!ss) {
- if ((ss = (struct us_data *)kmalloc(sizeof(*ss),
- GFP_KERNEL)) == NULL) {
- printk(KERN_WARNING USB_STORAGE "Out of memory\n");
- return NULL;
- }
- memset(ss, 0, sizeof(struct us_data));
- }
-
- /* Initialize the us_data structure with some useful info */
- interface = altsetting;
- ss->flags = flags;
- ss->ifnum = ifnum;
- ss->pusb_dev = dev;
- ss->attention_done = 0;
-
- /* If the device has subclass and protocol, then use that. Otherwise,
- * take data from the specific interface.
- */
- if (subclass) {
- ss->subclass = subclass;
- ss->protocol = protocol;
- } else {
- ss->subclass = interface->bInterfaceSubClass;
- ss->protocol = interface->bInterfaceProtocol;
- }
-
- /* set the handler pointers based on the protocol */
- US_DEBUGP("Transport: ");
- switch (ss->protocol) {
- case US_PR_CB:
- US_DEBUGPX("Control/Bulk\n");
- ss->transport = CB_transport;
- ss->transport_reset = CB_reset;
- break;
-
- case US_PR_CBI:
- US_DEBUGPX("Control/Bulk/Interrupt\n");
- ss->transport = CB_transport;
- ss->transport_reset = CB_reset;
- break;
-
- case US_PR_BULK:
- US_DEBUGPX("Bulk\n");
- ss->transport = Bulk_transport;
- ss->transport_reset = Bulk_reset;
- break;
-
- default:
- US_DEBUGPX("Unknown\n");
- kfree(ss);
- return NULL;
- break;
- }
-
- /*
- * We are expecting a minimum of 2 endpoints - in and out (bulk).
- * An optional interrupt is OK (necessary for CBI protocol).
- * We will ignore any others.
- */
- for (i = 0; i < interface->bNumEndpoints; i++) {
- /* is it an BULK endpoint? */
- if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK) {
- if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN)
- ss->ep_in = interface->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- else
- ss->ep_out = interface->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- }
-
- /* is it an interrupt endpoint? */
- if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT) {
- ss->ep_int = interface->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- }
- }
- US_DEBUGP("Endpoints In %d Out %d Int %d\n",
- ss->ep_in, ss->ep_out, ss->ep_int);
-
- /* Do some basic sanity checks, and bail if we find a problem */
- if (usb_set_interface(dev, interface->bInterfaceNumber, 0) ||
- !ss->ep_in || !ss->ep_out ||
- (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
- US_DEBUGP("Problems with device\n");
- if (ss->host) {
- scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
- kfree(ss->htmplt->name);
- kfree(ss->htmplt);
- }
-
- kfree(ss);
- return NULL;
- }
-
- /* If this is a new device (i.e. we haven't seen it before), we need to
- * generate a scsi host definition, and register with scsi above us
- */
- if (!ss->host) {
- /* copy the GUID we created before */
- US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
- memcpy(ss->guid, guid, sizeof(guid));
-
- /* set class specific stuff */
- US_DEBUGP("Protocol: ");
- switch (ss->subclass) {
- case US_SC_RBC:
- US_DEBUGPX("Reduced Block Commands\n");
- break;
-
- case US_SC_8020:
- US_DEBUGPX("8020\n");
- break;
-
- case US_SC_QIC:
- US_DEBUGPX("QIC157\n");
- break;
-
- case US_SC_8070:
- US_DEBUGPX("8070\n");
- break;
-
- case US_SC_SCSI:
- US_DEBUGPX("Transparent SCSI\n");
- ss->proto_handler = transparent_scsi_command;
- break;
-
- case US_SC_UFI:
- US_DEBUGPX("UFI\n");
- ss->proto_handler = ufi_command;
- break;
-
- default:
- US_DEBUGPX("Unknown\n");
- break;
- }
-
- /* We only handle certain protocols. Currently, these are
- *the only ones that devices use.
- */
- if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) {
- US_DEBUGP("Sorry, we do not support that protocol yet.\n");
- US_DEBUGP("If you have a device which uses one of the unsupported\n");
- US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n");
-
- kfree(ss);
- return NULL;
- }
-
- /* Allocate memory for the SCSI Host Template */
- if ((htmplt = (Scsi_Host_Template *)
- kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
-
- printk(KERN_WARNING USB_STORAGE "Out of memory\n");
-
- kfree(ss);
- return NULL;
- }
-
- /* Initialize the host template based on the default one */
- memcpy(htmplt, &my_host_template, sizeof(my_host_template));
-
- /* Grab the next host number */
- ss->host_number = my_host_number++;
-
- /* MDD: FIXME: this is bad. We abuse this pointer so we
- * can pass the ss pointer to the host controler thread
- * in us_detect
- */
- (struct us_data *)htmplt->proc_dir = ss;
-
- /* shuttle E-USB */
- if (dev->descriptor.idVendor == 0x04e6 &&
- dev->descriptor.idProduct == 0x0001) {
- __u8 qstat[2];
- int result;
-
- result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0),
- 1, 0xC0,
- 0, ss->ifnum,
- qstat, 2, HZ*5);
- US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
- init_waitqueue_head(&ss->ip_waitq);
- ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
- 255, (void *)ss, &ss->irq_handle);
- if (result)
- return NULL;
-
- interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6);
- } else if (ss->protocol == US_PR_CBI)
- {
- int result;
-
- init_waitqueue_head(&ss->ip_waitq);
-
- /* set up the IRQ pipe and handler */
- /* FIXME: This needs to get the period from the device */
- ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
- 255, (void *)ss, &ss->irq_handle);
- if (result) {
- US_DEBUGP("usb_request_irq failed (0x%x), No interrupt for CBI\n",
- result);
- }
- }
-
-
- /* start up our thread */
- {
- DECLARE_MUTEX_LOCKED(sem);
-
- init_waitqueue_head(&ss->waitq);
-
- ss->notify = &sem;
- ss->pid = kernel_thread(usb_stor_control_thread, ss,
- CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
- if (ss->pid < 0) {
- printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
- kfree(htmplt);
-
- kfree(ss);
- return NULL;
- }
-
- /* wait for it to start */
- down(&sem);
- }
-
- /* now register - our detect function will be called */
- scsi_register_module(MODULE_SCSI_HA, htmplt);
-
- /* put us in the list */
- prev = (struct us_data *)&us_list;
- while (prev->next)
- prev = prev->next;
- prev->next = ss;
- }
-
- printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n");
- printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum);
-
- return ss;
-}
-
-/* Handle a disconnect event from the USB core */
-static void storage_disconnect(struct usb_device *dev, void *ptr)
-{
- struct us_data *ss = ptr;
-
- if (!ss)
- return;
-
- ss->pusb_dev = NULL;
- // MOD_DEC_USE_COUNT;
-}
-
-
-/***********************************************************************
- * Initialization and registration
- ***********************************************************************/
-
-int __init usb_stor_init(void)
-{
- // MOD_INC_USE_COUNT;
-
- if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
- printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\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;
-
- printk(KERN_INFO "USB Mass Storage support registered.\n");
- return 0;
-}
-
-void __exit usb_stor_exit(void)
-{
- usb_deregister(&storage_driver) ;
-}
-
-module_init(usb_stor_init) ;
-module_exit(usb_stor_exit) ;
+++ /dev/null
-/* Driver for USB mass storage - include file
- *
- * (c) 1999 Michael Gee (michael@linuxspecific.com)
- * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
- *
- */
-
-#include <linux/config.h>
-
-#define USB_STORAGE "usb-storage: "
-
-extern int usb_stor_debug;
-
-#ifdef CONFIG_USB_STORAGE_DEBUG
-void us_show_command(Scsi_Cmnd *srb);
-#define US_DEBUGP(x...) { if(usb_stor_debug) printk( KERN_DEBUG USB_STORAGE ## x ); }
-#define US_DEBUGPX(x...) { if(usb_stor_debug) printk( ## x ); }
-#define US_DEBUG(x) { if(usb_stor_debug) x; }
-#else
-#define US_DEBUGP(x...)
-#define US_DEBUGPX(x...)
-#define US_DEBUG(x)
-#endif
-
-/* bit set if input */
-extern unsigned char us_direction[256/8];
-#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
-
-/* Sub Classes */
-
-#define US_SC_RBC 1 /* Typically, flash devices */
-#define US_SC_8020 2 /* CD-ROM */
-#define US_SC_QIC 3 /* QIC-157 Tapes */
-#define US_SC_UFI 4 /* Floppy */
-#define US_SC_8070 5 /* Removable media */
-#define US_SC_SCSI 6 /* Transparent */
-#define US_SC_MIN US_SC_RBC
-#define US_SC_MAX US_SC_SCSI
-
-/* Protocols */
-
-#define US_PR_CB 1 /* Control/Bulk w/o interrupt */
-#define US_PR_CBI 0 /* Control/Bulk/Interrupt */
-#define US_PR_BULK 0x50 /* bulk only */
-
-/*
- * Bulk only data structures (Zip 100, for example)
- */
-
-/* command block wrapper */
-struct bulk_cb_wrap {
- __u32 Signature; /* contains 'USBC' */
- __u32 Tag; /* unique per command id */
- __u32 DataTransferLength; /* size of data */
- __u8 Flags; /* direction in bit 0 */
- __u8 Lun; /* LUN normally 0 */
- __u8 Length; /* of of the CDB */
- __u8 CDB[16]; /* max command */
-};
-
-#define US_BULK_CB_WRAP_LEN 31
-#define US_BULK_CB_SIGN 0x43425355
-#define US_BULK_FLAG_IN 1
-#define US_BULK_FLAG_OUT 0
-
-/* command status wrapper */
-struct bulk_cs_wrap {
- __u32 Signature; /* should = 'USBS' */
- __u32 Tag; /* same as original command */
- __u32 Residue; /* amount not transferred */
- __u8 Status; /* see below */
- __u8 Filler[18];
-};
-
-#define US_BULK_CS_WRAP_LEN 13
-#define US_BULK_CS_SIGN 0x53425355
-#define US_BULK_STAT_OK 0
-#define US_BULK_STAT_FAIL 1
-#define US_BULK_STAT_PHASE 2
-
-#define US_BULK_RESET 0xff
-#define US_BULK_RESET_SOFT 1
-#define US_BULK_RESET_HARD 0
-
-/*
- * Transport return codes
- */
-
-#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */
-#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */
-#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead */
-
-/*
- * CBI style
- */
-
-#define US_CBI_ADSC 0
-
-/*
- * GUID definitions
- */
-
-#define GUID(x) __u32 x[3]
-#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2])
-#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0;
-#define GUID_NONE(x) (!x[0] && !x[1] && !x[2])
-#define GUID_FORMAT "%08x%08x%08x"
-#define GUID_ARGS(x) x[0], x[1], x[2]
-
-static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial)
-{
- pg[0] = (vendor << 16) | product;
- pg[1] = pg[2] = 0;
- while (*serial) {
- pg[1] <<= 4;
- pg[1] |= pg[2] >> 28;
- pg[2] <<= 4;
- if (*serial >= 'a')
- *serial -= 'a' - 'A';
- pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0'
- : *serial - 'A' + 10;
- serial++;
- }
-}
-
-/* Flag definitions */
-#define US_FL_IP_STATUS 0x00000001 /* status uses interrupt */
-#define US_FL_FIXED_COMMAND 0x00000002 /* expand commands to fixed size */
-#define US_FL_MODE_XLATE 0x00000004 /* translate _6 to _10 comands for
- Win/MacOS compatibility */
+++ /dev/null
-
-/* Driver for USB mass storage (scsi-like) devices
- *
- * (C) Michael Gee (michael@linuxspecific.com) 1999
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/miscdevice.h>
-#include <linux/random.h>
-#include <linux/poll.h>
-#include <linux/init.h>
-#include <linux/malloc.h>
-#include <linux/spinlock.h>
-
-#include <linux/blk.h>
-#include "../scsi/scsi.h"
-#include "../scsi/hosts.h"
-#include "../scsi/sd.h"
-
-#include "usb.h"
-#include "usb_storage.h"
-
-void us_show_command(Scsi_Cmnd *srb)
-{
- char *what = NULL;
-
- switch (srb->cmnd[0]) {
- case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
- case REZERO_UNIT: what = "REZERO_UNIT"; break;
- case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
- case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
- case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
- case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
- case READ_6: what = "READ_6"; break;
- case WRITE_6: what = "WRITE_6"; break;
- case SEEK_6: what = "SEEK_6"; break;
- case READ_REVERSE: what = "READ_REVERSE"; break;
- case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
- case SPACE: what = "SPACE"; break;
- case INQUIRY: what = "INQUIRY"; break;
- case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
- case MODE_SELECT: what = "MODE_SELECT"; break;
- case RESERVE: what = "RESERVE"; break;
- case RELEASE: what = "RELEASE"; break;
- case COPY: what = "COPY"; break;
- case ERASE: what = "ERASE"; break;
- case MODE_SENSE: what = "MODE_SENSE"; break;
- case START_STOP: what = "START_STOP"; break;
- case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
- case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
- case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
- case SET_WINDOW: what = "SET_WINDOW"; break;
- case READ_CAPACITY: what = "READ_CAPACITY"; break;
- case READ_10: what = "READ_10"; break;
- case WRITE_10: what = "WRITE_10"; break;
- case SEEK_10: what = "SEEK_10"; break;
- case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
- case VERIFY: what = "VERIFY"; break;
- case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
- case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
- case SEARCH_LOW: what = "SEARCH_LOW"; break;
- case SET_LIMITS: what = "SET_LIMITS"; break;
- case READ_POSITION: what = "READ_POSITION"; break;
- case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
- case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
- case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
- case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
- case COMPARE: what = "COMPARE"; break;
- case COPY_VERIFY: what = "COPY_VERIFY"; break;
- case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
- case READ_BUFFER: what = "READ_BUFFER"; break;
- case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
- case READ_LONG: what = "READ_LONG"; break;
- case WRITE_LONG: what = "WRITE_LONG"; break;
- case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
- case WRITE_SAME: what = "WRITE_SAME"; break;
- case READ_TOC: what = "READ_TOC"; break;
- case LOG_SELECT: what = "LOG_SELECT"; break;
- case LOG_SENSE: what = "LOG_SENSE"; break;
- case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
- case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
- case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
- case READ_12: what = "READ_12"; break;
- case WRITE_12: what = "WRITE_12"; break;
- case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
- case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
- case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
- case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
- case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
- case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
- case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
- default: break;
- }
- printk(KERN_DEBUG USB_STORAGE
- "Command %s (%d bytes)\n", what, srb->cmd_len);
- printk(KERN_DEBUG USB_STORAGE
- " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
- srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
- srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
-}
#
-# Filesystem configuration
+# File system configuration
#
mainmenu_option next_comment
-comment 'Filesystems'
+comment 'File systems'
bool 'Quota support' CONFIG_QUOTA
tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
tristate 'Minix fs support' CONFIG_MINIX_FS
-tristate 'NTFS filesystem support (read only)' CONFIG_NTFS_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
-tristate 'OS/2 HPFS filesystem support' CONFIG_HPFS_FS
+tristate 'OS/2 HPFS file system support' CONFIG_HPFS_FS
-bool '/proc filesystem support' CONFIG_PROC_FS
+bool '/proc file system support' CONFIG_PROC_FS
-dep_bool '/dev filesystem support (EXPERIMENTAL)' CONFIG_DEVFS_FS $CONFIG_EXPERIMENTAL
+dep_bool '/dev file system support (EXPERIMENTAL)' CONFIG_DEVFS_FS $CONFIG_EXPERIMENTAL
dep_bool ' Debug devfs' CONFIG_DEVFS_DEBUG $CONFIG_DEVFS_FS
# It compiles as a module for testing only. It should not be used
# as a module in general. If we make this "tristate", a bunch of people
# who don't know what they are doing turn it on and complain when it
# breaks.
-dep_bool '/dev/pts filesystem for Unix98 PTYs' CONFIG_DEVPTS_FS $CONFIG_UNIX98_PTYS
+dep_bool '/dev/pts file system for Unix98 PTYs' CONFIG_DEVPTS_FS $CONFIG_UNIX98_PTYS
-dep_tristate 'QNX4 filesystem support (read only) (EXPERIMENTAL)' CONFIG_QNX4FS_FS $CONFIG_EXPERIMENTAL
+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
-tristate 'ROM filesystem support' CONFIG_ROMFS_FS
+tristate 'ROM file system support' CONFIG_ROMFS_FS
tristate 'Second extended fs support' CONFIG_EXT2_FS
-tristate 'System V and Coherent filesystem support' CONFIG_SYSV_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 filesystem write support (DANGEROUS)' CONFIG_SYSV_FS_WRITE
+ bool ' SYSV file system write support (DANGEROUS)' CONFIG_SYSV_FS_WRITE
fi
-tristate 'UDF filesystem support (read only)' CONFIG_UDF_FS
+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
-tristate 'UFS filesystem support (read only)' CONFIG_UFS_FS
+tristate 'UFS file system support (read only)' CONFIG_UFS_FS
if [ "$CONFIG_UFS_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' UFS filesystem write support (DANGEROUS)' CONFIG_UFS_FS_WRITE
+ bool ' UFS file system write support (DANGEROUS)' CONFIG_UFS_FS_WRITE
fi
if [ "$CONFIG_NET" = "y" ]; then
if [ "$CONFIG_NFSD_V3" = "y" ]; then
define_bool CONFIG_LOCKD_V4 y
fi
- tristate 'SMB file system support (to mount WfW shares etc.)' CONFIG_SMB_FS
+ 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
i_name = cp;
i_arg = 0;
for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
- /* nothing */
+ /* nothing */ ;
while ((*cp == ' ') || (*cp == '\t'))
*cp++ = '\0';
if (*cp)
*/
void ext2_put_inode (struct inode * inode)
{
+ lock_kernel();
ext2_discard_prealloc (inode);
+ unlock_kernel();
}
/*
*/
void ext2_delete_inode (struct inode * inode)
{
+ lock_kernel();
+
if (is_bad_inode(inode) ||
inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
if (inode->i_blocks)
ext2_truncate (inode);
ext2_free_inode (inode);
+
+ unlock_kernel();
}
#define inode_bmap(inode, nr) (le32_to_cpu((inode)->u.ext2_i.i_data[(nr)]))
void ext2_write_inode (struct inode * inode)
{
+ lock_kernel();
ext2_update_inode (inode, 0);
+ unlock_kernel();
}
int ext2_sync_inode (struct inode *inode)
static inline void sync_one(struct inode *inode)
{
if (inode->i_state & I_LOCK) {
+ __iget(inode);
spin_unlock(&inode_lock);
__wait_on_inode(inode);
+ iput(inode);
spin_lock(&inode_lock);
} else {
list_del(&inode->i_list);
BUG();
if (!(inode->i_state & I_FREEING))
BUG();
+ if (inode->i_state & I_CLEAR)
+ BUG();
wait_on_inode(inode);
if (IS_QUOTAINIT(inode))
DQUOT_DROP(inode);
bdput(inode->i_bdev);
inode->i_bdev = NULL;
}
- inode->i_state = 0;
+ inode->i_state = I_CLEAR;
}
/*
entry = entry->prev;
inode = INODE(tmp);
+ if (inode->i_state & (I_FREEING|I_CLEAR))
+ BUG();
if (!CAN_UNUSE(inode))
continue;
if (inode->i_count)
if (!(inode->i_state & I_FREEING))
__iget(inode);
else
+ /*
+ * Handle the case where s_op->clear_inode is not been
+ * called yet, and somebody is calling igrab
+ * while the inode is getting freed.
+ */
inode = NULL;
spin_unlock(&inode_lock);
if (inode)
list_del(&inode->i_list);
INIT_LIST_HEAD(&inode->i_list);
inode->i_state|=I_FREEING;
+ spin_unlock(&inode_lock);
+
+ destroy = 1;
if (op && op->delete_inode) {
void (*delete)(struct inode *) = op->delete_inode;
- spin_unlock(&inode_lock);
if (inode->i_data.nrpages)
truncate_inode_pages(&inode->i_data, 0);
+ /* s_op->delete_inode internally recalls clear_inode() */
delete(inode);
- spin_lock(&inode_lock);
- }
- }
- if (list_empty(&inode->i_hash)) {
- list_del(&inode->i_list);
- INIT_LIST_HEAD(&inode->i_list);
- inode->i_state|=I_FREEING;
- spin_unlock(&inode_lock);
- clear_inode(inode);
- destroy = 1;
+ } else
+ clear_inode(inode);
+ if (inode->i_state != I_CLEAR)
+ BUG();
+
spin_lock(&inode_lock);
- }
- else
- {
- if (!(inode->i_state & I_DIRTY)) {
+ } else {
+ if (!list_empty(&inode->i_hash)) {
+ if (!(inode->i_state & I_DIRTY)) {
+ list_del(&inode->i_list);
+ list_add(&inode->i_list,
+ &inode_unused);
+ }
+ inodes_stat.nr_unused++;
+ } else {
+ /* magic nfs path */
list_del(&inode->i_list);
- list_add(&inode->i_list,
- &inode_unused);
+ INIT_LIST_HEAD(&inode->i_list);
+ inode->i_state|=I_FREEING;
+ spin_unlock(&inode_lock);
+ clear_inode(inode);
+ destroy = 1;
+ spin_lock(&inode_lock);
}
- inodes_stat.nr_unused++;
}
#ifdef INODE_PARANOIA
if (inode->i_flock)
call->a_args.lock = *lock;
call->a_args.lock.caller = system_utsname.nodename;
- init_waitqueue_head(&lock->fl.fl_wait);
+ init_waitqueue_head(&call->a_args.lock.fl.fl_wait);
/* set default data area */
call->a_args.lock.oh.data = call->a_owner;
3, 24, nlm_procedures,
};
+#ifdef CONFIG_LOCKD_V4
+extern struct rpc_version nlm_version4;
+#endif
+
static struct rpc_version * nlm_versions[] = {
NULL,
&nlm_version1,
NULL,
&nlm_version3,
+#ifdef CONFIG_LOCKD_V4
+ &nlm_version4,
+#endif
};
static struct rpc_stat nlm_stats;
static void minix_delete_inode(struct inode *inode)
{
+ lock_kernel();
+
inode->i_size = 0;
minix_truncate(inode);
minix_free_inode(inode);
+
+ unlock_kernel();
}
static void minix_commit_super(struct super_block * sb)
{
struct buffer_head *bh;
+ lock_kernel();
bh = minix_update_inode(inode);
+ unlock_kernel();
brelse(bh);
}
}
n = 0;
timeout = init_timeout;
- init_timeout <<= 1;
+ if (init_timeout < max_timeout)
+ init_timeout <<= 1;
if (!major_timeout_seen) {
printk(KERN_WARNING "NCP server not responding\n");
}
#include <linux/sunrpc/stats.h>
#include <linux/nfs_fs.h>
#include <linux/lockd/bind.h>
+#include <linux/smp_lock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
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 {
if (failed)
printk("NFS: inode %ld had %d failed requests\n",
inode->i_ino, failed);
+ unlock_kernel();
+
clear_inode(inode);
}
*/
for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
result = list_entry(lp,struct dentry, d_alias);
- if (! IS_ROOT(result) || inode->i_sb->s_root == result) {
+ if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
dget(result);
iput(inode);
return result;
iput(inode);
return ERR_PTR(-ENOMEM);
}
+ result->d_flags |= DCACHE_NFSD_DISCONNECTED;
d_rehash(result); /* so a dput won't loose it */
return result;
}
#ifdef NFSD_PARANOIA
if (!IS_ROOT(target))
printk("nfsd: d_splice with no-root target: %s/%s\n", parent->d_name.name, name->name);
+ if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED))
+ printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", parent->d_name.name, name->name);
#endif
name->hash = full_name_hash(name->name, name->len);
tdentry = d_alloc(parent, name);
tdentry->d_parent = tdentry;
d_rehash(target);
dput(tdentry);
+
+ /* if parent is properly connected, then we can assert that
+ * the children are connected, but it must be a singluar (non-forking)
+ * branch
+ */
+ if (!(parent->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+ while (target) {
+ target->d_flags &= ~DCACHE_NFSD_DISCONNECTED;
+ parent = target;
+ if (list_empty(&parent->d_subdirs))
+ target = NULL;
+ else {
+ target = list_entry(parent->d_subdirs.next, struct dentry, d_child);
+#ifdef NFSD_PARANOIA
+ /* must be only child */
+ if (target->d_child.next != &parent->d_subdirs
+ || target->d_child.prev != &parent->d_subdirs)
+ printk("nfsd: d_splice found non-singular disconnected branch: %s/%s\n",
+ parent->d_name.name, target->d_name.name);
+#endif
+ }
+ }
+ }
return 0;
}
}
if (pdentry == NULL) {
pdentry = d_alloc_root(igrab(tdentry->d_inode));
- if (pdentry) d_rehash(pdentry);
+ if (pdentry) {
+ pdentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
+ d_rehash(pdentry);
+ }
}
if (pdentry == NULL)
pdentry = ERR_PTR(-ENOMEM);
struct dentry *tmp;
int found =0;
int err;
- /* This semaphore is needed to make sure that only one unconnected (free)
+ /* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one unconnected (free)
* dcache path ever exists, as otherwise two partial paths might get
* joined together, which would be very confusing.
* If there is ever an unconnected non-root directory, then this lock
- * must be held. This could sensibly be per-filesystem.
+ * must be held.
*/
- static DECLARE_MUTEX(free_path_sem);
+
nfsdstats.fh_lookup++;
/*
dprintk("find_fh_dentry: No inode found.\n");
goto err_out;
}
- if (!IS_ROOT(result) || result->d_inode->i_sb->s_root ==result)
+ if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED))
return result;
/* result is now an anonymous dentry, which may be adequate as it stands, or else
* location in the tree.
*/
dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,fh->fh_ino);
- down(&free_path_sem);
+ down(&sb->s_nfsd_free_path_sem);
+
+ /* claiming the semaphore might have allow things to get fixed up */
+ if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+ up(&sb->s_nfsd_free_path_sem);
+ return result;
+ }
+
+
found = 0;
if (!S_ISDIR(result->d_inode->i_mode)) {
nfsdstats.fh_nocache_nondir++;
|| !S_ISDIR(dentry->d_inode->i_mode)) {
goto err_dentry;
}
- if (!IS_ROOT(dentry) || dentry->d_inode->i_sb->s_root ==dentry)
+ if ((!dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
found = 1;
tmp = splice(result, dentry);
err = PTR_ERR(tmp);
dput(pdentry);
goto err_dentry;
}
- /* I'm not sure that this is the best test for
- * "is it not a floating dentry?"
- */
- if (!IS_ROOT(pdentry) || parent->i_sb->s_root == pdentry)
+
+ if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
found = 1;
tmp = splice(dentry, pdentry);
dput(tmp);
dput(dentry);
dput(result); /* this will discard the whole free path, so we can up the semaphore */
- up(&free_path_sem);
+ up(&sb->s_nfsd_free_path_sem);
goto retry;
}
dput(dentry);
dentry = pdentry;
}
dput(dentry);
- up(&free_path_sem);
+ up(&sb->s_nfsd_free_path_sem);
return result;
err_dentry:
dput(dentry);
err_result:
dput(result);
- up(&free_path_sem);
+ up(&sb->s_nfsd_free_path_sem);
err_out:
if (err == -ESTALE)
nfsdstats.fh_stale++;
error = nfserrno(-PTR_ERR(dentry));
goto out;
}
+#ifdef NFSD_PARANOIA
+ if (S_ISDIR(dentry->d_inode->i_mode) &&
+ (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+ printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ }
+#endif
fhp->fh_dentry = dentry;
fhp->fh_export = exp;
#include <linux/limits.h>
#define __NO_VERSION__
#include <linux/module.h>
+#include <linux/smp_lock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
void de_put(struct proc_dir_entry *de)
{
if (de) {
+ lock_kernel(); /* FIXME: count should be atomic_t */
if (!de->count) {
printk("de_put: entry %s already free!\n", de->name);
return;
free_proc_entry(de);
}
}
+ unlock_kernel();
}
}
{
struct proc_dir_entry *de = inode->u.generic_ip;
+ inode->i_state = I_CLEAR;
+
if (PROC_INODE_PROPER(inode)) {
proc_pid_delete_inode(inode);
return;
s->s_flags = flags;
s->s_dirt = 0;
sema_init(&s->s_vfs_rename_sem,1);
+ sema_init(&s->s_nfsd_free_path_sem,1);
/* N.B. Should lock superblock now ... */
if (!type->read_super(s, data, silent))
goto out_fail;
sb->s_bdev = NULL;
sb->s_flags = root_mountflags;
sema_init(&sb->s_vfs_rename_sem,1);
+ sema_init(&s->s_nfsd_free_path_sem,1);
vfsmnt = add_vfsmnt(sb, "/dev/root", "/");
if (vfsmnt) {
if (nfs_root_mount(sb) >= 0) {
/*
* Include file for the interface to an APM BIOS
- * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com)
+ * Copyright 1994-2000 Stephen Rothwell (sfr@linuxcare.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
#define APM_FUNC_TIMER_ENABLE 1
#define APM_FUNC_TIMER_GET 2
-/*
- * Maximum number of events stored
- */
-#define APM_MAX_EVENTS 20
-
-/*
- * The per-file APM data
- */
-struct apm_bios_struct {
- int magic;
- struct apm_bios_struct * next;
- int suser;
- int suspends_pending;
- int standbys_pending;
- int suspends_read;
- int standbys_read;
- int event_head;
- int event_tail;
- apm_event_t events[APM_MAX_EVENTS];
-};
-
-/*
- * The magic number in apm_bios_struct
- */
-#define APM_BIOS_MAGIC 0x4101
-
/*
* in init/main.c
*/
* renamed" and has to be
* deleted on the last dput()
*/
+#define DCACHE_NFSD_DISCONNECTED 0x0004 /* This dentry is not currently connected to the
+ * dcache tree. Its parent will either be itself,
+ * or will have this flag as well.
+ * If this dentry points to a directory, then
+ * s_nfsd_free_path semaphore will be down
+ */
/*
* d_drop() unhashes the entry from the parent
#define I_DIRTY 1
#define I_LOCK 2
#define I_FREEING 4
+#define I_CLEAR 8
extern void __mark_inode_dirty(struct inode *);
static inline void mark_inode_dirty(struct inode *inode)
* even looking at it. You had been warned.
*/
struct semaphore s_vfs_rename_sem; /* Kludge */
+
+ /* The next field is used by knfsd when converting a (inode number based)
+ * file handle into a dentry. As it builds a path in the dcache tree from
+ * the bottom up, there may for a time be a subpath of dentrys which is not
+ * connected to the main tree. This semaphore ensure that there is only ever
+ * one such free path per filesystem. Note that unconnected files (or other
+ * non-directories) are allowed, but not unconnected diretories.
+ */
+ struct semaphore s_nfsd_free_path_sem;
};
/*
int (*revalidate) (struct dentry *);
};
+/*
+ * NOTE: write_inode, delete_inode, clear_inode, put_inode can be called
+ * without the big kernel lock held in all filesystems.
+ */
struct super_operations {
void (*read_inode) (struct inode *);
void (*write_inode) (struct inode *);
* Timeouts for various operations:
*/
#define WAIT_DRQ (5*HZ/100) /* 50msec - spec allows up to 20ms */
-#ifdef CONFIG_APM
+#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
#define WAIT_READY (5*HZ) /* 5sec - some laptops are very slow */
#else
#define WAIT_READY (3*HZ/100) /* 30msec - should be instantaneous */
-#endif /* CONFIG_APM */
+#endif /* CONFIG_APM || CONFIG_APM_MODULE */
#define WAIT_PIDENTIFY (10*HZ) /* 10sec - should be less than 3ms (?)
if all ATAPI CD is closed at boot */
#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */
#define QUEUE_FIRST(head, listnam) (head)->DLIST_NEXT(listnam)
#define QUEUE_LAST(head, listnam) (head)->DLIST_PREV(listnam)
-#define QUEUE_EMPTY(head, listnam) \
+#define QUEUE_IS_EMPTY(head, listnam) \
((QUEUE_FIRST(head, listnam) == QUEUE_LAST(head, listnam)) && \
((u_long)QUEUE_FIRST(head, listnam) == (u_long)head))
{ return NULL; }
extern inline void pci_set_master(struct pci_dev *dev) { }
-extern inline int pci_enable_device(struct pci_dev *dev) { return 0; }
+extern inline int pci_enable_device(struct pci_dev *dev) { return -EIO; }
+extern inline int pci_module_init(struct pci_driver *drv) { return -ENODEV; }
+
+#else
+
+/*
+ * a helper function which helps ensure correct pci_driver
+ * setup and cleanup for commonly-encountered hotplug/modular cases
+ *
+ * This MUST stay in a header, as it checks for -DMODULE
+ */
+extern inline int pci_module_init(struct pci_driver *drv)
+{
+ int rc = pci_register_driver (drv);
+
+ if (rc > 0)
+ return 0;
+
+ /* iff CONFIG_HOTPLUG and built into kernel, we should
+ * leave the driver around for future hotplug events.
+ * For the module case, a hotplug daemon of some sort
+ * should load a module in response to an insert event. */
+#if defined(CONFIG_HOTPLUG) && !defined(MODULE)
+ if (rc == 0)
+ return 0;
+#endif
+
+ /* if we get here, we need to clean up pci driver instance
+ * and return some sort of error */
+ pci_unregister_driver (drv);
+
+ return -ENODEV;
+}
#endif /* !CONFIG_PCI */
struct list_head entry;
};
-#if defined(CONFIG_ACPI) || defined(CONFIG_APM)
+#if defined(CONFIG_ACPI) || defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
extern int pm_active;
extern inline void pm_access(struct pm_dev *dev) {}
extern inline void pm_dev_idle(struct pm_dev *dev) {}
-#else // CONFIG_ACPI || CONFIG_APM
+#else /* CONFIG_ACPI || CONFIG_APM || CONFIG_APM_MODULE */
#define PM_IS_ACTIVE() 0
extern inline void pm_access(struct pm_dev *dev) {}
extern inline void pm_dev_idle(struct pm_dev *dev) {}
-#endif // CONFIG_ACPI || CONFIG_APM
+#endif /* CONFIG_ACPI || CONFIG_APM || CONFIG_APM_MODULE */
extern void (*pm_idle)(void);
extern void (*pm_power_off)(void);
-#endif // __KERNEL__
+#endif /* __KERNEL__ */
#endif /* _LINUX_PM_H */
#define PSCHED_CLOCK_SOURCE PSCHED_JIFFIES
+#include <linux/config.h>
#include <linux/pkt_sched.h>
#include <net/pkt_cls.h>
+#ifdef CONFIG_X86_TSC
+#include <asm/msr.h>
+#endif
+
struct rtattr;
struct Qdisc;
#define PSCHED_US2JIFFIE(delay) (((delay)+psched_clock_per_hz-1)/psched_clock_per_hz)
-#if CPU == 586 || CPU == 686
+#ifdef CONFIG_X86_TSC
#define PSCHED_GET_TIME(stamp) \
({ u64 __cur; \
- __asm__ __volatile__ (".byte 0x0f,0x31" :"=A" (__cur)); \
+ rdtscll(__cur); \
(stamp) = __cur>>psched_clock_scale; \
})
#include <linux/kernel.h>
#include <asm/system.h>
+#ifdef CONFIG_X86_TSC
+#include <asm/msr.h>
+#endif
+
struct net_profile_slot
{
char id[16];
extern struct timeval net_profile_adjust;
extern void net_profile_irq_adjust(struct timeval *entered, struct timeval* leaved);
-#if CPU == 586 || CPU == 686
-
+#ifdef CONFIG_X86_TSC
extern __inline__ void net_profile_stamp(struct timeval *pstamp)
{
- __asm__ __volatile__ (".byte 0x0f,0x31"
- :"=a" (pstamp->tv_usec),
- "=d" (pstamp->tv_sec));
+ rdtsc(pstamp->tv_usec, pstamp->tv_sec);
}
extern __inline__ void net_profile_accumulate(struct timeval *entered,
EXPORT_SYMBOL(loops_per_sec);
#endif
EXPORT_SYMBOL(kstat);
+EXPORT_SYMBOL(nr_running);
/* misc */
EXPORT_SYMBOL(panic);
/* library functions */
EXPORT_SYMBOL(strnicmp);
+EXPORT_SYMBOL(strspn);
/* software interrupts */
EXPORT_SYMBOL(tasklet_hi_vec);
EXPORT_SYMBOL(bh_task_vec);
EXPORT_SYMBOL(init_bh);
EXPORT_SYMBOL(remove_bh);
+EXPORT_SYMBOL(tasklet_init);
+EXPORT_SYMBOL(tasklet_kill);
/* init task, for moving kthread roots - ought to export a function ?? */
EXPORT_SYMBOL(pm_unregister_all);
EXPORT_SYMBOL(pm_send_request);
EXPORT_SYMBOL(pm_find);
+EXPORT_SYMBOL(pm_active);
#endif
/* .. and a slight advantage to the current MM */
- if (p->mm == this_mm)
+ if (p->mm == this_mm || !p->mm)
weight += 1;
weight += p->priority;
*/
static inline int preemption_goodness(struct task_struct * prev, struct task_struct * p, int cpu)
{
- return goodness(p, cpu, prev->mm) - goodness(prev, cpu, prev->mm);
+ return goodness(p, cpu, prev->active_mm) - goodness(prev, cpu, prev->active_mm);
}
/*
ret = -ENOMEM;
if (flags & MREMAP_MAYMOVE) {
if (!(flags & MREMAP_FIXED)) {
- new_addr = get_unmapped_area(addr, new_len);
+ new_addr = get_unmapped_area(0, new_len);
if (!new_addr)
goto out;
}
*/
if (!(current->flags & PF_MEMALLOC))
{
- if (classfree(z) > z->pages_high)
+ unsigned long free = classfree(z);
+
+ if (free > z->pages_high)
{
if (z->low_on_memory)
z->low_on_memory = 0;
if (z->low_on_memory)
goto balance;
- if (classfree(z) <= z->pages_low)
+ if (free <= z->pages_low)
{
wake_up_interruptible(&kswapd_wait);
- if (classfree(z) <= z->pages_min)
+ if (free <= z->pages_min)
{
z->low_on_memory = 1;
goto balance;
}
}
- /*
- * If we can schedule, do so, and make sure to yield.
- * We may be a real-time process, and if kswapd is
- * waiting for us we need to allow it to run a bit.
- */
- if (gfp_mask & __GFP_WAIT) {
- current->policy |= SCHED_YIELD;
- schedule();
- }
-
nopage:
return NULL;
net_device_init();
-#ifdef CONFIG_IP_PNP
- ip_auto_config();
-#endif
-
return 0;
}
register_netdevice(&whitehole_dev);
printk("Evaluating net profiler cost ...");
-#if CPU == 586 || CPU == 686
- if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC)) {
- printk(KERN_ERR "Sorry, your CPU does not support TSC. Net profiler disabled.\n");
- return -1;
- }
-#endif
#ifdef __alpha__
alpha_tick(0);
#endif
* IP Autoconfig dispatcher.
*/
-int __init ip_auto_config(void)
+static int __init ip_auto_config(void)
{
if (!ic_enable)
return 0;
return 0;
}
+module_init(ip_auto_config);
+
+
/*
* Decode any IP configuration options in the "ip=" or "nfsaddrs=" kernel
* command line parameter. It consists of option fields separated by colons in
}
} else {
/* Socket is locked, keep trying until memory is available. */
- do {
+ for (;;) {
skb = sock_wmalloc(sk,
MAX_TCP_HEADER + 15,
1, GFP_KERNEL);
- } while (skb == NULL);
+ if (skb)
+ break;
+ current->policy |= SCHED_YIELD;
+ schedule();
+ }
/* Reserve space for headers and prepare control bits. */
skb_reserve(skb, MAX_TCP_HEADER);
long rdelay;
unsigned long stop;
-#if CPU == 586 || CPU == 686
- if (!(boot_cpu_data.x86_capability & X86_FEATURE_TSC))
- return -1;
-#endif
-
#ifdef PSCHED_WATCHER
psched_tick(0);
#endif
bool "$ques" "$var"
}
+function dep_mbool () {
+ ques=$1
+ var=$2
+ shift 2
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ n)
+ define_bool "$var" "n"
+ return
+ ;;
+ m)
+ eval "$var=y"
+ ;;
+ esac
+ shift
+ done
+
+ bool "$ques" "$var"
+}
+
#
# define_int sets the value of a integer argument
#
fi
}
+function dep_mbool () {
+ ques="$1"
+ var="$2"
+ dep=y
+ shift 2
+ while [ $# -gt 0 ]; do
+ if [ "$1" = y -o "$1" = m ]; then
+ shift
+ else
+ dep=n
+ shift $#
+ fi
+ done
+ if [ "$dep" = y ]; then
+ bool "$ques" "$var"
+ else
+ define_bool "$var" n
+ fi
+}
+
#
# Add a menu item which will call our local int function.
#
define_bool "$var" "$x"
}
+ function dep_mbool () {
+ set_x_info "$2" "n"
+ var="$2"
+ shift 2
+ while [ $# -gt 0 ]; do
+ if [ "$1" = y -o "$1" = m ]; then
+ shift
+ else
+ x=n; shift $#
+ fi
+ done
+ define_bool "$var" "$x"
+ }
+
function int () {
set_x_info "$2" "$3"
echo "$2=$x" >>$CONFIG
return $var
}
-proc sync_bool { var dep } {
+proc sync_bool { var dep modset } {
set var [sync_tristate $var $dep]
- if {$dep == 2} then {
- set var 0
+ if {$dep == 2 && $var == 2} then {
+ set var $modset
}
return $var
}
-proc write_tristate { file1 file2 varname variable deplist } {
+proc write_tristate { file1 file2 varname variable deplist modset } {
set variable [sync_tristate $variable [effective_dep $deplist]]
- if { $variable == 1 }\
+ if { $variable == 2 } \
+ then { set variable $modset }
+ if { $variable == 1 } \
then { puts $file1 "$varname=y"; \
puts $file2 "#define $varname 1" } \
elseif { $variable == 2 } \
|| cfg->token == token_define_string
|| cfg->token == token_define_tristate
|| cfg->token == token_dep_bool
+ || cfg->token == token_dep_mbool
|| cfg->token == token_dep_tristate
|| cfg->token == token_hex
|| cfg->token == token_int
break;
case token_dep_bool:
+ case token_dep_mbool:
case token_dep_tristate:
/*
* Same as the other simple statements, plus an additional
*/
if ( line_num >= -1 )
{
+ int modtoyes = 0;
+
switch ( cfg->token )
{
default:
printf( " }\n" );
break;
+ case token_dep_mbool:
+ modtoyes = 1;
case token_dep_bool:
printf( "\n" );
for ( tmp = cfg->depend; tmp; tmp = tmp->next )
printf( "\tset tmpvar_dep [effective_dep [list" );
for ( tmp = cfg->depend; tmp; tmp = tmp->next )
printf( " $%s", tmp->name );
- printf( "]];set %s [sync_bool $%s $tmpvar_dep];",
- vartable[cfg->nameindex].name, vartable[cfg->nameindex].name );
- printf( "if {$tmpvar_dep != 1} then {" );
- printf( "configure_entry .menu%d.config.f.x%d disabled {y};",
+ printf( "]];set %s [sync_bool $%s $tmpvar_dep %d];",
+ vartable[cfg->nameindex].name, vartable[cfg->nameindex].name,
+ modtoyes );
+ printf( "if {$tmpvar_dep != 1" );
+ if (modtoyes)
+ printf( " && $tmpvar_dep != 2" );
+ printf( "} then {configure_entry .menu%d.config.f.x%d disabled {y};",
menu_num, line_num );
printf( "} else {" );
printf( "configure_entry .menu%d.config.f.x%d normal {y};",
}
else
{
+ int modtoyes = 0;
+
switch ( cfg->token )
{
default:
printf( " }\n" );
break;
+ case token_dep_mbool:
+ modtoyes = 1;
case token_dep_bool:
printf( "\n" );
for ( tmp = cfg->depend; tmp; tmp = tmp->next )
printf( "\tset tmpvar_dep [effective_dep [list" );
for ( tmp = cfg->depend; tmp; tmp = tmp->next )
printf( " $%s", tmp->name );
- printf( "]];set %s [sync_bool $%s $tmpvar_dep];",
- vartable[cfg->nameindex].name, vartable[cfg->nameindex].name );
+ printf( "]];set %s [sync_bool $%s $tmpvar_dep %d];",
+ vartable[cfg->nameindex].name, vartable[cfg->nameindex].name,
+ modtoyes );
case token_bool:
if ( cfg->token == token_bool )
printf( "\n\t" );
{
struct condition * cond;
struct dependency * tmp;
+ int depmod = 2;
/*
* Generate global declaration for this symbol.
case token_bool:
case token_tristate:
- printf( "write_tristate $cfg $autocfg %s $%s [list $notmod]",
+ printf( "write_tristate $cfg $autocfg %s $%s [list $notmod] 2",
vartable[cfg->nameindex].name, vartable[cfg->nameindex].name );
if ( cfg->cond != NULL )
printf( " }" );
cfg1 != NULL && cfg1->token == token_choice_item;
cfg1 = cfg1->next )
{
- printf("\n\tif { $tmpvar_%d == \"%s\" } then { write_tristate $cfg $autocfg %s 1 [list $notmod] } else { write_tristate $cfg $autocfg %s 0 [list $notmod] }",
+ printf("\n\tif { $tmpvar_%d == \"%s\" } then { write_tristate $cfg $autocfg %s 1 [list $notmod] 2 } else { write_tristate $cfg $autocfg %s 0 [list $notmod] 2 }",
-(cfg->nameindex), cfg1->label,
vartable[cfg1->nameindex].name,
vartable[cfg1->nameindex].name );
case token_define_tristate:
if ( cfg->cond == NULL )
{
- printf( "write_tristate $cfg $autocfg %s $%s [list $notmod]\n",
+ printf( "write_tristate $cfg $autocfg %s $%s [list $notmod] 2\n",
vartable[cfg->nameindex].name, vartable[cfg->nameindex].name );
}
else
{
- printf( "write_tristate $cfg $autocfg %s $%s [list $notmod] }\n",
+ printf( "write_tristate $cfg $autocfg %s $%s [list $notmod] 2 }\n",
vartable[cfg->nameindex].name, cfg->value );
}
break;
+ case token_dep_mbool:
+ depmod = 1;
case token_dep_bool:
case token_dep_tristate:
printf( "write_tristate $cfg $autocfg %s $%s [list",
vartable[cfg->nameindex].name, vartable[cfg->nameindex].name );
for ( tmp = cfg->depend; tmp; tmp = tmp->next )
printf( " $%s", tmp->name );
- printf( "]" );
+ printf( "] %d", depmod );
if ( cfg->cond != NULL )
printf( " }" );
printf( "\n" );
case token_define_string:
case token_dep_bool:
case token_dep_tristate:
+ case token_dep_mbool:
case token_int:
case token_hex:
case token_mainmenu_option:
case token_choice_item:
case token_dep_bool:
case token_dep_tristate:
+ case token_dep_mbool:
case token_hex:
case token_int:
case token_string:
break;
case token_dep_bool:
+ case token_dep_mbool:
cfg->menu_line = menu_line++;
printf( "\tdep_bool $w.config.f %d %d \"%s\" %s\n",
cfg->menu_number, cfg->menu_line, cfg->label,
case token_choice_item:
case token_dep_bool:
case token_dep_tristate:
+ case token_dep_mbool:
case token_tristate:
if ( ! vartable[cfg->nameindex].global_written )
{
case token_define_tristate:
case token_dep_bool:
case token_dep_tristate:
+ case token_dep_mbool:
case token_hex:
case token_int:
case token_string:
match_token( token_define_string, "define_string" );
match_token( token_define_tristate, "define_tristate" );
match_token( token_dep_bool, "dep_bool" );
+ match_token( token_dep_mbool, "dep_mbool" );
match_token( token_dep_tristate, "dep_tristate" );
break;
break;
case token_dep_bool:
+ case token_dep_mbool:
case token_dep_tristate:
pnt = get_qstring ( pnt, &cfg->label );
pnt = get_string ( pnt, &buffer );
}
else
{
- syntax_error( "can't handle dep_bool/dep_tristate condition" );
+ syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" );
}
dep_ptr = &(*dep_ptr)->next;
while ( *pnt == ' ' || *pnt == '\t' )
*cond_ptr = malloc( sizeof(struct condition) );
memset( *cond_ptr, 0, sizeof(struct condition) );
(*cond_ptr)->op = op_lparen;
- if ( token == token_dep_tristate )
- sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then",
- dep->name, dep->name, dep->name );
- else
+ if ( token == token_dep_bool )
sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"\" ]; then",
dep->name, dep->name );
+ else
+ sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then",
+ dep->name, dep->name, dep->name );
(*cond_ptr)->next = tokenize_if( fake_if );
while ( *cond_ptr )
cond_ptr = &(*cond_ptr)->next;
token_define_string,
token_define_tristate,
token_dep_bool,
+ token_dep_mbool,
token_dep_tristate,
token_else,
token_endmenu,
};
/*
- * Dependency list for dep_bool, dep_tristate
+ * Dependency list for dep_bool, dep_mbool, dep_tristate
*/
struct dependency