summaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /drivers/block
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/Config.in66
-rw-r--r--drivers/block/MAKEDEV.ide124
-rw-r--r--drivers/block/Makefile210
-rw-r--r--drivers/block/README.aztcd730
-rw-r--r--drivers/block/README.fd113
-rw-r--r--drivers/block/README.hd2
-rw-r--r--drivers/block/README.ide279
-rw-r--r--drivers/block/README.md4
-rw-r--r--drivers/block/README.sbpcd997
-rw-r--r--drivers/block/README.sonycd535119
-rw-r--r--drivers/block/ali14xx.c220
-rw-r--r--drivers/block/amiflop.c1782
-rw-r--r--drivers/block/ataflop.c1955
-rw-r--r--drivers/block/aztcd.c1713
-rw-r--r--drivers/block/blk.h333
-rw-r--r--drivers/block/cdu31a.c2966
-rw-r--r--drivers/block/cmd640.c849
-rw-r--r--drivers/block/dtc2278.c128
-rw-r--r--drivers/block/floppy.c3187
-rw-r--r--drivers/block/genhd.c746
-rw-r--r--drivers/block/hd.c107
-rw-r--r--drivers/block/ht6560b.c233
-rw-r--r--drivers/block/ide-cd.c4201
-rw-r--r--drivers/block/ide-cd.h553
-rw-r--r--drivers/block/ide-disk.c660
-rw-r--r--drivers/block/ide-floppy.c1432
-rw-r--r--drivers/block/ide-probe.c724
-rw-r--r--drivers/block/ide-tape.c3786
-rw-r--r--drivers/block/ide.c3948
-rw-r--r--drivers/block/ide.h604
-rw-r--r--drivers/block/ide_modes.h226
-rw-r--r--drivers/block/linear.c200
-rw-r--r--drivers/block/linear.h16
-rw-r--r--drivers/block/ll_rw_blk.c600
-rw-r--r--drivers/block/loop.c568
-rw-r--r--drivers/block/mcd.c1522
-rw-r--r--drivers/block/md.c581
-rw-r--r--drivers/block/opti621.c337
-rw-r--r--drivers/block/promise.c362
-rw-r--r--drivers/block/promise.h52
-rw-r--r--drivers/block/qd6580.c66
-rw-r--r--drivers/block/raid0.c278
-rw-r--r--drivers/block/ramdisk.c263
-rw-r--r--drivers/block/rd.c686
-rw-r--r--drivers/block/rz1000.c59
-rw-r--r--drivers/block/sbpcd.c5399
-rw-r--r--drivers/block/sbpcd2.c5
-rw-r--r--drivers/block/sbpcd3.c5
-rw-r--r--drivers/block/sbpcd4.c5
-rw-r--r--drivers/block/sonycd535.c1743
-rw-r--r--drivers/block/triton.c485
-rw-r--r--drivers/block/umc8672.c155
-rw-r--r--drivers/block/xd.c163
-rw-r--r--drivers/block/xd.h138
54 files changed, 25066 insertions, 21519 deletions
diff --git a/drivers/block/Config.in b/drivers/block/Config.in
new file mode 100644
index 000000000..99a5773f3
--- /dev/null
+++ b/drivers/block/Config.in
@@ -0,0 +1,66 @@
+#
+# Block device driver configuration
+#
+mainmenu_option next_comment
+comment 'Floppy, IDE, and other block devices'
+
+tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
+tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
+comment 'Please see Documentation/ide.txt for help/info on IDE drives'
+if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
+ bool 'Old harddisk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
+else
+ bool ' Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
+ dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE
+ dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
+ dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
+ dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
+ if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
+ bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
+ if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then
+ bool ' CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED
+ fi
+ if [ "$CONFIG_PCI" = "y" ]; then
+ bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000
+ bool ' Intel 82371 PIIX (Triton I/II) DMA support' CONFIG_BLK_DEV_TRITON
+ fi
+ bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS
+ if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then
+ comment 'Note: most of these also require special kernel boot parameters'
+ bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX
+ bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278
+ bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PROMISE
+ if [ "$CONFIG_PCI" = "y" ]; then
+ bool ' OPTi 82C621 support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
+ fi
+ fi
+ bool ' QDI QD6580 support' CONFIG_BLK_DEV_QD6580
+ bool ' UMC 8672 support' CONFIG_BLK_DEV_UMC8672
+ fi
+ fi
+fi
+
+comment 'Additional Block Devices'
+
+tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP
+bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
+if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
+ tristate ' Linear (append) mode' CONFIG_MD_LINEAR
+ tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED
+fi
+tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
+if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
+ bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
+fi
+tristate 'XT harddisk support' CONFIG_BLK_DEV_XD
+
+
+if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
+ define_bool CONFIG_BLK_DEV_HD y
+else
+ define_bool CONFIG_BLK_DEV_HD n
+fi
+
+endmenu
diff --git a/drivers/block/MAKEDEV.ide1 b/drivers/block/MAKEDEV.ide1
deleted file mode 100644
index 506115ca5..000000000
--- a/drivers/block/MAKEDEV.ide1
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-makedev () {
- rm -f /dev/$1
- echo mknod /dev/$1 $2 $3 $4 &&
- mknod /dev/$1 $2 $3 $4 &&
- chown $5 /dev/$1 &&
- chmod $6 /dev/$1
-}
-
-# Create /dev/hdc*
-makedev hdc b 22 0 root:$disk 660
-for part in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-do
- makedev hdc$part b 22 `expr 0 + $part` root:$disk 660
-done
-echo " "
-
-# Create /dev/hdd*
-makedev hdd b 22 64 root:$disk 660
-for part in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-do
- makedev hdd$part b 22 `expr 64 + $part` root:$disk 660
-done
-
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index ebf02d8a5..ca2064ba9 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -9,110 +9,176 @@
# parent makefile.
#
-.c.s:
- $(CC) $(CFLAGS) -S $<
-.s.o:
- $(AS) $(ASFLAGS) -c -o $*.o $<
-.c.o:
- $(CC) $(CFLAGS) -c $<
-
#
-# Note : at this point, these files are compiled on all systems.
+# Note : at this point, these files are compiled on all systems.
# In the future, some of these should be built conditionally.
#
-OBJS := ll_rw_blk.o ramdisk.o genhd.o
-SRCS := ll_rw_blk.c ramdisk.c genhd.c
-BLOCK_MODULE_OBJS =
-ifdef CONFIG_BLK_DEV_FD
-OBJS := $(OBJS) floppy.o
-SRCS := $(SRCS) floppy.c
-endif
+L_TARGET := block.a
+L_OBJS := ll_rw_blk.o genhd.o
+M_OBJS :=
+MOD_LIST_NAME := BLOCK_MODULES
+LX_OBJS :=
+MX_OBJS :=
-ifdef CONFIG_CDU31A
-OBJS := $(OBJS) cdu31a.o
-SRCS := $(SRCS) cdu31a.c
+ifeq ($(CONFIG_BLK_DEV_FD),y)
+L_OBJS += floppy.o
+else
+ ifeq ($(CONFIG_BLK_DEV_FD),m)
+ M_OBJS += floppy.o
+ endif
endif
-ifdef CONFIG_MCD
-OBJS := $(OBJS) mcd.o
-SRCS := $(SRCS) mcd.c
+ifeq ($(CONFIG_BLK_DEV_RAM),y)
+L_OBJS += rd.o
+else
+ ifeq ($(CONFIG_BLK_DEV_RAM),m)
+ M_OBJS += rd.o
+ endif
endif
-ifdef CONFIG_AZTCD
-OBJS := $(OBJS) aztcd.o
-SRCS := $(SRCS) aztcd.c
+ifeq ($(CONFIG_BLK_DEV_LOOP),y)
+L_OBJS += loop.o
else
-BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) aztcd.o
+ ifeq ($(CONFIG_BLK_DEV_LOOP),m)
+ M_OBJS += loop.o
+ endif
endif
-ifdef CONFIG_SBPCD
-OBJS := $(OBJS) sbpcd.o
-SRCS := $(SRCS) sbpcd.c
+ifeq ($(CONFIG_BLK_DEV_HD),y)
+L_OBJS += hd.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDE),y)
+LX_OBJS += ide.o
+L_OBJS += ide-probe.o
else
-BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sbpcd.o
-endif #CONFIG_SBPCD
+ ifeq ($(CONFIG_BLK_DEV_IDE),m)
+ MX_OBJS += ide.o
+ M_OBJS += ide-probe.o
+ endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_RZ1000),y)
+L_OBJS += rz1000.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_CMD640),y)
+L_OBJS += cmd640.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_TRITON),y)
+L_OBJS += triton.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
+L_OBJS += dtc2278.o
+endif
+
+ifeq ($(CONFIG_BLK_DEV_HT6560B),y)
+L_OBJS += ht6560b.o
+endif
-ifdef CONFIG_SBPCD2
-OBJS := $(OBJS) sbpcd2.o
-SRCS := $(SRCS) sbpcd2.c
-endif #CONFIG_SBPCD2
+ifeq ($(CONFIG_BLK_DEV_QD6580),y)
+L_OBJS += qd6580.o
+endif
-ifdef CONFIG_SBPCD3
-OBJS := $(OBJS) sbpcd3.o
-SRCS := $(SRCS) sbpcd3.c
-endif #CONFIG_SBPCD3
+ifeq ($(CONFIG_BLK_DEV_UMC8672),y)
+L_OBJS += umc8672.o
+endif
-ifdef CONFIG_SBPCD4
-OBJS := $(OBJS) sbpcd4.o
-SRCS := $(SRCS) sbpcd4.c
-endif #CONFIG_SBPCD4
+ifeq ($(CONFIG_BLK_DEV_ALI14XX),y)
+L_OBJS += ali14xx.o
+endif
-ifdef CONFIG_BLK_DEV_HD
-OBJS := $(OBJS) hd.o
-SRCS := $(SRCS) hd.c
+ifeq ($(CONFIG_BLK_DEV_PROMISE),y)
+L_OBJS += promise.o
endif
-ifdef CONFIG_BLK_DEV_IDE
-OBJS := ide.o $(OBJS)
-SRCS := ide.c $(SRCS)
+ifeq ($(CONFIG_BLK_DEV_OPTI621),y)
+L_OBJS += opti621.o
endif
-ifdef CONFIG_BLK_DEV_XD
-OBJS := $(OBJS) xd.o
-SRCS := $(SRCS) xd.c
+ifeq ($(CONFIG_BLK_DEV_IDEDISK),y)
+L_OBJS += ide-disk.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDEDISK),m)
+ M_OBJS += ide-disk.o
+ endif
endif
-ifdef CONFIG_CDU535
-OBJS := $(OBJS) sonycd535.o
-SRCS := $(SRCS) sonycd535.c
+ifeq ($(CONFIG_BLK_DEV_IDECD),y)
+L_OBJS += ide-cd.o
else
-BLOCK_MODULE_OBJS := $(BLOCK_MODULE_OBJS) sonycd535.o
+ ifeq ($(CONFIG_BLK_DEV_IDECD),m)
+ M_OBJS += ide-cd.o
+ endif
endif
-all: block.a
+ifeq ($(CONFIG_BLK_DEV_IDETAPE),y)
+L_OBJS += ide-tape.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDETAPE),m)
+ M_OBJS += ide-tape.o
+ endif
+endif
-block.a: $(OBJS)
- rm -f block.a
- $(AR) rcs block.a $(OBJS)
- sync
+ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
+L_OBJS += ide-floppy.o
+else
+ ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),m)
+ M_OBJS += ide-floppy.o
+ endif
+endif
-dep:
- $(CPP) -M $(SRCS) > .depend
-ifdef BLOCK_MODULE_OBJS
- $(CPP) -M -DMODULE $(BLOCK_MODULE_OBJS:.o=.c) >> .depend
+ifeq ($(CONFIG_BLK_DEV_XD),y)
+L_OBJS += xd.o
+else
+ ifeq ($(CONFIG_BLK_DEV_XD),m)
+ M_OBJS += xd.o
+ endif
endif
-modules: $(BLOCK_MODULE_OBJS)
- echo $(BLOCK_MODULE_OBJS) > ../../modules/BLOCK_MODULES
- (cd ../../modules;for i in $(BLOCK_MODULE_OBJS); do ln -sf ../drivers/block/$$i .; done)
+ifeq ($(CONFIG_BLK_DEV_MD),y)
+LX_OBJS += md.o
-dummy:
+ifeq ($(CONFIG_MD_LINEAR),y)
+L_OBJS += linear.o
+else
+ ifeq ($(CONFIG_MD_LINEAR),m)
+ M_OBJS += linear.o
+ endif
+endif
+ifeq ($(CONFIG_MD_STRIPED),y)
+L_OBJS += raid0.o
+else
+ ifeq ($(CONFIG_MD_STRIPED),m)
+ M_OBJS += raid0.o
+ endif
+endif
+
+#ifeq ($(CONFIG_MD_RAID1),y)
+#L_OBJS += raid1.o
+#else
+# ifeq ($(CONFIG_MD_SUPPORT_RAID1),y)
+# ifeq ($(CONFIG_MD_RAID1),m)
+# M_OBJS += raid1.o
+# endif
+# endif
+#endif
#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
+#ifeq ($(CONFIG_MD_RAID5),y)
+#L_OBJS += raid5.o
+#else
+# ifeq ($(CONFIG_MD_SUPPORT_RAID5),y)
+# ifeq ($(CONFIG_MD_RAID5),m)
+# M_OBJS += raid5.o
+# endif
+# endif
+#endif
+
endif
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/block/README.aztcd b/drivers/block/README.aztcd
deleted file mode 100644
index 797c11222..000000000
--- a/drivers/block/README.aztcd
+++ /dev/null
@@ -1,730 +0,0 @@
- Readme-File README.aztcd
- for Aztech CD-ROM CDA268-01A, ORCHID CD-3110, OKANO/WEARNES CDD110
- CD-ROM Driver
- Version 1.0 and newer
- (for other drives see 6.-8.)
-
-NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE
- A PROPRIETARY INTERFACE (implemented on a sound card or on a
- ISA-AT-bus card).
- IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE,
- such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE
- WITH IDE-INTERFACE, SOMETIMES ALSO CALLED ATAPI-COMPATIBLE, PLEASE
- USE THE ide-cd.c DRIVER, WRITTEN BY MARK LORD AND SCOTT SNYDER !!!
-----------------------------------------------------------------------------
-Contents of this file:
- 1. NOTE
- 2. INSTALLATION
- 3. CONFIGURING YOUR KERNEL
- 4. RECOMPILING YOUR KERNEL
- 5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS
- 6. BUG REPORTS
- 7. OTHER DRIVES
- 8. IF YOU DON'T SUCCEED ... DEBUGGING
- 9. TECHNICAL HISTORY OF THE DRIVER
- 10. ACKNOWLEDGMENTS
- 11. PROGRAMMING ADD ONS: CDPLAY.C
- APPENDIX: Source code of cdplay.c
-----------------------------------------------------------------------------
-
-1. NOTE
-This software has been successfully in alpha and beta test for quite a long
-time with AZTECH CDA268-01A, ORCHID CDS-3110 and ORCHID/WEARNES CDD110
-and has proven to be stable with kernel versions 1.0.9 to 1.2.0. But with
-any software there still may be bugs in it. So if you encounter problems,
-you are invited to help us improve this software. Please send me a detailed
-bug report (see chapter BUG REPORTS). You are also invited in helping us to
-increase the number of drives, which are supported.
-
-Please read the README-files carefully and always keep a backup copy of your
-old kernel, in order to reboot if something goes wrong!
-
-
-2. INSTALLATION
-If you received this software as a standalone package AZTECH.CDROM.Vxx.tgz
-(xx=version number) and not included in the standard Linux kernel, read the
-file AZTECH.CDROM.README included in that package to install it. The
-standalone package's home is 'ftp.gwdg.de : pub/linux/cdrom/drivers/aztech'.
-The driver consists of a header file 'aztcd.h', which normally should reside
-in /usr/include/linux and the source code 'aztcd.c', which normally resides in
-/usr/src/linux/drivers/block. It uses /dev/aztcd0, which must be a valid block
-device with major number 29 and reside in directory /dev. To mount a CD-ROM,
-your kernel needs to have the ISO9660-filesystem support included.
-
-
-3. CONFIGURING YOUR KERNEL
-If your kernel is already configured for using the AZTECH driver you will
-see the following message while Linux boots:
- Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
- Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>>>
- Aztech CD-ROM Init: <drive type> detected
- Aztech CD-ROM Init: End
-If the message looks different and you are sure to have a supported drive,
-it may have a different base address. The Aztech driver does look for the
-CD-ROM drive at the base address specified in aztcd.h at compile time. This
-address can be overwritten by boot parameter aztcd=....You should reboot and
-start Linux with boot parameter aztcd=<base address>, e.g. aztcd=0x320. If
-you do not know the base address, start your PC with DOS and look at the boot
-message of your CD-ROM's DOS driver.
-
-If the message looks correct, as user 'root' you should be able to mount the
-drive by
- mount -t iso9660 -r /dev/aztcd0 /mnt
-and use it as any other filesystem. (If this does not work, check if
- /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
- mknod /dev/aztcd0 b 29 0
- mkdir /mnt
-
-If you still get a different message while Linux boots or when you get the
-message, that the ISO9660-filesystem is not supported by your kernel, when
-you try to mount the CD-ROM drive, you have to recompile your kernel.
-
-If you do *not* have an Aztech/Orchid/Okano/Wearnes drive and want to bypass
-drive detection during Linux boot up, start with boot parameter aztcd=0.
-
-Joe Nardone has compiled a boot disk with the Aztech driver for installing
-Slackware from CDROM. You can find the disk images at 'sunsite.unc.edu';
-see file 'aztech.gz.README' for instructions on how to use it.
-
-
-4. RECOMPILING YOUR KERNEL
-If your kernel is not yet configured for the AZTECH driver and the ISO9660-
-filesystem, you have to recompile your kernel:
-
-- Edit aztcd.h to set the I/O-address to your I/O-Base address (AZT_BASE_ADDR),
- the driver does not use interrupts or DMA, so if you are using an AZTECH
- CD268, an ORCHID CD-3110 or ORCHID/WEARNES CDD110 that's the only item you
- have to set up.
- Users of other drives should read chapter OTHER DRIVES of this file.
- You also can configure that address by LILO boot parameter aztcd=...
-- Build a new kernel, configure it for 'Aztech/Orchid/Okano/Wearnes support'
- (if you want aztcd to be part of the kernel). Do not configure it for
- 'Aztech... support', if you want to use aztcd as a run time loadable module.
- But in any case you must have the ISO9660-filesystem included in your
- kernel.
-- Activate the new kernel, normally this is done by running lilo (don't for-
- get to configure it before and to keep a copy of your old kernel in case
- something goes wrong!).
-- Reboot
-- If you've included aztcd in your kernel, you now should see during boot
- some messages like
- Aztech CD-ROM Init: DriverVersion=<version number> BaseAddress=<baseaddress>
- Aztech CD-ROM Init: FirmwareVersion=<firmware version id of your I/O-card>
- Aztech CD-ROM Init: <drive type> detected
- Aztech CD-ROM Init: End
-- If you have not included aztcd in your kernel, but want to load aztcd as a
- run time loadable module see 4.1.
-- If the message looks correct, as user 'root' you should be able to mount
- the drive by
- mount -t iso9660 -r /dev/aztcd0 /mnt
- and use it as any other filesystem. (If this does not work, check if
- /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
- mknod /dev/aztcd0 b 29 0
- mkdir /mnt
-- If this still does not help, see chapters OTHER DRIVES and DEBUGGING.
-
-4.1 AZTCD AS A RUN-TIME LOADABLE MODULE
-If you do not need aztcd permanently, you can also load and remove the driver
-during runtime via insmod and rmmod. To build aztcd as a loadable module you
-must *not* configure your kernel for AZTECH support. But you need to have
-the ISO9660-filesystem included! So rebuild your kernel, if necessary.
-
-Now edit the base address of your AZTECH interface card in
-/usr/src/linux/include/linux/aztcd.h to the appropriate value. Then change
-to /usr/src/linux and do a
- make modules
- make modules_install
-After that you can run-time load the driver via
- insmod /lib/modules/X.X.X/misc/aztcd.o
-and remove it via rmmod aztcd.
-If you have not configured the correct base address, you can also supply the
-base address when loading the driver via
- insmod /lib/modules/X.X.X/misc/aztcd.o aztcd=<base address>
-In all commands 'X.X.X' is the current linux kernel version number. For details
-see file README.modules in /usr/src/linux.
-
-
-5. KNOWN PROBLEMS, FUTURE DEVELOPMENTS
-The driver does not support applications such as photo CD, multi session CD
-etc.. I do not plan to include the support for that in the driver, because I
-do not use such applications. If you are interested in that stuff and would
-like to extend the drivers capability on your own, please contact me, I'll
-support you as far as I can.
-
-The drive status recognition does not work correctly in all cases. Changing
-a disk or having the door open, when a drive is already mounted, is detected
-by the Aztech driver itself, but nevertheless causes multiple read attempts
-by the different layers of the ISO9660-filesystem driver, which finally timeout,
-so you have to wait quite a little... But isn't it bad style to change a disk
-in a mounted drive, anyhow ?!
-
-The driver uses busy wait in most cases for the drive handshake (macros
-STEN_LOW and DTEN_LOW). I tested with a 486/DX2 at 66MHz and a Pentium at
-60MHz. Whenever you use a much faster machine you are likely to get timeout
-messages. In that case edit aztcd.h and increase the timeout value AZT_TIMEOUT.
-
-For some 'slow' drive commands I implemented waiting with a timer waitqueue
-(macro STEN_LOW_WAIT). If you get this timeout message, you may also edit
-aztcd.h and increase the timeout value AZT_STATUS_DELAY. The waitqueue has
-shown to be a little critical. If you get kernel panic messages, edit aztcd.c
-and substitute STEN_LOW_WAIT by STEN_LOW. Busy waiting with STEN_LOW is more
-stable, but also causes CPU overhead.
-
-
-6. BUG REPORTS
-Please send detailed bug reports and bug fixes via EMail to
-
- zimmerma@rz.fht-esslingen.de
-
-Please include a description of your CD-ROM drive type and interface card,
-the exact firmware message during Linux bootup, the version number of the
-AZTECH-CDROM-driver and the Linux kernel version. Also a description of your
-system's other hardware could be of interest, especially microprocessor type,
-clock frequency, other interface cards such as soundcards, ethernet adapter,
-game cards etc..
-
-I will try to collect the reports and make the necessary modifications from
-time to time. I may also come back to you directly with some bug fixes and
-ask you to do further testing and debugging.
-
-Editors of CD-ROMs are invited to send a 'cooperation' copy of their
-CD-ROMs to the volunteers, who provided the CD-ROM support for Linux. My
-snail mail address for such 'stuff' is
- Prof. Dr. W. Zimmermann
- Fachhochschule fuer Technik Esslingen
- Fachbereich NT
- Flandernstrasse 101
- D-73732 Esslingen
- Germany
-
-
-7. OTHER DRIVES
-The following drives ORCHID CDS3110, OKANO CDD110 and WEARNES nearly look
-the same as AZTECH CDA268-01A, especially they seem to use the same command
-codes. So it was quite simple to make the AZTECH driver work with these drives.
-
-Unfortunately I do not have any of these drives available, so I couldn't test
-it myself. But I've got reports, that it works with ORCHID CDS3110 and Game-
-Wave32 sound cards and also with WEARNES CDD110 in some different combinations.
-In some installations, it seems necessary to initialize the drive with the DOS
-driver before (especially if combined with a sound card) and then do a warm
-boot (CTRL-ALT-RESET) or start Linux from DOS, e.g. with 'loadlin'.
-
-If you do not succeed, read chapter DEBUGGING. Thanks in advance!
-
-Sorry for the inconvenience, but it is difficult to develop for hardware,
-which you don't have available for testing. So if you like, please help us.
-
-
-8. DEBUGGING : IF YOU DON'T SUCCEED, TRY THE FOLLOWING
--reread the complete README file
--make sure, that your drive is hardware configured for
- transfer mode: polled
- IRQ: not used
- DMA: not used
- Base Address: something like 300, 320 ...
- You can check this, when you start the DOS driver, which came with your
- drive. By appropriately configuring the drive and the DOS driver you can
- check, whether your drive does operate in this mode correctly under DOS. If
- it does not operate under DOS, it won't under Linux.
- Make sure the Base Address is configured correctly in aztcd.h, also make
- sure, that /dev/aztcd0 exists with the correct major number (compare it with
- the entry in file /usr/include/linux/major.h for the Aztech drive).
--insert a CD-ROM and close the tray
--cold boot your PC (i.e. via the power on switch or the reset button)
--if you start Linux via DOS, e.g. using loadlin, make sure, that the DOS
- driver for the CD-ROM drive is not loaded (comment out the calling lines
- in DOS' config.sys!)
--look for the aztcd: init message during Linux init and note them exactly
--log in as root and do a mount -t iso9660 /dev/aztcd0 /mnt
--if you don't succeed in the first time, try several times. Try also to open
- and close the tray, then mount again. Please note carefully all commands
- you typed in and the aztcd-messages, which you get.
--if you get an 'Aztech CD-ROM init: aborted' message, read the remarks about
- the version string below.
-
-If this does not help, do the same with the following differences
--start DOS before; make now sure, that the DOS driver for the CD-ROM is
- loaded under DOS (i.e. uncomment it again in config.sys)
--warm boot your PC (i.e. via CTRL-ALT-DEL)
- if you have it, you can also start via loadlin (try both).
- ...
- Again note all commands and the aztcd-messages.
-
-If you see STEN_LOW or STEN_LOW_WAIT error messages, increase the timeout
-values.
-
-If this still does not help,
--look in aztcd.c for the lines #if 0
- #define AZT_TEST1
- ...
- #endif
- and substitute '#if 0' by '#if 1'.
--recompile your kernel and repeat the above two procedures. You will now get
- a bundle of debugging messages from the driver. Again note your commands
- and the appropriate messages. If you have syslogd running, these messages
- may also be found in syslogd's kernel log file. Nevertheless in some
- installations syslogd does not yet run, when init() is called, thus look for
- the aztcd-messages during init, before the login-prompt appears.
- Then look in aztcd.c, to find out, what happened. The normal calling sequence
- is: aztcd_init() during Linux bootup procedure init()
- after doing a 'mount -t iso9660 /dev/aztcd0 /mnt' the normal calling sequence is
- aztcd_open() -> Status 2c after cold reboot with CDROM or audio CD inserted
- -> Status 8 after warm reboot with CDROM inserted
- -> Status 2e after cold reboot with no disk, closed tray
- -> Status 6e after cold reboot, mount with door open
- aztUpdateToc()
- aztGetDiskInfo()
- aztGetQChannelInfo() repeated several times
- aztGetToc()
- aztGetQChannelInfo() repeated several times
- a list of track informations
- do_aztcd_request() }
- azt_transfer() } repeated several times
- azt_poll }
- Check, if there is a difference in the calling sequence or the status flags!
-
- There are a lot of other messages, eg. the ACMD-command code (defined in
- aztcd.h), status info from the getAztStatus-command and the state sequence of
- the finite state machine in azt_poll(). The most important are the status
- messages, look how they are defined and try to understand, if they make
- sense in the context where they appear. With a CD-ROM inserted the status
- should always be 8, except in aztcd_open(). Try to open the tray, insert a
- audio disk, insert no disk or reinsert the CD-ROM and check, if the status
- bits change accordingly. The status bits are the most likely point, where
- the drive manufacturers may implement changes.
-
-If you still don't succeed, a good point to start is to look in aztcd.c in
-function aztcd_init, where the drive should be detected during init. Do the
-following:
--reboot the system with boot parameter 'aztcd=<your base address>,0x79'. With
- parameter 0x79 most of the drive version detection is bypassed. After that
- you should see the complete version string including leading and trailing
- blanks during init.
- Now adapt the statement
- if ((result[1]=='A')&&(result[2]=='Z' ...)
- in aztcd_init() to exactly match the first 3 or 4 letters you have seen.
--Another point is the 'smart' card detection feature in aztcd_init(). Normally
- the CD-ROM drive is ready, when aztcd_init is trying to read the version
- string and a time consuming ACMD_SOFT_RESET command can be avoided. This is
- detected by looking, if AFL_OP_OK can be read correctly. If the CD-ROM drive
- hangs in some unknown state, e.g. because of an error before a warm start or
- because you first operated under DOS, even the version string may be correct,
- but the following commands will not. Then change the code in such a way,
- that the ACMD_SOFT_RESET is issued in any case, by substituting the
- if-statement 'if ( ...=AFL_OP_OK)' by 'if (1)'.
-
-If you succeed, please mail may the exact version string of your drive and
-the code modifications, you have made together with a short explanation.
-If you don't succeed, you may mail me the output of the debugging messages.
-But remember, they are only useful, if they are exact and complete and you
-describe in detail your hardware setup and what you did (cold/warm reboot,
-with/without DOS, DOS-driver started/not started, which Linux-commands etc.)
-
-
-9. TECHNICAL HISTORY OF THE DRIVER
-The AZTECH-Driver is a rework of the Mitsumi-Driver. Four major items had to
-be reworked:
-
-a) The Mitsumi drive does issue complete status information acknowledging
-each command, the Aztech drive does only signal that the command was
-processed. So whenever the complete status information is needed, an extra
-ACMD_GET_STATUS command is issued. The handshake procedure for the drive
-can be found in the functions aztSendCmd(), sendAztCmd() and getAztStatus().
-
-b) The Aztech Drive does not have a ACMD_GET_DISK_INFO command, so the
-necessary info about the number of tracks (firstTrack, lastTrack), disk
-length etc. has to be read from the TOC in the lead in track (see function
-aztGetDiskInfo()).
-
-c) Whenever data is read from the drive, the Mitsumi drive is started with a
-command to read an indefinite (0xffffff) number of sectors. When the appropriate
-number of sectors is read, the drive is stopped by a ACDM_STOP command. This
-does not work with the Aztech drive. I did not find a way to stop it. The
-stop and pause commands do only work in AUDIO mode but not in DATA mode.
-Therefore I had to modify the 'finite state machine' in function azt_poll to
-only read a certain number of sectors and then start a new read on demand. As I
-have not completely understood, how the buffer/caching scheme of the Mitsumi
-driver was implemented, I am not sure, if I have covered all cases correctly,
-whenever you get timeout messages, the bug is most likely to be in that
-function azt_poll() around switch(cmd) .... case ACD_S_DATA.
-
-d) I did not get information about changing drive mode. So I doubt, that the
-code around function azt_poll() case AZT_S_MODE does work. In my test I have
-not been able to switch to reading in raw mode. For reading raw mode, Aztech
-uses a different command than for cooked mode, which I only have implemen-
-ted in the ioctl-section but not in the section which is used by the ISO9660-
-
-The driver was developed on an AST PC with Intel 486/DX2, 8MB RAM, 340MB IDE
-hard disk and on an AST PC with Intel Pentium 60MHz, 16MB RAM, 520MB IDE
-running Linux kernel version 1.0.9 from the LST 1.8 Distribution. The kernel
-was compiled with gcc.2.5.8. My CD-ROM drive is an Aztech CDA268-01A. My
-drive says, that it has Firmware Version AZT26801A1.3. It came with a ISA-bus
-interface card and works with polled I/O without DMA and without interrupts.
-The code for all other drives was 'remote' tested and debugged by a number of
-volunteers on the Internet.
-
-Points, where I feel that possible problems might be and all points where I
-did not completely understand the drive's behaviour or trust my own code are
-marked with /*???*/ in the source code. There are also some parts in the
-Mitsumi driver, where I did not completely understand their code.
-
-
-10. ACKNOWLEDGMENTS
-Without the help of P.Bush, Aztech, who delivered technical information
-about the Aztech Drive and without the help of E.Moenkeberg, GWDG, who did a
-great job in analyzing the command structure of various CD-ROM drives, this
-work would not have been possible. E.Moenkeberg was also a great help in
-making the software 'kernel ready' and in answering many of the CDROM-related
-questions in the newsgroups. He really is *the* Linux CD-ROM guru. Thanks
-also to all the guys on the Internet, who collected valuable technical
-information about CDROMs.
-
-Joe Nardone (nardone@clark.net) was a patient tester even for my first
-trial, which was more than slow, and made suggestions for code improvement.
-Especially the 'finite state machine' azt_poll() was rewritten by Joe to get
-clean C code and avoid the ugly 'gotos', which I copied from mcd.c.
-
-Robby Schirmer (schirmer@fmi.uni-passau.de) tested the audio stuff (ioctls)
-and suggested a lot of patches for them.
-
-Joseph Piskor and Peter Nugent were the first users with the ORCHID CD3110
-and also were very patient with the problems which occurred.
-
-Anybody, who is interested in these items should have a look at 'ftp.gwdg.de',
-directory 'pub/linux/cdrom' and at 'ftp.cdrom.com', directory 'pub/cdrom'.
-
-11. PROGRAMMING ADD ONs: cdplay.c
-You can use the ioctl-functions included in aztcd.c in your own programs. As
-an example on how to do this, you will find a tiny CD Player for audio CDs
-named 'cdplay.c'. It allows you to play audio CDs. You can play a specified
-track, pause and resume or skip tracks forward and backwards. If you quit the
-program without stopping the drive, playing is continued. You can also
-(mis)use cdplay to read and hexdump data disks.
-'cdplay.c' can be found as a separate file in package AZTECH.CDROM.Vxx.tgz.
-If you don't have it, you can find the code in the APPENDIX of this file,
-which you should cut out with an editor and store in a separate file
-'cdplay.c'. To compile it and make it executable, do
- gcc -s -Wall -O2 -L/usr/lib cdplay.c -o /usr/local/bin/cdplay # compiles it
- chmod +755 /usr/local/bin/cdplay # makes it executable
- ln -s /dev/aztcd0 /dev/cdrom # creates a link
- (for /usr/lib substitute the top level directory, where your include files
- reside, and for /usr/local/bin the directory, where you want the executable
- binary to reside )
-
-You have to set the correct permissions for cdplay *and* for /dev/mcd0 or
-/dev/aztcd0 in order to use it. Remember, that you should not have /dev/cdrom
-mounted, when you're playing audio CDs.
-
-This program is just a hack for testing the ioctl-functions in aztcd.c, I will
-not maintain it, so if you run into problems, discard it or have a look into
-the source code 'cdplay.c'. The program does only contain a minimum of user
-protection and input error detection. If you use the commands in the wrong
-order or if you try to read a CD at wrong addresses, you may get error messages
-or even hang your machine. If you get STEN_LOW, STEN_LOW_WAIT or segment violation
-error messages when using cdplay, after that, the system might not be stable
-any more, so you'd better reboot. As the ioctl-functions run in kernel mode,
-most normal Linux-multitasking protection features do not work. By using
-uninitialized 'wild' pointers etc., it is easy to write to other users data and
-program areas, destroy kernel tables etc.. So if you experiment with ioctls
-as always when you are doing systems programming and kernel hacking, you
-should have a backup copy of your system in a safe place (and you also
-should try before, how to restore from a backup copy)!
-
-
-Werner Zimmermann
-Fachhochschule fuer Technik Esslingen
-(EMail: zimmerma@rz.fht-esslingen.de)
-Mar 24, 1995
-
----------------------------------------------------------------------------
-APPENDIX: Source code of cdplay.c
-
-/* Tiny Audio CD Player
-
- Copyright 1994, 1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
-
-This program originally was written to test the audio functions of the
-AZTECH.CDROM-driver, but it should work with every CD-ROM drive. Before
-using it, you should set a symlink from /dev/cdrom to your real CDROM
-device.
-
-The GNU General Public License applies to this program.
-
-History: V0.1 W.Zimmermann: First release. Nov. 8, 1994
- V0.2 W.Zimmermann: Enhanced functionality. Nov. 9, 1994
- V0.3 W.Zimmermann: Additional functions. Nov. 28, 1994
- V0.4 W.Zimmermann: fixed some bugs. Dec. 17, 1994
- V0.5 W.Zimmermann: clean 'scanf' commands without compiler warnings
- Jan. 6, 1995
- V0.6 W.Zimmermann: volume control (still experimental). Jan. 24, 1995
-*/
-
-#include <stdio.h>
-#include <ctype.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <linux/cdrom.h>
-#include <linux/aztcd.h>
-
-void help(void)
-{ printf("Available Commands: STOP s EJECT e QUIT q\n");
- printf(" PLAY TRACK t PAUSE p RESUME r\n");
- printf(" NEXT TRACK n REPEAT LAST l HELP h\n");
- printf(" SUB CHANNEL c TRACK INFO i PLAY AT a\n");
- printf(" READ d READ RAW w VOLUME v\n");
-}
-
-int main(void)
-{ int handle;
- unsigned char command=' ', ini=0, first=1, last=1;
- unsigned int cmd, i,j,k, arg1,arg2,arg3;
- struct cdrom_ti ti;
- struct cdrom_tochdr tocHdr;
- struct cdrom_subchnl subchnl;
- struct cdrom_tocentry entry;
- struct cdrom_msf msf;
- union { struct cdrom_msf msf;
- unsigned char buf[2336];
- } azt;
- struct cdrom_volctrl volctrl;
-
- printf("\nMini-Audio CD-Player V0.6 (C) 1994,1995 W.Zimmermann\n");
- handle=open("/dev/cdrom",O_RDWR);
- ioctl(handle,CDROMRESUME);
-
- if (handle<=0)
- { printf("Drive Error: already playing, no audio disk, door open\n");
- printf(" or no permission (you must be ROOT in order to use this program)\n");
- }
- else
- { help();
- while (1)
- { printf("Type command (h = help): ");
- scanf("%s",&command);
- switch (command)
- { case 'e': cmd=CDROMEJECT;
- ioctl(handle,cmd);
- break;
- case 'p': if (!ini)
- { printf("Command not allowed - play track first\n");
- }
- else
- { cmd=CDROMPAUSE;
- if (ioctl(handle,cmd)) printf("Drive Error\n");
- }
- break;
- case 'r': if (!ini)
- { printf("Command not allowed - play track first\n");
- }
- else
- { cmd=CDROMRESUME;
- if (ioctl(handle,cmd)) printf("Drive Error\n");
- }
- break;
- case 's': cmd=CDROMPAUSE;
- if (ioctl(handle,cmd)) printf("Drive error or already stopped\n");
- ini=0;
- cmd=CDROMSTOP;
- if (ioctl(handle,cmd)) printf("Drive error\n");
- ini=0;
- break;
- case 't': cmd=CDROMREADTOCHDR;
- if (ioctl(handle,cmd,&tocHdr)) printf("Drive Error\n");
- first=tocHdr.cdth_trk0;
- last= tocHdr.cdth_trk1;
- if ((first==0)||(first>last))
- { printf ("--could not read TOC\n");
- }
- else
- { printf("--first track: %d --last track: %d --enter track number: ",first,last);
- cmd=CDROMPLAYTRKIND;
- scanf("%i",&arg1);
- ti.cdti_trk0=arg1;
- if (ti.cdti_trk0<first) ti.cdti_trk0=first;
- if (ti.cdti_trk0>last) ti.cdti_trk0=last;
- ti.cdti_ind0=0;
- ti.cdti_trk1=last;
- ti.cdti_ind1=0;
- if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
- ini=1;
- }
- break;
- case 'n': if (!ini++)
- { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
- first=tocHdr.cdth_trk0;
- last= tocHdr.cdth_trk1;
- ti.cdti_trk0=first-1;
- }
- if ((first==0)||(first>last))
- { printf ("--could not read TOC\n");
- }
- else
- { cmd=CDROMPLAYTRKIND;
- if (++ti.cdti_trk0 > last) ti.cdti_trk0=last;
- ti.cdti_ind0=0;
- ti.cdti_trk1=last;
- ti.cdti_ind1=0;
- if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
- ini=1;
- }
- break;
- case 'l': if (!ini++)
- { if (ioctl(handle,CDROMREADTOCHDR,&tocHdr)) printf("Drive Error\n");
- first=tocHdr.cdth_trk0;
- last= tocHdr.cdth_trk1;
- ti.cdti_trk0=first+1;
- }
- if ((first==0)||(first>last))
- { printf ("--could not read TOC\n");
- }
- else
- { cmd=CDROMPLAYTRKIND;
- if (--ti.cdti_trk0 < first) ti.cdti_trk0=first;
- ti.cdti_ind0=0;
- ti.cdti_trk1=last;
- ti.cdti_ind1=0;
- if (ioctl(handle,cmd,&ti)) printf("Drive Error\n");
- ini=1;
- }
- break;
- case 'c': subchnl.cdsc_format=CDROM_MSF;
- if (ioctl(handle,CDROMSUBCHNL,&subchnl))
- printf("Drive Error\n");
- else
- { printf("AudioStatus:%s Track:%d Mode:%d MSF=%d:%d:%d\n", \
- subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",\
- subchnl.cdsc_trk,subchnl.cdsc_adr, \
- subchnl.cdsc_absaddr.msf.minute, subchnl.cdsc_absaddr.msf.second, \
- subchnl.cdsc_absaddr.msf.frame);
- }
- break;
- case 'i': if (!ini)
- { printf("Command not allowed - play track first\n");
- }
- else
- { cmd=CDROMREADTOCENTRY;
- printf("Track No.: ");
- scanf("%d",&arg1);
- entry.cdte_track=arg1;
- if (entry.cdte_track<first) entry.cdte_track=first;
- if (entry.cdte_track>last) entry.cdte_track=last;
- entry.cdte_format=CDROM_MSF;
- if (ioctl(handle,cmd,&entry))
- { printf("Drive error or invalid track no.\n");
- }
- else
- { printf("Mode %d Track, starts at %d:%d:%d\n", \
- entry.cdte_adr,entry.cdte_addr.msf.minute, \
- entry.cdte_addr.msf.second,entry.cdte_addr.msf.frame);
- }
- }
- break;
- case 'a': cmd=CDROMPLAYMSF;
- printf("Address (min:sec:frame) ");
- scanf("%d:%d:%d",&arg1,&arg2,&arg3);
- msf.cdmsf_min0 =arg1;
- msf.cdmsf_sec0 =arg2;
- msf.cdmsf_frame0=arg3;
- if (msf.cdmsf_sec0 > 59) msf.cdmsf_sec0 =59;
- if (msf.cdmsf_frame0> 74) msf.cdmsf_frame0=74;
- msf.cdmsf_min1=60;
- msf.cdmsf_sec1=00;
- msf.cdmsf_frame1=00;
- if (ioctl(handle,cmd,&msf))
- { printf("Drive error or invalid address\n");
- }
- break;
-#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
- case 'd': cmd=CDROMREADMODE1;
- printf("Address (min:sec:frame) ");
- scanf("%d:%d:%d",&arg1,&arg2,&arg3);
- azt.msf.cdmsf_min0 =arg1;
- azt.msf.cdmsf_sec0 =arg2;
- azt.msf.cdmsf_frame0=arg3;
- if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59;
- if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
- if (ioctl(handle,cmd,&azt.msf))
- { printf("Drive error, invalid address or unsupported command\n");
- }
- k=0;
- getchar();
- for (i=0;i<128;i++)
- { printf("%4d:",i*16);
- for (j=0;j<16;j++)
- { printf("%2x ",azt.buf[i*16+j]);
- }
- for (j=0;j<16;j++)
- { if (isalnum(azt.buf[i*16+j]))
- printf("%c",azt.buf[i*16+j]);
- else
- printf(".");
- }
- printf("\n");
- k++;
- if (k>=20)
- { printf("press ENTER to continue\n");
- getchar();
- k=0;
- }
- }
- break;
- case 'w': cmd=CDROMREADMODE2;
- printf("Address (min:sec:frame) ");
- scanf("%d:%d:%d",&arg1,&arg2,&arg3);
- azt.msf.cdmsf_min0 =arg1;
- azt.msf.cdmsf_sec0 =arg2;
- azt.msf.cdmsf_frame0=arg3;
- if (azt.msf.cdmsf_sec0 > 59) azt.msf.cdmsf_sec0 =59;
- if (azt.msf.cdmsf_frame0> 74) azt.msf.cdmsf_frame0=74;
- if (ioctl(handle,cmd,&azt))
- { printf("Drive error, invalid address or unsupported command\n");
- }
- k=0;
- for (i=0;i<146;i++)
- { printf("%4d:",i*16);
- for (j=0;j<16;j++)
- { printf("%2x ",azt.buf[i*16+j]);
- }
- for (j=0;j<16;j++)
- { if (isalnum(azt.buf[i*16+j]))
- printf("%c",azt.buf[i*16+j]);
- else
- printf(".");
- }
- printf("\n");
- k++;
- if (k>=20)
- { getchar();
- k=0;
- }
- }
- break;
-#endif
- case 'v': cmd=CDROMVOLCTRL;
- printf("--Channel 0 Left (0-255): ");
- scanf("%d",&arg1);
- printf("--Channel 1 Right (0-255): ");
- scanf("%d",&arg2);
- volctrl.channel0=arg1;
- volctrl.channel1=arg2;
- volctrl.channel2=0;
- volctrl.channel3=0;
- if (ioctl(handle,cmd,&volctrl))
- { printf("Drive error or unsupported command\n");
- }
- break;
- case 'q': if (close(handle)) printf("Drive Error: CLOSE\n");
- exit(0);
- case 'h': help();
- break;
- default: printf("unknown command\n");
- break;
- }
- }
- }
- return 0;
-}
-
diff --git a/drivers/block/README.fd b/drivers/block/README.fd
index 2bd07338c..f10b9218c 100644
--- a/drivers/block/README.fd
+++ b/drivers/block/README.fd
@@ -13,37 +13,50 @@ Lilo config options (Thinkpad users, read this)
The floppy driver is configured using the 'floppy=' option in
lilo. This option can be typed at the boot prompt, or entered in the
lilo configuration file.
- Example: If your kernel is called linux-72, type the following line
+ Example: If your kernel is called linux-pre2.0.9, type the following line
at the lilo boot prompt (if you have a thinkpad):
- linux-72 floppy=thinkpad
+ linux-pre2.0.9 floppy=thinkpad
You may also enter the following line in /etc/lilo.conf, in the description
-of linux-72:
+of linux-pre2.0.9:
append = "floppy=thinkpad"
Several floppy related options may be given, example:
- linux-72 floppy=daring floppy=two_fdc
+ linux-pre2.0.9 floppy=daring floppy=two_fdc
append = "floppy=daring floppy=two_fdc"
If you give options both in the lilo config file and on the boot
prompt, the option strings of both places are concatenated, the boot
prompt options coming last. That's why there are also options to
-restore the default behaviour.
+restore the default behavior.
+
+ If you use the floppy driver as a module, use the following syntax:
+ insmod floppy 'floppy="<options>"'
+
+Example:
+ insmod floppy 'floppy="daring two_fdc"'
+
+ Note that in this case 'floppy=' should only be typed out once, and
+not once for each option. You need at least modules-1.3.57 for this
+method. However, the older environment variable based syntax is still
+available:
+(sh syntax): floppy="daring two_fdc" insmod floppy
+(csh syntax): setenv floppy "daring two_fdc" ; insmod floppy
+
+ Some versions of insmod are buggy in one way or another. If you have
+any problems (options not being passed correctly, segfaults during
+insmod), first check whether there is a more recent version. If there
+isn't, use the old method using environment variables.
The floppy related options include:
floppy=<mask>,allowed_drive_mask
- Sets the bitmask of allowed drives to <mask>. By default, only units
- 0 and 1 of each floppy controller are allowed. This is done because
- certain non-standard hardware (ASUS PCI motherboards) mess up the
- keyboard when accessing units 2 or 3. This option is somewhat
- obsoleted by the cmos option.
+ Obsolete. Use the floppy=<drive>,<type>,cmos option instead
floppy=all_drives
- Sets the bitmask of allowed drives to all drives. Use this if you have
- more than two drives connected to a floppy controller.
+ Obsolete. Use the floppy=<drive>,<type>,cmos option instead
floppy=asus_pci
- Sets the bitmask to allow only units 0 and 1. (The default)
+ Sets the bit mask to allow only units 0 and 1. (The default)
floppy=daring
Tells the floppy driver that you have a well behaved floppy controller.
@@ -60,8 +73,9 @@ restore the default behaviour.
floppy=two_fdc
floppy=<address>,two_fdc
Tells the floppy driver that you have two floppy controllers. The
- second floppy controller is assumed to be at <address>. If <address>
- is not given, 0x370 is assumed.
+ second floppy controller is assumed to be at <address>. This
+ option is not needed if the second controller is at address
+ 0x370, and if you use the 'cmos' option
floppy=thinkpad
Tells the floppy driver that you have a Thinkpad. Thinkpads use an
@@ -70,28 +84,75 @@ restore the default behaviour.
floppy=0,thinkpad
Tells the floppy driver that you don't have a Thinkpad.
+ floppy=omnibook
+ floppy=nodma
+ Tells the floppy driver not to use Dma for data transfers.
+ This is needed on HP Omnibooks, which don't have a workable
+ DMA channel for the floppy driver. This option is also useful
+ if you frequently get "Unable to allocate DMA memory" messages.
+ Indeed, dma memory needs to be continuous in physical, and is
+ thus harder to find, whereas non-dma buffers may be allocated
+ in virtual memory. However, I advise against this if you have
+ an FDC without a FIFO (8272A or 82072). 82072A and later are
+ OK. You also need at least a 486 to use nodma.
+ If you use nodma mode, I suggest you also set the FIFO
+ threshold to 10 or lower, in order to limit the number of data
+ transfer interrupts.
+
+ floppy=dma
+ Tells the floppy driver that a workable DMA channel is available
+ (the default).
+
+floppy=nofifo
+ Disables the FIFO entirely. This is needed if you get "Bus
+ master arbitration error" messages from your ethernet card (or
+ from other devices) while accessing the floppy.
+
+floppy=fifo
+ Enables the FIFO (default)
+
+ floppy=<threshold>,fifo_depth
+ Sets the FIFO threshold. This is mostly relevant in DMA
+ mode. If this is higher, the floppy driver tolerates more
+ interrupt latency, but it triggers more interrupts (i.e. it
+ imposes more load on the rest of the system). If this is
+ lower, the interrupt latency should be lower too (faster
+ processor). The benefit of a lower threshold is less
+ interrupts.
+ To tune the fifo threshold, switch on over/underrun messages
+ using 'floppycontrol --messages'. Then access a floppy
+ disk. If you get a huge amount of "Over/Underrun - retrying"
+ messages, then the fifo threshold is too low. Try with a
+ higher value, until you only get an occasional Over/Underrun.
+ It is a good idea to compile the floppy driver as a module
+ when doing this tuning. Indeed, it allows to try different
+ fifo values without rebooting the machine for each test. Note
+ that you need to do 'floppycontrol --messages' every time you
+ re-insert the module.
+ Usually, tuning the fifo threshold should not be needed, as
+ the default (0xa) is reasonable.
+
floppy=<drive>,<type>,cmos
- Sets the cmos type of <drive> to <type>. Additionally, this drive is
- allowed in the bitmask. This is useful if you have more than two
- floppy drives (only two can be described in the physical cmos), or if
- your BIOS uses non-standard CMOS types. The CMOS types are:
- 0 - unknown or not installed
+ Sets the CMOS type of <drive> to <type>. This is mandatory if
+ you have more than two floppy drives (only two can be
+ described in the physical CMOS), or if your BIOS uses
+ non-standard CMOS types. The CMOS types are:
+ 0 - Use the value of the physical CMOS
1 - 5 1/4 DD
2 - 5 1/4 HD
3 - 3 1/2 DD
4 - 3 1/2 HD
5 - 3 1/2 ED
6 - 3 1/2 ED
+ 16 - unknown or not installed
(Note: there are two valid types for ED drives. This is because 5 was
initially chosen to represent floppy *tapes*, and 6 for ED drives.
AMI ignored this, and used 5 for ED drives. That's why the floppy
driver handles both)
- Setting the CMOS to 0 for the first two drives (default) makes the
- floppy driver read the physical cmos for those drives.
floppy=unexpected_interrupts
Print a warning message when an unexpected interrupt is received
- (default behaviour)
+ (default behavior)
floppy=no_unexpected_interrupts
floppy=L40SX
@@ -110,9 +171,9 @@ package. This package also contains a new version of mtools which
allows to access high capacity disks (up to 1992K on a high density 3
1/2 disk!). It also contains additional documentation about the floppy
driver. It can be found at:
- ftp.imag.fr:pub/Linux/ZLIBC/fdutils/fdutils-4.0.src.tar.gz
- sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.0.src.tar.gz
- tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.0.src.tar.gz
+ ftp.imag.fr:pub/Linux/ZLIBC/fdutils/fdutils-4.1.src.tar.gz
+ sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.1.src.tar.gz
+ tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.1.src.tar.gz
Alpha patches to these utilities are at:
ftp.imag.fr:pub/Linux/ZLIBC/fdutils/ALPHA
diff --git a/drivers/block/README.hd b/drivers/block/README.hd
index 162928178..ba2187425 100644
--- a/drivers/block/README.hd
+++ b/drivers/block/README.hd
@@ -5,4 +5,4 @@ support for drives where the BIOS reports "more than 16 heads".
The hdparm.c program for controlling various features is now packaged
separately. Look for it on your favorite linux FTP site.
--mlord@bnr.ca
+-mlord@pobox.com
diff --git a/drivers/block/README.ide b/drivers/block/README.ide
index 39ae4fa79..d15e05834 100644
--- a/drivers/block/README.ide
+++ b/drivers/block/README.ide
@@ -1,278 +1,3 @@
-README.ide -- Information regarding ide.c and ide-cd.c (IDE driver in 1.2.x)
-================================================================================
-Supported by: mlord@bnr.ca -- disks, interfaces, probing
- snyder@fnald0.fnal.gov -- cdroms, ATAPI, audio
+The IDE driver Documentation is now in the linux/Documentation directory.
-(see description later on below for handling BIG IDE drives with >1024 cyls).
-
-Major features of ide.c & ide-cd.c:
-
- - support for up to two IDE interfaces on one or two IRQs
- - support for any mix of up to four disk and/or cdrom drives
- - support for reading IDE ATAPI cdrom drives (NEC,MITSUMI,VERTOS,SONY)
- - support for audio functions
- - auto-detection of interfaces, drives, IRQs, and disk geometries
- -- "single" drives should be jumpered as "master", not "slave"
- - support for BIOSs which report "more than 16 heads" on disk drives
- - uses LBA (slightly faster) on disk drives which support it
- - support for lots of fancy (E)IDE drive functions with hdparm utility
- - optional (compile time) support for 32-bit VLB data transfers
- - support for IDE multiple (block) mode (same as hd.c)
- - support for interrupt unmasking during I/O (better than hd.c)
- - improved handshaking and error detection/recovery
- - can co-exist with hd.c to control only the secondary interface
-NEW! - support for reliable operation of buggy CMD-640 interfaces
- - use kernel command line option: hda=serialize
-NEW! - experimental support for DTC-2278D interfaces
- - use kernel command line option: hda=dtc2278
-NEW! - run-time selectable 32bit interface support (using hdparm-2.3)
-
-Under construction:
-
- - improved CMD support: tech info is supposedly "in the mail"
- - support for interface speed selection on jumperless interfaces
- - improved detection of non-standard IDE ATAPI cdrom drives
- - support for non-standard 3rd/4th drive interface on Promise cards
-
-***
-
-IMPORTANT NOTICE: "CMD" EIDE Interfaces will not (by default) work *reliably*
-when drives are attached to the second interface. To "fix" this, supply the
-special kernel "command line" parameter to LILO: hda=serialize
-Failure to do so can cause severe data corruption!
-
-***
-
-To access devices on the second interface, device entries must first be
-created in /dev for them. To create such entries, simply run the included
-shell script: MAKEDEV.ide1
-
-Apparently the early releases of Slackware 2.2 have incorrect entries
-in /dev for hdc* and hdd* -- this can also be corrected by running MAKEDEV.ide1
-
-ide.c automatically probes for the primary and secondary interfaces,
-for the drives/geometries attached to those interfaces, and for the
-IRQ numbers being used by the interfaces (normally IRQ14 & IRQ15).
-
-The primary and secondary interfaces may share a single IRQ if necessary,
-at a slight performance penalty, whether on separate cards or a single VLB card.
-
-Drives are normally found by auto-probing and/or examining the CMOS/BIOS data.
-For really weird situations, the apparent (fdisk) geometry can also be specified
-on the kernel "command line" using LILO. The format of such lines is:
-
- hdx=cyls,heads,sects,wpcom,irq
-or hdx=cdrom
-
-where hdx can be any of {hda,hdb,hdc,hdd}, or simply hd, for the "next" drive
-in sequence. Only the first three parameters are required (cyls,heads,sects),
-and wpcom is ignored for IDE drives. For example:
-
- hdc=1050,32,64 hdd=cdrom
-
-If an irq number is given, it will apply to both drives on the same interface,
-either {hda,hdb} or {hdc,hdd}. The results of successful auto-probing may
-override the physical geometry/irq specified, though the "original" geometry
-is retained as the "logical" geometry for partitioning purposes (fdisk).
-
-If the auto-probing during boot time confuses a drive (ie. the drive works
-with hd.c but not with ide.c), then an command line option may be specified
-for each drive for which you'd like the drive to skip the hardware
-probe/identification sequence. For example:
-
- hdb=noprobe
-or
- hdc=768,16,32
- hdc=noprobe
-
-Note that when only one IDE device is attached to an interface,
-it must be jumpered as "single" or "master", *not* "slave".
-Many folks have had "trouble" with cdroms because of this requirement
-of the ATA (IDE) standard.
-
-Courtesy of Scott Snyder, the driver now supports ATAPI cdrom drives
-such as the NEC-260 and the new MITSUMI triple/quad speed drives.
-Such drives will be identified at boot time, as hda,hdb,hdc or hdd,
-just like a harddisk.
-
-If for some reason your cdrom drive is *not* found at boot time, you can force
-the probe to look harder by supplying a kernel command line parameter
-via LILO, such as: hdc=cdrom
-
-For example, a GW2000 system might have a harddrive on the primary
-interface (/dev/hda) and an IDE cdrom drive on the secondary interface
-(/dev/hdc). To mount a CD in the cdrom drive, one would use something like:
-
- ln -sf /dev/hdc /dev/cdrom
- mkdir /cd
- mount /dev/cdrom /cd -t iso9660 -o ro
-
-Please pass on any feedback on the cdrom stuff to the author & maintainer,
-Scott Snyder (snyder@fnald0.fnal.gov).
-
-The kernel is now be able to execute binaries directly off of the cdrom,
-provided it is mounted with the default block size of 1024.
-
-The hdparm.c program for controlling various IDE features is now packaged
-separately. Look for it on popular linux FTP sites.
-
-mlord@bnr.ca
-snyder@fnald0.fnal.gov
-================================================================================
-
-Some Terminology
-----------------
-IDE = Integrated Drive Electronics, meaning that each drive has a built-in
-controller, which is why an "IDE interface card" is not a "controller card".
-
-IDE drives are designed to attach almost directly to the ISA bus of an AT-style
-computer. The typical IDE interface card merely provides I/O port address
-decoding and tri-state buffers, although several newer localbus cards go much
-beyond the basics. When purchasing a localbus IDE interface, avoid cards with
-an onboard BIOS and those which require special drivers. Instead, look for a
-card which uses hardware switches/jumpers to select the interface timing speed,
-to allow much faster data transfers than the original 8Mhz ISA bus allows.
-
-ATA = AT (the old IBM 286 computer) Attachment Interface, a draft American
-National Standard for connecting hard drives to PCs. This is the official
-name for "IDE".
-
-The latest standards define some enhancements, known as the ATA-2 spec,
-which grew out of vendor-specific "Enhanced IDE" (EIDE) implementations.
-
-ATAPI = ATA Packet Interface, a new protocol for controlling the drives,
-similar to SCSI protocols, created at the same time as the ATA2 standard.
-ATAPI is currently used for controlling CDROM and TAPE devices, and will
-likely also soon be used for Floppy drives, removable R/W cartridges,
-and for high capacity hard disk drives.
-
-How To Use *Big* ATA/IDE drives with Linux
-------------------------------------------
-The ATA Interface spec for IDE disk drives allows a total of 28 bits
-(8 bits for sector, 16 bits for cylinder, and 4 bits for head) for addressing
-individual disk sectors of 512 bytes each (in "Linear Block Address" (LBA)
-mode, there is still only a total of 28 bits available in the hardware).
-This "limits" the capacity of an IDE drive to no more than 128GB (Giga-bytes).
-All current day IDE drives are somewhat smaller than this upper limit, and
-within a few years, ATAPI disk drives will raise the limit considerably.
-
-All IDE disk drives "suffer" from a "16-heads" limitation: the hardware has
-only a four bit field for head selection, restricting the number of "physical"
-heads to 16 or less. Since the BIOS usually has a 63 sectors/track limit,
-this means that all IDE drivers larger than 504MB (528Meg) must use a "physical"
-geometry with more than 1024 cylinders.
-
- (1024cyls * 16heads * 63sects * 512bytes/sector) / (1024 * 1024) == 504MB
-
-(Some BIOSs (and controllers with onboard BIOS) pretend to allow "32" or "64"
- heads per drive (discussed below), but can only do so by playing games with
- the real (hidden) geometry, which is always limited to 16 or fewer heads).
-
-This presents two problems to most systems:
-
- 1. The INT13 interface to the BIOS only allows 10-bits for cylinder
- addresses, giving a limit of 1024cyls for programs which use it.
-
- 2. The physical geometry fields of the disk partition table only
- allow 10-bits for cylinder addresses, giving a similar limit of 1024
- cyls for operating systems that do not use the "sector count" fields
- instead of the physical Cyl/Head/Sect (CHS) geometry fields.
-
-Neither of these limitations affects Linux itself, as it (1) does not use the
-BIOS for disk access, and it (2) is clever enough to use the "sector count"
-fields of the partition table instead of the physical CHS geometry fields.
-
- a) Most folks use LILO to load linux. LILO uses the INT13 interface
- to the BIOS to load the kernel at boot time. Therefore, LILO can only
- load linux if the files it needs (usually just the kernel images) are
- located below the magic 1024 cylinder "boundary" (more on this later).
-
- b) Many folks also like to have bootable DOS partitions on their
- drive(s). DOS also uses the INT13 interface to the BIOS, not only
- for booting, but also for operation after booting. Therefore, DOS
- can normally only access partitions which are contained entirely below
- the magic 1024 cylinder "boundary".
-
-There are at least seven commonly used schemes for kludging DOS to work
-around this "limitation". In the long term, the problem is being solved
-by introduction of an alternative BIOS interface that does not have the
-same limitations as the INT13 interface. New versions of DOS are expected
-to detect and use this interface in systems whose BIOS provides it.
-
-But in the present day, alternative solutions are necessary.
-
-The most popular solution in newer systems is to have the BIOS shift bits
-between the cylinder and head number fields. This is activated by entering
-a translated logical geometry into the BIOS/CMOS setup for the drive.
-Thus, if the drive has a geometry of 2100/16/63 (CHS), then the BIOS could
-present a "logical" geometry of 525/64/63 by "shifting" two bits from the
-cylinder number into the head number field for purposes of the partition table,
-CMOS setup, and INT13 interfaces. Linux kernels 1.1.39 and higher detect and
-"handle" this translation automatically, making this a rather painless solution
-for the 1024 cyls problem. If for some reason Linux gets confused (unlikely),
-then use the kernel command line parameters to pass the *logical* geometry,
-as in: hda=525,64,63
-
-If the BIOS does not support this form of drive translation, then several
-options remain, listed below in inverse order of popularity:
-
- - boot from a floppy disk instead of the hard drive (takes 10 seconds).
- - use a partition below the 1024 cyl boundary to hold the linux
- boot files (kernel images and /boot directory), and place the rest
- of linux anywhere else on the drive. These files can reside in a DOS
- partition, or in a tailor-made linux boot partition.
-
-If you cannot use drive translation, *and* your BIOS also restricts you to
-entering no more than 1024 cylinders in the geometry field in the CMOS setup,
-then just set it to 1024. As of v3.5 of this driver, Linux automatically
-determines the *real* number of cylinders for fdisk to use, allowing easy
-access to the full disk capacity without having to fiddle around.
-
-Regardless of what you do, all DOS partitions *must* be contained entirely
-within the first 1024 logical cylinders. For a 1Gig WD disk drive, here's
-a good "half and half" partitioning scheme to start with:
-
- geometry = 2100/16/63
- /dev/hda1 from cyl 1 to 992 dos
- /dev/hda2 from cyl 993 to 1023 swap
- /dev/hda3 from cyl 1024 to 2100 linux
-
-To ensure that LILO can boot linux, the boot files (kernel and /boot/*)
-must reside within the first 1024 cylinders of the drive. If your linux
-root partition is *not* completely within the first 1024 cyls (quite common),
-then you can use LILO to boot linux from files on your DOS partition
-by doing the following after installing slackware (or whatever):
-
- 0. Boot from the "boot floppy" created during the installation
- 1. Mount your DOS partition as /dos (and stick it in /etc/fstab)
- 2. Move your kernel (/vmlinuz) to /dos/vmlinuz with: mv /vmlinuz /dos
- 3. Edit /etc/lilo.conf to change /vmlinuz to /dos/vmlinuz
- 4. Move /boot to /dos/boot with: cp -a /boot /dos ; rm -r /boot
- 5. Create a symlink for LILO to use with: ln -s /dos/boot /boot
- 6. Re-run LILO with: lilo
-
- A danger with this approach is that whenever an MS-DOS "defragmentation"
- program is run (like Norton "speeddisk"), it may move the Linux boot
- files around, confusing LILO and making the (Linux) system unbootable.
- Be sure to keep a kernel "boot floppy" at hand for such circumstances.
-
-If you "don't do DOS", then partition as you please, but remember to create
-a small partition to hold the /boot directory (and vmlinuz) as described above
-such that they stay within the first 1024 cylinders.
-
-Note that when creating partitions that span beyond cylinder 1024,
-Linux fdisk will complain about "Partition X has different physical/logical
-endings" and emit messages such as "This is larger than 1024, and may cause
-problems with some software". Ignore them for linux partitions. The "some
-software" refers to DOS, the BIOS, and LILO, as described previously.
-
-Western Digital now ships a "DiskManager 6.03" diskette with all of their big
-hard drives. Burn it! That idiotic piece of garbage isn't even universally
-compatible with DOS, let alone other operating systems like Linux. Eventually
-some kind person will kludge Linux to work with it, but at present the two
-are completely incompatible. If you have this version of DiskManager on your
-hard disk already, it can be exterminated at the expense of all data on the
-drive (back it up elsewhere), by using the "DM" command from the diskette
-as follows: DM /Y-
-
-mlord@bnr.ca
+-mlord@pobox.com
diff --git a/drivers/block/README.md b/drivers/block/README.md
new file mode 100644
index 000000000..6ad19ac99
--- /dev/null
+++ b/drivers/block/README.md
@@ -0,0 +1,4 @@
+Tools that manage md devices can be found at sweet-smoke.ufr-info-p7.ibp.fr
+in public/Linux/md035.tar.gz.
+
+ Marc ZYNGIER <zyngier@ufr-info-p7.ibp.fr>
diff --git a/drivers/block/README.sbpcd b/drivers/block/README.sbpcd
deleted file mode 100644
index 6a40abfb0..000000000
--- a/drivers/block/README.sbpcd
+++ /dev/null
@@ -1,997 +0,0 @@
-This README belongs to release 3.7 or newer of the SoundBlaster Pro
-(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and TEAC)
-CD-ROM driver for Linux.
-
-The driver is able to drive the whole family of "traditional" IDE-style (that
-is NOT the new "Enhanced IDE" or "ATAPI" drive standard) Matsushita,
-Kotobuki, Panasonic drives, sometimes labelled as "CreativeLabs". The
-well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563.
-
-The Longshine LCS-7260 is a double-speed drive which uses the "old"
-Matsushita command set. It is supported now - with help by Serge Robyns.
-
-There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-563
-with a special controller board. This drive is supported (the interface is
-of the "LaserMate" type), and it is possibly the best buy today (cheaper than
-an internal drive, and you can use it as an internal, too - f.e. plug it into
-a soundcard).
-
-CreativeLabs has a new drive "CD-200". Support is under construction.
-Drive detection and playing audio should already work. I need qualified
-feedback about the bugs within the data functions or a drive (I never saw
-a CD200).
-
-The quad-speed TEAC CD-55A drive is supported. The routines may still be
-a little bit buggy, but the data rate already reaches 500 kB/sec if you
-set SBP_BUFFER_FRAMES to 64. The drive is able to deliver 600 kB/sec, so
-this has to get a point of work.
-
-This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives,
-and this driver is in no way usable for any new IDE ATAPI drive.
-
-This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
-SoundFX, ...) and/or with the "no-sound" cards (Panasonic CI-101P, LaserMate,
-WDH-7001C, Longshine LCS-6853, older Aztech cards, ...).
-
-It should work too now with the "configurable" interface "Sequoia S-1000",
-which is found on the Spea Media FX sound card. I still need feedback about
-this, or such a card. Anyways, the procedure "boot DOS and wait until
-CONFIG.SYS is done, then use CTL-ALT-DEL to boot Linux" should make it
-work.
-
-The interface type has to get configured in /usr/include/linux/sbpcd.h,
-because the behavior of some sound card interfaces is different.
-
-With some TEAC drives I have seen interface cards which seem to lack the
-"drive select" lines; always drive 0 gets addressed. To avoid "mirror drives"
-with such interface cards, set MAX_DRIVES to 1 and jumper your drive to ID 0.
-
-The driver respects all known drive firmware releases - my old drive is a 2.11,
-but it should work with CR-52x drives <2.01 ... >3.00 and with CR-56x drives
-<0.75 .. 5.00.
-
-Up to 4 drives per interface card, and up to 4 interface cards are supported.
-All supported drive families can be mixed, but the CR-521 drives are
-hard-wired to drive ID 0. The drives have to use different drive IDs, and each
-drive has to get a unique minor number (0...3), corresponding indirectly to
-its drive ID.
-The drive IDs may be selected freely from 0 to 3 - they do not have to be in
-consecutive order.
-
-As Don Carroll, don@ds9.us.dell.com or FIDO 1:382/14, told me, it is possible
-to change old drives to any ID, too. He writes in this sense:
- "In order to be able to use more than one single speed drive
- (they do not have the ID jumpers) you must add a DIP switch
- and two resistors. The pads are already on the board next to
- the power connector. You will see the silkscreen for the
- switch if you remove the top cover.
- 1 2 3 4
- ID 0 = x F F x O = "on"
- ID 1 = x O F x F = "off"
- ID 2 = x F O x x = "don't care"
- ID 3 = x O O x
- Next to the switch are the positions for R76 (7k) and R78
- (12k). I had to play around with the resistor values - ID 3
- did not work with other values. If the values are not good,
- ID 3 behaves like ID 0."
-
-To use more than 4 drives, you simply need a second controller card at a
-different address and a second cable.
-
-The driver supports reading of data from the CD and playing of audio tracks.
-The audio part should run with WorkMan, xcdplayer, with the "non-X11" products
-CDplayer and WorkBone - tell me if it is not compatible with other software.
-
-With the CR-562 and CR-563 drives, the reading of audio frames is possible.
-This is implemented by an IOCTL function which reads READ_AUDIO frames of
-2352 bytes at once (configurable with the "READ_AUDIO" define, default is 0).
-Reading the same frame a second time gives different data; the frame data
-start at a different position, but all read bytes are valid, and we always
-read 98 consecutive chunks (of 24 Bytes) as a frame. Reading more than 1 frame
-at once possibly misses some chunks at each frame boundary. This lack has to
-get corrected by external, "higher level" software which reads the same frame
-again and tries to find and eliminate overlapping chunks (24-byte-pieces).
-
-The transfer rate with reading audio (1-frame-pieces) currently is very slow.
-This can be better reading bigger chunks, but the "missing" chunks possibly
-occur at the beginning of each single frame.
-The software interface possibly may change a bit the day the SCSI driver
-supports it too.
-
-With all but the CR-52x drives, MultiSession is supported.
-Photo CDs work (the "old" drives like CR-521 can access only the first
-session of a photoCD).
-At ftp.gwdg.de:/pub/linux/hpcdtoppm/ you will find Hadmut Danisch's package to
-convert photo CD image files and Gerd Knorr's viewing utility.
-
-The transfer rate will reach 150 kB/sec with "old" drives, 300 kB/sec with
-double-speed drives, and about 500 kB/sec with quad speed drives.
-XA (PhotoCD) disks with "old" drives give only 50 kB/sec.
-
-This release is part of the standard kernel and consists of
-- this README file
-- the driver file linux/drivers/block/sbpcd.c
-- the header file linux/include/linux/sbpcd.h.
-
-
-To install:
------------
-
-1. Setup your hardware parameters. Though the driver does "auto-probing" at a
- lot of (not all possible!) addresses, this step is recommended for
- every-day use.
- a. Go into /usr/src/linux/include/linux/sbpcd.h and configure it for your
- hardware (near the beginning):
- a1. Set it up for the appropriate type of interface board.
- "Original" CreativeLabs sound cards need "SBPRO 1".
- Most "compatible" sound cards (for example "Highscreen", "SoundFX"
- and "Galaxy") need "SBPRO 0".
- The "no-sound" board from OmniCd needs the "SBPRO 1" setup.
- All other "no-sound" boards need the "SBPRO 0" setup.
- The Spea Media FX sound card needs "SBPRO 2".
- sbpcd.c holds some examples in its auto-probe list.
- If you configure "SBPRO" wrong, the playing of audio CDs will work,
- but you will not be able to mount a data CD.
- a2. Tell the address of your CDROM_PORT (not of the sound port).
- a3. Set DISTRIBUTION to 0.
- b. Additionally for 2.a1 and 2.a2, the setup may be done during
- boot time (via the "kernel command line" or "LILO option"):
- sbpcd=0x230,SoundBlaster
- or
- sbpcd=0x320,LaserMate
- or
- sbpcd=0x330,SPEA
- This is especially useful if you install a fresh distribution.
-2. "cd /usr/src/linux" and do a "make config" and select "y" for Matsushita
- CD-ROM support and for ISO9660 FileSystem support. If you do not have a
- second, third, or fourth controller installed, do not say "y" to the
- secondary Matsushita CD-ROM questions.
- SCSI and/or SCSI CD-ROM support is not needed.
-
-3. Then do a "make dep", then make the kernel image ("make zlilo" or else).
-
-4. Make the device file(s). The driver uses definitely and exclusive the
- MAJOR 25, so do
- mknod /dev/sbpcd b 25 0 (if you have only one drive)
- and/or
- mknod /dev/sbpcd0 b 25 0
- mknod /dev/sbpcd1 b 25 1
- mknod /dev/sbpcd2 b 25 2
- mknod /dev/sbpcd3 b 25 3
- to make the node(s).
-
- The driver no longer uses the "AT bus style" device numbering; the SCSI
- scheme is used now; that means, the "first found" drive gets MINOR 0
- (regardless to its jumpered ID), the "next found" (at the same cable)
- gets MINOR 1, ...
-
- For a second interface board, you have to make nodes like
- mknod /dev/sbpcd4 b 26 0
- mknod /dev/sbpcd5 b 26 1
- and so on. Use the MAJORs 26, 27, 28.
-
- If you further make a link like
- ln -s sbpcd /dev/cdrom
- you can use the name /dev/cdrom, too.
-
-5. Reboot with the new kernel.
-
-You should now be able to do
- mkdir /CD
-and
- mount -t iso9660 -o ro /dev/sbpcd /CD
-or
- mount -t iso9660 -o ro,block=2048 /dev/sbpcd /CD
-and see the contents of your CD in the /CD directory, and/or hear music with
-"workman -c /dev/sbpcd &".
-
-
-Using sbpcd as a "loadable module":
------------------------------------
-
-If you do NOT select "Matsushita/Panasonic CDROM driver support" during the
-"make config" of your kernel, you can build the "loadable module" sbpcd.o.
-Read /usr/src/linux/README.modules on this.
-
-If sbpcd gets used as a module, the support of more than one interface
-card (i.e. drives 4...15) is disabled.
-
-You can specify interface address and type with the "insmod" command like:
- # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x340,0
-or
- # insmod /usr/src/linux/modules/sbpcd.o sbpcd=0x230,1
-where the last number represents the SBPRO setting (no strings allowed here).
-
-
-Things of interest:
--------------------
-
-The driver is configured to try the LaserMate type of interface at I/O port
-0x0340 first. If this is not appropriate, sbpcd.h should get changed
-(you will find the right place - just at the beginning).
-
-No DMA and no IRQ is used.
-
-To reduce or increase the amount of kernel messages, edit sbpcd.c and play
-with the "DBG_xxx" switches (initialization of the variable "sbpcd_debug").
-
-The driver uses the "variable BLOCK_SIZE" feature. To use it, you have to
-specify "block=2048" as a mount option. Doing this will disable the direct
-execution of a binary from the CD; you have to copy it to a device with the
-standard BLOCK_SIZE (1024) before. So, do not use this if your system is
-directly "running from the CDROM" (like some of YGGDRASIL's installation
-variants). There are CDs on the market (like the german "unifix" Linux
-distribution) which MUST get handled with a block_size of 1024. Generally,
-one can say all the CDs which hold files of the name YMTRANS.TBL are defective;
-do not use block=2048 with those.
-
-At the beginning of sbpcd.c, you will find some "#define"s (f.e. EJECT and
-JUKEBOX). With that, you can configure the driver for some special things.
-You can use the appended program "cdtester" to set the auto-eject feature
-during runtime. Jeff Tranter's "eject" utility can do this, too (and more)
-for you.
-
-There is a new ioctl CDROMMULTISESSION to obtain with a user program if
-the CD is an XA disk and - if it is - where the last session starts. The
-"cdtester" program illustrates how to call it.
-
-
-Auto-probing at boot time:
---------------------------
-
-The driver does auto-probing at many well-known interface card addresses,
-but not all:
-Some probings can cause a hang if an NE2000 ethernet card gets touched, because
-SBPCD's auto-probing happens before the initialization of the net drivers.
-Those "hazardous" addresses are excluded from auto-probing; the "kernel
-command line" feature has to be used during installation if you have your
-drive at those addresses. The "module" version is allowed to probe at those
-addresses, too.
-
-The auto-probing looks first at the configured address resp. the address
-submitted by the kernel command line. With this, it is possible to use this
-driver within installation boot floppies, and for any non-standard address,
-too.
-
-Auto-probing will make an assumption about the interface type ("SBPRO" or not),
-based upon the address. That assumption may be wrong (initialization will be
-o.k., but you will get I/O errors during mount). In that case, use the "kernel
-command line" feature and specify address & type at boot time to find out the
-right setup.
-
-For every-day use, address and type should get configured within sbpcd.h. That
-will stop the auto-probing due to success with the first try.
-
-The kernel command "sbpcd=0" suppresses each auto-probing and causes
-the driver not to find any drive; it is meant for people who love sbpcd
-so much that they do not want to miss it, even if they miss the drives. ;-)
-
-If you configure "#define CDROM_PORT 0" in sbpcd.h, the auto-probing is
-initially disabled and needs an explicit kernel command to get activated.
-Once activated, it does not stop before success or end-of-list. This may be
-useful within "universal" CDROM installation boot floppies (but using the
-loadable module would be better because it allows an "extended" auto-probing
-without fearing NE2000 cards).
-
-To shorten the auto-probing list to a single entry, set DISTRIBUTION 0 within
-sbpcd.h.
-
-
-Setting up address and interface type:
---------------------------------------
-
-If your I/O port address is not 0x340, you have to look for the #defines near
-the beginning of sbpcd.h and configure them: set SBPRO to 0 or 1 or 2, and
-change CDROM_PORT to the address of your CDROM I/O port.
-
-Most of the "SoundBlaster compatible" cards behave like the no-sound
-interfaces!
-
-With "original" SB Pro cards, an initial setting of CD_volume through the
-sound cards MIXER register gets done.
-If you are using a "compatible" sound card of types "LaserMate" or "SPEA",
-you can set SOUND_BASE (in sbpcd.h) to get it done with your card, too...
-
-
-Using audio CDs:
-----------------
-
-Workman, WorkBone, xcdplayer, cdplayer and the nice little tool "cdplay" (see
-README.aztcd from the Aztech driver package) should work.
-
-The program CDplayer likes to talk to "/dev/mcd" only, xcdplayer wants
-"/dev/rsr0", workman loves "/dev/sr0" or "/dev/cdrom" - so, do the appropriate
-links for using them without the need of supplying parameters.
-
-
-Copying audio tracks:
----------------------
-
-The following program will copy track 1 (or a piece of it) from an audio CD
-into the file "track01":
-
-/*=================== begin program ========================================*/
-/*
- * read an audio track from a CD
- *
- * (c) 1994 Eberhard Moenkeberg <emoenke@gwdg.de>
- * may be used & enhanced freely
- *
- * Due to non-existent sync bytes at the beginning of each audio frame (or due
- * to a firmware bug within all known drives?), it is currently a kind of
- * fortune if two consecutive frames fit together.
- * Usually, they overlap, or a little piece is missing. This happens in units
- * of 24-byte chunks. It has to get fixed by higher-level software (reading
- * until an overlap occurs, and then eliminate the overlapping chunks).
- * ftp.gwdg.de:/pub/linux/misc/cdda2wav-sbpcd.*.tar.gz holds an example of
- * such an algorithm.
- * This example program further is missing to obtain the SubChannel data
- * which belong to each frame.
- *
- * This is only an example of the low-level access routine. The read data are
- * pure 16-bit CDDA values; they have to get converted to make sound out of
- * them.
- * It is no fun to listen to it without prior overlap/underlap correction!
- */
-#include <stdio.h>
-#include <sys/ioctl.h>
-#include <linux/cdrom.h>
-
-static struct cdrom_tochdr hdr;
-static struct cdrom_tocentry entry[101];
-static struct cdrom_read_audio arg;
-static u_char buffer[CD_FRAMESIZE_RAW];
-static int datafile, drive;
-static int i, j, limit, track, err;
-static char filename[32];
-
-main(int argc, char *argv[])
-{
-/*
- * open /dev/cdrom
- */
- drive=open("/dev/cdrom", 0);
- if (drive<0)
- {
- fprintf(stderr, "can't open drive.\n");
- exit (-1);
- }
-/*
- * get TocHeader
- */
- fprintf(stdout, "getting TocHeader...\n");
- err=ioctl(drive, CDROMREADTOCHDR, &hdr);
- if (err!=0)
- {
- fprintf(stderr, "can't get TocHeader (error %d).\n", err);
- exit (-1);
- }
- else
- fprintf(stdout, "TocHeader: %d %d\n", hdr.cdth_trk0, hdr.cdth_trk1);
-/*
- * get and display all TocEntries
- */
- fprintf(stdout, "getting TocEntries...\n");
- for (i=1;i<=hdr.cdth_trk1+1;i++)
- {
- if (i!=hdr.cdth_trk1+1) entry[i].cdte_track = i;
- else entry[i].cdte_track = CDROM_LEADOUT;
- entry[i].cdte_format = CDROM_LBA;
- err=ioctl(drive, CDROMREADTOCENTRY, &entry[i]);
- if (err!=0)
- {
- fprintf(stderr, "can't get TocEntry #%d (error %d).\n", i, err);
- exit (-1);
- }
- else
- {
- fprintf(stdout, "TocEntry #%d: %1X %1X %06X %02X\n",
- entry[i].cdte_track,
- entry[i].cdte_adr,
- entry[i].cdte_ctrl,
- entry[i].cdte_addr.lba,
- entry[i].cdte_datamode);
- }
- }
- fprintf(stdout, "got all TocEntries.\n");
-/*
- * ask for track number (not implemented here)
- */
-track=1;
-#if 0 /* just read a little piece (4 seconds) */
-entry[track+1].cdte_addr.lba=entry[track].cdte_addr.lba+300;
-#endif
-/*
- * read track into file
- */
- sprintf(filename, "track%02d\0", track);
- datafile=creat(filename, 0755);
- if (datafile<0)
- {
- fprintf(stderr, "can't open datafile %s.\n", filename);
- exit (-1);
- }
- arg.addr.lba=entry[track].cdte_addr.lba;
- arg.addr_format=CDROM_LBA; /* CDROM_MSF would be possible here, too. */
- arg.nframes=1;
- arg.buf=&buffer[0];
- limit=entry[track+1].cdte_addr.lba;
- for (;arg.addr.lba<limit;arg.addr.lba++)
- {
- err=ioctl(drive, CDROMREADAUDIO, &arg);
- if (err!=0)
- {
- fprintf(stderr, "can't read abs. frame #%d (error %d).\n",
- arg.addr.lba, err);
- }
- j=write(datafile, &buffer[0], CD_FRAMESIZE_RAW);
- if (j!=CD_FRAMESIZE_RAW)
- {
- fprintf(stderr,"I/O error (datafile) at rel. frame %d\n",
- arg.addr.lba-entry[track].cdte_addr.lba);
- }
- arg.addr.lba++;
- }
-}
-/*===================== end program ========================================*/
-
-At ftp.gwdg.de:/pub/linux/misc/cdda2wav-sbpcd.*.tar.gz is an adapted version of
-Heiko Eissfeldt's digital-audio to .WAV converter (the original is there, too).
-This is preliminary, as Heiko himself will care about it.
-
-
-Known problems:
----------------
-
-Currently, the detection of disk change or removal is actively disabled.
-
-Most attempts to read the UPC/EAN code result in a stream of zeroes. All my
-drives are mostly telling there is no UPC/EAN code on disk or there is, but it
-is an all-zero number. I guess now almost no CD holds such a number.
-
-Bug reports, comments, wishes, donations (technical information is a donation,
-too :-) etc. to
- emoenke@gwdg.de
- or to my FIDO address: Eberhard Moenkeberg, 2:2437/210.27
-
-SnailMail address, preferable for CD editors if they want to submit a free
-"cooperation" copy:
- Eberhard Moenkeberg
- Reinholdstr. 14
- D-37083 Goettingen
- Germany
----
-
-
-Appendix -- the "cdtester" utility:
-
-/*
- * cdtester.c -- test the audio functions of a CD driver
- *
- * (c) 1995 Eberhard Moenkeberg <emoenke@gwdg.de>
- * published under the GPL
- *
- * made under heavy use of the "Tiny Audio CD Player"
- * from Werner Zimmermann <zimmerma@rz.fht-esslingen.de>
- * (see linux/drivers/block/README.aztcd)
- */
-#undef AZT_PRIVATE_IOCTLS /* not supported by every CDROM driver */
-#define SBP_PRIVATE_IOCTLS /* not supported by every CDROM driver */
-
-#include <stdio.h>
-#include <stdio.h>
-#include <malloc.h>
-#include <sys/ioctl.h>
-#include <linux/cdrom.h>
-
-#ifdef AZT_PRIVATE_IOCTLS
-#include <linux/aztcd.h>
-#endif AZT_PRIVATE_IOCTLS
-#ifdef SBP_PRIVATE_IOCTLS
-#include <linux/sbpcd.h>
-#include <linux/fs.h>
-#endif SBP_PRIVATE_IOCTLS
-
-struct cdrom_tochdr hdr;
-struct cdrom_tochdr tocHdr;
-struct cdrom_tocentry TocEntry[101];
-struct cdrom_tocentry entry;
-struct cdrom_multisession ms_info;
-struct cdrom_read_audio read_audio;
-struct cdrom_ti ti;
-struct cdrom_subchnl subchnl;
-struct cdrom_msf msf;
-struct cdrom_volctrl volctrl;
-#ifdef AZT_PRIVATE_IOCTLS
-union
-{
- struct cdrom_msf msf;
- unsigned char buf[CD_FRAMESIZE_RAW];
-} azt;
-#endif AZT_PRIVATE_IOCTLS
-int i, i1, i2, i3, j, k;
-unsigned char sequence=0;
-unsigned char command[80];
-unsigned char first=1, last=1;
-char *default_device="/dev/cdrom";
-char dev[20];
-char filename[20];
-int drive;
-int datafile;
-int rc;
-
-void help(void)
-{
- printf("Available Commands:\n");
- printf("STOP s EJECT e QUIT q\n");
- printf("PLAY TRACK t PAUSE p RESUME r\n");
- printf("NEXT TRACK n REPEAT LAST l HELP h\n");
- printf("SUBCHANNEL_Q c TRACK INFO i PLAY AT a\n");
- printf("READ d READ RAW w READ AUDIO A\n");
- printf("MS-INFO M TOC T START S\n");
- printf("SET EJECTSW X DEVICE D DEBUG Y\n");
- printf("AUDIO_BUFSIZ Z RESET R BLKRASET B\n");
- printf("SET VOLUME v GET VOLUME V\n");
-}
-
-/*
- * convert MSF number (3 bytes only) to Logical_Block_Address
- */
-int msf2lba(u_char *msf)
-{
- int i;
-
- i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_BLOCK_OFFSET;
- if (i<0) return (0);
- return (i);
-}
-/*
- * convert logical_block_address to m-s-f_number (3 bytes only)
- */
-void lba2msf(int lba, unsigned char *msf)
-{
- lba += CD_BLOCK_OFFSET;
- msf[0] = lba / (CD_SECS*CD_FRAMES);
- lba %= CD_SECS*CD_FRAMES;
- msf[1] = lba / CD_FRAMES;
- msf[2] = lba % CD_FRAMES;
-}
-
-int init_drive(char *dev)
-{
- unsigned char msf_ent[3];
-
- /*
- * open the device
- */
- drive=open(dev,0);
- if (drive<0) return (-1);
- /*
- * get TocHeader
- */
- printf("getting TocHeader...\n");
- rc=ioctl(drive,CDROMREADTOCHDR,&hdr);
- if (rc!=0)
- {
- printf("can't get TocHeader (error %d).\n",rc);
- return (-2);
- }
- else
- first=hdr.cdth_trk0;
- last=hdr.cdth_trk1;
- printf("TocHeader: %d %d\n",hdr.cdth_trk0,hdr.cdth_trk1);
- /*
- * get and display all TocEntries
- */
- printf("getting TocEntries...\n");
- for (i=1;i<=hdr.cdth_trk1+1;i++)
- {
- if (i!=hdr.cdth_trk1+1) TocEntry[i].cdte_track = i;
- else TocEntry[i].cdte_track = CDROM_LEADOUT;
- TocEntry[i].cdte_format = CDROM_LBA;
- rc=ioctl(drive,CDROMREADTOCENTRY,&TocEntry[i]);
- if (rc!=0)
- {
- printf("can't get TocEntry #%d (error %d).\n",i,rc);
- }
- else
- {
- lba2msf(TocEntry[i].cdte_addr.lba,&msf_ent[0]);
- if (TocEntry[i].cdte_track==CDROM_LEADOUT)
- {
- printf("TocEntry #%02X: %1X %1X %02d:%02d:%02d (lba: 0x%06X) %02X\n",
- TocEntry[i].cdte_track,
- TocEntry[i].cdte_adr,
- TocEntry[i].cdte_ctrl,
- msf_ent[0],
- msf_ent[1],
- msf_ent[2],
- TocEntry[i].cdte_addr.lba,
- TocEntry[i].cdte_datamode);
- }
- else
- {
- printf("TocEntry #%02d: %1X %1X %02d:%02d:%02d (lba: 0x%06X) %02X\n",
- TocEntry[i].cdte_track,
- TocEntry[i].cdte_adr,
- TocEntry[i].cdte_ctrl,
- msf_ent[0],
- msf_ent[1],
- msf_ent[2],
- TocEntry[i].cdte_addr.lba,
- TocEntry[i].cdte_datamode);
- }
- }
- }
- return (hdr.cdth_trk1); /* number of tracks */
-}
-
-void display(int size,unsigned char *buffer)
-{
- k=0;
- getchar();
- for (i=0;i<(size+1)/16;i++)
- {
- printf("%4d:",i*16);
- for (j=0;j<16;j++)
- {
- printf(" %02X",buffer[i*16+j]);
- }
- printf(" ");
- for (j=0;j<16;j++)
- {
- if (isalnum(buffer[i*16+j]))
- printf("%c",buffer[i*16+j]);
- else
- printf(".");
- }
- printf("\n");
- k++;
- if (k>=20)
- {
- printf("press ENTER to continue\n");
- getchar();
- k=0;
- }
- }
-}
-
-main(int argc, char *argv[])
-{
- printf("\nTesting tool for a CDROM driver's audio functions V0.1\n");
- printf("(C) 1995 Eberhard Moenkeberg <emoenke@gwdg.de>\n");
- printf("initializing...\n");
-
- rc=init_drive(default_device);
- if (rc<0) printf("could not open %s (rc=%d).\n",default_device,rc);
- help();
- while (1)
- {
- printf("Give a one-letter command (h = help): ");
- scanf("%s",command);
- command[1]=0;
- switch (command[0])
- {
- case 'D':
- printf("device name (f.e. /dev/sbpcd3): ? ");
- scanf("%s",&dev);
- close(drive);
- rc=init_drive(dev);
- if (rc<0) printf("could not open %s (rc %d).\n",dev,rc);
- break;
- case 'e':
- rc=ioctl(drive,CDROMEJECT);
- if (rc<0) printf("CDROMEJECT: rc=%d.\n",rc);
- break;
- case 'p':
- rc=ioctl(drive,CDROMPAUSE);
- if (rc<0) printf("CDROMPAUSE: rc=%d.\n",rc);
- break;
- case 'r':
- rc=ioctl(drive,CDROMRESUME);
- if (rc<0) printf("CDROMRESUME: rc=%d.\n",rc);
- break;
- case 's':
- rc=ioctl(drive,CDROMSTOP);
- if (rc<0) printf("CDROMSTOP: rc=%d.\n",rc);
- break;
- case 'S':
- rc=ioctl(drive,CDROMSTART);
- if (rc<0) printf("CDROMSTART: rc=%d.\n",rc);
- break;
- case 't':
- rc=ioctl(drive,CDROMREADTOCHDR,&tocHdr);
- if (rc<0)
- {
- printf("CDROMREADTOCHDR: rc=%d.\n",rc);
- break;
- }
- first=tocHdr.cdth_trk0;
- last= tocHdr.cdth_trk1;
- if ((first==0)||(first>last))
- {
- printf ("--got invalid TOC data.\n");
- }
- else
- {
- printf("--enter track number(first=%d, last=%d): ",first,last);
- scanf("%d",&i1);
- ti.cdti_trk0=i1;
- if (ti.cdti_trk0<first) ti.cdti_trk0=first;
- if (ti.cdti_trk0>last) ti.cdti_trk0=last;
- ti.cdti_ind0=0;
- ti.cdti_trk1=last;
- ti.cdti_ind1=0;
- rc=ioctl(drive,CDROMSTOP);
- rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
- if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
- }
- break;
- case 'n':
- rc=ioctl(drive,CDROMSTOP);
- if (++ti.cdti_trk0>last) ti.cdti_trk0=last;
- ti.cdti_ind0=0;
- ti.cdti_trk1=last;
- ti.cdti_ind1=0;
- rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
- if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
- break;
- case 'l':
- rc=ioctl(drive,CDROMSTOP);
- if (--ti.cdti_trk0<first) ti.cdti_trk0=first;
- ti.cdti_ind0=0;
- ti.cdti_trk1=last;
- ti.cdti_ind1=0;
- rc=ioctl(drive,CDROMPLAYTRKIND,&ti);
- if (rc<0) printf("CDROMPLAYTRKIND: rc=%d.\n",rc);
- break;
- case 'c':
- subchnl.cdsc_format=CDROM_MSF;
- rc=ioctl(drive,CDROMSUBCHNL,&subchnl);
- if (rc<0) printf("CDROMSUBCHNL: rc=%d.\n",rc);
- else
- {
- printf("AudioStatus:%s Track:%d Mode:%d MSF=%02d:%02d:%02d\n",
- subchnl.cdsc_audiostatus==CDROM_AUDIO_PLAY ? "PLAYING":"NOT PLAYING",
- subchnl.cdsc_trk,subchnl.cdsc_adr,
- subchnl.cdsc_absaddr.msf.minute,
- subchnl.cdsc_absaddr.msf.second,
- subchnl.cdsc_absaddr.msf.frame);
- }
- break;
- case 'i':
- printf("Track No.: ");
- scanf("%d",&i1);
- entry.cdte_track=i1;
- if (entry.cdte_track<first) entry.cdte_track=first;
- if (entry.cdte_track>last) entry.cdte_track=last;
- entry.cdte_format=CDROM_MSF;
- rc=ioctl(drive,CDROMREADTOCENTRY,&entry);
- if (rc<0) printf("CDROMREADTOCENTRY: rc=%d.\n",rc);
- else
- {
- printf("Mode %d Track, starts at %02d:%02d:%02d\n",
- entry.cdte_adr,
- entry.cdte_addr.msf.minute,
- entry.cdte_addr.msf.second,
- entry.cdte_addr.msf.frame);
- }
- break;
- case 'a':
- printf("Address (min:sec:frm) ");
- scanf("%d:%d:%d",&i1,&i2,&i3);
- msf.cdmsf_min0=i1;
- msf.cdmsf_sec0=i2;
- msf.cdmsf_frame0=i3;
- if (msf.cdmsf_sec0>59) msf.cdmsf_sec0=59;
- if (msf.cdmsf_frame0>74) msf.cdmsf_frame0=74;
- lba2msf(TocEntry[last+1].cdte_addr.lba-1,&msf.cdmsf_min1);
- rc=ioctl(drive,CDROMSTOP);
- rc=ioctl(drive,CDROMPLAYMSF,&msf);
- if (rc<0) printf("CDROMPLAYMSF: rc=%d.\n",rc);
- break;
-#ifdef AZT_PRIVATE_IOCTLS /*not supported by every CDROM driver*/
- case 'd':
- printf("Address (min:sec:frm) ");
- scanf("%d:%d:%d",&i1,&i2,&i3);
- azt.msf.cdmsf_min0=i1;
- azt.msf.cdmsf_sec0=i2;
- azt.msf.cdmsf_frame0=i3;
- if (azt.msf.cdmsf_sec0>59) azt.msf.cdmsf_sec0=59;
- if (azt.msf.cdmsf_frame0>74) azt.msf.cdmsf_frame0=74;
- rc=ioctl(drive,CDROMREADMODE1,&azt.msf);
- if (rc<0) printf("CDROMREADMODE1: rc=%d.\n",rc);
- else display(CD_FRAMESIZE,azt.buf);
- break;
- case 'w':
- printf("Address (min:sec:frame) ");
- scanf("%d:%d:%d",&i1,&i2,&i3);
- azt.msf.cdmsf_min0=i1;
- azt.msf.cdmsf_sec0=i2;
- azt.msf.cdmsf_frame0=i3;
- if (azt.msf.cdmsf_sec0>59) azt.msf.cdmsf_sec0=59;
- if (azt.msf.cdmsf_frame0>74) azt.msf.cdmsf_frame0=74;
- rc=ioctl(drive,CDROMREADMODE2,&azt.msf);
- if (rc<0) printf("CDROMREADMODE2: rc=%d.\n",rc);
- else display(CD_FRAMESIZE_RAW,azt.buf); /* currently only 2336 */
- break;
-#endif
- case 'v':
- printf("--Channel 0 (Left) (0-255): ");
- scanf("%d",&i1);
- volctrl.channel0=i1;
- printf("--Channel 1 (Right) (0-255): ");
- scanf("%d",&i1);
- volctrl.channel1=i1;
- volctrl.channel2=0;
- volctrl.channel3=0;
- rc=ioctl(drive,CDROMVOLCTRL,&volctrl);
- if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
- break;
- case 'q':
- close(drive);
- exit(0);
- case 'h':
- help();
- break;
- case 'T': /* display TOC entry - without involving the driver */
- scanf("%d",&i);
- if ((i<hdr.cdth_trk0)||(i>hdr.cdth_trk1))
- printf("invalid track number.\n");
- else
- printf("TocEntry %02d: adr=%01X ctrl=%01X msf=%02d:%02d:%02d mode=%02X\n",
- TocEntry[i].cdte_track,
- TocEntry[i].cdte_adr,
- TocEntry[i].cdte_ctrl,
- TocEntry[i].cdte_addr.msf.minute,
- TocEntry[i].cdte_addr.msf.second,
- TocEntry[i].cdte_addr.msf.frame,
- TocEntry[i].cdte_datamode);
- break;
- case 'A': /* read audio data into file */
- printf("Address (min:sec:frm) ? ");
- scanf("%d:%d:%d",&i1,&i2,&i3);
- read_audio.addr.msf.minute=i1;
- read_audio.addr.msf.second=i2;
- read_audio.addr.msf.frame=i3;
- read_audio.addr_format=CDROM_MSF;
- printf("# of frames ? ");
- scanf("%d",&i1);
- read_audio.nframes=i1;
- k=read_audio.nframes*CD_FRAMESIZE_RAW;
- read_audio.buf=malloc(k);
- if (read_audio.buf==NULL)
- {
- printf("can't malloc %d bytes.\n",k);
- break;
- }
- sprintf(filename,"audio_%02d%02d%02d_%02d.%02d\0",
- read_audio.addr.msf.minute,
- read_audio.addr.msf.second,
- read_audio.addr.msf.frame,
- read_audio.nframes,
- ++sequence);
- datafile=creat(filename, 0755);
- if (datafile<0)
- {
- printf("can't open datafile %s.\n",filename);
- break;
- }
- rc=ioctl(drive,CDROMREADAUDIO,&read_audio);
- if (rc!=0)
- {
- printf("CDROMREADAUDIO: rc=%d.\n",rc);
- }
- else
- {
- rc=write(datafile,&read_audio.buf,k);
- if (rc!=k) printf("datafile I/O error (%d).\n",rc);
- }
- close(datafile);
- break;
- case 'X': /* set EJECT_SW (0: disable, 1: enable auto-ejecting) */
- scanf("%d",&i);
- rc=ioctl(drive,CDROMEJECT_SW,i);
- if (rc!=0)
- printf("CDROMEJECT_SW: rc=%d.\n",rc);
- else
- printf("EJECT_SW set to %d\n",i);
- break;
- case 'M': /* get the multisession redirection info */
- ms_info.addr_format=CDROM_LBA;
- rc=ioctl(drive,CDROMMULTISESSION,&ms_info);
- if (rc!=0)
- {
- printf("CDROMMULTISESSION(lba): rc=%d.\n",rc);
- }
- else
- {
- if (ms_info.xa_flag) printf("MultiSession offset (lba): %d (0x%06X)\n",ms_info.addr.lba,ms_info.addr.lba);
- else
- {
- printf("this CD is not an XA disk.\n");
- break;
- }
- }
- ms_info.addr_format=CDROM_MSF;
- rc=ioctl(drive,CDROMMULTISESSION,&ms_info);
- if (rc!=0)
- {
- printf("CDROMMULTISESSION(msf): rc=%d.\n",rc);
- }
- else
- {
- if (ms_info.xa_flag)
- printf("MultiSession offset (msf): %02d:%02d:%02d (0x%02X%02X%02X)\n",
- ms_info.addr.msf.minute,
- ms_info.addr.msf.second,
- ms_info.addr.msf.frame,
- ms_info.addr.msf.minute,
- ms_info.addr.msf.second,
- ms_info.addr.msf.frame);
- else printf("this CD is not an XA disk.\n");
- }
- break;
-#ifdef SBP_PRIVATE_IOCTLS
- case 'Y': /* set the driver's message level */
-#if 0 /* not implemented yet */
- printf("enter switch name (f.e. DBG_CMD): ");
- scanf("%s",&dbg_switch);
- j=get_dbg_num(dbg_switch);
-#else
- printf("enter DDIOCSDBG switch number: ");
- scanf("%d",&j);
-#endif
- printf("enter 0 for \"off\", 1 for \"on\": ");
- scanf("%d",&i);
- if (i==0) j|=0x80;
- printf("calling \"ioctl(drive,DDIOCSDBG,%d)\"\n",j);
- rc=ioctl(drive,DDIOCSDBG,j);
- printf("DDIOCSDBG: rc=%d.\n",rc);
- break;
- case 'Z': /* set the audio buffer size */
- printf("# frames wanted: ? ");
- scanf("%d",&j);
- rc=ioctl(drive,CDROMAUDIOBUFSIZ,j);
- printf("%d frames granted.\n",rc);
- break;
- case 'V':
- rc=ioctl(drive,CDROMVOLREAD,&volctrl);
- if (rc<0) printf("CDROMVOLCTRL: rc=%d.\n",rc);
- printf("Volume: channel 0 (left) %d, channel 1 (right) %d\n",volctrl.channel0,volctrl.channel1);
- break;
- case 'R':
- rc=ioctl(drive,CDROMRESET);
- if (rc<0) printf("CDROMRESET: rc=%d.\n",rc);
- break;
- case 'B': /* set the driver's (?) read ahead value */
- printf("enter read-ahead size: ? ");
- scanf("%d",&i);
- rc=ioctl(drive,BLKRASET,i);
- if (rc<0) printf("BLKRASET: rc=%d.\n",rc);
- break;
-#endif SBP_PRIVATE_IOCTLS
- default:
- printf("unknown command: \"%s\".\n",command);
- break;
- }
- }
-}
-/*==========================================================================*/
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
-
diff --git a/drivers/block/README.sonycd535 b/drivers/block/README.sonycd535
deleted file mode 100644
index ca5c371fd..000000000
--- a/drivers/block/README.sonycd535
+++ /dev/null
@@ -1,119 +0,0 @@
- README FOR LINUX SONY CDU-535/531 DRIVER
- ========================================
-
-This is the the Sony CDU-535 (and 531) driver version 0.7 for Linux.
-I do not think I have the documentation to add features like DMA support
-so if anyone else wants to pursue it or help me with it, please do.
-(I need to see what was done for the CDU-31A driver -- perhaps I can
-steal some of that code.)
-
-This is a Linux device driver for the Sony CDU-535 CDROM drive. This is
-one of the older Sony drives with its own interface card (Sony bus).
-The DOS driver for this drive is named SONY_CDU.SYS - when you boot DOS
-your drive should be identified as a SONY CDU-535. The driver works
-with a CDU-531 also. One user reported that the driver worked on drives
-OEM'ed by Procomm, drive and interface board were labelled Procomm.
-
-The Linux driver is based on Corey Minyard's sonycd 0.3 driver for
-the CDU-31A. Ron Jeppesen just changed the commands that were sent
-to the drive to correspond to the CDU-535 commands and registers.
-There were enough changes to let bugs creep in but it seems to be stable.
-Ron was able to tar an entire CDROM (should read all blocks) and built
-ghostview and xfig off Walnut Creek's X11R5/GNU CDROM. xcdplayer and
-workman work with the driver. Others have used the driver without
-problems except those dealing with wait loops (fixed in third release).
-Like Minyard's original driver this one uses a polled interface (this
-is also the default setup for the DOS driver). It has not been tried
-with interrupts or DMA enabled on the board.
-
-REQUIREMENTS
-============
-
- - Sony CDU-535 drive, preferably without interrupts and DMA
- enabled on the card.
-
- - Drive must be set up as unit 1. Only the first unit will be
- recognized
-
- - you must enter your interface address into
- /usr/src/linux/include/linux/sonycd535.h and build the
- appropriate kernel or use the "kernel command line" parameter
- sonycd535=0x320
- with the correct interface address.
-
-NOTES:
-======
-
-1) The drive MUST be turned on when booting or it will not be recognized!
- (but see comments on modularized version below)
-
-2) when the cdrom device is opened the eject button is disabled to keep the
- user from ejecting a mounted disk and replacing it with another.
- Unfortunately xcdplayer and workman also open the cdrom device so you
- have to use the eject button in the software. Keep this in mind if your
- cdrom player refuses to give up its disk -- exit workman or xcdplayer, or
- umount the drive if it has been mounted.
-
-THANKS
-======
-
-Many thanks to Ron Jeppesen (ronj.an@site007.saic.com) for getting
-this project off the ground. He wrote the initial release
-and the first two patches to this driver (0.1, 0.2, and 0.3).
-Thanks also to Eberhard Moenkeberg (emoenke@gwdg.de) for prodding
-me to place this code into the mainstream Linux source tree
-(as of Linux version 1.1.91), as well as some patches to make
-it a better device citizen. Further thanks to "S. Joel Katz"
-<stimpson@panix.com> for his MODULE patches (see details below),
-Porfiri Claudio <C.Porfiri@nisms.tei.ericsson.se> for patches
-to make the driver work with the older CDU-510/515 series, and
-Heiko Eissfeldt <heiko@colossus.escape.de> for pointing out that
-the verify_area() checks were ignoring the results of said checks.
-
-(Acknowledgments from Ron Jeppesen in the 0.3 release:)
-Thanks to Corey Minyard who wrote the original CDU-31A driver on which
-this driver is based. Thanks to Ken Pizzini and Bob Blair who provided
-patches and feedback on the first release of this driver.
-
-Ken Pizzini
-ken@halcyon.com
-
-------------------------------------------------------------------------------
-(The following is from Joel Katz <Stimpson@Panix.COM>.)
-
- To build a version of sony535.o that can be installed as a module,
-use the following command:
-
-gcc -c -D__KERNEL__ -DMODULE -O2 sonycd535.c -o sonycd535.o
-
- To install the module, simply type:
-
-insmod sony535.o
-
- And to remove it:
-
-rmmod sony535
-
- The code checks to see if MODULE is defined and behaves as it used
-to if MODULE is not defined. That means your patched file should behave
-exactly as it used to if compiled into the kernel.
-
- I have an external drive, and I usually leave it powered off. I used
-to have to reboot if I needed to use the CDROM drive. Now I don't.
-
- Even if you have an internal drive, why waste the 268K of memory
-(unswappable) that the driver uses if you use your CD-ROM drive infrequently?
-
- This driver will not install (whether compiled in or loaded as a
-module) if the CDROM drive is not available during its initialization. This
-means that you can have the driver compiled into the kernel and still load
-the module later (assuming the driver doesn't install itself during
-power-on). This only wastes 12K when you boot with the CDROM drive off.
-
- This is what I usually do; I leave the driver compiled into the
-kernel, but load it as a module if I powered the system up with the drive
-off and then later decided to use the CDROM drive.
-
- Since the driver only uses a single page to point to the chunks,
-attempting to set the buffer cache to more than 2 Megabytes would be very
-bad; don't do that.
diff --git a/drivers/block/ali14xx.c b/drivers/block/ali14xx.c
new file mode 100644
index 000000000..00de8e7cb
--- /dev/null
+++ b/drivers/block/ali14xx.c
@@ -0,0 +1,220 @@
+/*
+ * linux/drivers/block/ali14xx.c Version 0.03 Feb 09, 1996
+ *
+ * Copyright (C) 1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * ALI M14xx chipset EIDE controller
+ *
+ * Works for ALI M1439/1443/1445/1487/1489 chipsets.
+ *
+ * Adapted from code developed by derekn@vw.ece.cmu.edu. -ml
+ * Derek's notes follow:
+ *
+ * I think the code should be pretty understandable,
+ * but I'll be happy to (try to) answer questions.
+ *
+ * The critical part is in the setupDrive function. The initRegisters
+ * function doesn't seem to be necessary, but the DOS driver does it, so
+ * I threw it in.
+ *
+ * I've only tested this on my system, which only has one disk. I posted
+ * it to comp.sys.linux.hardware, so maybe some other people will try it
+ * out.
+ *
+ * Derek Noonburg (derekn@ece.cmu.edu)
+ * 95-sep-26
+ *
+ * Update 96-jul-13:
+ *
+ * I've since upgraded to two disks and a CD-ROM, with no trouble, and
+ * I've also heard from several others who have used it successfully.
+ * This driver appears to work with both the 1443/1445 and the 1487/1489
+ * chipsets. I've added support for PIO mode 4 for the 1487. This
+ * seems to work just fine on the 1443 also, although I'm not sure it's
+ * advertised as supporting mode 4. (I've been running a WDC AC21200 in
+ * mode 4 for a while now with no trouble.) -Derek
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "ide_modes.h"
+
+/* port addresses for auto-detection */
+#define ALI_NUM_PORTS 4
+static int ports[ALI_NUM_PORTS] = {0x074, 0x0f4, 0x034, 0x0e4};
+
+/* register initialization data */
+typedef struct { byte reg, data; } RegInitializer;
+
+static RegInitializer initData[] = {
+ {0x01, 0x0f}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00},
+ {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x2b}, {0x0a, 0x0f},
+ {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00},
+ {0x29, 0x00}, {0x2a, 0x00}, {0x2f, 0x00}, {0x2b, 0x00},
+ {0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x30, 0x00},
+ {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, {0x34, 0xff},
+ {0x35, 0x03}, {0x00, 0x00}
+};
+
+#define ALI_MAX_PIO 4
+
+/* timing parameter registers for each drive */
+static struct { byte reg1, reg2, reg3, reg4; } regTab[4] = {
+ {0x03, 0x26, 0x04, 0x27}, /* drive 0 */
+ {0x05, 0x28, 0x06, 0x29}, /* drive 1 */
+ {0x2b, 0x30, 0x2c, 0x31}, /* drive 2 */
+ {0x2d, 0x32, 0x2e, 0x33}, /* drive 3 */
+};
+
+static int basePort = 0; /* base port address */
+static int regPort = 0; /* port for register number */
+static int dataPort = 0; /* port for register data */
+static byte regOn; /* output to base port to access registers */
+static byte regOff; /* output to base port to close registers */
+
+/*------------------------------------------------------------------------*/
+
+/*
+ * Read a controller register.
+ */
+static inline byte inReg (byte reg)
+{
+ outb_p(reg, regPort);
+ return inb(dataPort);
+}
+
+/*
+ * Write a controller register.
+ */
+static void outReg (byte data, byte reg)
+{
+ outb_p(reg, regPort);
+ outb_p(data, dataPort);
+}
+
+/*
+ * Set PIO mode for the specified drive.
+ * This function computes timing parameters
+ * and sets controller registers accordingly.
+ */
+static void ali14xx_tune_drive (ide_drive_t *drive, byte pio)
+{
+ int driveNum;
+ int time1, time2;
+ byte param1, param2, param3, param4;
+ unsigned long flags;
+ ide_pio_data_t d;
+ int bus_speed = ide_system_bus_speed();
+
+ pio = ide_get_best_pio_mode(drive, pio, ALI_MAX_PIO, &d);
+
+ /* calculate timing, according to PIO mode */
+ time1 = d.cycle_time;
+ time2 = ide_pio_timings[pio].active_time;
+ param3 = param1 = (time2 * bus_speed + 999) / 1000;
+ param4 = param2 = (time1 * bus_speed + 999) / 1000 - param1;
+ if (pio < 3) {
+ param3 += 8;
+ param4 += 8;
+ }
+ printk("%s: PIO mode%d, t1=%dns, t2=%dns, cycles = %d+%d, %d+%d\n",
+ drive->name, pio, time1, time2, param1, param2, param3, param4);
+
+ /* stuff timing parameters into controller registers */
+ driveNum = (HWIF(drive)->index << 1) + drive->select.b.unit;
+ save_flags(flags);
+ cli();
+ outb_p(regOn, basePort);
+ outReg(param1, regTab[driveNum].reg1);
+ outReg(param2, regTab[driveNum].reg2);
+ outReg(param3, regTab[driveNum].reg3);
+ outReg(param4, regTab[driveNum].reg4);
+ outb_p(regOff, basePort);
+ restore_flags(flags);
+}
+
+/*
+ * Auto-detect the IDE controller port.
+ */
+static int findPort (void)
+{
+ int i;
+ byte t;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < ALI_NUM_PORTS; ++i) {
+ basePort = ports[i];
+ regOff = inb(basePort);
+ for (regOn = 0x30; regOn <= 0x33; ++regOn) {
+ outb_p(regOn, basePort);
+ if (inb(basePort) == regOn) {
+ regPort = basePort + 4;
+ dataPort = basePort + 8;
+ t = inReg(0) & 0xf0;
+ outb_p(regOff, basePort);
+ restore_flags(flags);
+ if (t != 0x50)
+ return 0;
+ return 1; /* success */
+ }
+ }
+ outb_p(regOff, basePort);
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * Initialize controller registers with default values.
+ */
+static int initRegisters (void) {
+ RegInitializer *p;
+ byte t;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(regOn, basePort);
+ for (p = initData; p->reg != 0; ++p)
+ outReg(p->data, p->reg);
+ outb_p(0x01, regPort);
+ t = inb(regPort) & 0x01;
+ outb_p(regOff, basePort);
+ restore_flags(flags);
+ return t;
+}
+
+void init_ali14xx (void)
+{
+ /* auto-detect IDE controller port */
+ if (!findPort()) {
+ printk("ali14xx: not found\n");
+ return;
+ }
+
+ printk("ali14xx: base= 0x%03x, regOn = 0x%02x\n", basePort, regOn);
+ ide_hwifs[0].chipset = ide_ali14xx;
+ ide_hwifs[1].chipset = ide_ali14xx;
+ ide_hwifs[0].tuneproc = &ali14xx_tune_drive;
+ ide_hwifs[1].tuneproc = &ali14xx_tune_drive;
+
+ /* initialize controller registers */
+ if (!initRegisters()) {
+ printk("ali14xx: Chip initialization failed\n");
+ return;
+ }
+}
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
new file mode 100644
index 000000000..7afc22888
--- /dev/null
+++ b/drivers/block/amiflop.c
@@ -0,0 +1,1782 @@
+/*
+ * linux/amiga/amiflop.c
+ *
+ * Copyright (C) 1993 Greg Harp
+ * Portions of this driver are based on code contributed by Brad Pepers
+ *
+ * revised 28.5.95 by Joerg Dorchain
+ * - now no bugs(?) any more for both HD & DD
+ * - added support for 40 Track 5.25" drives, 80-track hopefully behaves
+ * like 3.5" dd (no way to test - are there any 5.25" drives out there
+ * that work on an A4000?)
+ * - wrote formatting routine (maybe dirty, but works)
+ *
+ * june/july 1995 added ms-dos support by Joerg Dorchain
+ * (portions based on messydos.device and various contributors)
+ * - currently only 9 and 18 sector disks
+ *
+ * - fixed a bug with the internal trackbuffer when using multiple
+ * disks the same time
+ * - made formatting a bit safer
+ * - added command line and machine based default for "silent" df0
+ *
+ * december 1995 adapted for 1.2.13pl4 by Joerg Dorchain
+ * - works but I think it's inefficient. (look in redo_fd_request)
+ * But the changes were very efficient. (only three and a half lines)
+ *
+ * january 1995 added special ioctl for tracking down read/write problems
+ * - usage ioctl(d, RAW_TRACK, ptr); the raw track buffer (MFM-encoded data
+ * is copied to area. (area should be large enough since no checking is
+ * done - 30K is currently sufficient). return the actual size of the
+ * trackbuffer
+ * - replaced udelays() by a timer (CIAA timer B) for the waits
+ * needed for the disk mechanic.
+ *
+ * revised Marts 3rd, 1996 by Jes Sorensen for use in the 1.3.28 kernel.
+ * - Minor changes to accept the kdev_t.
+ * - Replaced some more udelays with ms_delays. Udelay is just a loop,
+ * and so the delay will be different depending on the given
+ * processor :-(
+ * - The driver could use a major cleanup because of the new
+ * major/minor handling that came with kdev_t. It seems to work for
+ * the time being, but I can't guarantee that it will stay like
+ * that when we start using 16 (24?) bit minors.
+ */
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/fd.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+
+#include <asm/amifdreg.h>
+#include <asm/amifd.h>
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+#include <asm/irq.h>
+#include <asm/bootinfo.h>
+#include <asm/amigatypes.h>
+
+#define MAJOR_NR FLOPPY_MAJOR
+#include <linux/blk.h>
+
+#undef DEBUG /* print _LOTS_ of infos */
+
+#define RAW_IOCTL
+#ifdef RAW_IOCTL
+#define IOCTL_RAW_TRACK 0x5254524B /* 'RTRK' */
+#endif
+
+/* prototypes */
+
+static int amiga_read(int,unsigned char *, unsigned long, int);
+static void amiga_write(int, unsigned long, unsigned char *, int);
+static int dos_read(int, unsigned char *, unsigned long, int);
+static void dos_write(int, unsigned long, unsigned char *,int);
+static ushort dos_crc(void *, int, int, int);
+static void fd_probe(int);
+
+
+/*
+ * Defines
+ */
+#define MAX_SECTORS 22
+
+/*
+ * Error codes
+ */
+#define FD_OK 0 /* operation succeeded */
+#define FD_ERROR -1 /* general error (seek, read, write, etc) */
+#define FD_NOUNIT 1 /* unit does not exist */
+#define FD_UNITBUSY 2 /* unit already active */
+#define FD_NOTACTIVE 3 /* unit is not active */
+#define FD_NOTREADY 4 /* unit is not ready (motor not on/no disk) */
+
+/*
+ * Floppy ID values
+ */
+#define FD_NODRIVE 0x00000000 /* response when no unit is present */
+#define FD_DD_3 0xffffffff /* double-density 3.5" (880K) drive */
+#define FD_HD_3 0x55555555 /* high-density 3.5" (1760K) drive */
+#define FD_DD_5 0xaaaaaaaa /* double-density 5.25" (440K) drive */
+
+static int fd_def_df0 = 0; /* default for df0 if it doesn't identify */
+
+
+/*
+ * Macros
+ */
+#define MOTOR_ON (ciab.prb &= ~DSKMOTOR)
+#define MOTOR_OFF (ciab.prb |= DSKMOTOR)
+#define SELECT(mask) (ciab.prb &= ~mask)
+#define DESELECT(mask) (ciab.prb |= mask)
+#define SELMASK(drive) (1 << (3 + (drive & 3)))
+
+#define DRIVE(x) ((x) & 3)
+#define PROBE(x) ((x) >> 2) & 1)
+#define TYPE(x) ((x) >> 3) & 2)
+#define DATA(x) ((x) >> 5) & 3)
+
+static struct fd_drive_type drive_types[] = {
+/* code name tr he rdsz wrsz sm pc1 pc2 sd st st*/
+/* warning: times are now in milliseconds (ms) */
+ { FD_DD_3, "DD 3.5", 160, 2, 14716, 13630, 1, 80,161, 3, 18, 1},
+ { FD_HD_3, "HD 3.5", 160, 2, 28344, 27258, 2, 80,161, 3, 18, 1},
+ { FD_DD_5, "DD 5.25", 80, 2, 14716, 13630, 1, 40, 81, 6, 30, 2},
+ { FD_NODRIVE, "No Drive", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+};
+static int num_dr_types = sizeof(drive_types) / sizeof(drive_types[0]);
+
+/* defaults for 3 1/2" HD-Disks */
+static int floppy_sizes[256]={880,880,880,880,720,720,720,};
+static int floppy_blocksizes[256]={0,};
+/* hardsector size assumed to be 512 */
+
+static struct fd_data_type data_types[] = {
+ { "Amiga", 11 , amiga_read, amiga_write},
+ { "MS-Dos", 9, dos_read, dos_write}
+};
+static int num_da_types = sizeof(data_types) / sizeof(data_types[0]);
+
+/* current info on each unit */
+static struct amiga_floppy_struct unit[FD_MAX_UNITS];
+
+static struct timer_list flush_track_timer;
+static struct timer_list post_write_timer;
+static struct timer_list motor_on_timer;
+static struct timer_list motor_off_timer[FD_MAX_UNITS];
+static int on_attempts;
+
+/* track buffer */
+static int lastdrive = -1;
+static int savedtrack = -1;
+static int writepending = 0;
+static int writefromint = 0;
+static unsigned char trackdata[MAX_SECTORS * 512];
+static char *raw_buf;
+
+#define RAW_BUF_SIZE 30000 /* size of raw disk data */
+
+/*
+ * These are global variables, as that's the easiest way to give
+ * information to interrupts. They are the data used for the current
+ * request.
+ */
+static char block_flag = 0;
+static int selected = 0;
+static struct wait_queue *wait_fd_block = NULL;
+
+/* Synchronization of FDC access. */
+static volatile int fdc_busy = 0;
+static struct wait_queue *fdc_wait = NULL;
+static struct wait_queue *motor_wait = NULL;
+
+/* MS-Dos MFM Coding tables (should go quick and easy) */
+static unsigned char mfmencode[16]={
+ 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15,
+ 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55
+};
+static unsigned char mfmdecode[128];
+
+/* floppy internal millisecond timer stuff */
+static struct semaphore ms_sem = MUTEX;
+static struct wait_queue *ms_wait = NULL;
+#define MS_TICKS ((amiga_eclock+50)/1000)
+
+static void ms_isr(int irq, struct pt_regs *fp, void *dummy)
+{
+wake_up(&ms_wait);
+}
+
+/* with the semaphore waits are queued up
+ A more generic routine would do a schedule a la timer.device */
+static void ms_delay(int ms)
+{
+ int ticks;
+ if (ms > 0) {
+ down(&ms_sem);
+ ticks=MS_TICKS*ms-1;
+ ciaa.tblo=ticks%256;
+ ciaa.tbhi=ticks/256;
+ ciaa.crb=0x19; /* count clock, force load, one-shot, start */
+ sleep_on(&ms_wait);
+ up(&ms_sem);
+ }
+}
+
+/*
+ * Functions
+ */
+/*======================================================================
+ Turn off the motor of the given drive. Unit must already be active.
+ Returns standard floppy error code.
+======================================================================*/
+static void fd_motor_off(unsigned long drive)
+{
+ unsigned long flags;
+ unsigned char prb = ~0;
+
+ drive&=3;
+ save_flags(flags);
+ cli();
+
+ if (unit[drive].track % 2 != 0)
+ prb &= ~DSKSIDE;
+ ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
+ ciab.prb = prb;
+ prb &= ~SELMASK(drive);
+ ciab.prb = prb;
+ udelay (1);
+ prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
+ ciab.prb = prb;
+ selected = -1;
+ unit[drive].motor = 0;
+
+ restore_flags(flags);
+}
+
+static void motor_on_callback(unsigned long nr)
+{
+ nr &= 3;
+
+ if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) {
+ unit[nr].motor = 1;
+ wake_up (&motor_wait);
+ } else {
+ motor_on_timer.expires = jiffies + HZ/10;
+ add_timer(&motor_on_timer);
+ }
+}
+
+static int motor_on(int nr)
+{
+ unsigned long flags;
+ unsigned char prb = ~0;
+
+ nr &= 3;
+ save_flags (flags);
+ cli();
+ del_timer(motor_off_timer + nr);
+
+ if (!unit[nr].motor) {
+ del_timer(&motor_on_timer);
+ motor_on_timer.data = nr;
+ motor_on_timer.expires = jiffies + HZ/2;
+ add_timer(&motor_on_timer);
+ on_attempts = 10;
+
+
+ prb &= ~DSKMOTOR;
+ if (unit[nr].track % 2 != 0)
+ prb &= ~DSKSIDE;
+ ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
+ ciab.prb = prb;
+ prb &= ~SELMASK(nr);
+ ciab.prb = prb;
+ selected = nr;
+
+ while (!unit[nr].motor)
+ sleep_on (&motor_wait);
+ }
+ restore_flags(flags);
+
+ if (on_attempts == 0) {
+ printk ("motor_on failed, turning motor off\n");
+ fd_motor_off (nr);
+ return 0;
+ }
+
+ return 1;
+}
+
+static void floppy_off (unsigned int nr)
+{
+ nr&=3;
+ del_timer(motor_off_timer+nr);
+ motor_off_timer[nr].expires = jiffies + 3*HZ;
+ add_timer(motor_off_timer+nr);
+}
+
+static void fd_select (int drive)
+{
+ unsigned char prb = ~0;
+
+ drive&=3;
+ if (drive == selected)
+ return;
+ selected = drive;
+
+ if (unit[drive].track % 2 != 0)
+ prb &= ~DSKSIDE;
+ if (unit[drive].motor == 1)
+ prb &= ~DSKMOTOR;
+ ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
+ ciab.prb = prb;
+ prb &= ~SELMASK(drive);
+ ciab.prb = prb;
+}
+
+static void fd_deselect (int drive)
+{
+ unsigned char prb;
+ unsigned long flags;
+
+ drive&=3;
+ if (drive != selected)
+ return;
+
+ save_flags (flags);
+ sti();
+
+ selected = -1;
+
+ prb = ciab.prb;
+ prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3));
+ ciab.prb = prb;
+
+ restore_flags (flags);
+
+}
+
+/*======================================================================
+ Seek the drive to track 0.
+ The drive must be active and the motor must be running.
+ Returns standard floppy error code.
+======================================================================*/
+static int fd_calibrate(int drive)
+{
+ unsigned char prb;
+ int n;
+
+ drive &= 3;
+ if (!motor_on (drive))
+ return 0;
+ fd_select (drive);
+ prb = ciab.prb;
+ prb |= DSKSIDE;
+ prb &= ~DSKDIREC;
+ ciab.prb = prb;
+ for (n = unit[drive].type->tracks/4; n != 0; --n) {
+ if (ciaa.pra & DSKTRACK0)
+ break;
+ prb &= ~DSKSTEP;
+ ciab.prb = prb;
+ prb |= DSKSTEP;
+ ms_delay (2);
+ ciab.prb = prb;
+ ms_delay(unit[drive].type->step_delay);
+ }
+ ms_delay (unit[drive].type->settle_time);
+ prb |= DSKDIREC;
+ n = unit[drive].type->tracks/2 + 20;
+ for (;;) {
+ prb &= ~DSKSTEP;
+ ciab.prb = prb;
+ prb |= DSKSTEP;
+ ms_delay (2);
+ ciab.prb = prb;
+ ms_delay(unit[drive].type->step_delay + 1);
+ if ((ciaa.pra & DSKTRACK0) == 0)
+ break;
+ if (--n == 0) {
+ printk ("calibrate failed, turning motor off\n");
+ fd_motor_off (drive);
+ unit[drive].track = -1;
+ return 0;
+ }
+ }
+ unit[drive].track = 0;
+ ms_delay(unit[drive].type->settle_time);
+
+ return 1;
+}
+
+/*======================================================================
+ Seek the drive to the requested cylinder.
+ The drive must have been calibrated at some point before this.
+ The drive must also be active and the motor must be running.
+======================================================================*/
+static int fd_seek(int drive, int track)
+{
+ unsigned char prb;
+ int cnt;
+
+ drive &= 3;
+ if (unit[drive].track == track)
+ return 1;
+ if (!motor_on(drive))
+ return 0;
+ fd_select (drive);
+ if (unit[drive].track < 0 && !fd_calibrate(drive))
+ return 0;
+
+ cnt = unit[drive].track/2 - track/2;
+ prb = ciab.prb;
+ prb |= DSKSIDE | DSKDIREC;
+ if (track % 2 != 0)
+ prb &= ~DSKSIDE;
+ if (cnt < 0) {
+ cnt = - cnt;
+ prb &= ~DSKDIREC;
+ }
+ ciab.prb = prb;
+ if (track % 2 != unit[drive].track % 2)
+ ms_delay (unit[drive].type->side_time);
+ unit[drive].track = track;
+ if (cnt == 0)
+ return 1;
+ do {
+ prb &= ~DSKSTEP;
+ ciab.prb = prb;
+ prb |= DSKSTEP;
+ ms_delay (1);
+ ciab.prb = prb;
+ ms_delay (unit[drive].type->step_delay);
+ } while (--cnt != 0);
+ ms_delay (unit[drive].type->settle_time);
+
+ return 1;
+}
+
+static void encode(unsigned long data, unsigned long *dest)
+{
+ unsigned long data2;
+
+ data &= 0x55555555;
+ data2 = data ^ 0x55555555;
+ data |= ((data2 >> 1) | 0x80000000) & (data2 << 1);
+
+ if (*(dest - 1) & 0x00000001)
+ data &= 0x7FFFFFFF;
+
+ *dest = data;
+}
+
+static void encode_block(unsigned long *dest, unsigned long *src, int len)
+{
+ int cnt, to_cnt = 0;
+ unsigned long data;
+
+ /* odd bits */
+ for (cnt = 0; cnt < len / 4; cnt++) {
+ data = src[cnt] >> 1;
+ encode(data, dest + to_cnt++);
+ }
+
+ /* even bits */
+ for (cnt = 0; cnt < len / 4; cnt++) {
+ data = src[cnt];
+ encode(data, dest + to_cnt++);
+ }
+}
+
+unsigned long checksum(unsigned long *addr, int len)
+{
+ unsigned long csum = 0;
+
+ len /= sizeof(*addr);
+ while (len-- > 0)
+ csum ^= *addr++;
+ csum = ((csum>>1) & 0x55555555) ^ (csum & 0x55555555);
+
+ return csum;
+}
+
+struct header {
+ unsigned char magic;
+ unsigned char track;
+ unsigned char sect;
+ unsigned char ord;
+ unsigned char labels[16];
+ unsigned long hdrchk;
+ unsigned long datachk;
+};
+
+static unsigned long *putsec(int disk, unsigned long *raw, int track, int cnt,
+ unsigned char *data)
+{
+ struct header hdr;
+ int i;
+
+ if (!AMIGAHW_PRESENT(AMI_FLOPPY))
+ return 0;
+
+ disk&=3;
+ *raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA;
+ raw++;
+ *raw++ = 0x44894489;
+
+ hdr.magic = 0xFF;
+ hdr.track = track;
+ hdr.sect = cnt;
+ hdr.ord = unit[disk].sects-cnt;
+ for (i = 0; i < 16; i++)
+ hdr.labels[i] = 0;
+ hdr.hdrchk = checksum((ulong *)&hdr,
+ (char *)&hdr.hdrchk-(char *)&hdr);
+ hdr.datachk = checksum((ulong *)data, 512);
+
+ encode_block(raw, (ulong *)&hdr.magic, 4);
+ raw += 2;
+ encode_block(raw, (ulong *)&hdr.labels, 16);
+ raw += 8;
+ encode_block(raw, (ulong *)&hdr.hdrchk, 4);
+ raw += 2;
+ encode_block(raw, (ulong *)&hdr.datachk, 4);
+ raw += 2;
+ encode_block(raw, (ulong *)data, 512);
+ raw += 256;
+
+ return raw;
+}
+
+
+/*==========================================================================
+ amiga_write converts track/labels data to raw track data
+==========================================================================*/
+static void amiga_write(int disk, unsigned long raw, unsigned char *data,
+ int track)
+{
+ int cnt;
+ unsigned long *ptr = (unsigned long *)raw;
+
+ disk&=3;
+ /* gap space */
+ for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++)
+ *ptr++ = 0xaaaaaaaa;
+
+ /* sectors */
+ for (cnt = 0; cnt < unit[disk].sects; cnt++)
+ ptr = putsec (disk, ptr, track, cnt, data + cnt*512);
+ *(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8;
+ raw = (unsigned long)ptr + 2;
+}
+
+static unsigned long decode (unsigned long *data, unsigned long *raw,
+ int len)
+{
+ ulong *odd, *even;
+
+ /* convert length from bytes to longwords */
+ len >>= 2;
+ odd = raw;
+ even = odd + len;
+
+ /* prepare return pointer */
+ raw += len * 2;
+
+ do {
+ *data++ = ((*odd++ & 0x55555555) << 1) | (*even++ & 0x55555555);
+ } while (--len != 0);
+
+ return (ulong)raw;
+}
+
+#define MFM_NOSYNC 1
+#define MFM_HEADER 2
+#define MFM_DATA 3
+#define MFM_TRACK 4
+
+/*==========================================================================
+ scan_sync - looks for the next start of sector marked by a sync. d3 is the
+ sector number (10..0). When d3 = 10, can't be certain of a
+ starting sync.
+==========================================================================*/
+static unsigned long scan_sync(unsigned long raw, unsigned long end)
+{
+ ushort *ptr = (ushort *)raw, *endp = (ushort *)end;
+
+ while (ptr < endp && *ptr++ != 0x4489)
+ ;
+ if (ptr < endp) {
+ while (*ptr == 0x4489 && ptr < endp)
+ ptr++;
+ return (ulong)ptr;
+ }
+ return 0;
+}
+
+/*==========================================================================
+ amiga_read reads a raw track of data into a track buffer
+==========================================================================*/
+static int amiga_read(int drive, unsigned char *track_data,
+ unsigned long raw, int track)
+{
+ unsigned long end;
+ int scnt;
+ unsigned long csum;
+ struct header hdr;
+
+ drive&=3;
+ end = raw + unit[drive].type->read_size;
+
+ for (scnt = 0;scnt < unit[drive].sects; scnt++) {
+ if (!(raw = scan_sync(raw, end))) {
+ printk ("can't find sync for sector %d\n", scnt);
+ return MFM_NOSYNC;
+ }
+
+ raw = decode ((ulong *)&hdr.magic, (ulong *)raw, 4);
+ raw = decode ((ulong *)&hdr.labels, (ulong *)raw, 16);
+ raw = decode ((ulong *)&hdr.hdrchk, (ulong *)raw, 4);
+ raw = decode ((ulong *)&hdr.datachk, (ulong *)raw, 4);
+ csum = checksum((ulong *)&hdr,
+ (char *)&hdr.hdrchk-(char *)&hdr);
+
+#ifdef DEBUG
+ printk ("(%x,%d,%d,%d) (%lx,%lx,%lx,%lx) %lx %lx\n",
+ hdr.magic, hdr.track, hdr.sect, hdr.ord,
+ *(ulong *)&hdr.labels[0], *(ulong *)&hdr.labels[4],
+ *(ulong *)&hdr.labels[8], *(ulong *)&hdr.labels[12],
+ hdr.hdrchk, hdr.datachk);
+#endif
+
+ if (hdr.hdrchk != csum) {
+ printk("MFM_HEADER: %08lx,%08lx\n", hdr.hdrchk, csum);
+ return MFM_HEADER;
+ }
+
+ /* verify track */
+ if (hdr.track != track) {
+ printk("MFM_TRACK: %d, %d\n", hdr.track, track);
+ return MFM_TRACK;
+ }
+
+ raw = decode ((ulong *)(track_data + hdr.sect*512),
+ (ulong *)raw, 512);
+ csum = checksum((ulong *)(track_data + hdr.sect*512), 512);
+
+ if (hdr.datachk != csum) {
+ printk("MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n",
+ hdr.magic, hdr.track, hdr.sect, hdr.ord, scnt,
+ hdr.datachk, csum);
+ printk ("data=(%lx,%lx,%lx,%lx)\n",
+ ((ulong *)(track_data+hdr.sect*512))[0],
+ ((ulong *)(track_data+hdr.sect*512))[1],
+ ((ulong *)(track_data+hdr.sect*512))[2],
+ ((ulong *)(track_data+hdr.sect*512))[3]);
+ return MFM_DATA;
+ }
+ }
+
+ return 0;
+}
+
+struct dos_header {
+unsigned char track, /* 0-80 */
+ side, /* 0-1 */
+ sec, /* 0-...*/
+ len_desc;/* 2 */
+unsigned short crc; /* on 68000 we got an alignment problem,
+ but this compiler solves it by adding silently
+ adding a pad byte so data won't fit
+ and this cost about 3h to discover.... */
+unsigned char gap1[22]; /* for longword-alignedness (0x4e) */
+};
+
+/* crc routines are borrowed from the messydos-handler */
+
+static inline ushort dos_hdr_crc (struct dos_header *hdr)
+{
+return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */
+}
+
+static inline ushort dos_data_crc(unsigned char *data)
+{
+return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */
+}
+
+/* excerpt from the messydos-device
+; The CRC is computed not only over the actual data, but including
+; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb).
+; As we don't read or encode these fields into our buffers, we have to
+; preload the registers containing the CRC with the values they would have
+; after stepping over these fields.
+;
+; How CRCs "really" work:
+;
+; First, you should regard a bitstring as a series of coefficients of
+; polynomials. We calculate with these polynomials in modulo-2
+; arithmetic, in which both add and subtract are done the same as
+; exclusive-or. Now, we modify our data (a very long polynomial) in
+; such a way that it becomes divisible by the CCITT-standard 16-bit
+; 16 12 5
+; polynomial: x + x + x + 1, represented by $11021. The easiest
+; way to do this would be to multiply (using proper arithmetic) our
+; datablock with $11021. So we have:
+; data * $11021 =
+; data * ($10000 + $1021) =
+; data * $10000 + data * $1021
+; The left part of this is simple: Just add two 0 bytes. But then
+; the right part (data $1021) remains difficult and even could have
+; a carry into the left part. The solution is to use a modified
+; multiplication, which has a result that is not correct, but with
+; a difference of any multiple of $11021. We then only need to keep
+; the 16 least significant bits of the result.
+;
+; The following algorithm does this for us:
+;
+; unsigned char *data, c, crclo, crchi;
+; while (not done) {
+; c = *data++ + crchi;
+; crchi = (@ c) >> 8 + crclo;
+; crclo = @ c;
+; }
+;
+; Remember, + is done with EOR, the @ operator is in two tables (high
+; and low byte separately), which is calculated as
+;
+; $1021 * (c & $F0)
+; xor $1021 * (c & $0F)
+; xor $1021 * (c >> 4) (* is regular multiplication)
+;
+;
+; Anyway, the end result is the same as the remainder of the division of
+; the data by $11021. I am afraid I need to study theory a bit more...
+
+
+my only works was to code this from manx to C....
+
+*/
+
+static ushort dos_crc(void * data_a3, int data_d0, int data_d1, int data_d3)
+{
+static unsigned char CRCTable1[] = {
+ 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x81,0x91,0xa1,0xb1,0xc1,0xd1,0xe1,0xf1,
+ 0x12,0x02,0x32,0x22,0x52,0x42,0x72,0x62,0x93,0x83,0xb3,0xa3,0xd3,0xc3,0xf3,0xe3,
+ 0x24,0x34,0x04,0x14,0x64,0x74,0x44,0x54,0xa5,0xb5,0x85,0x95,0xe5,0xf5,0xc5,0xd5,
+ 0x36,0x26,0x16,0x06,0x76,0x66,0x56,0x46,0xb7,0xa7,0x97,0x87,0xf7,0xe7,0xd7,0xc7,
+ 0x48,0x58,0x68,0x78,0x08,0x18,0x28,0x38,0xc9,0xd9,0xe9,0xf9,0x89,0x99,0xa9,0xb9,
+ 0x5a,0x4a,0x7a,0x6a,0x1a,0x0a,0x3a,0x2a,0xdb,0xcb,0xfb,0xeb,0x9b,0x8b,0xbb,0xab,
+ 0x6c,0x7c,0x4c,0x5c,0x2c,0x3c,0x0c,0x1c,0xed,0xfd,0xcd,0xdd,0xad,0xbd,0x8d,0x9d,
+ 0x7e,0x6e,0x5e,0x4e,0x3e,0x2e,0x1e,0x0e,0xff,0xef,0xdf,0xcf,0xbf,0xaf,0x9f,0x8f,
+ 0x91,0x81,0xb1,0xa1,0xd1,0xc1,0xf1,0xe1,0x10,0x00,0x30,0x20,0x50,0x40,0x70,0x60,
+ 0x83,0x93,0xa3,0xb3,0xc3,0xd3,0xe3,0xf3,0x02,0x12,0x22,0x32,0x42,0x52,0x62,0x72,
+ 0xb5,0xa5,0x95,0x85,0xf5,0xe5,0xd5,0xc5,0x34,0x24,0x14,0x04,0x74,0x64,0x54,0x44,
+ 0xa7,0xb7,0x87,0x97,0xe7,0xf7,0xc7,0xd7,0x26,0x36,0x06,0x16,0x66,0x76,0x46,0x56,
+ 0xd9,0xc9,0xf9,0xe9,0x99,0x89,0xb9,0xa9,0x58,0x48,0x78,0x68,0x18,0x08,0x38,0x28,
+ 0xcb,0xdb,0xeb,0xfb,0x8b,0x9b,0xab,0xbb,0x4a,0x5a,0x6a,0x7a,0x0a,0x1a,0x2a,0x3a,
+ 0xfd,0xed,0xdd,0xcd,0xbd,0xad,0x9d,0x8d,0x7c,0x6c,0x5c,0x4c,0x3c,0x2c,0x1c,0x0c,
+ 0xef,0xff,0xcf,0xdf,0xaf,0xbf,0x8f,0x9f,0x6e,0x7e,0x4e,0x5e,0x2e,0x3e,0x0e,0x1e
+};
+
+static unsigned char CRCTable2[] = {
+ 0x00,0x21,0x42,0x63,0x84,0xa5,0xc6,0xe7,0x08,0x29,0x4a,0x6b,0x8c,0xad,0xce,0xef,
+ 0x31,0x10,0x73,0x52,0xb5,0x94,0xf7,0xd6,0x39,0x18,0x7b,0x5a,0xbd,0x9c,0xff,0xde,
+ 0x62,0x43,0x20,0x01,0xe6,0xc7,0xa4,0x85,0x6a,0x4b,0x28,0x09,0xee,0xcf,0xac,0x8d,
+ 0x53,0x72,0x11,0x30,0xd7,0xf6,0x95,0xb4,0x5b,0x7a,0x19,0x38,0xdf,0xfe,0x9d,0xbc,
+ 0xc4,0xe5,0x86,0xa7,0x40,0x61,0x02,0x23,0xcc,0xed,0x8e,0xaf,0x48,0x69,0x0a,0x2b,
+ 0xf5,0xd4,0xb7,0x96,0x71,0x50,0x33,0x12,0xfd,0xdc,0xbf,0x9e,0x79,0x58,0x3b,0x1a,
+ 0xa6,0x87,0xe4,0xc5,0x22,0x03,0x60,0x41,0xae,0x8f,0xec,0xcd,0x2a,0x0b,0x68,0x49,
+ 0x97,0xb6,0xd5,0xf4,0x13,0x32,0x51,0x70,0x9f,0xbe,0xdd,0xfc,0x1b,0x3a,0x59,0x78,
+ 0x88,0xa9,0xca,0xeb,0x0c,0x2d,0x4e,0x6f,0x80,0xa1,0xc2,0xe3,0x04,0x25,0x46,0x67,
+ 0xb9,0x98,0xfb,0xda,0x3d,0x1c,0x7f,0x5e,0xb1,0x90,0xf3,0xd2,0x35,0x14,0x77,0x56,
+ 0xea,0xcb,0xa8,0x89,0x6e,0x4f,0x2c,0x0d,0xe2,0xc3,0xa0,0x81,0x66,0x47,0x24,0x05,
+ 0xdb,0xfa,0x99,0xb8,0x5f,0x7e,0x1d,0x3c,0xd3,0xf2,0x91,0xb0,0x57,0x76,0x15,0x34,
+ 0x4c,0x6d,0x0e,0x2f,0xc8,0xe9,0x8a,0xab,0x44,0x65,0x06,0x27,0xc0,0xe1,0x82,0xa3,
+ 0x7d,0x5c,0x3f,0x1e,0xf9,0xd8,0xbb,0x9a,0x75,0x54,0x37,0x16,0xf1,0xd0,0xb3,0x92,
+ 0x2e,0x0f,0x6c,0x4d,0xaa,0x8b,0xe8,0xc9,0x26,0x07,0x64,0x45,0xa2,0x83,0xe0,0xc1,
+ 0x1f,0x3e,0x5d,0x7c,0x9b,0xba,0xd9,0xf8,0x17,0x36,0x55,0x74,0x93,0xb2,0xd1,0xf0
+};
+
+/* look at the asm-code - what looks in C a bit strange is almost as good as handmade */
+register int i;
+register unsigned char *CRCT1, *CRCT2, *data, c, crch, crcl;
+
+CRCT1=CRCTable1;
+CRCT2=CRCTable2;
+data=data_a3;
+crcl=data_d1;
+crch=data_d0;
+for (i=data_d3; i>=0; i--) {
+ c = (*data++) ^ crch;
+ crch = CRCT1[c] ^ crcl;
+ crcl = CRCT2[c];
+}
+return (crch<<8)|crcl;
+}
+
+static inline unsigned char dos_decode_byte(ushort word)
+{
+register ushort w2;
+register unsigned char byte;
+register unsigned char *dec = mfmdecode;
+
+w2=word;
+w2>>=8;
+w2&=127;
+byte = dec[w2];
+byte <<= 4;
+w2 = word & 127;
+byte |= dec[w2];
+return byte;
+}
+
+static unsigned long dos_decode(unsigned char *data, unsigned short *raw, int len)
+{
+int i;
+
+for (i = 0; i < len; i++)
+ *data++=dos_decode_byte(*raw++);
+return ((ulong)raw);
+}
+
+#ifdef DEBUG
+static void dbg(unsigned long ptr)
+{
+printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n",ptr,
+ ((ulong *)ptr)[0],((ulong *)ptr)[1],((ulong *)ptr)[2],((ulong *)ptr)[3]);
+}
+#endif
+
+/*******************************************************************
+ this reads a raw track of data into trackbuffer for ms-disks
+*******************************************************************/
+static int dos_read(int drive, unsigned char *track_data,
+ unsigned long raw, int track)
+{
+ unsigned long end;
+ int scnt;
+ unsigned short crc,data_crc[2];
+ struct dos_header hdr;
+
+ drive&=3;
+ end = raw + unit[drive].type->read_size;
+
+ for (scnt=0;scnt<unit[drive].sects;scnt++) {
+ do { /* search for the right sync of each sec-hdr */
+ if (!(raw = scan_sync (raw, end))) {
+ printk("dos_read: no hdr sync on track %d, unit %d for sector %d\n",
+ track,drive,scnt);
+ return MFM_NOSYNC;
+ }
+#ifdef DEBUG
+ dbg(raw);
+#endif
+ } while (*((ushort *)raw)!=0x5554); /* loop usually only once done */
+ raw+=2; /* skip over headermark */
+ raw = dos_decode((unsigned char *)&hdr,(ushort *) raw,8);
+ crc = dos_hdr_crc(&hdr);
+
+#ifdef DEBUG
+ printk("(%3d,%d,%2d,%d) %x\n", hdr.track, hdr.side,
+ hdr.sec, hdr.len_desc, hdr.crc);
+#endif
+
+ if (crc != hdr.crc) {
+ printk("dos_read: MFM_HEADER %04x,%04x\n", hdr.crc, crc);
+ return MFM_HEADER;
+ }
+ if (hdr.track != track/unit[drive].type->heads) {
+ printk("dos_read: MFM_TRACK %d, %d\n", hdr.track,
+ track/unit[drive].type->heads);
+ return MFM_TRACK;
+ }
+
+ if (hdr.side != track%unit[drive].type->heads) {
+ printk("dos_read: MFM_SIDE %d, %d\n", hdr.side,
+ track%unit[drive].type->heads);
+ return MFM_TRACK;
+ }
+
+ if (hdr.len_desc != 2) {
+ printk("dos_read: unknown sector len descriptor %d\n", hdr.len_desc);
+ return MFM_DATA;
+ }
+#ifdef DEBUG
+ printk("hdr accepted\n");
+#endif
+ if (!(raw = scan_sync (raw, end))) {
+ printk("dos_read: no data sync on track %d, unit %d for sector%d, disk sector %d\n",
+ track, drive, scnt, hdr.sec);
+ return MFM_NOSYNC;
+ }
+#ifdef DEBUG
+ dbg(raw);
+#endif
+
+ if (*((ushort *)raw)!=0x5545) {
+ printk("dos_read: no data mark after sync (%d,%d,%d,%d) sc=%d\n",
+ hdr.track,hdr.side,hdr.sec,hdr.len_desc,scnt);
+ return MFM_NOSYNC;
+ }
+
+ raw+=2; /* skip data mark (included in checksum) */
+ raw = dos_decode((unsigned char *)(track_data + (hdr.sec - 1) * 512), (ushort *) raw, 512);
+ raw = dos_decode((unsigned char *)data_crc,(ushort *) raw,4);
+ crc = dos_data_crc(track_data + (hdr.sec - 1) * 512);
+
+ if (crc != data_crc[0]) {
+ printk("dos_read: MFM_DATA (%d,%d,%d,%d) sc=%d, %x %x\n",
+ hdr.track, hdr.side, hdr.sec, hdr.len_desc,
+ scnt,data_crc[0], crc);
+ printk("data=(%lx,%lx,%lx,%lx,...)\n",
+ ((ulong *)(track_data+(hdr.sec-1)*512))[0],
+ ((ulong *)(track_data+(hdr.sec-1)*512))[1],
+ ((ulong *)(track_data+(hdr.sec-1)*512))[2],
+ ((ulong *)(track_data+(hdr.sec-1)*512))[3]);
+ return MFM_DATA;
+ }
+ }
+ return 0;
+}
+
+static inline ushort dos_encode_byte(unsigned char byte)
+{
+register unsigned char *enc, b2, b1;
+register ushort word;
+
+enc=mfmencode;
+b1=byte;
+b2=b1>>4;
+b1&=15;
+word=enc[b2] <<8 | enc [b1];
+return (word|((word&(256|64)) ? 0: 128));
+}
+
+static void dos_encode_block(ushort *dest, unsigned char *src, int len)
+{
+int i;
+
+for (i = 0; i < len; i++) {
+ *dest=dos_encode_byte(*src++);
+ *dest|=((dest[-1]&1)||(*dest&0x4000))? 0: 0x8000;
+ dest++;
+}
+}
+
+static unsigned long *ms_putsec(int drive, unsigned long *raw, int track, int cnt,
+ unsigned char *data)
+{
+static struct dos_header hdr={0,0,0,2,0,
+ {78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78}};
+int i;
+static ushort crc[2]={0,0x4e4e};
+
+drive&=3;
+/* id gap 1 */
+/* the MFM word before is always 9254 */
+for(i=0;i<6;i++)
+ *raw++=0xaaaaaaaa;
+/* 3 sync + 1 headermark */
+*raw++=0x44894489;
+*raw++=0x44895554;
+
+/* fill in the variable parts of the header */
+hdr.track=track/unit[drive].type->heads;
+hdr.side=track%unit[drive].type->heads;
+hdr.sec=cnt+1;
+hdr.crc=dos_hdr_crc(&hdr);
+
+/* header (without "magic") and id gap 2*/
+dos_encode_block((ushort *)raw,(unsigned char *) &hdr.track,28);
+raw+=14;
+
+/*id gap 3 */
+for(i=0;i<6;i++)
+ *raw++=0xaaaaaaaa;
+
+/* 3 syncs and 1 datamark */
+*raw++=0x44894489;
+*raw++=0x44895545;
+
+/* data */
+dos_encode_block((ushort *)raw,(unsigned char *)data,512);
+raw+=256;
+
+/*data crc + jd's special gap (long words :-/) */
+crc[0]=dos_data_crc(data);
+dos_encode_block((ushort *) raw,(unsigned char *)crc,4);
+raw+=2;
+
+/* data gap */
+for(i=0;i<38;i++)
+ *raw++=0x92549254;
+
+return raw; /* wrote 652 MFM words */
+}
+
+
+/**************************************************************
+ builds encoded track data from trackbuffer data
+**************************************************************/
+static void dos_write(int disk, unsigned long raw, unsigned char *data,
+ int track)
+{
+int cnt;
+unsigned long *ptr=(unsigned long *)raw;
+
+disk&=3;
+/* really gap4 + indexgap , but we write it first and round it up */
+for (cnt=0;cnt<425;cnt++)
+ *ptr++=0x92549254;
+
+/* the following is just guessed */
+if (unit[disk].type->sect_mult==2) /* check for HD-Disks */
+ for(cnt=0;cnt<473;cnt++)
+ *ptr++=0x92549254;
+
+/* now the index marks...*/
+for (cnt=0;cnt<20;cnt++)
+ *ptr++=0x92549254;
+for (cnt=0;cnt<6;cnt++)
+ *ptr++=0xaaaaaaaa;
+*ptr++=0x52245224;
+*ptr++=0x52245552;
+for (cnt=0;cnt<20;cnt++)
+ *ptr++=0x92549254;
+
+/* sectors */
+for(cnt=0;cnt<unit[disk].sects;cnt++)
+ ptr=ms_putsec(disk,ptr,track,cnt,data+cnt*512);
+
+*(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */
+}
+
+/*
+ * Note that MAX_ERRORS=X doesn't imply that we retry every bad read
+ * max X times - some types of errors increase the errorcount by 2 or
+ * even 3, so we might actually retry only X/2 times before giving up.
+ */
+#define MAX_ERRORS 12
+
+/*
+ * The driver is trying to determine the correct media format
+ * while probing is set. rw_interrupt() clears it after a
+ * successful access.
+ */
+static int probing = 0;
+
+/* Prevent "aliased" accesses. */
+static fd_ref[4] = { 0,0,0,0 };
+static fd_device[4] = { 0,0,0,0 };
+
+/*
+ * Current device number. Taken either from the block header or from the
+ * format request descriptor.
+ */
+#define CURRENT_DEVICE (CURRENT->rq_dev)
+
+/* Current error count. */
+#define CURRENT_ERRORS (CURRENT->errors)
+
+static void request_done(int uptodate)
+{
+ timer_active &= ~(1 << FLOPPY_TIMER);
+ end_request(uptodate);
+}
+
+/*
+ * floppy-change is never called from an interrupt, so we can relax a bit
+ * here, sleep etc. Note that floppy-on tries to set current_DOR to point
+ * to the desired drive, but it will probably not survive the sleep if
+ * several floppies are used at the same time: thus the loop.
+ */
+static int amiga_floppy_change(kdev_t dev)
+{
+ int drive = dev & 3;
+ int changed;
+
+ if (MAJOR(dev) != MAJOR_NR) {
+ printk("floppy_change: not a floppy\n");
+ return 0;
+ }
+
+ fd_select (drive);
+ changed = !(ciaa.pra & DSKCHANGE);
+ fd_deselect (drive);
+
+ if (changed) {
+ fd_probe(dev);
+ unit[drive].track = -1;
+ selected = -1;
+ savedtrack = -1;
+ writepending = 0; /* if this was true before, too bad! */
+ writefromint = 0;
+ return 1;
+ }
+ return 0;
+}
+
+static __inline__ void copy_buffer(void *from, void *to)
+{
+ ulong *p1,*p2;
+ int cnt;
+
+ p1 = (ulong *)from;
+ p2 = (ulong *)to;
+
+ for (cnt = 0; cnt < 512/4; cnt++)
+ *p2++ = *p1++;
+}
+
+static void raw_read(int drive, int track, char *ptrack, int len)
+{
+ drive&=3;
+ /* setup adkcon bits correctly */
+ custom.adkcon = ADK_MSBSYNC;
+ custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST;
+
+ custom.dsksync = MFM_SYNC;
+
+ custom.dsklen = 0;
+#if 0
+ ms_delay (unit[drive].type->side_time);
+#endif
+ custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)ptrack);
+ custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN;
+ custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN;
+
+ block_flag = 1;
+
+ while (block_flag == 1)
+ sleep_on (&wait_fd_block);
+
+ custom.dsklen = 0;
+}
+
+static int raw_write(int drive, int track, char *ptrack, int len)
+{
+ ushort adk;
+
+ drive&=3;
+ if ((ciaa.pra & DSKPROT) == 0)
+ return 0;
+
+ /* clear adkcon bits */
+ custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC;
+ /* set appropriate adkcon bits */
+ adk = ADK_SETCLR|ADK_FAST;
+ if ((ulong)track >= unit[drive].type->precomp2)
+ adk |= ADK_PRECOMP1;
+ else if ((ulong)track >= unit[drive].type->precomp1)
+ adk |= ADK_PRECOMP0;
+ custom.adkcon = adk;
+
+ custom.dsklen = DSKLEN_WRITE;
+#if 0
+ ms_delay (unit[drive].type->side_time);
+#endif
+ custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)ptrack);
+ custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
+ custom.dsklen = len/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE;
+
+ block_flag = 2;
+ return 1;
+}
+
+static void post_write (unsigned long dummy)
+{
+ custom.dsklen = 0;
+ writepending = 0;
+ writefromint = 0;
+}
+
+static int get_track(int drive, int track)
+{
+ int error;
+
+ drive&=3;
+ if ((lastdrive == drive) && (savedtrack == track))
+ return 0;
+
+ lastdrive = drive;
+ raw_read(drive, track, raw_buf, unit[drive].type->read_size);
+ savedtrack = -1;
+ error = (*unit[drive].dtype->read_fkt)(drive, trackdata, (unsigned long)raw_buf, track);
+ switch (error) {
+ case 0:
+ savedtrack = track;
+ return 0;
+ case MFM_TRACK:
+ unit[drive].track = -1;
+ /* fall through */
+ default:
+ return -1;
+ }
+}
+
+static void flush_track_callback(unsigned long nr)
+{
+ nr&=3;
+ writefromint = 1;
+ (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack);
+ if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) {
+ printk ("floppy disk write protected\n");
+ writefromint = 0;
+ writepending = 0;
+ }
+}
+
+static int non_int_flush_track (unsigned long nr)
+{
+unsigned long flags;
+
+ nr&=3;
+ writefromint = 0;
+ del_timer(&post_write_timer);
+ save_flags(flags);
+ cli();
+ if (writepending != 2) {
+ restore_flags(flags);
+ (*unit[nr].dtype->write_fkt)(nr, (unsigned long)raw_buf, trackdata, savedtrack);
+ if (!raw_write(nr, savedtrack, raw_buf, unit[nr].type->write_size)) {
+ printk ("floppy disk write protected in write!\n");
+ writepending = 0;
+ return 0;
+ }
+ while (block_flag == 2)
+ sleep_on (&wait_fd_block);
+ }
+ else
+ restore_flags(flags);
+ ms_delay(2); /* 2 ms post_write delay */
+ post_write(0);
+ return 1;
+}
+
+static void redo_fd_request(void)
+{
+ unsigned int block, track, sector;
+ int device, drive, cnt;
+ struct amiga_floppy_struct *floppy;
+ char *data;
+ unsigned long flags;
+
+ if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
+ return;
+ }
+
+ repeat:
+ if (!CURRENT) {
+ if (!fdc_busy)
+ printk("FDC access conflict!");
+ fdc_busy = 0;
+ wake_up(&fdc_wait);
+ CLEAR_INTR;
+ return;
+ }
+
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+ panic(DEVICE_NAME ": request list destroyed");
+
+ if (CURRENT->bh && !buffer_locked(CURRENT->bh))
+ panic(DEVICE_NAME ": block not locked");
+
+ probing = 0;
+ device = MINOR(CURRENT_DEVICE);
+ if (device > 3) {
+ /* manual selection */
+ drive = device & 3;
+ floppy = unit + drive;
+ } else {
+ /* Auto-detection */
+ /* printk("redo_fd_request: can't handle auto detect\n");*/
+ /* printk("redo_fd_request: default to normal\n");*/
+ drive = device & 3;
+ floppy = unit + drive;
+ }
+
+ save_flags (flags);
+ cli();
+ if (drive != selected && writepending) {
+ del_timer (&flush_track_timer);
+ restore_flags (flags);
+ if (!non_int_flush_track (selected)) {
+ end_request(0);
+ goto repeat;
+ }
+ } else
+ restore_flags (flags);
+
+ /* Here someone could investigate to be more efficient */
+ for (cnt = 0; cnt < CURRENT->current_nr_sectors; cnt++) {
+#ifdef DEBUG
+ printk("fd: sector %d + %d requested\n",CURRENT->sector,cnt);
+#endif
+ block = CURRENT->sector + cnt;
+ if ((int)block > floppy->blocks) {
+ request_done(0);
+ goto repeat;
+ }
+
+ track = block / floppy->sects;
+ sector = block % floppy->sects;
+ data = CURRENT->buffer + 512 * cnt;
+
+ save_flags (flags);
+ cli();
+ if (track != savedtrack && writepending) {
+ del_timer (&flush_track_timer);
+ restore_flags (flags);
+ if (!non_int_flush_track (selected)) {
+ end_request(0);
+ goto repeat;
+ }
+ } else
+ restore_flags (flags);
+
+ switch (CURRENT->cmd) {
+ case READ:
+ if (!motor_on (drive)) {
+ end_request(0);
+ goto repeat;
+ }
+ fd_select (drive);
+ if (!fd_seek(drive, track)) {
+ end_request(0);
+ goto repeat;
+ }
+ if (get_track(drive, track) == -1) {
+ end_request(0);
+ goto repeat;
+ }
+ copy_buffer(trackdata + sector * 512, data);
+ break;
+
+ case WRITE:
+ if (!motor_on (drive)) {
+ end_request(0);
+ goto repeat;
+ }
+ fd_select (drive);
+ if (!fd_seek(drive, track)) {
+ end_request(0);
+ goto repeat;
+ }
+ if (get_track(drive, track) == -1) {
+ end_request(0);
+ goto repeat;
+ }
+ copy_buffer(data, trackdata + sector * 512);
+ /*
+ * setup a callback to write the track buffer
+ * after a short (1 tick) delay.
+ */
+ save_flags (flags);
+ cli();
+
+ if (writepending)
+ /* reset the timer */
+ del_timer (&flush_track_timer);
+
+ writepending = 1;
+ flush_track_timer.data = drive;
+ flush_track_timer.expires = jiffies + 1;
+ add_timer (&flush_track_timer);
+ restore_flags (flags);
+ break;
+
+ default:
+ printk("do_fd_request: unknown command\n");
+ request_done(0);
+ goto repeat;
+ }
+ }
+ CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+ CURRENT->sector += CURRENT->current_nr_sectors;
+
+ request_done(1);
+ goto repeat;
+}
+
+static void do_fd_request(void)
+{
+unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ while (fdc_busy) sleep_on(&fdc_wait);
+ fdc_busy = 1;
+ restore_flags(flags); /* sti(); */
+ redo_fd_request();
+}
+
+static int fd_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long param)
+{
+ int drive = inode->i_rdev & 3;
+ static struct floppy_struct getprm;
+ int error;
+
+ switch(cmd)
+ {
+ case FDFMTBEG:
+ if (fd_ref[drive] > 1)
+ return -EBUSY;
+ fsync_dev(inode->i_rdev);
+ if (motor_on(drive) == 0)
+ return -ENODEV;
+ if (fd_calibrate(drive) == 0)
+ return -ENXIO;
+ floppy_off(drive);
+ break;
+ case FDFMTTRK:
+ if (param < unit[drive].type->tracks)
+ {
+ fd_select(drive);
+ if (fd_seek(drive,param)!=0)
+ {
+ savedtrack=param;
+ memset(trackdata,FD_FILL_BYTE,unit[drive].sects*512);
+ non_int_flush_track(drive);
+ }
+ floppy_off(drive);
+ }
+ else
+ return -EINVAL;
+ break;
+ case FDFMTEND:
+ floppy_off(drive);
+ invalidate_inodes(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ break;
+ case FDGETPRM:
+ error = verify_area(VERIFY_WRITE, (void *)param,
+ sizeof(struct floppy_struct));
+ if (error)
+ return error;
+ memset((void *)&getprm, 0, sizeof (getprm));
+ getprm.track=unit[drive].type->tracks/unit[drive].type->heads;
+ getprm.head=unit[drive].type->heads;
+ getprm.sect=unit[drive].sects;
+ getprm.size=unit[drive].blocks;
+ copy_to_user((void *)param,(void *)&getprm,sizeof(struct floppy_struct));
+ break;
+ case BLKGETSIZE:
+ error = verify_area(VERIFY_WRITE, (void *)param,
+ sizeof(long));
+ if (error)
+ return error;
+ put_fs_long(unit[drive].blocks,(long *)param);
+ break;
+ case FDSETPRM:
+ case FDDEFPRM:
+ return -EINVAL;
+ case FDFLUSH:
+ if ((drive == selected) && (writepending)) {
+ del_timer (&flush_track_timer);
+ non_int_flush_track(selected);
+ }
+ break;
+#ifdef RAW_IOCTL
+ case IOCTL_RAW_TRACK:
+ error = verify_area(VERIFY_WRITE, (void *)param,
+ unit[drive].type->read_size);
+ if (error)
+ return error;
+ copy_to_user((void *)param, raw_buf, unit[drive].type->read_size);
+ return unit[drive].type->read_size;
+#endif
+ default:
+ printk("fd_ioctl: unknown cmd %d for drive %d.",cmd,drive);
+ return -ENOSYS;
+ }
+ return 0;
+}
+
+/*======================================================================
+ Return unit ID number of given disk
+======================================================================*/
+static unsigned long get_drive_id(int drive)
+{
+ int i;
+ ulong id = 0;
+
+ drive&=3;
+ /* set up for ID */
+ MOTOR_ON;
+ udelay(2);
+ SELECT(SELMASK(drive));
+ udelay(2);
+ DESELECT(SELMASK(drive));
+ udelay(2);
+ MOTOR_OFF;
+ udelay(2);
+ SELECT(SELMASK(drive));
+ udelay(2);
+ DESELECT(SELMASK(drive));
+ udelay(2);
+
+ /* loop and read disk ID */
+ for (i=0; i<32; i++) {
+ SELECT(SELMASK(drive));
+ udelay(2);
+
+ /* read and store value of DSKRDY */
+ id <<= 1;
+ id |= (ciaa.pra & DSKRDY) ? 0 : 1; /* cia regs are low-active! */
+
+ DESELECT(SELMASK(drive));
+ }
+
+ selected = -1;
+
+ /*
+ * RB: At least A500/A2000's df0: don't identify themselves.
+ * As every (real) Amiga has at least a 3.5" DD drive as df0:
+ * we default to that if df0: doesn't identify as a certain
+ * type.
+ */
+ if(drive == 0 && id == FD_NODRIVE)
+ {
+ id = fd_def_df0;
+ printk("fd: drive 0 didn't identify, setting default %08lx\n",(ulong)fd_def_df0);
+ }
+ /* return the ID value */
+ return (id);
+}
+
+static void fd_probe(int dev)
+{
+ unsigned long code;
+ int type;
+ int drive;
+ int system;
+
+ drive = dev & 3;
+ code = get_drive_id(drive);
+
+ /* get drive type */
+ unit[drive].type = NULL;
+ for (type = 0; type < num_dr_types; type++)
+ if (drive_types[type].code == code)
+ break;
+
+ if (type >= num_dr_types) {
+ printk("fd_probe: unsupported drive type %08lx found\n",
+ code);
+ return;
+ }
+
+ unit[drive].type = &drive_types[type];
+ unit[drive].track = -1;
+
+ unit[drive].disk = -1;
+ unit[drive].motor = 0;
+ unit[drive].busy = 0;
+ unit[drive].status = -1;
+
+
+ system=(dev & 4)>>2;
+ unit[drive].dtype=&data_types[system];
+ unit[drive].sects=data_types[system].sects*unit[drive].type->sect_mult;
+ unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
+ unit[drive].sects;
+
+ floppy_sizes[MINOR(dev)] = unit[drive].blocks >> 1;
+
+}
+
+static void probe_drives(void)
+{
+ int drive,found;
+
+ printk("FD: probing units\nfound ");
+ found=0;
+ for(drive=0;drive<FD_MAX_UNITS;drive++) {
+ fd_probe(drive);
+ if (unit[drive].type->code != FD_NODRIVE) {
+ printk("fd%d ",drive);
+ found=1;
+ }
+ }
+ printk("%s\n",(found==0)?" no drives":"");
+}
+
+/*
+ * floppy_open check for aliasing (/dev/fd0 can be the same as
+ * /dev/PS0 etc), and disallows simultaneous access to the same
+ * drive with different device numbers.
+ */
+static int floppy_open(struct inode *inode, struct file *filp)
+{
+ int drive;
+ int old_dev;
+ int system;
+
+ drive = inode->i_rdev & 3;
+ old_dev = fd_device[drive];
+
+ if (fd_ref[drive])
+ if (old_dev != inode->i_rdev)
+ return -EBUSY;
+
+ if (unit[drive].type->code == FD_NODRIVE)
+ return -ENODEV;
+
+ fd_ref[drive]++;
+ fd_device[drive] = inode->i_rdev;
+
+ if (old_dev && old_dev != inode->i_rdev)
+ invalidate_buffers(old_dev);
+
+ if (filp && filp->f_mode)
+ check_disk_change(inode->i_rdev);
+
+ if (filp && (filp->f_flags & (O_WRONLY|O_RDWR))) {
+ int wrprot;
+
+ fd_select (drive);
+ wrprot = !(ciaa.pra & DSKPROT);
+ fd_deselect (drive);
+
+ if (wrprot)
+ return -EROFS;
+ }
+
+ system=(inode->i_rdev & 4)>>2;
+ unit[drive].dtype=&data_types[system];
+ unit[drive].sects=data_types[system].sects*unit[drive].type->sect_mult;
+ unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
+ unit[drive].sects;
+
+printk("fd%d: accessing %s-disk with %s-layout\n",drive,unit[drive].type->name,
+ data_types[system].name);
+
+ return 0;
+}
+
+static void floppy_release(struct inode * inode, struct file * filp)
+{
+ unsigned long flags;
+
+ fsync_dev(inode->i_rdev);
+ invalidate_inodes(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ save_flags (flags);
+ cli();
+ if ((inode->i_rdev & 3) == selected && writepending) {
+ del_timer (&flush_track_timer);
+ restore_flags (flags);
+ non_int_flush_track (selected);
+ } else
+ restore_flags (flags);
+
+ if (!fd_ref[inode->i_rdev & 3]--) {
+ printk("floppy_release with fd_ref == 0");
+ fd_ref[inode->i_rdev & 3] = 0;
+ }
+}
+
+void amiga_floppy_setup (char *str, int *ints)
+{
+printk ("amiflop: Setting default df0 to %x\n", ints[1]);
+fd_def_df0 = ints[1];
+}
+
+static struct file_operations floppy_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ fd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ floppy_open, /* open */
+ floppy_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ amiga_floppy_change, /* check_media_change */
+ NULL, /* revalidate */
+};
+
+static void fd_block_done(int irq, struct pt_regs *fp, void *dummy)
+{
+ if (block_flag)
+ custom.dsklen = 0x4000;
+
+ block_flag = 0;
+ wake_up (&wait_fd_block);
+
+ if (writefromint) {
+ /*
+ * if it was a write from an interrupt,
+ * we will call post_write from here
+ */
+ writepending = 2;
+ post_write_timer.expires = 1; /* at least 2 ms */
+ add_timer(&post_write_timer);
+ }
+
+}
+
+int amiga_floppy_init(void)
+{
+ int i;
+
+ if (!AMIGAHW_PRESENT(AMI_FLOPPY))
+ return -ENXIO;
+
+ if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
+ printk("Unable to get major %d for floppy\n",MAJOR_NR);
+ return -EBUSY;
+ }
+
+ /* initialize variables */
+ motor_on_timer.next = NULL;
+ motor_on_timer.prev = NULL;
+ motor_on_timer.expires = 0;
+ motor_on_timer.data = 0;
+ motor_on_timer.function = motor_on_callback;
+ for (i = 0; i < FD_MAX_UNITS; i++) {
+ motor_off_timer[i].next = NULL;
+ motor_off_timer[i].prev = NULL;
+ motor_off_timer[i].expires = 0;
+ motor_off_timer[i].data = i;
+ motor_off_timer[i].function = fd_motor_off;
+
+ unit[i].track = -1;
+ }
+
+ flush_track_timer.next = NULL;
+ flush_track_timer.prev = NULL;
+ flush_track_timer.expires = 0;
+ flush_track_timer.data = 0;
+ flush_track_timer.function = flush_track_callback;
+
+ post_write_timer.next = NULL;
+ post_write_timer.prev = NULL;
+ post_write_timer.expires = 0;
+ post_write_timer.data = 0;
+ post_write_timer.function = post_write;
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ blksize_size[MAJOR_NR] = floppy_blocksizes;
+ blk_size[MAJOR_NR] = floppy_sizes;
+
+
+ timer_table[FLOPPY_TIMER].fn = NULL;
+ timer_active &= ~(1 << FLOPPY_TIMER);
+
+ if (fd_def_df0==0) {
+ if ((boot_info.bi_amiga.model == AMI_3000) ||
+ (boot_info.bi_amiga.model == AMI_4000))
+ fd_def_df0=FD_HD_3;
+ else
+ fd_def_df0=FD_DD_3;
+ }
+
+ probe_drives();
+
+ raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE);
+
+ for (i = 0; i < 128; i++)
+ mfmdecode[i]=255;
+ for (i = 0; i < 16; i++)
+ mfmdecode[mfmencode[i]]=i;
+
+ /* make sure that disk DMA is enabled */
+ custom.dmacon = DMAF_SETCLR | DMAF_DISK;
+
+ add_isr(IRQ_FLOPPY, fd_block_done, 0, NULL, "floppy_dma");
+ add_isr(IRQ_AMIGA_CIAA_TB, ms_isr, 0, NULL, "floppy_timer");
+
+ return 0;
+}
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
new file mode 100644
index 000000000..559740d95
--- /dev/null
+++ b/drivers/block/ataflop.c
@@ -0,0 +1,1955 @@
+/*
+ * drivers/block/ataflop.c
+ *
+ * Copyright (C) 1993 Greg Harp
+ * Atari Support by Bjoern Brauel, Roman Hodek
+ *
+ * Big cleanup Sep 11..14 1994 Roman Hodek:
+ * - Driver now works interrupt driven
+ * - Support for two drives; should work, but I cannot test that :-(
+ * - Reading is done in whole tracks and buffered to speed up things
+ * - Disk change detection and drive deselecting after motor-off
+ * similar to TOS
+ * - Autodetection of disk format (DD/HD); untested yet, because I
+ * don't have an HD drive :-(
+ *
+ * Fixes Nov 13 1994 Martin Schaller:
+ * - Autodetection works now
+ * - Support for 5 1/4'' disks
+ * - Removed drive type (unknown on atari)
+ * - Do seeks with 8 Mhz
+ *
+ * Changes by Andreas Schwab:
+ * - After errors in multiple read mode try again reading single sectors
+ * (Feb 1995):
+ * - Clean up error handling
+ * - Set blk_size for proper size checking
+ * - Initialize track register when testing presence of floppy
+ * - Implement some ioctl's
+ *
+ * Changes by Torsten Lang:
+ * - When probing the floppies we should add the FDCCMDADD_H flag since
+ * the FDC will otherwise wait forever when no disk is inserted...
+ *
+ * ++ Freddi Aschwanden (fa) 20.9.95 fixes for medusa:
+ * - MFPDELAY() after each FDC access -> atari
+ * - more/other disk formats
+ * - DMA to the block buffer directly if we have a 32bit DMA
+ * - for medusa, the step rate is always 3ms
+ * - on medusa, use only cache_push()
+ * Roman:
+ * - Make disk format numbering independent from minors
+ * - Let user set max. supported drive type (speeds up format
+ * detection, saves buffer space)
+ *
+ * Roman 10/15/95:
+ * - implement some more ioctls
+ * - disk formatting
+ *
+ * Andreas 95/12/12:
+ * - increase gap size at start of track for HD/ED disks
+ *
+ * Things left to do:
+ * - Formatting
+ * - Maybe a better strategy for disk change detection (does anyone
+ * know one?)
+ */
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/fd.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+
+#include <asm/bootinfo.h>
+#include <asm/atafd.h>
+#include <asm/atafdreg.h>
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_stdma.h>
+
+#define MAJOR_NR FLOPPY_MAJOR
+#include <linux/blk.h>
+
+#define FD_MAX_UNITS 2
+
+#undef DEBUG
+
+/* Disk types: DD, HD, ED */
+static struct atari_disk_type {
+ const char *name;
+ unsigned spt; /* sectors per track */
+ unsigned blocks; /* total number of blocks */
+ unsigned fdc_speed; /* fdc_speed setting */
+ unsigned stretch; /* track doubling ? */
+} disk_type[] = {
+ { "d360", 9, 720, 0, 0}, /* 0: 360kB diskette */
+ { "D360", 9, 720, 0, 1}, /* 1: 360kb in 720k or 1.2MB drive */
+ { "D720", 9,1440, 0, 0}, /* 2: 720kb in 720k or 1.2MB drive */
+ { "D820", 10,1640, 0, 0}, /* 3: DD disk with 82 tracks/10 sectors */
+/* formats above are probed for type DD */
+#define MAX_TYPE_DD 3
+ { "h1200",15,2400, 3, 0}, /* 4: 1.2MB diskette */
+ { "H1440",18,2880, 3, 0}, /* 5: 1.4 MB diskette (HD) */
+ { "H1640",20,3280, 3, 0}, /* 6: 1.64MB diskette (fat HD) 82 tr 20 sec */
+/* formats above are probed for types DD and HD */
+#define MAX_TYPE_HD 6
+ { "E2880",36,5760, 3, 0}, /* 7: 2.8 MB diskette (ED) */
+ { "E3280",40,6560, 3, 0}, /* 8: 3.2 MB diskette (fat ED) 82 tr 40 sec */
+/* formats above are probed for types DD, HD and ED */
+#define MAX_TYPE_ED 8
+/* types below are never autoprobed */
+ { "H1680",21,3360, 3, 0}, /* 9: 1.68MB diskette (fat HD) 80 tr 21 sec */
+ { "h410",10,820, 0, 1}, /* 10: 410k diskette 41 tr 10 sec, stretch */
+ { "h1476",18,2952, 3, 0}, /* 11: 1.48MB diskette 82 tr 18 sec */
+ { "H1722",21,3444, 3, 0}, /* 12: 1.72MB diskette 82 tr 21 sec */
+ { "h420",10,840, 0, 1}, /* 13: 420k diskette 42 tr 10 sec, stretch */
+ { "H830",10,1660, 0, 0}, /* 14: 820k diskette 83 tr 10 sec */
+ { "h1494",18,2952, 3, 0}, /* 15: 1.49MB diskette 83 tr 18 sec */
+ { "H1743",21,3486, 3, 0}, /* 16: 1.74MB diskette 83 tr 21 sec */
+ { "h880",11,1760, 0, 0}, /* 17: 880k diskette 80 tr 11 sec */
+ { "D1040",13,2080, 0, 0}, /* 18: 1.04MB diskette 80 tr 13 sec */
+ { "D1120",14,2240, 0, 0}, /* 19: 1.12MB diskette 80 tr 14 sec */
+ { "h1600",20,3200, 3, 0}, /* 20: 1.60MB diskette 80 tr 20 sec */
+ { "H1760",22,3520, 3, 0}, /* 21: 1.76MB diskette 80 tr 22 sec */
+ { "H1920",24,3840, 3, 0}, /* 22: 1.92MB diskette 80 tr 24 sec */
+ { "E3200",40,6400, 3, 0}, /* 23: 3.2MB diskette 80 tr 40 sec */
+ { "E3520",44,7040, 3, 0}, /* 24: 3.52MB diskette 80 tr 44 sec */
+ { "E3840",48,7680, 3, 0}, /* 25: 3.84MB diskette 80 tr 48 sec */
+ { "H1840",23,3680, 3, 0}, /* 26: 1.84MB diskette 80 tr 23 sec */
+ { "D800",10,1600, 0, 0}, /* 27: 800k diskette 80 tr 10 sec */
+};
+
+static int StartDiskType[] = {
+ MAX_TYPE_DD,
+ MAX_TYPE_HD,
+ MAX_TYPE_ED
+};
+
+#define TYPE_DD 0
+#define TYPE_HD 1
+#define TYPE_ED 2
+
+static int DriveType = TYPE_HD;
+
+/* Array for translating minors into disk formats */
+static struct {
+ int index;
+ unsigned drive_types;
+} minor2disktype[] = {
+ { 0, TYPE_DD }, /* 1: d360 */
+ { 4, TYPE_HD }, /* 2: h1200 */
+ { 1, TYPE_DD }, /* 3: D360 */
+ { 2, TYPE_DD }, /* 4: D720 */
+ { 1, TYPE_DD }, /* 5: h360 = D360 */
+ { 2, TYPE_DD }, /* 6: h720 = D720 */
+ { 5, TYPE_HD }, /* 7: H1440 */
+ { 7, TYPE_ED }, /* 8: E2880 */
+/* some PC formats :-) */
+ { 8, TYPE_ED }, /* 9: E3280 <- was "CompaQ" == E2880 for PC */
+ { 5, TYPE_HD }, /* 10: h1440 = H1440 */
+ { 9, TYPE_HD }, /* 11: H1680 */
+ { 10, TYPE_DD }, /* 12: h410 */
+ { 3, TYPE_DD }, /* 13: H820 <- == D820, 82x10 */
+ { 11, TYPE_HD }, /* 14: h1476 */
+ { 12, TYPE_HD }, /* 15: H1722 */
+ { 13, TYPE_DD }, /* 16: h420 */
+ { 14, TYPE_DD }, /* 17: H830 */
+ { 15, TYPE_HD }, /* 18: h1494 */
+ { 16, TYPE_HD }, /* 19: H1743 */
+ { 17, TYPE_DD }, /* 20: h880 */
+ { 18, TYPE_DD }, /* 21: D1040 */
+ { 19, TYPE_DD }, /* 22: D1120 */
+ { 20, TYPE_HD }, /* 23: h1600 */
+ { 21, TYPE_HD }, /* 24: H1760 */
+ { 22, TYPE_HD }, /* 25: H1920 */
+ { 23, TYPE_ED }, /* 26: E3200 */
+ { 24, TYPE_ED }, /* 27: E3520 */
+ { 25, TYPE_ED }, /* 28: E3840 */
+ { 26, TYPE_HD }, /* 29: H1840 */
+ { 27, TYPE_DD }, /* 30: D800 */
+ { 6, TYPE_HD }, /* 31: H1640 <- was H1600 == h1600 for PC */
+};
+
+#define NUM_DISK_MINORS (sizeof(minor2disktype)/sizeof(*minor2disktype))
+
+/*
+ * Maximum disk size (in kilobytes). This default is used whenever the
+ * current disk size is unknown.
+ */
+#define MAX_DISK_SIZE 3280
+
+static int floppy_sizes[256];
+static int floppy_blocksizes[256] = { 0, };
+
+/* current info on each unit */
+static struct atari_floppy_struct {
+ int connected; /* !=0 : drive is connected */
+ int autoprobe; /* !=0 : do autoprobe */
+
+ struct atari_disk_type *disktype; /* current type of disk */
+
+ int track; /* current head position or -1 if
+ unknown */
+ unsigned int steprate; /* steprate setting */
+ unsigned int wpstat; /* current state of WP signal (for
+ disk change detection) */
+ int flags; /* flags */
+} unit[FD_MAX_UNITS];
+
+#define UD unit[drive]
+#define UDT unit[drive].disktype
+#define SUD unit[SelectedDrive]
+#define SUDT unit[SelectedDrive].disktype
+
+
+#define FDC_READ(reg) ({ \
+ /* unsigned long __flags; */ \
+ unsigned short __val; \
+ /* save_flags(__flags); cli(); */ \
+ dma_wd.dma_mode_status = 0x80 | (reg); \
+ udelay(25); \
+ __val = dma_wd.fdc_acces_seccount; \
+ MFPDELAY(); \
+ /* restore_flags(__flags); */ \
+ __val & 0xff; \
+})
+
+#define FDC_WRITE(reg,val) \
+ do { \
+ /* unsigned long __flags; */ \
+ /* save_flags(__flags); cli(); */ \
+ dma_wd.dma_mode_status = 0x80 | (reg); \
+ udelay(25); \
+ dma_wd.fdc_acces_seccount = (val); \
+ MFPDELAY(); \
+ /* restore_flags(__flags); */ \
+ } while(0)
+
+
+/* Buffering variables:
+ * First, there is a DMA buffer in ST-RAM that is used for floppy DMA
+ * operations. Second, a track buffer is used to cache a whole track
+ * of the disk to save read operations. These are two separate buffers
+ * because that allows write operations without clearing the track buffer.
+ */
+
+static int MaxSectors[] = {
+ 11, 22, 44
+};
+static int BufferSize[] = {
+ 15*512, 30*512, 60*512
+};
+
+#define MAX_SECTORS (MaxSectors[DriveType])
+#define BUFFER_SIZE (BufferSize[DriveType])
+
+unsigned char *DMABuffer; /* buffer for writes */
+static unsigned long PhysDMABuffer; /* physical address */
+
+static int UseTrackbuffer = -1; /* Do track buffering? */
+
+unsigned char *TrackBuffer; /* buffer for reads */
+static unsigned long PhysTrackBuffer; /* physical address */
+static int BufferDrive, BufferSide, BufferTrack;
+static int read_track; /* non-zero if we are reading whole tracks */
+
+#define SECTOR_BUFFER(sec) (TrackBuffer + ((sec)-1)*512)
+#define IS_BUFFERED(drive,side,track) \
+ (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track))
+
+/*
+ * These are global variables, as that's the easiest way to give
+ * information to interrupts. They are the data used for the current
+ * request.
+ */
+static int SelectedDrive = 0;
+static int ReqCmd, ReqBlock;
+static int ReqSide, ReqTrack, ReqSector, ReqCnt;
+static int HeadSettleFlag = 0;
+static unsigned char *ReqData, *ReqBuffer;
+static int MotorOn = 0, MotorOffTrys;
+static int IsFormatting = 0, FormatError;
+
+static int UserSteprate[FD_MAX_UNITS] = { -1, -1 };
+
+/* Synchronization of FDC access. */
+static volatile int fdc_busy = 0;
+static struct wait_queue *fdc_wait = NULL;
+static struct wait_queue *format_wait = NULL;
+
+static unsigned int changed_floppies = 0xff, fake_change = 0;
+#define CHECK_CHANGE_DELAY HZ/2
+
+#define FD_MOTOR_OFF_DELAY (3*HZ)
+#define FD_MOTOR_OFF_MAXTRY (10*20)
+
+#define FLOPPY_TIMEOUT (6*HZ)
+#define RECALIBRATE_ERRORS 4 /* After this many errors the drive
+ * will be recalibrated. */
+#define MAX_ERRORS 8 /* After this many errors the driver
+ * will give up. */
+
+
+#define START_MOTOR_OFF_TIMER(delay) \
+ do { \
+ motor_off_timer.expires = jiffies + (delay); \
+ add_timer( &motor_off_timer ); \
+ MotorOffTrys = 0; \
+ } while(0)
+
+#define START_CHECK_CHANGE_TIMER(delay) \
+ do { \
+ timer_table[FLOPPY_TIMER].expires = jiffies + (delay); \
+ timer_active |= (1 << FLOPPY_TIMER); \
+ } while(0)
+
+#define START_TIMEOUT() \
+ do { \
+ del_timer( &timeout_timer ); \
+ timeout_timer.expires = jiffies + FLOPPY_TIMEOUT; \
+ add_timer( &timeout_timer ); \
+ } while(0)
+
+#define STOP_TIMEOUT() \
+ do { \
+ del_timer( &timeout_timer ); \
+ } while(0)
+
+
+/*
+ * The driver is trying to determine the correct media format
+ * while Probing is set. fd_rwsec_done() clears it after a
+ * successful access.
+ */
+static int Probing = 0;
+
+/* This flag is set when a dummy seek is necessary to make the WP
+ * status bit accessible.
+ */
+static int NeedSeek = 0;
+
+
+#ifdef DEBUG
+#define DPRINT(a) printk a
+#else
+#define DPRINT(a)
+#endif
+
+/***************************** Prototypes *****************************/
+
+static void fd_select_side( int side );
+static void fd_select_drive( int drive );
+static void fd_deselect( void );
+static void fd_motor_off_timer( unsigned long dummy );
+static void check_change( void );
+static __inline__ void set_head_settle_flag( void );
+static __inline__ int get_head_settle_flag( void );
+static void floppy_irq (int irq, struct pt_regs *fp, void *dummy);
+static void fd_error( void );
+static int do_format(kdev_t drive, struct atari_format_descr *desc);
+static void do_fd_action( int drive );
+static void fd_calibrate( void );
+static void fd_calibrate_done( int status );
+static void fd_seek( void );
+static void fd_seek_done( int status );
+static void fd_rwsec( void );
+static void fd_readtrack_check( unsigned long dummy );
+static void fd_rwsec_done( int status );
+static void fd_writetrack( void );
+static void fd_writetrack_done( int status );
+static void fd_times_out( unsigned long dummy );
+static void finish_fdc( void );
+static void finish_fdc_done( int dummy );
+static void floppy_off( unsigned int nr);
+static __inline__ void copy_buffer( void *from, void *to);
+static void setup_req_params( int drive );
+static void redo_fd_request( void);
+static int invalidate_drive(kdev_t rdev);
+static int fd_ioctl( struct inode *inode, struct file *filp, unsigned int
+ cmd, unsigned long param);
+static void fd_probe( int drive );
+static int fd_test_drive_present( int drive );
+static void config_types( void );
+static int floppy_open( struct inode *inode, struct file *filp );
+static void floppy_release( struct inode * inode, struct file * filp );
+
+/************************* End of Prototypes **************************/
+
+static struct timer_list motor_off_timer =
+ { NULL, NULL, 0, 0, fd_motor_off_timer };
+static struct timer_list readtrack_timer =
+ { NULL, NULL, 0, 0, fd_readtrack_check };
+
+static struct timer_list timeout_timer =
+ { NULL, NULL, 0, 0, fd_times_out };
+
+
+
+/* Select the side to use. */
+
+static void fd_select_side( int side )
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli(); /* protect against various other ints mucking around with the PSG */
+
+ sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */
+ sound_ym.wd_data = (side == 0) ? sound_ym.rd_data_reg_sel | 0x01 :
+ sound_ym.rd_data_reg_sel & 0xfe;
+
+ restore_flags(flags);
+}
+
+
+/* Select a drive, update the FDC's track register and set the correct
+ * clock speed for this disk's type.
+ */
+
+static void fd_select_drive( int drive )
+{
+ unsigned long flags;
+ unsigned char tmp;
+
+ if (drive == SelectedDrive)
+ return;
+
+ save_flags(flags);
+ cli(); /* protect against various other ints mucking around with the PSG */
+ sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */
+ tmp = sound_ym.rd_data_reg_sel;
+ sound_ym.wd_data = (tmp | DSKDRVNONE) & ~(drive == 0 ? DSKDRV0 : DSKDRV1);
+ restore_flags(flags);
+
+ /* restore track register to saved value */
+ FDC_WRITE( FDCREG_TRACK, UD.track );
+ udelay(25);
+
+ /* select 8/16 MHz */
+ if (UDT)
+ if (ATARIHW_PRESENT(FDCSPEED))
+ dma_wd.fdc_speed = UDT->fdc_speed;
+
+ SelectedDrive = drive;
+}
+
+
+/* Deselect both drives. */
+
+static void fd_deselect( void )
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli(); /* protect against various other ints mucking around with the PSG */
+ sound_ym.rd_data_reg_sel=14; /* Select PSG Port A */
+ sound_ym.wd_data = sound_ym.rd_data_reg_sel | 7; /* no drives selected */
+ SelectedDrive = -1;
+ restore_flags(flags);
+}
+
+
+/* This timer function deselects the drives when the FDC switched the
+ * motor off. The deselection cannot happen earlier because the FDC
+ * counts the index signals, which arrive only if one drive is selected.
+ */
+
+static void fd_motor_off_timer( unsigned long dummy )
+{
+/* unsigned long flags; */
+ unsigned char status;
+ int delay;
+
+ del_timer( &motor_off_timer );
+
+ if (SelectedDrive < 0)
+ /* no drive selected, needn't deselect anyone */
+ return;
+
+/* save_flags(flags);
+ cli(); */
+
+ if (stdma_islocked())
+ goto retry;
+
+ status = FDC_READ( FDCREG_STATUS );
+
+ if (!(status & 0x80)) {
+ /* motor already turned off by FDC -> deselect drives */
+ MotorOn = 0;
+ fd_deselect();
+/* restore_flags(flags); */
+ return;
+ }
+ /* not yet off, try again */
+
+ retry:
+/* restore_flags(flags); */
+ /* Test again later; if tested too often, it seems there is no disk
+ * in the drive and the FDC will leave the motor on forever (or,
+ * at least until a disk is inserted). So we'll test only twice
+ * per second from then on...
+ */
+ delay = (MotorOffTrys < FD_MOTOR_OFF_MAXTRY) ?
+ (++MotorOffTrys, HZ/20) : HZ/2;
+ START_MOTOR_OFF_TIMER( delay );
+}
+
+
+/* This function is repeatedly called to detect disk changes (as good
+ * as possible) and keep track of the current state of the write protection.
+ */
+
+static void check_change( void )
+{
+ static int drive = 0;
+
+ unsigned long flags;
+ unsigned char old_porta;
+ int stat;
+
+ if (++drive > 1 || !UD.connected)
+ drive = 0;
+
+ save_flags(flags);
+ cli(); /* protect against various other ints mucking around with the PSG */
+
+ if (!stdma_islocked()) {
+ sound_ym.rd_data_reg_sel = 14;
+ old_porta = sound_ym.rd_data_reg_sel;
+ sound_ym.wd_data = (old_porta | DSKDRVNONE) &
+ ~(drive == 0 ? DSKDRV0 : DSKDRV1);
+ stat = !!(FDC_READ( FDCREG_STATUS ) & FDCSTAT_WPROT);
+ sound_ym.wd_data = old_porta;
+
+ if (stat != UD.wpstat) {
+ DPRINT(( "wpstat[%d] = %d\n", drive, stat ));
+ UD.wpstat = stat;
+ set_bit (drive, &changed_floppies);
+ }
+ }
+ restore_flags(flags);
+
+ START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY );
+}
+
+
+/* Handling of the Head Settling Flag: This flag should be set after each
+ * seek operation, because we don't use seeks with verify.
+ */
+
+static __inline__ void set_head_settle_flag( void )
+{
+ HeadSettleFlag = FDCCMDADD_E;
+}
+
+static __inline__ int get_head_settle_flag( void )
+{
+ int tmp = HeadSettleFlag;
+ HeadSettleFlag = 0;
+ return( tmp );
+}
+
+
+
+
+/* General Interrupt Handling */
+
+static void (*FloppyIRQHandler)( int status ) = NULL;
+
+static void floppy_irq (int irq, struct pt_regs *fp, void *dummy)
+{
+ unsigned char status;
+ void (*handler)( int );
+
+ handler = FloppyIRQHandler;
+ FloppyIRQHandler = NULL;
+
+ if (handler) {
+ nop();
+ status = FDC_READ( FDCREG_STATUS );
+ DPRINT(("FDC irq, status = %02x handler = %08lx\n",status,(unsigned long)handler));
+ handler( status );
+ }
+ else {
+ DPRINT(("FDC irq, no handler\n"));
+ }
+}
+
+
+/* Error handling: If some error happened, retry some times, then
+ * recalibrate, then try again, and fail after MAX_ERRORS.
+ */
+
+static void fd_error( void )
+{
+ if (IsFormatting) {
+ IsFormatting = 0;
+ FormatError = 1;
+ wake_up( &format_wait );
+ return;
+ }
+
+ if (!CURRENT) return;
+ CURRENT->errors++;
+ if (CURRENT->errors >= MAX_ERRORS) {
+ printk(KERN_ERR "fd%d: too many errors.\n", SelectedDrive );
+ end_request( 0 );
+ }
+ else if (CURRENT->errors == RECALIBRATE_ERRORS) {
+ printk(KERN_WARNING "fd%d: recalibrating\n", SelectedDrive );
+ if (SelectedDrive != -1)
+ SUD.track = -1;
+ }
+ redo_fd_request();
+}
+
+
+
+#define SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0)
+
+
+/* ---------- Formatting ---------- */
+
+#define FILL(n,val) \
+ do { \
+ memset( p, val, n ); \
+ p += n; \
+ } while(0)
+
+static int do_format(kdev_t device, struct atari_format_descr *desc)
+{
+ unsigned char *p;
+ int sect, nsect;
+ unsigned long flags;
+ int type, drive = MINOR(device) & 3;
+
+ DPRINT(("do_format( dr=%d tr=%d he=%d offs=%d )\n",
+ drive, desc->track, desc->head, desc->sect_offset ));
+
+ save_flags(flags);
+ cli();
+ while( fdc_busy ) sleep_on( &fdc_wait );
+ fdc_busy = 1;
+ stdma_lock(floppy_irq, NULL);
+ atari_turnon_irq( IRQ_MFP_FDC ); /* should be already, just to be sure */
+ restore_flags(flags);
+
+ type = MINOR(device) >> 2;
+ if (type) {
+ if (--type >= NUM_DISK_MINORS ||
+ minor2disktype[type].drive_types > DriveType) {
+ redo_fd_request();
+ return -EINVAL;
+ }
+ type = minor2disktype[type].index;
+ UDT = &disk_type[type];
+ }
+
+ if (!UDT || desc->track >= UDT->blocks/UDT->spt/2 || desc->head >= 2) {
+ redo_fd_request();
+ return -EINVAL;
+ }
+
+ nsect = UDT->spt;
+ p = TrackBuffer;
+ /* The track buffer is used for the raw track data, so its
+ contents become invalid! */
+ BufferDrive = -1;
+ /* stop deselect timer */
+ del_timer( &motor_off_timer );
+
+ FILL( 60 * (nsect / 9), 0x4e );
+ for( sect = 0; sect < nsect; ++sect ) {
+ FILL( 12, 0 );
+ FILL( 3, 0xf5 );
+ *p++ = 0xfe;
+ *p++ = desc->track;
+ *p++ = desc->head;
+ *p++ = (nsect + sect - desc->sect_offset) % nsect + 1;
+ *p++ = 2;
+ *p++ = 0xf7;
+ FILL( 22, 0x4e );
+ FILL( 12, 0 );
+ FILL( 3, 0xf5 );
+ *p++ = 0xfb;
+ FILL( 512, 0xe5 );
+ *p++ = 0xf7;
+ FILL( 40, 0x4e );
+ }
+ FILL( TrackBuffer+BUFFER_SIZE-p, 0x4e );
+
+ IsFormatting = 1;
+ FormatError = 0;
+ ReqTrack = desc->track;
+ ReqSide = desc->head;
+ do_fd_action( drive );
+
+ sleep_on( &format_wait );
+
+ redo_fd_request();
+ return( FormatError ? -EIO : 0 );
+}
+
+
+/* do_fd_action() is the general procedure for a fd request: All
+ * required parameter settings (drive select, side select, track
+ * position) are checked and set if needed. For each of these
+ * parameters and the actual reading or writing exist two functions:
+ * one that starts the setting (or skips it if possible) and one
+ * callback for the "done" interrupt. Each done func calls the next
+ * set function to propagate the request down to fd_rwsec_done().
+ */
+
+static void do_fd_action( int drive )
+{
+ DPRINT(("do_fd_action\n"));
+
+ if (UseTrackbuffer && !IsFormatting) {
+ repeat:
+ if (IS_BUFFERED( drive, ReqSide, ReqTrack )) {
+ if (ReqCmd == READ) {
+ copy_buffer( SECTOR_BUFFER(ReqSector), ReqData );
+ if (++ReqCnt < CURRENT->current_nr_sectors) {
+ /* read next sector */
+ setup_req_params( drive );
+ goto repeat;
+ }
+ else {
+ /* all sectors finished */
+ CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+ CURRENT->sector += CURRENT->current_nr_sectors;
+ end_request( 1 );
+ redo_fd_request();
+ return;
+ }
+ }
+ else {
+ /* cmd == WRITE, pay attention to track buffer
+ * consistency! */
+ copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) );
+ }
+ }
+ }
+
+ if (SelectedDrive != drive)
+ fd_select_drive( drive );
+
+ if (UD.track == -1)
+ fd_calibrate();
+ else if (UD.track != ReqTrack << UDT->stretch)
+ fd_seek();
+ else if (IsFormatting)
+ fd_writetrack();
+ else
+ fd_rwsec();
+}
+
+
+/* Seek to track 0 if the current track is unknown */
+
+static void fd_calibrate( void )
+{
+ if (SUD.track >= 0) {
+ fd_calibrate_done( 0 );
+ return;
+ }
+
+ if (ATARIHW_PRESENT(FDCSPEED))
+ dma_wd.fdc_speed = 0; /* always seek with 8 Mhz */;
+ DPRINT(("fd_calibrate\n"));
+ SET_IRQ_HANDLER( fd_calibrate_done );
+ /* we can't verify, since the speed may be incorrect */
+ FDC_WRITE( FDCREG_CMD, FDCCMD_RESTORE | SUD.steprate );
+
+ NeedSeek = 1;
+ MotorOn = 1;
+ START_TIMEOUT();
+ /* wait for IRQ */
+}
+
+
+static void fd_calibrate_done( int status )
+{
+ DPRINT(("fd_calibrate_done()\n"));
+ STOP_TIMEOUT();
+
+ /* set the correct speed now */
+ if (ATARIHW_PRESENT(FDCSPEED))
+ dma_wd.fdc_speed = SUDT->fdc_speed;
+ if (status & FDCSTAT_RECNF) {
+ printk(KERN_ERR "fd%d: restore failed\n", SelectedDrive );
+ fd_error();
+ }
+ else {
+ SUD.track = 0;
+ fd_seek();
+ }
+}
+
+
+/* Seek the drive to the requested track. The drive must have been
+ * calibrated at some point before this.
+ */
+
+static void fd_seek( void )
+{
+ if (SUD.track == ReqTrack << SUDT->stretch) {
+ fd_seek_done( 0 );
+ return;
+ }
+
+ if (ATARIHW_PRESENT(FDCSPEED)) {
+ dma_wd.fdc_speed = 0; /* always seek witch 8 Mhz */
+ MFPDELAY();
+ }
+
+ DPRINT(("fd_seek() to track %d\n",ReqTrack));
+ FDC_WRITE( FDCREG_DATA, ReqTrack << SUDT->stretch);
+ udelay(25);
+ SET_IRQ_HANDLER( fd_seek_done );
+ FDC_WRITE( FDCREG_CMD, FDCCMD_SEEK | SUD.steprate );
+
+ MotorOn = 1;
+ set_head_settle_flag();
+ START_TIMEOUT();
+ /* wait for IRQ */
+}
+
+
+static void fd_seek_done( int status )
+{
+ DPRINT(("fd_seek_done()\n"));
+ STOP_TIMEOUT();
+
+ /* set the correct speed */
+ if (ATARIHW_PRESENT(FDCSPEED))
+ dma_wd.fdc_speed = SUDT->fdc_speed;
+ if (status & FDCSTAT_RECNF) {
+ printk(KERN_ERR "fd%d: seek error (to track %d)\n",
+ SelectedDrive, ReqTrack );
+ /* we don't know exactly which track we are on now! */
+ SUD.track = -1;
+ fd_error();
+ }
+ else {
+ SUD.track = ReqTrack << SUDT->stretch;
+ NeedSeek = 0;
+ if (IsFormatting)
+ fd_writetrack();
+ else
+ fd_rwsec();
+ }
+}
+
+
+/* This does the actual reading/writing after positioning the head
+ * over the correct track.
+ */
+
+static int MultReadInProgress = 0;
+
+
+static void fd_rwsec( void )
+{
+ unsigned long paddr, flags;
+ unsigned int rwflag, old_motoron;
+ unsigned int track;
+
+ DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n",ReqSector, ReqCmd == WRITE ? 'w' : 'r' ));
+ if (ReqCmd == WRITE) {
+ if (ATARIHW_PRESENT(EXTD_DMA)) {
+ paddr = (unsigned long)VTOP(ReqData);
+ }
+ else {
+ copy_buffer( ReqData, DMABuffer );
+ paddr = PhysDMABuffer;
+ }
+ dma_cache_maintenance( paddr, 512, 1 );
+ rwflag = 0x100;
+ }
+ else {
+ if (read_track)
+ paddr = PhysTrackBuffer;
+ else
+ paddr = ATARIHW_PRESENT(EXTD_DMA) ? VTOP(ReqData) : PhysDMABuffer;
+ rwflag = 0;
+ }
+
+ fd_select_side( ReqSide );
+
+ /* Start sector of this operation */
+ FDC_WRITE( FDCREG_SECTOR, read_track ? 1 : ReqSector );
+ MFPDELAY();
+ /* Cheat for track if stretch != 0 */
+ if (SUDT->stretch) {
+ track = FDC_READ( FDCREG_TRACK);
+ MFPDELAY();
+ FDC_WRITE( FDCREG_TRACK, track >> SUDT->stretch);
+ }
+ udelay(25);
+
+ /* Setup DMA */
+ save_flags(flags);
+ cli();
+ dma_wd.dma_lo = (unsigned char)paddr;
+ MFPDELAY();
+ paddr >>= 8;
+ dma_wd.dma_md = (unsigned char)paddr;
+ MFPDELAY();
+ paddr >>= 8;
+ if (ATARIHW_PRESENT(EXTD_DMA))
+ st_dma_ext_dmahi = (unsigned short)paddr;
+ else
+ dma_wd.dma_hi = (unsigned char)paddr;
+ MFPDELAY();
+ restore_flags(flags);
+
+ /* Clear FIFO and switch DMA to correct mode */
+ dma_wd.dma_mode_status = 0x90 | rwflag;
+ MFPDELAY();
+ dma_wd.dma_mode_status = 0x90 | (rwflag ^ 0x100);
+ MFPDELAY();
+ dma_wd.dma_mode_status = 0x90 | rwflag;
+ MFPDELAY();
+
+ /* How many sectors for DMA */
+ dma_wd.fdc_acces_seccount = read_track ? SUDT->spt : 1;
+
+ udelay(25);
+
+ /* Start operation */
+ dma_wd.dma_mode_status = FDCSELREG_STP | rwflag;
+ udelay(25);
+ SET_IRQ_HANDLER( fd_rwsec_done );
+ dma_wd.fdc_acces_seccount =
+ (get_head_settle_flag() |
+ (rwflag ? FDCCMD_WRSEC : (FDCCMD_RDSEC | (read_track ? FDCCMDADD_M : 0))));
+
+ old_motoron = MotorOn;
+ MotorOn = 1;
+ NeedSeek = 1;
+ /* wait for interrupt */
+
+ if (read_track) {
+ /* If reading a whole track, wait about one disk rotation and
+ * then check if all sectors are read. The FDC will even
+ * search for the first non-existent sector and need 1 sec to
+ * recognise that it isn't present :-(
+ */
+ readtrack_timer.expires =
+ jiffies + HZ/5 + (old_motoron ? 0 : HZ);
+ /* 1 rot. + 5 rot.s if motor was off */
+ add_timer( &readtrack_timer );
+ MultReadInProgress = 1;
+ }
+ START_TIMEOUT();
+}
+
+
+static void fd_readtrack_check( unsigned long dummy )
+{
+ unsigned long flags, addr, addr2;
+
+ save_flags(flags);
+ cli();
+
+ del_timer( &readtrack_timer );
+
+ if (!MultReadInProgress) {
+ /* This prevents a race condition that could arise if the
+ * interrupt is triggered while the calling of this timer
+ * callback function takes place. The IRQ function then has
+ * already cleared 'MultReadInProgress' when flow of control
+ * gets here.
+ */
+ restore_flags(flags);
+ return;
+ }
+
+ /* get the current DMA address */
+ /* ++ f.a. read twice to avoid being fooled by switcher */
+ addr = 0;
+ do {
+ addr2 = addr;
+ addr = dma_wd.dma_lo & 0xff;
+ MFPDELAY();
+ addr |= (dma_wd.dma_md & 0xff) << 8;
+ MFPDELAY();
+ if (ATARIHW_PRESENT( EXTD_DMA ))
+ addr |= (st_dma_ext_dmahi & 0xffff) << 16;
+ else
+ addr |= (dma_wd.dma_hi & 0xff) << 16;
+ MFPDELAY();
+ } while(addr != addr2);
+
+ if (addr >= PhysTrackBuffer + SUDT->spt*512) {
+ /* already read enough data, force an FDC interrupt to stop
+ * the read operation
+ */
+ SET_IRQ_HANDLER( NULL );
+ restore_flags(flags);
+ DPRINT(("fd_readtrack_check(): done\n"));
+ FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
+ udelay(25);
+
+ /* No error until now -- the FDC would have interrupted
+ * otherwise!
+ */
+ fd_rwsec_done( 0 );
+ }
+ else {
+ /* not yet finished, wait another tenth rotation */
+ restore_flags(flags);
+ DPRINT(("fd_readtrack_check(): not yet finished\n"));
+ readtrack_timer.expires = jiffies + HZ/5/10;
+ add_timer( &readtrack_timer );
+ }
+}
+
+
+static void fd_rwsec_done( int status )
+{
+ unsigned int track;
+
+ DPRINT(("fd_rwsec_done()\n"));
+
+ STOP_TIMEOUT();
+
+ if (read_track) {
+ if (!MultReadInProgress)
+ return;
+ MultReadInProgress = 0;
+ del_timer( &readtrack_timer );
+ }
+
+ /* Correct the track if stretch != 0 */
+ if (SUDT->stretch) {
+ track = FDC_READ( FDCREG_TRACK);
+ MFPDELAY();
+ FDC_WRITE( FDCREG_TRACK, track << SUDT->stretch);
+ }
+
+ if (!UseTrackbuffer) {
+ dma_wd.dma_mode_status = 0x90;
+ MFPDELAY();
+ if (!(dma_wd.dma_mode_status & 0x01)) {
+ printk(KERN_ERR "fd%d: DMA error\n", SelectedDrive );
+ goto err_end;
+ }
+ }
+ MFPDELAY();
+
+ if (ReqCmd == WRITE && (status & FDCSTAT_WPROT)) {
+ printk(KERN_NOTICE "fd%d: is write protected\n", SelectedDrive );
+ goto err_end;
+ }
+ if ((status & FDCSTAT_RECNF) &&
+ /* RECNF is no error after a multiple read when the FDC
+ searched for a non-existent sector! */
+ !(read_track && FDC_READ(FDCREG_SECTOR) > SUDT->spt)) {
+ if (Probing) {
+ if (SUDT > disk_type) {
+ /* try another disk type */
+ SUDT--;
+ floppy_sizes[SelectedDrive] = SUDT->blocks >> 1;
+ }
+ else {
+ if (SUD.flags & FTD_MSG)
+ printk(KERN_INFO "fd%d: Auto-detected floppy type %s\n",
+ SelectedDrive, SUDT->name );
+ Probing=0;
+ }
+ } else {
+/* record not found, but not probing. Maybe stretch wrong ? Restart probing */
+ if (SUD.autoprobe) {
+ SUDT = disk_type + StartDiskType[DriveType];
+ floppy_sizes[SelectedDrive] = SUDT->blocks >> 1;
+ Probing = 1;
+ }
+ }
+ if (Probing) {
+ if (ATARIHW_PRESENT(FDCSPEED)) {
+ dma_wd.fdc_speed = SUDT->fdc_speed;
+ MFPDELAY();
+ }
+ setup_req_params( SelectedDrive );
+ BufferDrive = -1;
+ do_fd_action( SelectedDrive );
+ return;
+ }
+
+ printk(KERN_ERR "fd%d: sector %d not found (side %d, track %d)\n",
+ SelectedDrive, FDC_READ (FDCREG_SECTOR), ReqSide, ReqTrack );
+ goto err_end;
+ }
+ if (status & FDCSTAT_CRC) {
+ printk(KERN_ERR "fd%d: CRC error (side %d, track %d, sector %d)\n",
+ SelectedDrive, ReqSide, ReqTrack, FDC_READ (FDCREG_SECTOR) );
+ goto err_end;
+ }
+ if (status & FDCSTAT_LOST) {
+ printk(KERN_ERR "fd%d: lost data (side %d, track %d, sector %d)\n",
+ SelectedDrive, ReqSide, ReqTrack, FDC_READ (FDCREG_SECTOR) );
+ goto err_end;
+ }
+
+ Probing = 0;
+
+ if (ReqCmd == READ) {
+ if (!read_track) {
+ void *addr;
+ addr = ATARIHW_PRESENT( EXTD_DMA ) ? ReqData : DMABuffer;
+ dma_cache_maintenance( VTOP(addr), 512, 0 );
+ if (!ATARIHW_PRESENT( EXTD_DMA ))
+ copy_buffer (addr, ReqData);
+ } else {
+ dma_cache_maintenance( PhysTrackBuffer, MAX_SECTORS * 512, 0 );
+ BufferDrive = SelectedDrive;
+ BufferSide = ReqSide;
+ BufferTrack = ReqTrack;
+ copy_buffer (SECTOR_BUFFER (ReqSector), ReqData);
+ }
+ }
+
+ if (++ReqCnt < CURRENT->current_nr_sectors) {
+ /* read next sector */
+ setup_req_params( SelectedDrive );
+ do_fd_action( SelectedDrive );
+ }
+ else {
+ /* all sectors finished */
+ CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+ CURRENT->sector += CURRENT->current_nr_sectors;
+ end_request( 1 );
+ redo_fd_request();
+ }
+ return;
+
+ err_end:
+ BufferDrive = -1;
+ fd_error();
+}
+
+
+static void fd_writetrack( void )
+{
+ unsigned long paddr, flags;
+ unsigned int track;
+
+ DPRINT(("fd_writetrack() Tr=%d Si=%d\n", ReqTrack, ReqSide ));
+
+ paddr = PhysTrackBuffer;
+ dma_cache_maintenance( paddr, BUFFER_SIZE, 1 );
+
+ fd_select_side( ReqSide );
+
+ /* Cheat for track if stretch != 0 */
+ if (SUDT->stretch) {
+ track = FDC_READ( FDCREG_TRACK);
+ MFPDELAY();
+ FDC_WRITE(FDCREG_TRACK,track >> SUDT->stretch);
+ }
+ udelay(40);
+
+ /* Setup DMA */
+ save_flags(flags);
+ cli();
+ dma_wd.dma_lo = (unsigned char)paddr;
+ MFPDELAY();
+ paddr >>= 8;
+ dma_wd.dma_md = (unsigned char)paddr;
+ MFPDELAY();
+ paddr >>= 8;
+ if (ATARIHW_PRESENT( EXTD_DMA ))
+ st_dma_ext_dmahi = (unsigned short)paddr;
+ else
+ dma_wd.dma_hi = (unsigned char)paddr;
+ MFPDELAY();
+ restore_flags(flags);
+
+ /* Clear FIFO and switch DMA to correct mode */
+ dma_wd.dma_mode_status = 0x190;
+ MFPDELAY();
+ dma_wd.dma_mode_status = 0x90;
+ MFPDELAY();
+ dma_wd.dma_mode_status = 0x190;
+ MFPDELAY();
+
+ /* How many sectors for DMA */
+ dma_wd.fdc_acces_seccount = BUFFER_SIZE/512;
+ udelay(40);
+
+ /* Start operation */
+ dma_wd.dma_mode_status = FDCSELREG_STP | 0x100;
+ udelay(40);
+ SET_IRQ_HANDLER( fd_writetrack_done );
+ dma_wd.fdc_acces_seccount = FDCCMD_WRTRA | get_head_settle_flag();
+
+ MotorOn = 1;
+ START_TIMEOUT();
+ /* wait for interrupt */
+}
+
+
+static void fd_writetrack_done( int status )
+{
+ DPRINT(("fd_writetrack_done()\n"));
+
+ STOP_TIMEOUT();
+
+ if (status & FDCSTAT_WPROT) {
+ printk(KERN_NOTICE "fd%d: is write protected\n", SelectedDrive );
+ goto err_end;
+ }
+ if (status & FDCSTAT_LOST) {
+ printk(KERN_ERR "fd%d: lost data (side %d, track %d)\n",
+ SelectedDrive, ReqSide, ReqTrack );
+ goto err_end;
+ }
+
+ wake_up( &format_wait );
+ return;
+
+ err_end:
+ fd_error();
+}
+
+static void fd_times_out( unsigned long dummy )
+{
+ atari_disable_irq( IRQ_MFP_FDC );
+ if (!FloppyIRQHandler) goto end; /* int occurred after timer was fired, but
+ * before we came here... */
+
+ SET_IRQ_HANDLER( NULL );
+ /* If the timeout occurred while the readtrack_check timer was
+ * active, we need to cancel it, else bad things will happen */
+ if (UseTrackbuffer)
+ del_timer( &readtrack_timer );
+ FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
+ udelay( 25 );
+
+ printk(KERN_ERR "floppy timeout\n" );
+ fd_error();
+ end:
+ atari_enable_irq( IRQ_MFP_FDC );
+}
+
+
+/* The (noop) seek operation here is needed to make the WP bit in the
+ * FDC status register accessible for check_change. If the last disk
+ * operation would have been a RDSEC, this bit would always read as 0
+ * no matter what :-( To save time, the seek goes to the track we're
+ * already on.
+ */
+
+static void finish_fdc( void )
+{
+ if (!NeedSeek) {
+ finish_fdc_done( 0 );
+ }
+ else {
+ DPRINT(("finish_fdc: dummy seek started\n"));
+ FDC_WRITE (FDCREG_DATA, SUD.track);
+ SET_IRQ_HANDLER( finish_fdc_done );
+ FDC_WRITE (FDCREG_CMD, FDCCMD_SEEK);
+ MotorOn = 1;
+ START_TIMEOUT();
+ /* we must wait for the IRQ here, because the ST-DMA
+ is released immediately afterwards and the interrupt
+ may be delivered to the wrong driver. */
+ }
+}
+
+
+static void finish_fdc_done( int dummy )
+{
+ unsigned long flags;
+
+ DPRINT(("finish_fdc_done entered\n"));
+ STOP_TIMEOUT();
+ NeedSeek = 0;
+
+ if ((timer_active & (1 << FLOPPY_TIMER)) &&
+ timer_table[FLOPPY_TIMER].expires < jiffies + 5)
+ /* If the check for a disk change is done too early after this
+ * last seek command, the WP bit still reads wrong :-((
+ */
+ timer_table[FLOPPY_TIMER].expires = jiffies + 5;
+ else
+ START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY );
+ del_timer( &motor_off_timer );
+ START_MOTOR_OFF_TIMER( FD_MOTOR_OFF_DELAY );
+
+ save_flags(flags);
+ cli();
+ stdma_release();
+ fdc_busy = 0;
+ wake_up( &fdc_wait );
+ restore_flags(flags);
+
+ DPRINT(("finish_fdc() finished\n"));
+}
+
+
+/* Prevent "aliased" accesses. */
+static fd_ref[4] = { 0,0,0,0 };
+static fd_device[4] = { 0,0,0,0 };
+
+/*
+ * Current device number. Taken either from the block header or from the
+ * format request descriptor.
+ */
+#define CURRENT_DEVICE (CURRENT->rq_dev)
+
+/* Current error count. */
+#define CURRENT_ERRORS (CURRENT->errors)
+
+
+/* dummy for blk.h */
+static void floppy_off( unsigned int nr) {}
+
+
+/* The detection of disk changes is a dark chapter in Atari history :-(
+ * Because the "Drive ready" signal isn't present in the Atari
+ * hardware, one has to rely on the "Write Protect". This works fine,
+ * as long as no write protected disks are used. TOS solves this
+ * problem by introducing tri-state logic ("maybe changed") and
+ * looking at the serial number in block 0. This isn't possible for
+ * Linux, since the floppy driver can't make assumptions about the
+ * filesystem used on the disk and thus the contents of block 0. I've
+ * chosen the method to always say "The disk was changed" if it is
+ * unsure whether it was. This implies that every open or mount
+ * invalidates the disk buffers if you work with write protected
+ * disks. But at least this is better than working with incorrect data
+ * due to unrecognised disk changes.
+ */
+
+static int check_floppy_change (kdev_t dev)
+{
+ unsigned int drive = MINOR(dev) & 0x03;
+
+ if (MAJOR(dev) != MAJOR_NR) {
+ printk(KERN_ERR "floppy_changed: not a floppy\n");
+ return 0;
+ }
+
+ if (test_bit (drive, &fake_change)) {
+ /* simulated change (e.g. after formatting) */
+ return 1;
+ }
+ if (test_bit (drive, &changed_floppies)) {
+ /* surely changed (the WP signal changed at least once) */
+ return 1;
+ }
+ if (UD.wpstat) {
+ /* WP is on -> could be changed: to be sure, buffers should be
+ * invalidated...
+ */
+ return 1;
+ }
+
+ return 0;
+}
+
+static int floppy_revalidate (kdev_t dev)
+{
+ int drive = MINOR(dev) & 3;
+
+ if (test_bit (drive, &changed_floppies) || test_bit (drive, &fake_change)
+ || unit[drive].disktype == 0)
+ {
+ BufferDrive = -1;
+ clear_bit (drive, &fake_change);
+ clear_bit (drive, &changed_floppies);
+ UDT = 0;
+ }
+ return 0;
+}
+
+static __inline__ void copy_buffer(void *from, void *to)
+{
+ ulong *p1 = (ulong *)from, *p2 = (ulong *)to;
+ int cnt;
+
+ for( cnt = 512/4; cnt; cnt-- )
+ *p2++ = *p1++;
+}
+
+
+/* This sets up the global variables describing the current request. */
+
+static void setup_req_params( int drive )
+{
+ int block = ReqBlock + ReqCnt;
+
+ ReqTrack = block / UDT->spt;
+ ReqSector = block - ReqTrack * UDT->spt + 1;
+ ReqSide = ReqTrack & 1;
+ ReqTrack >>= 1;
+ ReqData = ReqBuffer + 512 * ReqCnt;
+
+ if (UseTrackbuffer)
+ read_track = (ReqCmd == READ && CURRENT_ERRORS == 0);
+ else
+ read_track = 0;
+
+ DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n",ReqSide,
+ ReqTrack, ReqSector, (unsigned long)ReqData ));
+}
+
+
+static void redo_fd_request(void)
+{
+ int device, drive, type;
+
+ DPRINT(("redo_fd_request: CURRENT=%08lx CURRENT->dev=%04x CURRENT->sector=%ld\n",
+ (unsigned long)CURRENT, CURRENT ? CURRENT->rq_dev : 0,
+ CURRENT ? CURRENT->sector : 0 ));
+
+ IsFormatting = 0;
+
+ if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
+ return;
+ }
+
+repeat:
+
+ if (!CURRENT)
+ goto the_end;
+
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+ panic(DEVICE_NAME ": request list destroyed");
+
+ if (CURRENT->bh && !buffer_locked(CURRENT->bh))
+ panic(DEVICE_NAME ": block not locked");
+
+ device = MINOR(CURRENT_DEVICE);
+ drive = device & 3;
+ type = device >> 2;
+
+ if (!UD.connected) {
+ /* drive not connected */
+ printk(KERN_ERR "Unknown Device: fd%d\n", drive );
+ end_request(0);
+ goto repeat;
+ }
+
+ if (type == 0) {
+ if (!UDT) {
+ Probing = 1;
+ UDT = disk_type + StartDiskType[DriveType];
+ floppy_sizes[drive] = UDT->blocks >> 1;
+ UD.autoprobe = 1;
+ }
+ }
+ else {
+ /* user supplied disk type */
+ if (--type >= NUM_DISK_MINORS) {
+ printk(KERN_WARNING "fd%d: invalid disk format", drive );
+ end_request( 0 );
+ goto repeat;
+ }
+ if (minor2disktype[type].drive_types > DriveType) {
+ printk(KERN_WARNING "fd%d: unsupported disk format", drive );
+ end_request( 0 );
+ goto repeat;
+ }
+ type = minor2disktype[type].index;
+ UDT = &disk_type[type];
+ floppy_sizes[drive] = UDT->blocks >> 1;
+ UD.autoprobe = 0;
+ }
+
+ if (CURRENT->sector + 1 > UDT->blocks) {
+ end_request(0);
+ goto repeat;
+ }
+
+ /* stop deselect timer */
+ del_timer( &motor_off_timer );
+
+ ReqCnt = 0;
+ ReqCmd = CURRENT->cmd;
+ ReqBlock = CURRENT->sector;
+ ReqBuffer = CURRENT->buffer;
+ setup_req_params( drive );
+ do_fd_action( drive );
+
+ return;
+
+ the_end:
+ finish_fdc();
+}
+
+
+void do_fd_request(void)
+{
+ unsigned long flags;
+
+ DPRINT(("do_fd_request for pid %d\n",current->pid));
+ while( fdc_busy ) sleep_on( &fdc_wait );
+ fdc_busy = 1;
+ stdma_lock(floppy_irq, NULL);
+
+ atari_disable_irq( IRQ_MFP_FDC );
+ save_flags(flags); /* The request function is called with ints
+ sti(); * disabled... so must save the IPL for later */
+ redo_fd_request();
+ restore_flags(flags);
+ atari_enable_irq( IRQ_MFP_FDC );
+}
+
+
+static int
+invalidate_drive (kdev_t rdev)
+{
+ /* invalidate the buffer track to force a reread */
+ BufferDrive = -1;
+ set_bit (MINOR(rdev) & 3, &fake_change);
+ check_disk_change (rdev);
+ return 0;
+}
+
+static int fd_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long param)
+{
+#define IOCTL_MODE_BIT 8
+#define OPEN_WRITE_BIT 16
+#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
+#define COPYIN(x) (copy_from_user( &(x), (void *) param, sizeof(x)))
+
+ int drive, type, error;
+ kdev_t device;
+ struct atari_format_descr fmt_desc;
+ struct atari_disk_type *dtp;
+ struct floppy_struct getprm;
+
+ device = inode->i_rdev;
+ switch (cmd) {
+ RO_IOCTLS (device, param);
+ }
+ drive = MINOR (device);
+ type = drive >> 2;
+ drive &= 3;
+ switch (cmd) {
+ case FDGETPRM:
+ if (type) {
+ if (--type >= NUM_DISK_MINORS)
+ return -ENODEV;
+ if (minor2disktype[type].drive_types > DriveType)
+ return -ENODEV;
+ type = minor2disktype[type].index;
+ dtp = &disk_type[type];
+ }
+ else {
+ if (!UDT)
+ return -ENXIO;
+ else
+ dtp = UDT;
+ }
+ error = verify_area(VERIFY_WRITE, (void *)param,
+ sizeof(struct floppy_struct));
+ if (error)
+ return( error );
+ memset((void *)&getprm, 0, sizeof(getprm));
+ getprm.size = dtp->blocks;
+ getprm.sect = dtp->spt;
+ getprm.head = 2;
+ getprm.track = dtp->blocks/dtp->spt/2;
+ getprm.stretch = dtp->stretch;
+ copy_to_user((void *)param, &getprm, sizeof(struct floppy_struct));
+ return 0;
+ }
+ if (!IOCTL_ALLOWED)
+ return -EPERM;
+ switch (cmd) {
+ case FDSETPRM:
+ case FDDEFPRM:
+ return -EINVAL;
+ case FDMSGON:
+ UD.flags |= FTD_MSG;
+ return 0;
+ case FDMSGOFF:
+ UD.flags &= ~FTD_MSG;
+ return 0;
+ case FDSETEMSGTRESH:
+ return -EINVAL;
+ case FDFMTBEG:
+ return 0;
+ case FDFMTTRK:
+ if (fd_ref[drive] != 1 && fd_ref[drive] != -1)
+ return -EBUSY;
+ if ((error = verify_area(VERIFY_READ, (void *)param,
+ sizeof(struct atari_format_descr) )))
+ return( error );
+ COPYIN( fmt_desc );
+ return do_format(device, &fmt_desc);
+ case FDCLRPRM:
+ UDT = NULL;
+ floppy_sizes[drive] = MAX_DISK_SIZE;
+ return invalidate_drive (device);
+ case FDFMTEND:
+ case FDFLUSH:
+ return invalidate_drive (drive);
+ }
+ return -EINVAL;
+}
+
+
+/* Initialize the 'unit' variable for drive 'drive' */
+
+static void fd_probe( int drive )
+{
+ UD.connected = 0;
+ UDT = NULL;
+
+ if (!fd_test_drive_present( drive ))
+ return;
+
+ UD.connected = 1;
+ UD.track = 0;
+ switch( UserSteprate[drive] ) {
+ case 2:
+ UD.steprate = FDCSTEP_2;
+ break;
+ case 3:
+ UD.steprate = FDCSTEP_3;
+ break;
+ case 6:
+ UD.steprate = FDCSTEP_6;
+ break;
+ case 12:
+ UD.steprate = FDCSTEP_12;
+ break;
+ default: /* should be -1 for "not set by user" */
+ if (ATARIHW_PRESENT( FDCSPEED ) || is_medusa)
+ UD.steprate = FDCSTEP_3;
+ else
+ UD.steprate = FDCSTEP_6;
+ break;
+ }
+ MotorOn = 1; /* from probe restore operation! */
+}
+
+
+/* This function tests the physical presence of a floppy drive (not
+ * whether a disk is inserted). This is done by issuing a restore
+ * command, waiting max. 2 seconds (that should be enough to move the
+ * head across the whole disk) and looking at the state of the "TR00"
+ * signal. This should now be raised if there is a drive connected
+ * (and there is no hardware failure :-) Otherwise, the drive is
+ * declared absent.
+ */
+
+static int fd_test_drive_present( int drive )
+{
+ unsigned long timeout;
+ unsigned char status;
+ int ok;
+
+ if (drive > 1) return( 0 );
+ fd_select_drive( drive );
+
+ /* disable interrupt temporarily */
+ atari_turnoff_irq( IRQ_MFP_FDC );
+ FDC_WRITE (FDCREG_TRACK, 0xff00);
+ FDC_WRITE( FDCREG_CMD, FDCCMD_RESTORE | FDCCMDADD_H | FDCSTEP_6 );
+
+ for( ok = 0, timeout = jiffies + 2*HZ+HZ/2; jiffies < timeout; ) {
+ if (!(mfp.par_dt_reg & 0x20))
+ break;
+ }
+
+ status = FDC_READ( FDCREG_STATUS );
+ ok = (status & FDCSTAT_TR00) != 0;
+
+ /* force interrupt to abort restore operation (FDC would try
+ * about 50 seconds!) */
+ FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
+ udelay(500);
+ status = FDC_READ( FDCREG_STATUS );
+ udelay(20);
+
+ if (ok) {
+ /* dummy seek command to make WP bit accessible */
+ FDC_WRITE( FDCREG_DATA, 0 );
+ FDC_WRITE( FDCREG_CMD, FDCCMD_SEEK );
+ while( mfp.par_dt_reg & 0x20 )
+ ;
+ status = FDC_READ( FDCREG_STATUS );
+ }
+
+ atari_turnon_irq( IRQ_MFP_FDC );
+ return( ok );
+}
+
+
+/* Look how many and which kind of drives are connected. If there are
+ * floppies, additionally start the disk-change and motor-off timers.
+ */
+
+static void config_types( void )
+{
+ int drive, cnt = 0;
+
+ /* for probing drives, set the FDC speed to 8 MHz */
+ if (ATARIHW_PRESENT(FDCSPEED))
+ dma_wd.fdc_speed = 0;
+
+ printk(KERN_INFO "Probing floppy drive(s):\n");
+ for( drive = 0; drive < FD_MAX_UNITS; drive++ ) {
+ fd_probe( drive );
+ if (UD.connected) {
+ printk(KERN_INFO "fd%d\n", drive);
+ ++cnt;
+ }
+ }
+
+ if (FDC_READ( FDCREG_STATUS ) & FDCSTAT_BUSY) {
+ /* If FDC is still busy from probing, give it another FORCI
+ * command to abort the operation. If this isn't done, the FDC
+ * will interrupt later and its IRQ line stays low, because
+ * the status register isn't read. And this will block any
+ * interrupts on this IRQ line :-(
+ */
+ FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
+ udelay(500);
+ FDC_READ( FDCREG_STATUS );
+ udelay(20);
+ }
+
+ if (cnt > 0) {
+ START_MOTOR_OFF_TIMER( FD_MOTOR_OFF_DELAY );
+ if (cnt == 1) fd_select_drive( 0 );
+ START_CHECK_CHANGE_TIMER( CHECK_CHANGE_DELAY );
+ }
+}
+
+/*
+ * floppy_open check for aliasing (/dev/fd0 can be the same as
+ * /dev/PS0 etc), and disallows simultaneous access to the same
+ * drive with different device numbers.
+ */
+
+static int floppy_open( struct inode *inode, struct file *filp )
+{
+ int drive, type;
+ int old_dev;
+
+ if (!filp)
+ {
+ DPRINT (("Weird, open called with filp=0\n"));
+ return -EIO;
+ }
+
+ drive = MINOR (inode->i_rdev) & 3;
+ type = MINOR(inode->i_rdev) >> 2;
+ DPRINT(("fd_open: type=%d\n",type));
+ if (type > NUM_DISK_MINORS)
+ return -ENXIO;
+
+ old_dev = fd_device[drive];
+
+ if (fd_ref[drive])
+ if (old_dev != inode->i_rdev)
+ return -EBUSY;
+
+ if (fd_ref[drive] == -1 || (fd_ref[drive] && filp->f_flags & O_EXCL))
+ return -EBUSY;
+
+ if (filp->f_flags & O_EXCL)
+ fd_ref[drive] = -1;
+ else
+ fd_ref[drive]++;
+
+ fd_device[drive] = inode->i_rdev;
+
+ if (old_dev && old_dev != inode->i_rdev)
+ invalidate_buffers(old_dev);
+
+ /* Allow ioctls if we have write-permissions even if read-only open */
+ if (filp->f_mode & 2 || permission (inode, 2) == 0)
+ filp->f_mode |= IOCTL_MODE_BIT;
+ if (filp->f_mode & 2)
+ filp->f_mode |= OPEN_WRITE_BIT;
+
+ MOD_INC_USE_COUNT;
+
+ if (filp->f_flags & O_NDELAY)
+ return 0;
+
+ if (filp->f_mode & 3) {
+ check_disk_change( inode->i_rdev );
+ if (filp->f_mode & 2) {
+ if (UD.wpstat) {
+ floppy_release(inode, filp);
+ return -EROFS;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static void floppy_release( struct inode * inode, struct file * filp )
+{
+ int drive;
+
+ drive = inode->i_rdev & 3;
+
+ if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+ /* if the file is mounted OR (writable now AND writable at open
+ time) Linus: Does this cover all cases? */
+ block_fsync (inode, filp);
+
+ if (fd_ref[drive] < 0)
+ fd_ref[drive] = 0;
+ else if (!fd_ref[drive]--)
+ {
+ printk(KERN_ERR "floppy_release with fd_ref == 0");
+ fd_ref[drive] = 0;
+ }
+
+ MOD_DEC_USE_COUNT;
+}
+
+static struct file_operations floppy_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ fd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ floppy_open, /* open */
+ floppy_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ check_floppy_change, /* media_change */
+ floppy_revalidate, /* revalidate */
+};
+
+int atari_floppy_init (void)
+{
+ int i;
+
+ if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
+ printk(KERN_ERR "Unable to get major %d for floppy\n",MAJOR_NR);
+ return -EBUSY;
+ }
+
+ if (UseTrackbuffer < 0)
+ /* not set by user -> use default: for now, we turn
+ track buffering off for all Medusas, though it
+ could be used with ones that have a counter
+ card. But the test is too hard :-( */
+ UseTrackbuffer = !is_medusa;
+
+ /* initialize variables */
+ SelectedDrive = -1;
+ BufferDrive = -1;
+
+ /* initialize check_change timer */
+ timer_table[FLOPPY_TIMER].fn = check_change;
+ timer_active &= ~(1 << FLOPPY_TIMER);
+
+ DMABuffer = kmalloc(BUFFER_SIZE + 512, GFP_KERNEL | GFP_DMA);
+ if (!DMABuffer) {
+ printk(KERN_ERR "atari_floppy_init: cannot get dma buffer\n");
+ unregister_blkdev(MAJOR_NR, "fd");
+ return -ENOMEM;
+ }
+ TrackBuffer = DMABuffer + 512;
+ PhysDMABuffer = (unsigned long) VTOP(DMABuffer);
+ PhysTrackBuffer = (unsigned long) VTOP(TrackBuffer);
+ BufferDrive = BufferSide = BufferTrack = -1;
+
+ for (i = 0; i < FD_MAX_UNITS; i++) {
+ unit[i].track = -1;
+ unit[i].flags = 0;
+ }
+
+ for (i = 0; i < 256; i++)
+ if ((i >> 2) > 0 && (i >> 2) <= NUM_DISK_MINORS) {
+ int type = minor2disktype[(i >> 2) - 1].index;
+ floppy_sizes[i] = disk_type[type].blocks >> 1;
+ } else
+ floppy_sizes[i] = MAX_DISK_SIZE;
+
+ blk_size[MAJOR_NR] = floppy_sizes;
+ blksize_size[MAJOR_NR] = floppy_blocksizes;
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+
+ printk(KERN_INFO "Atari floppy driver: max. %cD, %strack buffering\n",
+ DriveType == 0 ? 'D' : DriveType == 1 ? 'H' : 'E',
+ UseTrackbuffer ? "" : "no ");
+ config_types();
+
+ return 0;
+}
+
+
+void atari_floppy_setup( char *str, int *ints )
+{
+ int i;
+
+ if (ints[0] < 1) {
+ printk(KERN_ERR "ataflop_setup: no arguments!\n" );
+ return;
+ }
+ else if (ints[0] > 2+FD_MAX_UNITS) {
+ printk(KERN_ERR "ataflop_setup: too many arguments\n" );
+ }
+
+ if (ints[1] < 0 || ints[1] > 2)
+ printk(KERN_ERR "ataflop_setup: bad drive type\n" );
+ else
+ DriveType = ints[1];
+
+ if (ints[0] >= 2)
+ UseTrackbuffer = (ints[2] > 0);
+
+ for( i = 3; i <= ints[0] && i-3 < FD_MAX_UNITS; ++i ) {
+ if (ints[i] != 2 && ints[i] != 3 && ints[i] != 6 && ints[i] != 12)
+ printk(KERN_ERR "ataflop_setup: bad steprate\n" );
+ else
+ UserSteprate[i-3] = ints[i];
+ }
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ if (!MACH_IS_ATARI)
+ return -ENXIO;
+ return atari_floppy_init ();
+}
+
+void cleanup_module (void)
+{
+ unregister_blkdev(MAJOR_NR, "fd");
+
+ blk_dev[MAJOR_NR].request_fn = 0;
+ timer_active &= ~(1 << FLOPPY_TIMER);
+ timer_table[FLOPPY_TIMER].fn = 0;
+ kfree (DMABuffer);
+}
+#endif
+
diff --git a/drivers/block/aztcd.c b/drivers/block/aztcd.c
deleted file mode 100644
index 66e289452..000000000
--- a/drivers/block/aztcd.c
+++ /dev/null
@@ -1,1713 +0,0 @@
-#define AZT_VERSION "V1.0"
-/* $Id: aztcd.c,v 1.0 1995/03/25 08:27:11 root Exp $
- linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
-
- Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
-
- based on Mitsumi CDROM driver by Martin Hariss and preworks by
- Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby
- Schirmer.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- HISTORY
- V0.0 Adaption to Adaptec CD268-01A Version 1.3
- Version is PRE_ALPHA, unresolved points:
- 1. I use busy wait instead of timer wait in STEN_LOW,DTEN_LOW
- thus driver causes CPU overhead and is very slow
- 2. could not find a way to stop the drive, when it is
- in data read mode, therefore I had to set
- msf.end.min/sec/frame to 0:0:1 (in azt_poll); so only one
- frame can be read in sequence, this is also the reason for
- 3. getting 'timeout in state 4' messages, but nevertheless
- it works
- W.Zimmermann, Oct. 31, 1994
- V0.1 Version is ALPHA, problems #2 and #3 resolved.
- W.Zimmermann, Nov. 3, 1994
- V0.2 Modification to some comments, debugging aids for partial test
- with Borland C under DOS eliminated. Timer interrupt wait
- STEN_LOW_WAIT additionally to busy wait for STEN_LOW implemented;
- use it only for the 'slow' commands (ACMD_GET_Q_CHANNEL, ACMD_
- SEEK_TO_LEAD_IN), all other commands are so 'fast', that busy
- waiting seems better to me than interrupt rescheduling.
- Besides that, when used in the wrong place, STEN_LOW_WAIT causes
- kernel panic.
- In function aztPlay command ACMD_PLAY_AUDIO added, should make
- audio functions work. The Aztech drive needs different commands
- to read data tracks and play audio tracks.
- W.Zimmermann, Nov. 8, 1994
- V0.3 Recognition of missing drive during boot up improved (speeded up).
- W.Zimmermann, Nov. 13, 1994
- V0.35 Rewrote the control mechanism in azt_poll (formerly mcd_poll)
- including removal of all 'goto' commands. :-);
- J. Nardone, Nov. 14, 1994
- V0.4 Renamed variables and constants to 'azt' instead of 'mcd'; had
- to make some "compatibility" defines in azt.h; please note,
- that the source file was renamed to azt.c, the include file to
- azt.h
- Speeded up drive recognition during init (will be a little bit
- slower than before if no drive is installed!); suggested by
- Robby Schirmer.
- read_count declared volatile and set to AZT_BUF_SIZ to make
- drive faster (now 300kB/sec, was 60kB/sec before, measured
- by 'time dd if=/dev/cdrom of=/dev/null bs=2048 count=4096';
- different AZT_BUF_SIZes were test, above 16 no further im-
- provement seems to be possible; suggested by E.Moenkeberg.
- W.Zimmermann, Nov. 18, 1994
- V0.42 Included getAztStatus command in GetQChannelInfo() to allow
- reading Q-channel info on audio disks, if drive is stopped,
- and some other bug fixes in the audio stuff, suggested by
- Robby Schirmer.
- Added more ioctls (reading data in mode 1 and mode 2).
- Completely removed the old azt_poll() routine.
- Detection of ORCHID CDS-3110 in aztcd_init implemented.
- Additional debugging aids (see the readme file).
- W.Zimmermann, Dec. 9, 1994
- V0.50 Autodetection of drives implemented.
- W.Zimmermann, Dec. 12, 1994
- V0.52 Prepared for including in the standard kernel, renamed most
- variables to contain 'azt', included autoconf.h
- W.Zimmermann, Dec. 16, 1994
- V0.6 Version for being included in the standard Linux kernel.
- Renamed source and header file to aztcd.c and aztcd.h
- W.Zimmermann, Dec. 24, 1994
- V0.7 Changed VERIFY_READ to VERIFY_WRITE in aztcd_ioctl, case
- CDROMREADMODE1 and CDROMREADMODE2; bug fix in the ioctl,
- which causes kernel crashes when playing audio, changed
- include-files (config.h instead of autoconf.h, removed
- delay.h)
- W.Zimmermann, Jan. 8, 1995
- V0.72 Some more modifications for adaption to the standard kernel.
- W.Zimmermann, Jan. 16, 1995
- V0.80 aztcd is now part of the standard kernel since version 1.1.83.
- Modified the SET_TIMER and CLEAR_TIMER macros to comply with
- the new timer scheme.
- W.Zimmermann, Jan. 21, 1995
- V0.90 Included CDROMVOLCTRL, but with my Aztech drive I can only turn
- the channels on and off. If it works better with your drive,
- please mail me. Also implemented ACMD_CLOSE for CDROMSTART.
- W.Zimmermann, Jan. 24, 1995
- V1.00 Implemented close and lock tray commands. Patches supplied by
- Frank Racis
- Added support for loadable MODULEs, so aztcd can now also be
- loaded by insmod and removed by rmmod during run time
- Werner Zimmermann, Mar. 24, 95
- NOTE:
- Points marked with ??? are questionable !
-*/
-#include <linux/major.h>
-#include <linux/config.h>
-
-#ifdef MODULE
-# include <linux/module.h>
-# include <linux/version.h>
-# ifndef CONFIG_MODVERSIONS
- char kernel_version[]= UTS_RELEASE;
-# endif
-#endif
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
-#define MAJOR_NR AZTECH_CDROM_MAJOR
-
-#ifdef MODULE
-# include "/usr/src/linux/drivers/block/blk.h"
-#else
-# include "blk.h"
-# define MOD_INC_USE_COUNT
-# define MOD_DEC_USE_COUNT
-#endif
-
-#include <linux/aztcd.h>
-
-static int aztPresent = 0;
-
-#if 0
-#define AZT_TEST1 /* <int-..> */
-#define AZT_TEST2 /* do_aztcd_request */
-#define AZT_TEST3 /* AZT_S_state */
-#define AZT_TEST4 /* QUICK_LOOP-counter */
-#define AZT_TEST5 /* port(1) state */
-#define AZT_DEBUG
-#endif
-
-#define CURRENT_VALID \
- (CURRENT && MAJOR(CURRENT -> dev) == MAJOR_NR && CURRENT -> cmd == READ \
- && CURRENT -> sector != -1)
-
-#define AFL_STATUSorDATA (AFL_STATUS | AFL_DATA)
-#define AZT_BUF_SIZ 16
-
-static volatile int azt_transfer_is_active=0;
-
-static char azt_buf[2048*AZT_BUF_SIZ]; /*buffer for block size conversion*/
-#ifdef AZT_PRIVATE_IOCTLS
-static char buf[2336]; /*separate buffer for the ioctls*/
-#endif
-
-static volatile int azt_buf_bn[AZT_BUF_SIZ], azt_next_bn;
-static volatile int azt_buf_in, azt_buf_out = -1;
-static volatile int azt_error=0;
-static int azt_open_count=0;
-enum azt_state_e {
- AZT_S_IDLE, /* 0 */
- AZT_S_START, /* 1 */
- AZT_S_MODE, /* 2 */
- AZT_S_READ, /* 3 */
- AZT_S_DATA, /* 4 */
- AZT_S_STOP, /* 5 */
- AZT_S_STOPPING /* 6 */
-};
-static volatile enum azt_state_e azt_state = AZT_S_IDLE;
-#ifdef AZT_TEST3
-static volatile enum azt_state_e azt_state_old = AZT_S_STOP;
-static volatile int azt_st_old = 0;
-#endif
-
-static int azt_mode = -1;
-static int ACMD_DATA_READ= ACMD_PLAY_READ;
-static volatile int azt_read_count = 1;
-
-#define READ_TIMEOUT 3000
-
-#define azt_port aztcd /*needed for the modutils*/
-static short azt_port = AZT_BASE_ADDR;
-
-static char azt_cont = 0;
-static char azt_init_end = 0;
-
-static int AztTimeout, AztTries;
-static struct wait_queue *azt_waitq = NULL;
-static struct timer_list delay_timer = { NULL, NULL, 0, 0, NULL };
-
-static struct azt_DiskInfo DiskInfo;
-static struct azt_Toc Toc[MAX_TRACKS];
-static struct azt_Play_msf azt_Play;
-
-static int aztAudioStatus = CDROM_AUDIO_NO_STATUS;
-static char aztDiskChanged = 1;
-static char aztTocUpToDate = 0;
-
-
-static void azt_transfer(void);
-static void azt_poll(void);
-static void azt_invalidate_buffers(void);
-static void do_aztcd_request(void);
-static void azt_hsg2msf(long hsg, struct msf *msf);
-static void azt_bin2bcd(unsigned char *p);
-static int azt_bcd2bin(unsigned char bcd);
-static int aztStatus(void);
-static int getAztStatus(void);
-static int aztSendCmd(int cmd);
-static int sendAztCmd(int cmd, struct azt_Play_msf *params);
-static int aztGetQChannelInfo(struct azt_Toc *qp);
-static int aztUpdateToc(void);
-static int aztGetDiskInfo(void);
-static int aztGetToc(void);
-static int aztGetValue(unsigned char *result);
-static void aztStatTimer(void);
-static void aztCloseDoor(void);
-static void aztLockDoor(void);
-static void aztUnlockDoor(void);
-
-static unsigned char aztIndatum;
-static unsigned long aztTimeOutCount;
-
-/* Macros for the drive hardware interface handshake, these macros use
- busy waiting */
-/* Wait for OP_OK = drive answers with AFL_OP_OK after receiving a command*/
-# define OP_OK op_ok()
-void op_ok(void)
-{ aztTimeOutCount=0;
- do { aztIndatum=inb(DATA_PORT);
- aztTimeOutCount++;
- if (aztTimeOutCount>=AZT_TIMEOUT)
- { printk("aztcd: Error Wait OP_OK\n");
- break;
- }
- } while (aztIndatum!=AFL_OP_OK);
-}
-
-/* Wait for PA_OK = drive answers with AFL_PA_OK after receiving parameters*/
-# define PA_OK pa_ok()
-void pa_ok(void)
-{ aztTimeOutCount=0;
- do { aztIndatum=inb(DATA_PORT);
- aztTimeOutCount++;
- if (aztTimeOutCount>=AZT_TIMEOUT)
- { printk("aztcd: Error Wait PA_OK\n");
- break;
- }
- } while (aztIndatum!=AFL_PA_OK);
-}
-
-/* Wait for STEN=Low = handshake signal 'AFL_.._OK available or command executed*/
-# define STEN_LOW sten_low()
-void sten_low(void)
-{ aztTimeOutCount=0;
- do { aztIndatum=inb(STATUS_PORT);
- aztTimeOutCount++;
- if (aztTimeOutCount>=AZT_TIMEOUT)
- { if (azt_init_end) printk("aztcd: Error Wait STEN_LOW\n");
- break;
- }
- } while (aztIndatum&AFL_STATUS);
-}
-
-/* Wait for DTEN=Low = handshake signal 'Data available'*/
-# define DTEN_LOW dten_low()
-void dten_low(void)
-{ aztTimeOutCount=0;
- do { aztIndatum=inb(STATUS_PORT);
- aztTimeOutCount++;
- if (aztTimeOutCount>=AZT_TIMEOUT)
- { printk("aztcd: Error Wait DTEN_OK\n");
- break;
- }
- } while (aztIndatum&AFL_DATA);
-}
-
-/*
- * Macro for timer wait on STEN=Low, should only be used for 'slow' commands;
- * may cause kernel panic when used in the wrong place
-*/
-#define STEN_LOW_WAIT statusAzt()
-void statusAzt(void)
-{ AztTimeout = AZT_STATUS_DELAY;
- SET_TIMER(aztStatTimer, 1);
- sleep_on(&azt_waitq);
- if (AztTimeout <= 0) printk("aztcd: Error Wait STEN_LOW_WAIT\n");
- return;
-}
-
-static void aztStatTimer(void)
-{ if (!(inb(STATUS_PORT) & AFL_STATUS))
- { wake_up(&azt_waitq);
- return;
- }
- AztTimeout--;
- if (AztTimeout <= 0)
- { wake_up(&azt_waitq);
- return;
- }
- SET_TIMER(aztStatTimer, 1);
-}
-
-
-void aztcd_setup(char *str, int *ints)
-{ if (ints[0] > 0)
- azt_port = ints[1];
- if (ints[0] > 1)
- azt_cont = ints[2];
-}
-
-/*
- * Subroutines to automatically close the door (tray) and
- * lock it closed when the cd is mounted. Leave the tray
- * locking as an option
- */
-static void aztCloseDoor(void)
-{
- aztSendCmd(ACMD_CLOSE);
- STEN_LOW;
- return;
-}
-
-static void aztLockDoor(void)
-{
-#ifdef AZT_ALLOW_TRAY_LOCK
- aztSendCmd(ACMD_LOCK);
- STEN_LOW;
-#endif
- return;
-}
-
-static void aztUnlockDoor(void)
-{
-#ifdef AZT_ALLOW_TRAY_LOCK
- aztSendCmd(ACMD_UNLOCK);
- STEN_LOW;
-#endif
- return;
-}
-
-/*
- * Send a single command, return -1 on error, else 0
-*/
-static int aztSendCmd(int cmd)
-{ unsigned char data;
- int retry;
-
-#ifdef AZT_DEBUG
- printk("aztcd: Executing command %x\n",cmd);
-#endif
- outb(POLLED,MODE_PORT);
- do { if (inb(STATUS_PORT)&AFL_STATUS) break;
- inb(DATA_PORT); /* if status left from last command, read and */
- } while (1); /* discard it */
- do { if (inb(STATUS_PORT)&AFL_DATA) break;
- inb(DATA_PORT); /* if data left from last command, read and */
- } while (1); /* discard it */
- for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
- { outb((unsigned char) cmd,CMD_PORT);
- STEN_LOW;
- data=inb(DATA_PORT);
- if (data==AFL_OP_OK)
- { return 0;} /*OP_OK?*/
- if (data==AFL_OP_ERR)
- { STEN_LOW;
- data=inb(DATA_PORT);
- printk("### Error 1 aztcd: aztSendCmd %x Error Code %x\n",cmd,data);
- }
- }
- if (retry>=AZT_RETRY_ATTEMPTS)
- { printk("### Error 2 aztcd: aztSendCmd %x \n",cmd);
- azt_error=0xA5;
- }
- return -1;
-}
-
-/*
- * Send a play or read command to the drive, return -1 on error, else 0
-*/
-static int sendAztCmd(int cmd, struct azt_Play_msf *params)
-{ unsigned char data;
- int retry;
-
-#ifdef AZT_DEBUG
- printk("start=%02x:%02x:%02x end=%02x:%02x:%02x\n", \
- params->start.min, params->start.sec, params->start.frame, \
- params->end.min, params->end.sec, params->end.frame);
-#endif
- for (retry=0;retry<AZT_RETRY_ATTEMPTS;retry++)
- { aztSendCmd(cmd);
- outb(params -> start.min,CMD_PORT);
- outb(params -> start.sec,CMD_PORT);
- outb(params -> start.frame,CMD_PORT);
- outb(params -> end.min,CMD_PORT);
- outb(params -> end.sec,CMD_PORT);
- outb(params -> end.frame,CMD_PORT);
- STEN_LOW;
- data=inb(DATA_PORT);
- if (data==AFL_PA_OK)
- { return 0;} /*PA_OK ?*/
- if (data==AFL_PA_ERR)
- { STEN_LOW;
- data=inb(DATA_PORT);
- printk("### Error 1 aztcd: sendAztCmd %x Error Code %x\n",cmd,data);
- }
- }
- if (retry>=AZT_RETRY_ATTEMPTS)
- { printk("### Error 2 aztcd: sendAztCmd %x\n ",cmd);
- azt_error=0xA5;
- }
- return -1;
-}
-
-
-/*
- * Checking if the media has been changed not yet implemented
-*/
-static int check_aztcd_media_change(dev_t full_dev)
-{ return 0;
-}
-
-
-/* used in azt_poll to poll the status, expects another program to issue a
- * ACMD_GET_STATUS directly before
- */
-static int aztStatus(void)
-{ int st;
- int i;
-
- i = inb(STATUS_PORT) & AFL_STATUS; /* is STEN=0? ???*/
- if (!i)
- {
- st = inb(DATA_PORT) & 0xFF;
- return st;
- }
- else
- return -1;
-}
-
-/*
- * Get the drive status
- */
-static int getAztStatus(void)
-{ int st;
-
- if (aztSendCmd(ACMD_GET_STATUS)) return -1;
- STEN_LOW;
- st = inb(DATA_PORT) & 0xFF;
-#ifdef AZT_DEBUG
- printk("aztcd: Status = %x\n",st);
-#endif
- if ((st == 0xFF)||(st&AST_CMD_CHECK))
- { printk("aztcd: AST_CMD_CHECK error or no status available\n");
- return -1;
- }
-
- if (((st&AST_MODE_BITS)!=AST_BUSY) && (aztAudioStatus == CDROM_AUDIO_PLAY))
- /* XXX might be an error? look at q-channel? */
- aztAudioStatus = CDROM_AUDIO_COMPLETED;
-
- if (st & AST_DSK_CHG)
- {
- aztDiskChanged = 1;
- aztTocUpToDate = 0;
- aztAudioStatus = CDROM_AUDIO_NO_STATUS;
- }
- return st;
-}
-
-
-/*
- * Send a 'Play' command and get the status. Use only from the top half.
- */
-static int aztPlay(struct azt_Play_msf *arg)
-{ if (sendAztCmd(ACMD_PLAY_AUDIO, arg) < 0) return -1;
- return 0;
-}
-
-
-long azt_msf2hsg(struct msf *mp)
-{
-#ifdef AZT_DEBUG
- if (mp->min >=70) printk("aztcd: Error msf2hsg address Minutes\n");
- if (mp->sec >=60) printk("aztcd: Error msf2hsg address Seconds\n");
- if (mp->frame>=75) printk("aztcd: Error msf2hsg address Frames\n");
-#endif
- return azt_bcd2bin(mp -> frame)
- + azt_bcd2bin(mp -> sec) * 75
- + azt_bcd2bin(mp -> min) * 4500
- - 150;
-}
-
-static int aztcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
-{ int i, st;
- struct azt_Toc qInfo;
- struct cdrom_ti ti;
- struct cdrom_tochdr tocHdr;
- struct cdrom_msf msf;
- struct cdrom_tocentry entry;
- struct azt_Toc *tocPtr;
- struct cdrom_subchnl subchnl;
- struct cdrom_volctrl volctrl;
-
-#ifdef AZT_DEBUG
- printk("aztcd: starting aztcd_ioctl - Command:%x\n",cmd);
-#endif
- if (!ip) return -EINVAL;
- if (getAztStatus()<0) return -EIO;
- if (!aztTocUpToDate)
- { if ((i=aztUpdateToc())<0) return i; /* error reading TOC */
- }
-
- switch (cmd)
- {
- case CDROMSTART: /* Spin up the drive. Don't know, what to do,
- at least close the tray */
-#ifdef AZT_PRIVATE_IOCTLS
- if (aztSendCmd(ACMD_CLOSE)) return -1;
- STEN_LOW_WAIT;
-#endif
- break;
- case CDROMSTOP: /* Spin down the drive */
- if (aztSendCmd(ACMD_STOP)) return -1;
- STEN_LOW_WAIT;
- /* should we do anything if it fails? */
- aztAudioStatus = CDROM_AUDIO_NO_STATUS;
- break;
- case CDROMPAUSE: /* Pause the drive */
- if (aztAudioStatus != CDROM_AUDIO_PLAY) return -EINVAL;
-
- if (aztGetQChannelInfo(&qInfo) < 0)
- { /* didn't get q channel info */
- aztAudioStatus = CDROM_AUDIO_NO_STATUS;
- return 0;
- }
- azt_Play.start = qInfo.diskTime; /* remember restart point */
- if (aztSendCmd(ACMD_PAUSE)) return -1;
- STEN_LOW_WAIT;
- aztAudioStatus = CDROM_AUDIO_PAUSED;
- break;
- case CDROMRESUME: /* Play it again, Sam */
- if (aztAudioStatus != CDROM_AUDIO_PAUSED) return -EINVAL;
- /* restart the drive at the saved position. */
- i = aztPlay(&azt_Play);
- if (i < 0)
- { aztAudioStatus = CDROM_AUDIO_ERROR;
- return -EIO;
- }
- aztAudioStatus = CDROM_AUDIO_PLAY;
- break;
- case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
- st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
- if (st) return st;
- memcpy_fromfs(&ti, (void *) arg, sizeof ti);
- if (ti.cdti_trk0 < DiskInfo.first
- || ti.cdti_trk0 > DiskInfo.last
- || ti.cdti_trk1 < ti.cdti_trk0)
- { return -EINVAL;
- }
- if (ti.cdti_trk1 > DiskInfo.last)
- ti. cdti_trk1 = DiskInfo.last;
- azt_Play.start = Toc[ti.cdti_trk0].diskTime;
- azt_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
-#ifdef AZT_DEBUG
-printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
- azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
- azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
-#endif
- i = aztPlay(&azt_Play);
- if (i < 0)
- { aztAudioStatus = CDROM_AUDIO_ERROR;
- return -EIO;
- }
- aztAudioStatus = CDROM_AUDIO_PLAY;
- break;
- case CDROMPLAYMSF: /* Play starting at the given MSF address. */
-/* if (aztAudioStatus == CDROM_AUDIO_PLAY)
- { if (aztSendCmd(ACMD_STOP)) return -1;
- STEN_LOW;
- aztAudioStatus = CDROM_AUDIO_NO_STATUS;
- }
-*/
- st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
- if (st) return st;
- memcpy_fromfs(&msf, (void *) arg, sizeof msf);
- /* convert to bcd */
- azt_bin2bcd(&msf.cdmsf_min0);
- azt_bin2bcd(&msf.cdmsf_sec0);
- azt_bin2bcd(&msf.cdmsf_frame0);
- azt_bin2bcd(&msf.cdmsf_min1);
- azt_bin2bcd(&msf.cdmsf_sec1);
- azt_bin2bcd(&msf.cdmsf_frame1);
- azt_Play.start.min = msf.cdmsf_min0;
- azt_Play.start.sec = msf.cdmsf_sec0;
- azt_Play.start.frame = msf.cdmsf_frame0;
- azt_Play.end.min = msf.cdmsf_min1;
- azt_Play.end.sec = msf.cdmsf_sec1;
- azt_Play.end.frame = msf.cdmsf_frame1;
-#ifdef AZT_DEBUG
-printk("aztcd play: %02x:%02x.%02x to %02x:%02x.%02x\n",
-azt_Play.start.min, azt_Play.start.sec, azt_Play.start.frame,
-azt_Play.end.min, azt_Play.end.sec, azt_Play.end.frame);
-#endif
- i = aztPlay(&azt_Play);
- if (i < 0)
- { aztAudioStatus = CDROM_AUDIO_ERROR;
- return -EIO;
- }
- aztAudioStatus = CDROM_AUDIO_PLAY;
- break;
-
- case CDROMREADTOCHDR: /* Read the table of contents header */
- st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
- if (st) return st;
- tocHdr.cdth_trk0 = DiskInfo.first;
- tocHdr.cdth_trk1 = DiskInfo.last;
- memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
- break;
- case CDROMREADTOCENTRY: /* Read an entry in the table of contents */
- st = verify_area(VERIFY_READ, (void *) arg, sizeof entry);
- if (st) return st;
- st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
- if (st) return st;
- memcpy_fromfs(&entry, (void *) arg, sizeof entry);
- if (!aztTocUpToDate) aztGetDiskInfo();
- if (entry.cdte_track == CDROM_LEADOUT)
- tocPtr = &Toc[DiskInfo.last + 1]; /* ??? */
- else if (entry.cdte_track > DiskInfo.last
- || entry.cdte_track < DiskInfo.first)
- { return -EINVAL;
- }
- else
- tocPtr = &Toc[entry.cdte_track];
- entry.cdte_adr = tocPtr -> ctrl_addr;
- entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
- if (entry.cdte_format == CDROM_LBA)
- entry.cdte_addr.lba = azt_msf2hsg(&tocPtr -> diskTime);
- else if (entry.cdte_format == CDROM_MSF)
- { entry.cdte_addr.msf.minute = azt_bcd2bin(tocPtr -> diskTime.min);
- entry.cdte_addr.msf.second = azt_bcd2bin(tocPtr -> diskTime.sec);
- entry.cdte_addr.msf.frame = azt_bcd2bin(tocPtr -> diskTime.frame);
- }
- else
- { return -EINVAL;
- }
- memcpy_tofs((void *) arg, &entry, sizeof entry);
- break;
- case CDROMSUBCHNL: /* Get subchannel info */
- st = verify_area(VERIFY_READ, (void *) arg, sizeof subchnl);
- if (st) return st;
- st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);
- if (st) return st;
- memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
- if (aztGetQChannelInfo(&qInfo) < 0)
- return -EIO;
- subchnl.cdsc_audiostatus = aztAudioStatus;
- subchnl.cdsc_adr = qInfo.ctrl_addr;
- subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
- subchnl.cdsc_trk = azt_bcd2bin(qInfo.track);
- subchnl.cdsc_ind = azt_bcd2bin(qInfo.pointIndex);
- if (subchnl.cdsc_format == CDROM_LBA)
- { subchnl.cdsc_absaddr.lba = azt_msf2hsg(&qInfo.diskTime);
- subchnl.cdsc_reladdr.lba = azt_msf2hsg(&qInfo.trackTime);
- }
- else if (subchnl.cdsc_format == CDROM_MSF)
- { subchnl.cdsc_absaddr.msf.minute = azt_bcd2bin(qInfo.diskTime.min);
- subchnl.cdsc_absaddr.msf.second = azt_bcd2bin(qInfo.diskTime.sec);
- subchnl.cdsc_absaddr.msf.frame = azt_bcd2bin(qInfo.diskTime.frame);
- subchnl.cdsc_reladdr.msf.minute = azt_bcd2bin(qInfo.trackTime.min);
- subchnl.cdsc_reladdr.msf.second = azt_bcd2bin(qInfo.trackTime.sec);
- subchnl.cdsc_reladdr.msf.frame = azt_bcd2bin(qInfo.trackTime.frame);
- }
- else
- return -EINVAL;
- memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
- break;
- case CDROMVOLCTRL: /* Volume control
- * With my Aztech CD268-01A volume control does not work, I can only
- turn the channels on (any value !=0) or off (value==0). Maybe it
- works better with your drive */
- st=verify_area(VERIFY_READ,(void *) arg, sizeof(volctrl));
- if (st) return (st);
- memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
- azt_Play.start.min = 0x21;
- azt_Play.start.sec = 0x84;
- azt_Play.start.frame = volctrl.channel0;
- azt_Play.end.min = volctrl.channel1;
- azt_Play.end.sec = volctrl.channel2;
- azt_Play.end.frame = volctrl.channel3;
- sendAztCmd(ACMD_SET_VOLUME, &azt_Play);
- STEN_LOW_WAIT;
- break;
- case CDROMEJECT:
- aztUnlockDoor(); /* Assume user knows what they're doing */
- /* all drives can at least stop! */
- if (aztAudioStatus == CDROM_AUDIO_PLAY)
- { if (aztSendCmd(ACMD_STOP)) return -1;
- STEN_LOW_WAIT;
- }
- if (aztSendCmd(ACMD_EJECT)) return -1;
- aztAudioStatus = CDROM_AUDIO_NO_STATUS;
- break;
- case CDROMREADMODE1: /*read data in mode 1 (2048 Bytes)*/
- case CDROMREADMODE2: /*read data in mode 2 (2336 Bytes)*/
-/*Take care, the following code is not compatible with other CD-ROM drivers,
- use it at your own risk with cdplay.c. Normally it is not activated, as
- AZT_PRIVATE_IOCTLS is not defined
-*/
-#ifdef AZT_PRIVATE_IOCTLS
- { st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
- if (st) return st;
- st = verify_area(VERIFY_WRITE, (void *) arg, sizeof buf);
- if (st) return st;
- memcpy_fromfs(&msf, (void *) arg, sizeof msf);
- /* convert to bcd */
- azt_bin2bcd(&msf.cdmsf_min0);
- azt_bin2bcd(&msf.cdmsf_sec0);
- azt_bin2bcd(&msf.cdmsf_frame0);
- msf.cdmsf_min1=0;
- msf.cdmsf_sec1=0;
- msf.cdmsf_frame1=1; /*read only one frame*/
- azt_Play.start.min = msf.cdmsf_min0;
- azt_Play.start.sec = msf.cdmsf_sec0;
- azt_Play.start.frame = msf.cdmsf_frame0;
- azt_Play.end.min = msf.cdmsf_min1;
- azt_Play.end.sec = msf.cdmsf_sec1;
- azt_Play.end.frame = msf.cdmsf_frame1;
- if (cmd==CDROMREADMODE1)
- { sendAztCmd(ACMD_DATA_READ, &azt_Play);
- DTEN_LOW;
- insb(DATA_PORT,buf,2048);
- memcpy_tofs((void *) arg, &buf, 2048);
- }
- else /*CDROMREADMODE2*/
- { sendAztCmd(ACMD_DATA_READ_RAW, &azt_Play);
- DTEN_LOW;
- insb(DATA_PORT,buf,2336);
- memcpy_tofs((void *) arg, &buf, 2336);
- }
- }
-#endif /*end of incompatible code*/
- break;
- default:
- return -EINVAL;
- }
-#ifdef AZT_DEBUG
- printk("aztcd: exiting aztcd_ioctl\n");
-#endif
- return 0;
-}
-
-
-/*
- * Take care of the different block sizes between cdrom and Linux.
- * When Linux gets variable block sizes this will probably go away.
- */
-static void azt_transfer(void)
-{
-#ifdef AZT_TEST
- printk("aztcd: executing azt_transfer\n");
-#endif
- if (CURRENT_VALID) {
- while (CURRENT -> nr_sectors) {
- int bn = CURRENT -> sector / 4;
- int i;
- for (i = 0; i < AZT_BUF_SIZ && azt_buf_bn[i] != bn; ++i)
- ;
- if (i < AZT_BUF_SIZ) {
- int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
- int nr_sectors = 4 - (CURRENT -> sector & 3);
- if (azt_buf_out != i) {
- azt_buf_out = i;
- if (azt_buf_bn[i] != bn) {
- azt_buf_out = -1;
- continue;
- }
- }
- if (nr_sectors > CURRENT -> nr_sectors)
- nr_sectors = CURRENT -> nr_sectors;
- memcpy(CURRENT -> buffer, azt_buf + offs, nr_sectors * 512);
- CURRENT -> nr_sectors -= nr_sectors;
- CURRENT -> sector += nr_sectors;
- CURRENT -> buffer += nr_sectors * 512;
- } else {
- azt_buf_out = -1;
- break;
- }
- }
- }
-}
-
-
-static void do_aztcd_request(void)
-{
-#ifdef AZT_TEST
- printk(" do_aztcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors);
-#endif
- azt_transfer_is_active = 1;
- while (CURRENT_VALID) {
- if (CURRENT->bh) {
- if (!CURRENT->bh->b_lock)
- panic(DEVICE_NAME ": block not locked");
- }
- azt_transfer();
- if (CURRENT -> nr_sectors == 0) {
- end_request(1);
- } else {
- azt_buf_out = -1; /* Want to read a block not in buffer */
- if (azt_state == AZT_S_IDLE) {
- if (!aztTocUpToDate) {
- if (aztUpdateToc() < 0) {
- while (CURRENT_VALID)
- end_request(0);
- break;
- }
- }
- azt_state = AZT_S_START;
- AztTries = 5;
- SET_TIMER(azt_poll, 1);
- }
- break;
- }
- }
- azt_transfer_is_active = 0;
-#ifdef AZT_TEST2
- printk("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", \
- azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
- printk(" do_aztcd_request ends\n");
-#endif
-
-}
-
-static void azt_poll(void)
-{
- int st = 0;
- int loop_ctl = 1;
- int skip = 0;
-
- if (azt_error) { /* ???*/
- if (aztSendCmd(ACMD_GET_ERROR)) return;
- STEN_LOW;
- azt_error=inb(DATA_PORT)&0xFF;
- printk("aztcd: I/O error 0x%02x\n", azt_error);
- azt_invalidate_buffers();
-#ifdef WARN_IF_READ_FAILURE
- if (AztTries == 5)
- printk("aztcd: read of block %d failed - maybe audio disk?\n", azt_next_bn);
-#endif
- if (!AztTries--) {
- printk("aztcd: read of block %d failed, maybe audio disk? Giving up\n", azt_next_bn);
- if (azt_transfer_is_active) {
- AztTries = 0;
- loop_ctl = 0;
- }
- if (CURRENT_VALID)
- end_request(0);
- AztTries = 5;
- }
- azt_error = 0;
- azt_state = AZT_S_STOP;
- }
-
- while (loop_ctl)
- {
- loop_ctl = 0; /* each case must flip this back to 1 if we want
- to come back up here */
- switch (azt_state) {
-
- case AZT_S_IDLE:
-#ifdef AZT_TEST3
- if (azt_state!=azt_state_old) {
- azt_state_old=azt_state;
- printk("AZT_S_IDLE\n");
- }
-#endif
- return;
-
- case AZT_S_START:
-#ifdef AZT_TEST3
- if (azt_state!=azt_state_old) {
- azt_state_old=azt_state;
- printk("AZT_S_START\n");
- }
-#endif
-
- if(aztSendCmd(ACMD_GET_STATUS)) return; /*result will be checked by aztStatus() */
- azt_state = azt_mode == 1 ? AZT_S_READ : AZT_S_MODE;
- AztTimeout = 3000;
- break;
-
- case AZT_S_MODE:
-#ifdef AZT_TEST3
- if (azt_state!=azt_state_old) {
- azt_state_old=azt_state;
- printk("AZT_S_MODE\n");
- }
-#endif
- if (!skip) {
- if ((st = aztStatus()) != -1) {
- if (st & AST_DSK_CHG) {
- aztDiskChanged = 1;
- aztTocUpToDate = 0;
- azt_invalidate_buffers();
- }
- } else break;
- }
- skip = 0;
-
- if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
- aztDiskChanged = 1;
- aztTocUpToDate = 0;
- printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
- if (azt_transfer_is_active) {
- azt_state = AZT_S_START;
- loop_ctl = 1; /* goto immediately */
- break;
- }
- azt_state = AZT_S_IDLE;
- while (CURRENT_VALID)
- end_request(0);
- return;
- }
- /*???*/
- if (aztSendCmd(ACMD_SET_MODE)) return;
- outb(0x01, DATA_PORT); /*Mode 1*/
- PA_OK;
- STEN_LOW;
- if (aztSendCmd(ACMD_GET_STATUS)) return;
- azt_mode = 1;
- azt_state = AZT_S_READ;
- AztTimeout = 3000;
-
- break;
-
-
- case AZT_S_READ:
-#ifdef AZT_TEST3
- if (azt_state!=azt_state_old) {
- azt_state_old=azt_state;
- printk("AZT_S_READ\n");
- }
-#endif
- if (!skip) {
- if ((st = aztStatus()) != -1) {
- if (st & AST_DSK_CHG) {
- aztDiskChanged = 1;
- aztTocUpToDate = 0;
- azt_invalidate_buffers();
- }
- } else break;
- }
-
- skip = 0;
- if ((st & AST_DOOR_OPEN)||(st & AST_NOT_READY)) {
- aztDiskChanged = 1;
- aztTocUpToDate = 0;
- printk((st & AST_DOOR_OPEN) ? "aztcd: door open\n" : "aztcd: disk removed\n");
- if (azt_transfer_is_active) {
- azt_state = AZT_S_START;
- loop_ctl = 1;
- break;
- }
- azt_state = AZT_S_IDLE;
- while (CURRENT_VALID)
- end_request(0);
- return;
- }
-
- if (CURRENT_VALID) {
- struct azt_Play_msf msf;
- azt_next_bn = CURRENT -> sector / 4;
- azt_hsg2msf(azt_next_bn, &msf.start);
- azt_read_count=AZT_BUF_SIZ; /*??? fast, because we read ahead*/
-/* azt_read_count= CURRENT->nr_sectors; slow
-*/
- msf.end.min = 0;
- msf.end.sec = 0;
- msf.end.frame = azt_read_count ;/*Mitsumi here reads 0xffffff sectors*/
-#ifdef AZT_TEST3
- printk("---reading msf-address %x:%x:%x %x:%x:%x\n",msf.start.min,msf.start.sec,msf.start.frame,msf.end.min,msf.end.sec,msf.end.frame);
- printk("azt_next_bn:%x azt_buf_in:%x azt_buf_out:%x azt_buf_bn:%x\n", \
- azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
-#endif
- sendAztCmd(ACMD_DATA_READ, &msf);
- azt_state = AZT_S_DATA;
- AztTimeout = READ_TIMEOUT;
- } else {
- azt_state = AZT_S_STOP;
- loop_ctl = 1;
- break;
- }
-
- break;
-
-
- case AZT_S_DATA:
-#ifdef AZT_TEST3
- if (azt_state!=azt_state_old) {
- azt_state_old=azt_state;
- printk("AZT_S_DATA\n");
- }
-#endif
-
- st = inb(STATUS_PORT) & AFL_STATUSorDATA; /*???*/
-
- switch (st) {
-
- case AFL_DATA:
-#ifdef AZT_TEST3
- if (st!=azt_st_old) {
- azt_st_old=st;
- printk("---AFL_DATA st:%x\n",st);
- }
-#endif
-#ifdef WARN_IF_READ_FAILURE
- if (AztTries == 5)
- printk("aztcd: read of block %d failed - maybe audio disk?\n", azt_next_bn);
-#endif
- if (!AztTries--) {
- printk("aztcd: read of block %d failed, maybe audio disk ? Giving up\n", azt_next_bn);
- if (azt_transfer_is_active) {
- AztTries = 0;
- break;
- }
- if (CURRENT_VALID)
- end_request(0);
- AztTries = 5;
- }
- azt_state = AZT_S_START;
- AztTimeout = READ_TIMEOUT;
- loop_ctl = 1;
- break;
-
- case AFL_STATUSorDATA:
-#ifdef AZT_TEST3
- if (st!=azt_st_old) {
- azt_st_old=st;
- printk("---AFL_STATUSorDATA st:%x\n",st);
- }
-#endif
- break;
-
- default:
-#ifdef AZT_TEST3
- if (st!=azt_st_old) {
- azt_st_old=st;
- printk("---default: st:%x\n",st);
- }
-#endif
- AztTries = 5;
- if (!CURRENT_VALID && azt_buf_in == azt_buf_out) {
- azt_state = AZT_S_STOP;
- loop_ctl = 1;
- break;
- }
- if (azt_read_count<=0)
- printk("aztcd: warning - try to read 0 frames\n");
- while (azt_read_count) /*??? fast read ahead loop*/
- { azt_buf_bn[azt_buf_in] = -1;
- DTEN_LOW; /*??? unsolved problem, very
- seldom we get timeouts
- here, don't now the real
- reason. With my drive this
- sometimes also happens with
- Aztech's original driver under
- DOS. Is it a hardware bug?
- I tried to recover from such
- situations here. Zimmermann*/
- if (aztTimeOutCount>=AZT_TIMEOUT)
- { printk("read_count:%d CURRENT->nr_sectors:%ld azt_buf_in:%d\n", azt_read_count,CURRENT->nr_sectors,azt_buf_in);
- printk("azt_transfer_is_active:%x\n",azt_transfer_is_active);
- azt_read_count=0;
- azt_state = AZT_S_STOP;
- loop_ctl = 1;
- end_request(1); /*should we have here (1) or (0)? */
- }
- else
- { insb(DATA_PORT, azt_buf + 2048 * azt_buf_in, 2048);
- azt_read_count--;
-#ifdef AZT_TEST3
- printk("AZT_S_DATA; ---I've read data- read_count: %d\n",azt_read_count);
- printk("azt_next_bn:%d azt_buf_in:%d azt_buf_out:%d azt_buf_bn:%d\n", \
- azt_next_bn, azt_buf_in, azt_buf_out, azt_buf_bn[azt_buf_in]);
-#endif
- azt_buf_bn[azt_buf_in] = azt_next_bn++;
- if (azt_buf_out == -1)
- azt_buf_out = azt_buf_in;
- azt_buf_in = azt_buf_in + 1 == AZT_BUF_SIZ ? 0 : azt_buf_in + 1;
- }
- }
- if (!azt_transfer_is_active) {
- while (CURRENT_VALID) {
- azt_transfer();
- if (CURRENT -> nr_sectors == 0)
- end_request(1);
- else
- break;
- }
- }
-
- if (CURRENT_VALID
- && (CURRENT -> sector / 4 < azt_next_bn ||
- CURRENT -> sector / 4 > azt_next_bn + AZT_BUF_SIZ)) {
- azt_state = AZT_S_STOP;
- loop_ctl = 1;
- break;
- }
- AztTimeout = READ_TIMEOUT;
- if (azt_read_count==0) {
- azt_state = AZT_S_STOP; /*???*/
- loop_ctl = 1;
- break;
- }
- break;
- }
- break;
-
-
- case AZT_S_STOP:
-#ifdef AZT_TEST3
- if (azt_state!=azt_state_old) {
- azt_state_old=azt_state;
- printk("AZT_S_STOP\n");
- }
-#endif
- if (azt_read_count!=0) printk("aztcd: discard data=%x frames\n",azt_read_count); /*???*/
- while (azt_read_count!=0) {
- int i;
- if ( !(inb(STATUS_PORT) & AFL_DATA) ) {
- for (i=0; i<2048; i++) {
- inb(DATA_PORT);
- }
- }
- azt_read_count--;
- }
- if (aztSendCmd(ACMD_GET_STATUS)) return;
- azt_state = AZT_S_STOPPING;
- AztTimeout = 1000;
- break;
-
- case AZT_S_STOPPING:
-#ifdef AZT_TEST3
- if (azt_state!=azt_state_old) {
- azt_state_old=azt_state;
- printk("AZT_S_STOPPING\n");
- }
-#endif
-
- if ((st = aztStatus()) == -1 && AztTimeout)
- break;
-
- if ((st != -1) && (st & AST_DSK_CHG)) {
- aztDiskChanged = 1;
- aztTocUpToDate = 0;
- azt_invalidate_buffers();
- }
-
-
-#ifdef AZT_TEST3
- printk("CURRENT_VALID %d azt_mode %d\n",
- CURRENT_VALID, azt_mode);
-#endif
-
- if (CURRENT_VALID) {
- if (st != -1) {
- if (azt_mode == 1) {
- azt_state = AZT_S_READ;
- loop_ctl = 1;
- skip = 1;
- break;
- } else {
- azt_state = AZT_S_MODE;
- loop_ctl = 1;
- skip = 1;
- break;
- }
- } else {
- azt_state = AZT_S_START;
- AztTimeout = 1;
- }
- } else {
- azt_state = AZT_S_IDLE;
- return;
- }
- break;
-
- default:
- printk("aztcd: invalid state %d\n", azt_state);
- return;
- } /* case */
- } /* while */
-
-
- if (!AztTimeout--)
- { printk("aztcd: timeout in state %d\n", azt_state);
- azt_state = AZT_S_STOP;
- if (aztSendCmd(ACMD_STOP)) return;
- STEN_LOW_WAIT;
- };
-
- SET_TIMER(azt_poll, 1);
-}
-
-static void azt_invalidate_buffers(void)
-{ int i;
-
-#ifdef AZT_DEBUG
- printk("aztcd: executing azt_invalidate_buffers\n");
-#endif
- for (i = 0; i < AZT_BUF_SIZ; ++i)
- azt_buf_bn[i] = -1;
- azt_buf_out = -1;
-}
-
-/*
- * Open the device special file. Check that a disk is in.
- */
-int aztcd_open(struct inode *ip, struct file *fp)
-{ int st;
-
-#ifdef AZT_DEBUG
- printk("aztcd: starting aztcd_open\n");
-#endif
- if (aztPresent == 0)
- return -ENXIO; /* no hardware */
-
- if (!azt_open_count && azt_state == AZT_S_IDLE) {
-
- azt_invalidate_buffers();
-
- st = getAztStatus(); /* check drive status */
- if (st == -1)
- return -EIO; /* drive doesn't respond */
-
- if (st&AST_DOOR_OPEN)
- {
- /* close door, then get the status again. */
- aztCloseDoor();
- st = getAztStatus();
- }
-
- if ((st&AST_DOOR_OPEN)||(st&AST_NOT_READY)) /* no disk in drive or door open*/
- { /* Door should be closed, probably no disk in drive */
- printk("aztcd: no disk in drive or door open\n");
- return -EIO;
- }
-
- if (aztUpdateToc() < 0)
- return -EIO;
-
- }
- ++azt_open_count;
- MOD_INC_USE_COUNT;
- aztLockDoor();
-#ifdef AZT_DEBUG
- printk("aztcd: exiting aztcd_open\n");
-#endif
- return 0;
-}
-
-
-/*
- * On close, we flush all azt blocks from the buffer cache.
- */
-static void aztcd_release(struct inode * inode, struct file * file)
-{
-#ifdef AZT_DEBUG
- printk("aztcd: executing aztcd_release\n");
- printk("inode: %p, inode->i_rdev: %x file: %p\n",inode,inode->i_rdev,file);
-#endif
- MOD_DEC_USE_COUNT;
- if (!--azt_open_count) {
- azt_invalidate_buffers();
- sync_dev(inode->i_rdev); /*??? isn't it a read only dev?*/
- invalidate_buffers(inode -> i_rdev);
- aztUnlockDoor();
- CLEAR_TIMER;
- }
- return;
-}
-
-
-static struct file_operations azt_fops = {
- NULL, /* lseek - default */
- block_read, /* read - general block-dev read */
- block_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- aztcd_ioctl, /* ioctl */
- NULL, /* mmap */
- aztcd_open, /* open */
- aztcd_release, /* release */
- NULL, /* fsync */
- NULL, /* fasync*/
- check_aztcd_media_change, /*media change*/
- NULL /* revalidate*/
-};
-
-/*
- * Test for presence of drive and initialize it. Called at boot time.
- */
-#ifndef MODULE
-unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end)
-#else
-int init_module(void)
-#endif
-{ long int count, max_count;
- unsigned char result[50];
- int st;
-
- if (azt_port <= 0) {
- printk("aztcd: no Aztech CD-ROM Initialization");
-#ifndef MODULE
- return (mem_start);
-#else
- return -EIO;
-#endif
- }
- printk("Aztech CD-ROM Init: Aztech, Orchid, Okano, Wearnes CD-ROM Driver\n");
- printk("Aztech CD-ROM Init: (C) 1994,1995 Werner Zimmermann\n");
- printk("Aztech CD-ROM Init: DriverVersion=%s BaseAddress=0x%x \n",AZT_VERSION,azt_port);
-
- if (check_region(azt_port, 4)) {
- printk("aztcd: conflict, I/O port (%X) already used\n",
- azt_port);
-#ifndef MODULE
- return (mem_start);
-#else
- return -EIO;
-#endif
- }
-
- /* check for card */
- outb(POLLED,MODE_PORT); /*???*/
- inb(CMD_PORT);
- inb(CMD_PORT);
- outb(ACMD_GET_VERSION,CMD_PORT); /*Try to get version info*/
- STEN_LOW;
- if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK? If not, reset and try again*/
- { printk("aztcd: drive reset - please wait\n");
- for (count=0;count<50;count++)
- { inb(STATUS_PORT); /*removing all data from earlier tries*/
- inb(DATA_PORT);
- }
- outb(POLLED,MODE_PORT); /*???*/
- inb(CMD_PORT);
- inb(CMD_PORT);
- outb(ACMD_SOFT_RESET,CMD_PORT); /*send reset*/
- STEN_LOW;
- if (inb(DATA_PORT)!=AFL_OP_OK) /*OP_OK?*/
- { printk("aztcd: no AZTECH CD-ROM drive found\n");
-#ifndef MODULE
- return (mem_start);
-#else
- return -EIO;
-#endif
- }
- for (count = 0; count < AZT_TIMEOUT; count++); /* delay a bit */
- if ((st=getAztStatus())==-1)
- { printk("aztcd: Drive Status Error Status=%x\n",st);
-#ifndef MODULE
- return (mem_start);
-#else
- return -EIO;
-#endif
- }
-#ifdef AZT_DEBUG
- printk("aztcd: Status = %x\n",st);
-#endif
- outb(POLLED,MODE_PORT); /*???*/
- inb(CMD_PORT);
- inb(CMD_PORT);
- outb(ACMD_GET_VERSION,CMD_PORT); /*GetVersion*/
- STEN_LOW;
- OP_OK;
- }
- azt_init_end=1;
- STEN_LOW;
- result[0]=inb(DATA_PORT); /*reading in a null byte???*/
- for (count=1;count<50;count++) /*Reading version string*/
- { aztTimeOutCount=0; /*here we must implement STEN_LOW differently*/
- do { aztIndatum=inb(STATUS_PORT);/*because we want to exit by timeout*/
- aztTimeOutCount++;
- if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break;
- } while (aztIndatum&AFL_STATUS);
- if (aztTimeOutCount>=AZT_FAST_TIMEOUT) break; /*all chars read?*/
- result[count]=inb(DATA_PORT);
- }
- if (count>30) max_count=30; /*print max.30 chars of the version string*/
- else max_count=count;
- printk("Aztech CD-ROM Init: FirmwareVersion=");
- for (count=1;count<max_count;count++) printk("%c",result[count]);
- printk("<<<\n");
-
- if ((result[1]=='A')&&(result[2]=='Z')&&(result[3]=='T'))
- { printk("Aztech CD-ROM Init: AZTECH drive detected\n"); /*AZTECH*/
- }
- else if ((result[2]=='C')&&(result[3]=='D')&&(result[4]=='D'))
- { printk("Aztech CD-ROM Init: ORCHID or WEARNES drive detected\n"); /*ORCHID or WEARNES*/
- }
- else /*OTHERS or none*/
- { printk("Aztech CD-ROM Init: : unknown drive or firmware version detected\n");
- printk(" azt may not run stable, if you want to try anyhow,\n");
- printk(" boot with: aztcd=base_address,0x79\n");
- if ((azt_cont!=0x79))
- { printk("Aztech CD-ROM Init: FirmwareVersion=");
- for (count=1;count<5;count++) printk("%c",result[count]);
- printk("\n");
- printk("Aztech CD-ROM Init: Aborted\n");
-#ifndef MODULE
- return (mem_start);
-#else
- return -EIO;
-#endif
- }
- }
- if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0)
- {
- printk("aztcd: Unable to get major %d for Aztech CD-ROM\n",
- MAJOR_NR);
-#ifndef MODULE
- return (mem_start);
-#else
- return -EIO;
-#endif
- }
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- read_ahead[MAJOR_NR] = 4;
-
- request_region(azt_port, 4, "aztcd");
-
- azt_invalidate_buffers();
- aztPresent = 1;
- aztCloseDoor();
- printk("Aztech CD-ROM Init: End\n");
-#ifndef MODULE
- return (mem_start);
-#else
- return (0);
-#endif
-}
-
-
-static void azt_hsg2msf(long hsg, struct msf *msf)
-{ hsg += 150;
- msf -> min = hsg / 4500;
- hsg %= 4500;
- msf -> sec = hsg / 75;
- msf -> frame = hsg % 75;
-#ifdef AZT_DEBUG
- if (msf->min >=70) printk("aztcd: Error hsg2msf address Minutes\n");
- if (msf->sec >=60) printk("aztcd: Error hsg2msf address Seconds\n");
- if (msf->frame>=75) printk("aztcd: Error hsg2msf address Frames\n");
-#endif
- azt_bin2bcd(&msf -> min); /* convert to BCD */
- azt_bin2bcd(&msf -> sec);
- azt_bin2bcd(&msf -> frame);
-}
-
-
-static void azt_bin2bcd(unsigned char *p)
-{ int u, t;
-
- u = *p % 10;
- t = *p / 10;
- *p = u | (t << 4);
-}
-
-static int azt_bcd2bin(unsigned char bcd)
-{ return (bcd >> 4) * 10 + (bcd & 0xF);
-}
-
-
-
-/*
- * Read a value from the drive. Should return quickly, so a busy wait
- * is used to avoid excessive rescheduling. The read command itself must
- * be issued with aztSendCmd() directly before
- */
-static int aztGetValue(unsigned char *result)
-{ int s;
-
- STEN_LOW;
- if (aztTimeOutCount>=AZT_TIMEOUT)
- { printk("aztcd: aztGetValue timeout\n");
- return -1;
- }
- s = inb(DATA_PORT) & 0xFF;
- *result = (unsigned char) s;
- return 0;
-}
-
-
-/*
- * Read the current Q-channel info. Also used for reading the
- * table of contents.
- */
-int aztGetQChannelInfo(struct azt_Toc *qp)
-{ unsigned char notUsed;
- int st;
-
-#ifdef AZT_DEBUG
- printk("aztcd: starting aztGetQChannelInfo\n");
-#endif
- if ((st=getAztStatus())==-1) return -1;
- if (aztSendCmd(ACMD_GET_Q_CHANNEL)) return -1;
- STEN_LOW_WAIT;
- if (aztGetValue(&notUsed) <0) return -1; /*Nullbyte ein-*/
- /*lesen ???*/
- if ((st&AST_MODE_BITS)==AST_INITIAL)
- { qp->ctrl_addr=0; /* when audio stop ACMD_GET_Q_CHANNEL returns */
- qp->track=0; /* only one byte with Aztech drives */
- qp->pointIndex=0;
- qp->trackTime.min=0;
- qp->trackTime.sec=0;
- qp->trackTime.frame=0;
- qp->diskTime.min=0;
- qp->diskTime.sec=0;
- qp->diskTime.frame=0;
- return 0;
- }
- else
- { if (aztGetValue(&qp -> ctrl_addr) < 0) return -1;
- if (aztGetValue(&qp -> track) < 0) return -1;
- if (aztGetValue(&qp -> pointIndex) < 0) return -1;
- if (aztGetValue(&qp -> trackTime.min) < 0) return -1;
- if (aztGetValue(&qp -> trackTime.sec) < 0) return -1;
- if (aztGetValue(&qp -> trackTime.frame) < 0) return -1;
- if (aztGetValue(&notUsed) < 0) return -1;
- if (aztGetValue(&qp -> diskTime.min) < 0) return -1;
- if (aztGetValue(&qp -> diskTime.sec) < 0) return -1;
- if (aztGetValue(&qp -> diskTime.frame) < 0) return -1;
- }
-#ifdef AZT_DEBUG
- printk("aztcd: exiting aztGetQChannelInfo\n");
-#endif
- return 0;
-}
-
-/*
- * Read the table of contents (TOC) and TOC header if necessary
- */
-static int aztUpdateToc()
-{
-#ifdef AZT_DEBUG
- printk("aztcd: starting aztUpdateToc\n");
-#endif
- if (aztTocUpToDate)
- return 0;
-
- if (aztGetDiskInfo() < 0)
- return -EIO;
-
- if (aztGetToc() < 0)
- return -EIO;
-
- aztTocUpToDate = 1;
-#ifdef AZT_DEBUG
- printk("aztcd: exiting aztUpdateToc\n");
-#endif
- return 0;
-}
-
-
-/*
- * Read the table of contents header
- */
-static int aztGetDiskInfo()
-{ int limit;
- unsigned char test;
- struct azt_Toc qInfo;
-
-#ifdef AZT_DEBUG
- printk("aztcd: starting aztGetDiskInfo\n");
-#endif
- if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) return -1;
- STEN_LOW_WAIT;
- test=0;
- for (limit=300;limit>0;limit--)
- { if (aztGetQChannelInfo(&qInfo)<0) return -1;
- if (qInfo.pointIndex==0xA0) /*Number of FirstTrack*/
- { DiskInfo.first=qInfo.diskTime.min;
- DiskInfo.first = azt_bcd2bin(DiskInfo.first);
- test=test|0x01;
- }
- if (qInfo.pointIndex==0xA1) /*Number of LastTrack*/
- { DiskInfo.last=qInfo.diskTime.min;
- DiskInfo.last = azt_bcd2bin(DiskInfo.last);
- test=test|0x02;
- }
- if (qInfo.pointIndex==0xA2) /*DiskLength*/
- { DiskInfo.diskLength.min=qInfo.diskTime.min;
- DiskInfo.diskLength.sec=qInfo.diskTime.sec-2;
- DiskInfo.diskLength.frame=qInfo.diskTime.frame;
- test=test|0x04;
- }
- if ((qInfo.pointIndex==DiskInfo.first)&&(test&0x01)) /*StartTime of First Track*/
- { DiskInfo.firstTrack.min=qInfo.diskTime.min;
- DiskInfo.firstTrack.sec=qInfo.diskTime.sec;
- DiskInfo.firstTrack.frame=qInfo.diskTime.frame;
- test=test|0x08;
- }
- if (test==0x0F) break;
- }
-#ifdef AZT_DEBUG
-printk ("aztcd: exiting aztGetDiskInfo\n");
-printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n",
- DiskInfo.first,
- DiskInfo.last,
- DiskInfo.diskLength.min,
- DiskInfo.diskLength.sec,
- DiskInfo.diskLength.frame,
- DiskInfo.firstTrack.min,
- DiskInfo.firstTrack.sec,
- DiskInfo.firstTrack.frame);
-#endif
- if (test!=0x0F) return -1;
- return 0;
-}
-
-
-/*
- * Read the table of contents (TOC)
- */
-static int aztGetToc()
-{ int i, px;
- int limit;
- struct azt_Toc qInfo;
-
-#ifdef AZT_DEBUG
- printk("aztcd: starting aztGetToc\n");
-#endif
- for (i = 0; i < MAX_TRACKS; i++)
- Toc[i].pointIndex = 0;
-
- i = DiskInfo.last + 3;
-
-/* Is there a good reason to stop motor before TOC read?
- if (aztSendCmd(ACMD_STOP)) return -1;
- STEN_LOW_WAIT;
-*/
-
- azt_mode = 0x05;
- if (aztSendCmd(ACMD_SEEK_TO_LEADIN)) return -1; /*???*/
- STEN_LOW_WAIT;
-
- for (limit = 300; limit > 0; limit--)
- {
- if (aztGetQChannelInfo(&qInfo) < 0)
- break;
-
- px = azt_bcd2bin(qInfo.pointIndex);
- if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
- if (Toc[px].pointIndex == 0)
- {
- Toc[px] = qInfo;
- i--;
- }
-
- if (i <= 0)
- break;
- }
-
- Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
-
-#ifdef AZT_DEBUG
-printk("aztcd: exiting aztGetToc\n");
-for (i = 1; i <= DiskInfo.last+1; i++)
-printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n",
-i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
-for (i = 100; i < 103; i++)
-printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n",
-i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
-#endif
-
- return limit > 0 ? 0 : -1;
-}
-
-#ifdef MODULE
-void cleanup_module(void)
-{ if (MOD_IN_USE)
- { printk("aztcd module in use - can't remove it.\n");
- return;
- }
- if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL))
- { printk("What's that: can't unregister aztcd\n");
- return;
- }
- release_region(azt_port,4);
- printk("aztcd module released.\n");
-}
-#endif MODULE
diff --git a/drivers/block/blk.h b/drivers/block/blk.h
deleted file mode 100644
index 797c6a95e..000000000
--- a/drivers/block/blk.h
+++ /dev/null
@@ -1,333 +0,0 @@
-#ifndef _BLK_H
-#define _BLK_H
-
-#include <linux/blkdev.h>
-#include <linux/locks.h>
-#include <linux/config.h>
-
-/*
- * NR_REQUEST is the number of entries in the request-queue.
- * NOTE that writes may use only the low 2/3 of these: reads
- * take precedence.
- */
-#define NR_REQUEST 64
-
-/*
- * This is used in the elevator algorithm: Note that
- * reads always go before writes. This is natural: reads
- * are much more time-critical than writes.
- */
-#define IN_ORDER(s1,s2) \
-((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \
-((s1)->dev < (s2)->dev || (((s1)->dev == (s2)->dev && \
-(s1)->sector < (s2)->sector)))))
-
-/*
- * These will have to be changed to be aware of different buffer
- * sizes etc.. It actually needs a major cleanup.
- */
-#ifdef IDE_DRIVER
-#define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1)
-#else
-#define SECTOR_MASK (blksize_size[MAJOR_NR] && \
- blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] ? \
- ((blksize_size[MAJOR_NR][MINOR(CURRENT->dev)] >> 9) - 1) : \
- ((BLOCK_SIZE >> 9) - 1))
-#endif /* IDE_DRIVER */
-
-#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
-
-extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end);
-extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end);
-#ifdef CONFIG_AZTCD
-extern unsigned long aztcd_init(unsigned long mem_start, unsigned long mem_end);
-#endif
-#ifdef CONFIG_CDU535
-extern unsigned long sony535_init(unsigned long mem_start, unsigned long mem_end);
-#endif
-#ifdef CONFIG_BLK_DEV_HD
-extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end);
-#endif
-#ifdef CONFIG_BLK_DEV_IDE
-extern unsigned long ide_init(unsigned long mem_start, unsigned long mem_end);
-#endif
-#ifdef CONFIG_SBPCD
-extern unsigned long sbpcd_init(unsigned long, unsigned long);
-#endif CONFIG_SBPCD
-extern void set_device_ro(int dev,int flag);
-
-extern void floppy_init(void);
-#ifdef FD_MODULE
-static
-#else
-extern
-#endif
-int new_floppy_init(void);
-extern void rd_load(void);
-extern long rd_init(long mem_start, int length);
-extern int ramdisk_size;
-
-extern unsigned long xd_init(unsigned long mem_start, unsigned long mem_end);
-
-#define RO_IOCTLS(dev,where) \
- case BLKROSET: if (!suser()) return -EACCES; \
- set_device_ro((dev),get_fs_long((long *) (where))); return 0; \
- case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
- if (!__err) put_fs_long(0!=is_read_only(dev),(long *) (where)); return __err; }
-
-#if defined(MAJOR_NR) || defined(IDE_DRIVER)
-
-/*
- * Add entries as needed.
- */
-
-#ifdef IDE_DRIVER
-
-#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
-#define DEVICE_ON(device) /* nothing */
-#define DEVICE_OFF(device) /* nothing */
-
-#elif (MAJOR_NR == MEM_MAJOR)
-
-/* ram disk */
-#define DEVICE_NAME "ramdisk"
-#define DEVICE_REQUEST do_rd_request
-#define DEVICE_NR(device) ((device) & 7)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == FLOPPY_MAJOR)
-
-static void floppy_off(unsigned int nr);
-
-#define DEVICE_NAME "floppy"
-#define DEVICE_INTR do_floppy
-#define DEVICE_REQUEST do_fd_request
-#define DEVICE_NR(device) ( ((device) & 3) | (((device) & 0x80 ) >> 5 ))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
-
-#elif (MAJOR_NR == HD_MAJOR)
-
-/* harddisk: timeout is 6 seconds.. */
-#define DEVICE_NAME "harddisk"
-#define DEVICE_INTR do_hd
-#define DEVICE_TIMEOUT HD_TIMER
-#define TIMEOUT_VALUE 600
-#define DEVICE_REQUEST do_hd_request
-#define DEVICE_NR(device) (MINOR(device)>>6)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == SCSI_DISK_MAJOR)
-
-#define DEVICE_NAME "scsidisk"
-#define DEVICE_INTR do_sd
-#define TIMEOUT_VALUE 200
-#define DEVICE_REQUEST do_sd_request
-#define DEVICE_NR(device) (MINOR(device) >> 4)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
-
-#define DEVICE_NAME "scsitape"
-#define DEVICE_INTR do_st
-#define DEVICE_NR(device) (MINOR(device) & 0x7f)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
-
-#define DEVICE_NAME "CD-ROM"
-#define DEVICE_INTR do_sr
-#define DEVICE_REQUEST do_sr_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == XT_DISK_MAJOR)
-
-#define DEVICE_NAME "xt disk"
-#define DEVICE_REQUEST do_xd_request
-#define DEVICE_NR(device) (MINOR(device) >> 6)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
-
-#define DEVICE_NAME "CDU31A"
-#define DEVICE_REQUEST do_cdu31a_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
-
-#define DEVICE_NAME "Mitsumi CD-ROM"
-/* #define DEVICE_INTR do_mcd */
-#define DEVICE_REQUEST do_mcd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
-
-#define DEVICE_NAME "Aztech CD-ROM"
-#define DEVICE_REQUEST do_aztcd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
-
-#define DEVICE_NAME "SONY-CDU535"
-#define DEVICE_INTR do_cdu535
-#define DEVICE_REQUEST do_cdu535_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
-
-#define DEVICE_NAME "Matsushita CD-ROM controller #1"
-#define DEVICE_REQUEST do_sbpcd_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
-
-#define DEVICE_NAME "Matsushita CD-ROM controller #2"
-#define DEVICE_REQUEST do_sbpcd2_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
-
-#define DEVICE_NAME "Matsushita CD-ROM controller #3"
-#define DEVICE_REQUEST do_sbpcd3_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
-
-#define DEVICE_NAME "Matsushita CD-ROM controller #4"
-#define DEVICE_REQUEST do_sbpcd4_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#endif /* MAJOR_NR == whatever */
-
-#if (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)
-
-#ifndef CURRENT
-#define CURRENT (blk_dev[MAJOR_NR].current_request)
-#endif
-
-#define CURRENT_DEV DEVICE_NR(CURRENT->dev)
-
-#ifdef DEVICE_INTR
-void (*DEVICE_INTR)(void) = NULL;
-#endif
-#ifdef DEVICE_TIMEOUT
-
-#define SET_TIMER \
-((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
-(timer_active |= 1<<DEVICE_TIMEOUT))
-
-#define CLEAR_TIMER \
-timer_active &= ~(1<<DEVICE_TIMEOUT)
-
-#define SET_INTR(x) \
-if ((DEVICE_INTR = (x)) != NULL) \
- SET_TIMER; \
-else \
- CLEAR_TIMER;
-
-#else /* !DEVICE_TIMEOUT */
-
-#define SET_INTR(x) (DEVICE_INTR = (x))
-
-#endif /* DEVICE_TIMEOUT */
-
-static void (DEVICE_REQUEST)(void);
-
-#ifdef DEVICE_INTR
-#define CLEAR_INTR SET_INTR(NULL)
-#else
-#define CLEAR_INTR
-#endif
-
-#define INIT_REQUEST \
- if (!CURRENT) {\
- CLEAR_INTR; \
- return; \
- } \
- if (MAJOR(CURRENT->dev) != MAJOR_NR) \
- panic(DEVICE_NAME ": request list destroyed"); \
- if (CURRENT->bh) { \
- if (!CURRENT->bh->b_lock) \
- panic(DEVICE_NAME ": block not locked"); \
- }
-
-#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER) */
-
-/* end_request() - SCSI devices have their own version */
-
-#if ! SCSI_MAJOR(MAJOR_NR)
-
-#ifdef IDE_DRIVER
-static void end_request(byte uptodate, byte hwif) {
- struct request *req = ide_cur_rq[HWIF];
-#else
-static void end_request(int uptodate) {
- struct request *req = CURRENT;
-#endif /* IDE_DRIVER */
- struct buffer_head * bh;
-
- req->errors = 0;
- if (!uptodate) {
- printk("end_request: I/O error, dev %04lX, sector %lu\n",
- (unsigned long)req->dev, req->sector);
- req->nr_sectors--;
- req->nr_sectors &= ~SECTOR_MASK;
- req->sector += (BLOCK_SIZE / 512);
- req->sector &= ~SECTOR_MASK;
- }
-
- if ((bh = req->bh) != NULL) {
- req->bh = bh->b_reqnext;
- bh->b_reqnext = NULL;
- bh->b_uptodate = uptodate;
- if (!uptodate) bh->b_req = 0; /* So no "Weird" errors */
- unlock_buffer(bh);
- if ((bh = req->bh) != NULL) {
- req->current_nr_sectors = bh->b_size >> 9;
- if (req->nr_sectors < req->current_nr_sectors) {
- req->nr_sectors = req->current_nr_sectors;
- printk("end_request: buffer-list destroyed\n");
- }
- req->buffer = bh->b_data;
- return;
- }
- }
-#ifdef IDE_DRIVER
- ide_cur_rq[HWIF] = NULL;
-#else
- DEVICE_OFF(req->dev);
- CURRENT = req->next;
-#endif /* IDE_DRIVER */
- if (req->sem != NULL)
- up(req->sem);
- req->dev = -1;
- wake_up(&wait_for_request);
-}
-#endif /* ! SCSI_MAJOR(MAJOR_NR) */
-
-#endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
-
-#endif /* _BLK_H */
diff --git a/drivers/block/cdu31a.c b/drivers/block/cdu31a.c
deleted file mode 100644
index 06847baf1..000000000
--- a/drivers/block/cdu31a.c
+++ /dev/null
@@ -1,2966 +0,0 @@
-/*
- * Sony CDU-31A CDROM interface device driver.
- *
- * Corey Minyard (minyard@wf-rch.cirr.com)
- *
- * Colossians 3:17
- *
- * The Sony interface device driver handles Sony interface CDROM
- * drives and provides a complete block-level interface as well as an
- * ioctl() interface compatible with the Sun (as specified in
- * include/linux/cdrom.h). With this interface, CDROMs can be
- * accessed and standard audio CDs can be played back normally.
- *
- * WARNING - All autoprobes have been removed from the driver.
- * You MUST configure the CDU31A via a LILO config
- * at boot time or in lilo.conf. I have the
- * following in my lilo.conf:
- *
- * append="cdu31a=0x1f88,0,PAS"
- *
- * The first number is the I/O base address of the
- * card. The second is the interrupt (0 means none).
- * The third should be "PAS" if on a Pro-Audio
- * spectrum, or nothing if on something else.
- *
- * This interface is (unfortunately) a polled interface. This is
- * because most Sony interfaces are set up with DMA and interrupts
- * disables. Some (like mine) do not even have the capability to
- * handle interrupts or DMA. For this reason you will see a lot of
- * the following:
- *
- * retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
- * while ((retry_count > jiffies) && (! <some condition to wait for))
- * {
- * while (handle_sony_cd_attention())
- * ;
- *
- * sony_sleep();
- * }
- * if (the condition not met)
- * {
- * return an error;
- * }
- *
- * This ugly hack waits for something to happen, sleeping a little
- * between every try. it also handles attentions, which are
- * asynchronous events from the drive informing the driver that a disk
- * has been inserted, removed, etc.
- *
- * NEWS FLASH - The driver now supports interrupts but they are
- * turned off by default. Use of interrupts is highly encouraged, it
- * cuts CPU usage down to a reasonable level. I had DMA in for a while
- * but PC DMA is just too slow. Better to just insb() it.
- *
- * One thing about these drives: They talk in MSF (Minute Second Frame) format.
- * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
- * disk. The funny thing is that these are sent to the drive in BCD, but the
- * interface wants to see them in decimal. A lot of conversion goes on.
- *
- * DRIVER SPECIAL FEATURES
- * -----------------------
- *
- * This section describes features beyond the normal audio and CD-ROM
- * functions of the drive.
- *
- * 2048 byte buffer mode
- *
- * If a disk is mounted with -o block=2048, data is copied straight
- * from the drive data port to the buffer. Otherwise, the readahead
- * buffer must be involved to hold the other 1K of data when a 1K
- * block operation is done. Note that with 2048 byte blocks you
- * cannot execute files from the CD.
- *
- * XA compatibility
- *
- * The driver should support XA disks for both the CDU31A and CDU33A.
- * It does this transparently, the using program doesn't need to set it.
- *
- * Multi-Session
- *
- * A multi-session disk looks just like a normal disk to the user.
- * Just mount one normally, and all the data should be there.
- * A special thanks to Koen for help with this!
- *
- * Raw sector I/O
- *
- * Using the CDROMREADAUDIO it is possible to read raw audio and data
- * tracks. Both operations return 2352 bytes per sector. On the data
- * tracks, the first 12 bytes is not returned by the drive and the value
- * of that data is indeterminate.
- *
- *
- * Copyright (C) 1993 Corey Minyard
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-/*
- *
- * Setting up the Sony CDU31A/CDU33A drive interface card. If
- * You have another card, you are on your own.
- *
- * +----------+-----------------+----------------------+
- * | JP1 | 34 Pin Conn | |
- * | JP2 +-----------------+ |
- * | JP3 |
- * | JP4 |
- * | +--+
- * | | +-+
- * | | | | External
- * | | | | Connector
- * | | | |
- * | | +-+
- * | +--+
- * | |
- * | +--------+
- * | |
- * +------------------------------------------+
- *
- * JP1 sets the Base Address, using the following settings:
- *
- * Address Pin 1 Pin 2
- * ------- ----- -----
- * 0x320 Short Short
- * 0x330 Short Open
- * 0x340 Open Short
- * 0x360 Open Open
- *
- * JP2 and JP3 configure the DMA channel; they must be set the same.
- *
- * DMA Pin 1 Pin 2 Pin 3
- * --- ----- ----- -----
- * 1 On Off On
- * 2 Off On Off
- * 3 Off Off On
- *
- * JP4 Configures the IRQ:
- *
- * IRQ Pin 1 Pin 2 Pin 3 Pin 4
- * --- ----- ----- ----- -----
- * 3 Off Off On Off
- * 4 Off Off* Off On
- * 5 On Off Off Off
- * 6 Off On Off Off
- *
- * * The documentation states to set this for interrupt
- * 4, but I think that is a mistake.
- */
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/hdreg.h>
-#include <linux/genhd.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-#include <linux/malloc.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-#include <asm/dma.h>
-
-#include <linux/cdrom.h>
-#include <linux/cdu31a.h>
-
-#define MAJOR_NR CDU31A_CDROM_MAJOR
-#include "blk.h"
-
-#define DEBUG 0
-
-#define CDU31A_READAHEAD 64 /* 64 sector, 32kB, 16 reads read-ahead */
-#define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10
-
-/* Define the following if you have data corruption problems. */
-#undef SONY_POLL_EACH_BYTE
-
-/*
-** Edit the following data to change interrupts, DMA channels, etc.
-** Default is polled and no DMA. DMA is not recommended for double-speed
-** drives.
-*/
-static struct
-{
- unsigned short base; /* I/O Base Address */
- short int_num; /* Interrupt Number (-1 means scan for it,
- 0 means don't use) */
-} cdu31a_addresses[] =
-{
-#if 0 /* No autoconfig any more. See Note at beginning
- of this file. */
- { 0x340, 0 }, /* Standard configuration Sony Interface */
- { 0x1f88, 0 }, /* Fusion CD-16 */
- { 0x230, 0 }, /* SoundBlaster 16 card */
- { 0x360, 0 }, /* Secondary standard Sony Interface */
- { 0x320, 0 }, /* Secondary standard Sony Interface */
- { 0x330, 0 }, /* Secondary standard Sony Interface */
- { 0x634, 0 }, /* Sound FX SC400 */
- { 0x654, 0 }, /* Sound FX SC400 */
-#endif
- { 0 }
-};
-
-static int handle_sony_cd_attention(void);
-static int read_subcode(void);
-static void sony_get_toc(void);
-static int scd_open(struct inode *inode, struct file *filp);
-static void do_sony_cd_cmd(unsigned char cmd,
- unsigned char *params,
- unsigned int num_params,
- unsigned char *result_buffer,
- unsigned int *result_size);
-static void size_to_buf(unsigned int size,
- unsigned char *buf);
-
-/* Parameters for the read-ahead. */
-static unsigned int sony_next_block; /* Next 512 byte block offset */
-static unsigned int sony_blocks_left = 0; /* Number of 512 byte blocks left
- in the current read command. */
-
-
-/* The base I/O address of the Sony Interface. This is a variable (not a
- #define) so it can be easily changed via some future ioctl() */
-static unsigned short sony_cd_base_io = 0;
-
-/*
- * The following are I/O addresses of the various registers for the drive. The
- * comment for the base address also applies here.
- */
-static volatile unsigned short sony_cd_cmd_reg;
-static volatile unsigned short sony_cd_param_reg;
-static volatile unsigned short sony_cd_write_reg;
-static volatile unsigned short sony_cd_control_reg;
-static volatile unsigned short sony_cd_status_reg;
-static volatile unsigned short sony_cd_result_reg;
-static volatile unsigned short sony_cd_read_reg;
-static volatile unsigned short sony_cd_fifost_reg;
-
-
-static int sony_spun_up = 0; /* Has the drive been spun up? */
-
-static int sony_xa_mode = 0; /* Is an XA disk in the drive
- and the drive a CDU31A? */
-
-static int sony_raw_data_mode = 1; /* 1 if data tracks, 0 if audio.
- For raw data reads. */
-
-static unsigned int sony_usage = 0; /* How many processes have the
- drive open. */
-
-static int sony_pas_init = 0; /* Initialize the Pro-Audio
- Spectrum card? */
-
-static struct s_sony_session_toc *sony_toc; /* Points to the
- table of
- contents. */
-
-static int sony_toc_read = 0; /* Has the TOC been read for
- the drive? */
-
-static struct s_sony_subcode * volatile last_sony_subcode; /* Points to the last
- subcode address read */
-
-static volatile int sony_inuse = 0; /* Is the drive in use? Only one operation
- at a time allowed */
-
-static struct wait_queue * sony_wait = NULL; /* Things waiting for the drive */
-
-static struct task_struct *has_cd_task = NULL; /* The task that is currently
- using the CDROM drive, or
- NULL if none. */
-
-static int is_double_speed = 0; /* Is the drive a CDU33A? */
-
-/*
- * The audio status uses the values from read subchannel data as specified
- * in include/linux/cdrom.h.
- */
-static volatile int sony_audio_status = CDROM_AUDIO_NO_STATUS;
-
-/*
- * The following are a hack for pausing and resuming audio play. The drive
- * does not work as I would expect it, if you stop it then start it again,
- * the drive seeks back to the beginning and starts over. This holds the
- * position during a pause so a resume can restart it. It uses the
- * audio status variable above to tell if it is paused.
- */
-static unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 };
-static unsigned volatile char final_pos_msf[3] = { 0, 0, 0 };
-
-/* What IRQ is the drive using? 0 if none. */
-static int irq_used = 0;
-
-/* The interrupt handler will wake this queue up when it gets an
- interrupts. */
-static struct wait_queue *cdu31a_irq_wait = NULL;
-
-static int curr_control_reg = 0; /* Current value of the control register */
-
-/* A disk changed variable. When a disk change is detected, it will
- all be set to TRUE. As the upper layers ask for disk_changed status
- it will be cleared. */
-static char disk_changed;
-
-/* Variable for using the readahead buffer. The readahead buffer
- is used for raw sector reads and for blocksizes that are smaller
- than 2048 bytes. */
-static char *readahead_buffer = NULL; /* Used for 1024 byte blocksize. */
-static int readahead_dataleft = 0;
-static int readahead_bad = 0;
-
-
-/*
- * This routine returns 1 if the disk has been changed since the last
- * check or 0 if it hasn't.
- */
-static int
-scd_disk_change(dev_t full_dev)
-{
- int retval;
-
- retval = disk_changed;
- disk_changed = 0;
-
- return retval;
-}
-
-static inline void
-enable_interrupts(void)
-{
- curr_control_reg |= ( SONY_ATTN_INT_EN_BIT
- | SONY_RES_RDY_INT_EN_BIT
- | SONY_DATA_RDY_INT_EN_BIT);
- outb(curr_control_reg, sony_cd_control_reg);
-}
-
-static inline void
-disable_interrupts(void)
-{
- curr_control_reg &= ~( SONY_ATTN_INT_EN_BIT
- | SONY_RES_RDY_INT_EN_BIT
- | SONY_DATA_RDY_INT_EN_BIT);
- outb(curr_control_reg, sony_cd_control_reg);
-}
-
-static void
-cdu31a_interrupt(int irq, struct pt_regs *regs)
-{
- disable_interrupts();
- if (cdu31a_irq_wait != NULL)
- {
- wake_up(&cdu31a_irq_wait);
- }
- else
- {
- printk("CDU31A: Got an interrupt but nothing was waiting\n");
- }
-}
-
-/*
- * Wait a little while (used for polling the drive). If in initialization,
- * setting a timeout doesn't work, so just loop for a while.
- */
-static inline void
-sony_sleep(void)
-{
- if (irq_used <= 0)
- {
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies;
- schedule();
- }
- else /* Interrupt driven */
- {
- cli();
- enable_interrupts();
- interruptible_sleep_on(&cdu31a_irq_wait);
- sti();
- }
-}
-
-
-/*
- * The following are convenience routine to read various status and set
- * various conditions in the drive.
- */
-static inline int
-is_attention(void)
-{
- return((inb(sony_cd_status_reg) & SONY_ATTN_BIT) != 0);
-}
-
-static inline int
-is_busy(void)
-{
- return((inb(sony_cd_status_reg) & SONY_BUSY_BIT) != 0);
-}
-
-static inline int
-is_data_ready(void)
-{
- return((inb(sony_cd_status_reg) & SONY_DATA_RDY_BIT) != 0);
-}
-
-static inline int
-is_data_requested(void)
-{
- return((inb(sony_cd_status_reg) & SONY_DATA_REQUEST_BIT) != 0);
-}
-
-static inline int
-is_result_ready(void)
-{
- return((inb(sony_cd_status_reg) & SONY_RES_RDY_BIT) != 0);
-}
-
-static inline int
-is_param_write_rdy(void)
-{
- return((inb(sony_cd_fifost_reg) & SONY_PARAM_WRITE_RDY_BIT) != 0);
-}
-
-static inline int
-is_result_reg_not_empty(void)
-{
- return((inb(sony_cd_fifost_reg) & SONY_RES_REG_NOT_EMP_BIT) != 0);
-}
-
-static inline void
-reset_drive(void)
-{
- curr_control_reg = 0;
- outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg);
-}
-
-static inline void
-clear_attention(void)
-{
- outb(curr_control_reg | SONY_ATTN_CLR_BIT, sony_cd_control_reg);
-}
-
-static inline void
-clear_result_ready(void)
-{
- outb(curr_control_reg | SONY_RES_RDY_CLR_BIT, sony_cd_control_reg);
-}
-
-static inline void
-clear_data_ready(void)
-{
- outb(curr_control_reg | SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg);
-}
-
-static inline void
-clear_param_reg(void)
-{
- outb(curr_control_reg | SONY_PARAM_CLR_BIT, sony_cd_control_reg);
-}
-
-static inline unsigned char
-read_status_register(void)
-{
- return(inb(sony_cd_status_reg));
-}
-
-static inline unsigned char
-read_result_register(void)
-{
- return(inb(sony_cd_result_reg));
-}
-
-static inline unsigned char
-read_data_register(void)
-{
- return(inb(sony_cd_read_reg));
-}
-
-static inline void
-write_param(unsigned char param)
-{
- outb(param, sony_cd_param_reg);
-}
-
-static inline void
-write_cmd(unsigned char cmd)
-{
- outb(curr_control_reg | SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg);
- outb(cmd, sony_cd_cmd_reg);
-}
-
-/*
- * Set the drive parameters so the drive will auto-spin-up when a
- * disk is inserted.
- */
-static void
-set_drive_params(void)
-{
- unsigned char res_reg[12];
- unsigned int res_size;
- unsigned char params[3];
-
-
- params[0] = SONY_SD_MECH_CONTROL;
- params[1] = 0x03; /* Set auto spin up and auto eject */
- if (is_double_speed)
- {
- params[1] |= 0x04; /* Set the drive to double speed if possible */
- }
- do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
- params,
- 2,
- res_reg,
- &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk(" Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
- }
-}
-
-/*
- * This code will reset the drive and attempt to restore sane parameters.
- */
-static void
-restart_on_error(void)
-{
- unsigned char res_reg[12];
- unsigned int res_size;
- unsigned int retry_count;
-
-
- printk("cdu31a: Resetting drive on error\n");
- reset_drive();
- retry_count = jiffies + SONY_RESET_TIMEOUT;
- while ((retry_count > jiffies) && (!is_attention()))
- {
- sony_sleep();
- }
- set_drive_params();
- do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("cdu31a: Unable to spin up drive: 0x%2.2x\n", res_reg[1]);
- }
-
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + 200;
- schedule();
-
- do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("cdu31a: Unable to read TOC: 0x%2.2x\n", res_reg[1]);
- }
-}
-
-/*
- * This routine writes data to the parameter register. Since this should
- * happen fairly fast, it is polled with no OS waits between.
- */
-static int
-write_params(unsigned char *params,
- int num_params)
-{
- unsigned int retry_count;
-
-
- retry_count = SONY_READY_RETRIES;
- while ((retry_count > 0) && (!is_param_write_rdy()))
- {
- retry_count--;
- }
- if (!is_param_write_rdy())
- {
- return -EIO;
- }
-
- while (num_params > 0)
- {
- write_param(*params);
- params++;
- num_params--;
- }
-
- return 0;
-}
-
-
-/*
- * The following reads data from the command result register. It is a
- * fairly complex routine, all status info flows back through this
- * interface. The algorithm is stolen directly from the flowcharts in
- * the drive manual.
- */
-static void
-get_result(unsigned char *result_buffer,
- unsigned int *result_size)
-{
- unsigned char a, b;
- int i;
- unsigned int retry_count;
-
-
- while (handle_sony_cd_attention())
- ;
- /* Wait for the result data to be ready */
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && (is_busy() || (!(is_result_ready()))))
- {
- sony_sleep();
-
- while (handle_sony_cd_attention())
- ;
- }
- if (is_busy() || (!(is_result_ready())))
- {
-#if DEBUG
- printk("CDU31A timeout out %d\n", __LINE__);
-#endif
- result_buffer[0] = 0x20;
- result_buffer[1] = SONY_TIMEOUT_OP_ERR;
- *result_size = 2;
- return;
- }
-
- /*
- * Get the first two bytes. This determines what else needs
- * to be done.
- */
- clear_result_ready();
- a = read_result_register();
- *result_buffer = a;
- result_buffer++;
-
- /* Check for block error status result. */
- if ((a & 0xf0) == 0x50)
- {
- *result_size = 1;
- return;
- }
-
- b = read_result_register();
- *result_buffer = b;
- result_buffer++;
- *result_size = 2;
-
- /*
- * 0x20 means an error occurred. Byte 2 will have the error code.
- * Otherwise, the command succeeded, byte 2 will have the count of
- * how many more status bytes are coming.
- *
- * The result register can be read 10 bytes at a time, a wait for
- * result ready to be asserted must be done between every 10 bytes.
- */
- if ((a & 0xf0) != 0x20)
- {
- if (b > 8)
- {
- for (i=0; i<8; i++)
- {
- *result_buffer = read_result_register();
- result_buffer++;
- (*result_size)++;
- }
- b = b - 8;
-
- while (b > 10)
- {
- retry_count = SONY_READY_RETRIES;
- while ((retry_count > 0) && (!is_result_ready()))
- {
- retry_count--;
- }
- if (!is_result_ready())
- {
-#if DEBUG
- printk("CDU31A timeout out %d\n", __LINE__);
-#endif
- result_buffer[0] = 0x20;
- result_buffer[1] = SONY_TIMEOUT_OP_ERR;
- *result_size = 2;
- return;
- }
-
- clear_result_ready();
-
- for (i=0; i<10; i++)
- {
- *result_buffer = read_result_register();
- result_buffer++;
- (*result_size)++;
- }
- b = b - 10;
- }
-
- if (b > 0)
- {
- retry_count = SONY_READY_RETRIES;
- while ((retry_count > 0) && (!is_result_ready()))
- {
- retry_count--;
- }
- if (!is_result_ready())
- {
-#if DEBUG
- printk("CDU31A timeout out %d\n", __LINE__);
-#endif
- result_buffer[0] = 0x20;
- result_buffer[1] = SONY_TIMEOUT_OP_ERR;
- *result_size = 2;
- return;
- }
- }
- }
-
- while (b > 0)
- {
- *result_buffer = read_result_register();
- result_buffer++;
- (*result_size)++;
- b--;
- }
- }
-}
-
-/*
- * Do a command that does not involve data transfer. This routine must
- * be re-entrant from the same task to support being called from the
- * data operation code when an error occurs.
- */
-static void
-do_sony_cd_cmd(unsigned char cmd,
- unsigned char *params,
- unsigned int num_params,
- unsigned char *result_buffer,
- unsigned int *result_size)
-{
- unsigned int retry_count;
- int num_retries;
- int recursive_call;
-
-
- cli();
- if (current != has_cd_task) /* Allow recursive calls to this routine */
- {
- while (sony_inuse)
- {
- interruptible_sleep_on(&sony_wait);
- if (current->signal & ~current->blocked)
- {
- result_buffer[0] = 0x20;
- result_buffer[1] = SONY_SIGNAL_OP_ERR;
- *result_size = 2;
- return;
- }
- }
- sony_inuse = 1;
- has_cd_task = current;
- recursive_call = 0;
- }
- else
- {
- recursive_call = 1;
- }
- sti();
-
- num_retries = 0;
-retry_cd_operation:
-
- while (handle_sony_cd_attention())
- ;
-
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && (is_busy()))
- {
- sony_sleep();
-
- while (handle_sony_cd_attention())
- ;
- }
- if (is_busy())
- {
-#if DEBUG
- printk("CDU31A timeout out %d\n", __LINE__);
-#endif
- result_buffer[0] = 0x20;
- result_buffer[1] = SONY_TIMEOUT_OP_ERR;
- *result_size = 2;
- }
- else
- {
- clear_result_ready();
- clear_param_reg();
-
- write_params(params, num_params);
- write_cmd(cmd);
-
- get_result(result_buffer, result_size);
- }
-
- if ( ((result_buffer[0] & 0xf0) == 0x20)
- && (num_retries < MAX_CDU31A_RETRIES))
- {
- num_retries++;
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + 10; /* Wait .1 seconds on retries */
- schedule();
- goto retry_cd_operation;
- }
-
- if (!recursive_call)
- {
- has_cd_task = NULL;
- sony_inuse = 0;
- wake_up_interruptible(&sony_wait);
- }
-}
-
-
-/*
- * Handle an attention from the drive. This will return 1 if it found one
- * or 0 if not (if one is found, the caller might want to call again).
- *
- * This routine counts the number of consecutive times it is called
- * (since this is always called from a while loop until it returns
- * a 0), and returns a 0 if it happens too many times. This will help
- * prevent a lockup.
- */
-static int
-handle_sony_cd_attention(void)
-{
- unsigned char atten_code;
- static int num_consecutive_attentions = 0;
-
-
- if (is_attention())
- {
- if (num_consecutive_attentions > CDU31A_MAX_CONSECUTIVE_ATTENTIONS)
- {
- printk("cdu31a: Too many consecutive attentions: %d\n",
- num_consecutive_attentions);
- num_consecutive_attentions = 0;
- return(0);
- }
-
- clear_attention();
- atten_code = read_result_register();
-
- switch (atten_code)
- {
- /* Someone changed the CD. Mark it as changed */
- case SONY_MECH_LOADED_ATTN:
- disk_changed = 1;
- sony_toc_read = 0;
- sony_audio_status = CDROM_AUDIO_NO_STATUS;
- sony_blocks_left = 0;
- break;
-
- case SONY_SPIN_DOWN_COMPLETE_ATTN:
- /* Mark the disk as spun down. */
- sony_spun_up = 0;
- break;
-
- case SONY_AUDIO_PLAY_DONE_ATTN:
- sony_audio_status = CDROM_AUDIO_COMPLETED;
- read_subcode();
- break;
-
- case SONY_EJECT_PUSHED_ATTN:
- sony_audio_status = CDROM_AUDIO_INVALID;
- break;
-
- case SONY_LEAD_IN_ERR_ATTN:
- case SONY_LEAD_OUT_ERR_ATTN:
- case SONY_DATA_TRACK_ERR_ATTN:
- case SONY_AUDIO_PLAYBACK_ERR_ATTN:
- sony_audio_status = CDROM_AUDIO_ERROR;
- break;
- }
-
- num_consecutive_attentions++;
- return(1);
- }
-
- num_consecutive_attentions = 0;
- return(0);
-}
-
-
-/* Convert from an integer 0-99 to BCD */
-static inline unsigned int
-int_to_bcd(unsigned int val)
-{
- int retval;
-
-
- retval = (val / 10) << 4;
- retval = retval | val % 10;
- return(retval);
-}
-
-
-/* Convert from BCD to an integer from 0-99 */
-static unsigned int
-bcd_to_int(unsigned int bcd)
-{
- return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f));
-}
-
-
-/*
- * Convert a logical sector value (like the OS would want to use for
- * a block device) to an MSF format.
- */
-static void
-log_to_msf(unsigned int log, unsigned char *msf)
-{
- log = log + LOG_START_OFFSET;
- msf[0] = int_to_bcd(log / 4500);
- log = log % 4500;
- msf[1] = int_to_bcd(log / 75);
- msf[2] = int_to_bcd(log % 75);
-}
-
-
-/*
- * Convert an MSF format to a logical sector.
- */
-static unsigned int
-msf_to_log(unsigned char *msf)
-{
- unsigned int log;
-
-
- log = bcd_to_int(msf[2]);
- log += bcd_to_int(msf[1]) * 75;
- log += bcd_to_int(msf[0]) * 4500;
- log = log - LOG_START_OFFSET;
-
- return log;
-}
-
-
-/*
- * Take in integer size value and put it into a buffer like
- * the drive would want to see a number-of-sector value.
- */
-static void
-size_to_buf(unsigned int size,
- unsigned char *buf)
-{
- buf[0] = size / 65536;
- size = size % 65536;
- buf[1] = size / 256;
- buf[2] = size % 256;
-}
-
-/* Starts a read operation. Returns 0 on success and 1 on failure.
- The read operation used here allows multiple sequential sectors
- to be read and status returned for each sector. The driver will
- read the out one at a time as the requests come and abort the
- operation if the requested sector is not the next one from the
- drive. */
-static int
-start_request(unsigned int sector,
- unsigned int nsect,
- int read_nsect_only)
-{
- unsigned char params[6];
- unsigned int read_size;
- unsigned int retry_count;
-
-
- log_to_msf(sector, params);
- /* If requested, read exactly what was asked. */
- if (read_nsect_only)
- {
- read_size = nsect;
- }
- /*
- * If the full read-ahead would go beyond the end of the media, trim
- * it back to read just till the end of the media.
- */
- else if ((sector + nsect) >= sony_toc->lead_out_start_lba)
- {
- read_size = sony_toc->lead_out_start_lba - sector;
- }
- /* Read the full readahead amount. */
- else
- {
- read_size = CDU31A_READAHEAD;
- }
- size_to_buf(read_size, &params[3]);
-
- /*
- * Clear any outstanding attentions and wait for the drive to
- * complete any pending operations.
- */
- while (handle_sony_cd_attention())
- ;
-
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && (is_busy()))
- {
- sony_sleep();
-
- while (handle_sony_cd_attention())
- ;
- }
-
- if (is_busy())
- {
- printk("CDU31A: Timeout while waiting to issue command\n");
- return(1);
- }
- else
- {
- /* Issue the command */
- clear_result_ready();
- clear_param_reg();
-
- write_params(params, 6);
- write_cmd(SONY_READ_BLKERR_STAT_CMD);
-
- sony_blocks_left = read_size * 4;
- sony_next_block = sector * 4;
- readahead_dataleft = 0;
- readahead_bad = 0;
- return(0);
- }
-}
-
-/* Abort a pending read operation. Clear all the drive status and
- readahead variables. */
-static void
-abort_read(void)
-{
- unsigned char result_reg[2];
- int result_size;
- volatile int val;
-
-
- do_sony_cd_cmd(SONY_ABORT_CMD, NULL, 0, result_reg, &result_size);
- if ((result_reg[0] & 0xf0) == 0x20)
- {
- printk("CDU31A: Error aborting read, error = 0x%2.2x\n",
- result_reg[1]);
- }
-
- while (is_result_reg_not_empty())
- {
- val = read_result_register();
- }
- clear_data_ready();
- clear_result_ready();
- /* Clear out the data */
- while (is_data_requested())
- {
- val = read_data_register();
- }
-
- sony_blocks_left = 0;
- readahead_dataleft = 0;
- readahead_bad = 0;
-}
-
-/* Actually get data and status from the drive. */
-static void
-input_data(char *buffer,
- unsigned int bytesleft,
- unsigned int nblocks,
- unsigned int offset,
- unsigned int skip)
-{
- int i;
- volatile unsigned char val;
-
-
- /* If an XA disk on a CDU31A, skip the first 12 bytes of data from
- the disk. The real data is after that. */
- if (sony_xa_mode)
- {
- for(i=0; i<CD_XA_HEAD; i++)
- {
- val = read_data_register();
- }
- }
-
- clear_data_ready();
-
- if (bytesleft == 2048) /* 2048 byte direct buffer transfer */
- {
- insb(sony_cd_read_reg, buffer, 2048);
- readahead_dataleft = 0;
- }
- else
- {
- /* If the input read did not align with the beginning of the block,
- skip the necessary bytes. */
- if (skip != 0)
- {
- insb(sony_cd_read_reg, readahead_buffer, skip);
- }
-
- /* Get the data into the buffer. */
- insb(sony_cd_read_reg, &buffer[offset], bytesleft);
-
- /* Get the rest of the data into the readahead buffer at the
- proper location. */
- readahead_dataleft = (2048 - skip) - bytesleft;
- insb(sony_cd_read_reg,
- readahead_buffer + bytesleft,
- readahead_dataleft);
- }
- sony_blocks_left -= nblocks;
- sony_next_block += nblocks;
-
- /* If an XA disk, we have to clear out the rest of the unused
- error correction data. */
- if (sony_xa_mode)
- {
- for(i=0; i<CD_XA_TAIL; i++)
- {
- val = read_data_register();
- }
- }
-}
-
-/* read data from the drive. Note the nsect must be <= 4. */
-static void
-read_data_block(char *buffer,
- unsigned int block,
- unsigned int nblocks,
- unsigned char res_reg[],
- int *res_size)
-{
- unsigned int retry_count;
- unsigned int bytesleft;
- unsigned int offset;
- unsigned int skip;
-
-
- res_reg[0] = 0;
- res_reg[1] = 0;
- *res_size = 0;
- bytesleft = nblocks * 512;
- offset = 0;
-
- /* If the data in the read-ahead does not match the block offset,
- then fix things up. */
- if (((block % 4) * 512) != ((2048 - readahead_dataleft) % 2048))
- {
- sony_next_block += block % 4;
- sony_blocks_left -= block % 4;
- skip = (block % 4) * 512;
- }
- else
- {
- skip = 0;
- }
-
- /* We have readahead data in the buffer, get that first before we
- decide if a read is necessary. */
- if (readahead_dataleft != 0)
- {
- if (bytesleft > readahead_dataleft)
- {
- /* The readahead will not fill the requested buffer, but
- get the data out of the readahead into the buffer. */
- memcpy(buffer,
- readahead_buffer + (2048 - readahead_dataleft),
- readahead_dataleft);
- readahead_dataleft = 0;
- bytesleft -= readahead_dataleft;
- offset += readahead_dataleft;
- }
- else
- {
- /* The readahead will fill the whole buffer, get the data
- and return. */
- memcpy(buffer,
- readahead_buffer + (2048 - readahead_dataleft),
- bytesleft);
- readahead_dataleft -= bytesleft;
- bytesleft = 0;
- sony_blocks_left -= nblocks;
- sony_next_block += nblocks;
-
- /* If the data in the readahead is bad, return an error so the
- driver will abort the buffer. */
- if (readahead_bad)
- {
- res_reg[0] = 0x20;
- res_reg[1] = SONY_BAD_DATA_ERR;
- *res_size = 2;
- }
-
- if (readahead_dataleft == 0)
- {
- readahead_bad = 0;
- }
-
- /* Final transfer is done for read command, get final result. */
- if (sony_blocks_left == 0)
- {
- get_result(res_reg, res_size);
- }
- return;
- }
- }
-
- /* Wait for the drive to tell us we have something */
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && !(is_data_ready()))
- {
- while (handle_sony_cd_attention())
- ;
-
- sony_sleep();
- }
- if (!(is_data_ready()))
- {
- if (is_result_ready())
- {
- get_result(res_reg, res_size);
- if ((res_reg[0] & 0xf0) != 0x20)
- {
- printk("CDU31A: Got result that should have been error: %d\n",
- res_reg[0]);
- res_reg[0] = 0x20;
- res_reg[1] = SONY_BAD_DATA_ERR;
- *res_size = 2;
- }
- abort_read();
- }
- else
- {
-#if DEBUG
- printk("CDU31A timeout out %d\n", __LINE__);
-#endif
- res_reg[0] = 0x20;
- res_reg[1] = SONY_TIMEOUT_OP_ERR;
- *res_size = 2;
- abort_read();
- }
- }
- else
- {
- input_data(buffer, bytesleft, nblocks, offset, skip);
-
- /* Wait for the status from the drive. */
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && !(is_result_ready()))
- {
- while (handle_sony_cd_attention())
- ;
-
- sony_sleep();
- }
-
- if (!is_result_ready())
- {
-#if DEBUG
- printk("CDU31A timeout out %d\n", __LINE__);
-#endif
- res_reg[0] = 0x20;
- res_reg[1] = SONY_TIMEOUT_OP_ERR;
- *res_size = 2;
- abort_read();
- }
- else
- {
- get_result(res_reg, res_size);
-
- /* If we got a buffer status, handle that. */
- if ((res_reg[0] & 0xf0) == 0x50)
- {
-
- if ( (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT)
- || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT)
- || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT))
- {
- /* The data was successful, but if data was read from
- the readahead and it was bad, set the whole
- buffer as bad. */
- if (readahead_bad)
- {
- readahead_bad = 0;
- res_reg[0] = 0x20;
- res_reg[1] = SONY_BAD_DATA_ERR;
- *res_size = 2;
- }
- }
- else
- {
- printk("CDU31A: Data block error: 0x%x\n", res_reg[0]);
- res_reg[0] = 0x20;
- res_reg[1] = SONY_BAD_DATA_ERR;
- *res_size = 2;
-
- /* Data is in the readahead buffer but an error was returned.
- Make sure future requests don't use the data. */
- if (bytesleft != 2048)
- {
- readahead_bad = 1;
- }
- }
-
- /* Final transfer is done for read command, get final result. */
- if (sony_blocks_left == 0)
- {
- get_result(res_reg, res_size);
- }
- }
- else if ((res_reg[0] & 0xf0) != 0x20)
- {
- /* The drive gave me bad status, I don't know what to do.
- Reset the driver and return an error. */
- printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]);
- restart_on_error();
- res_reg[0] = 0x20;
- res_reg[1] = SONY_BAD_DATA_ERR;
- *res_size = 2;
- }
- }
- }
-}
-
-/*
- * The OS calls this to perform a read or write operation to the drive.
- * Write obviously fail. Reads to a read ahead of sony_buffer_size
- * bytes to help speed operations. This especially helps since the OS
- * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most
- * data access on a CD is done sequentially, this saves a lot of operations.
- */
-static void
-do_cdu31a_request(void)
-{
- int block;
- int nblock;
- unsigned char res_reg[12];
- unsigned int res_size;
- int num_retries;
-
-
- /*
- * Make sure no one else is using the driver; wait for them
- * to finish if it is so.
- */
- cli();
- while (sony_inuse)
- {
- interruptible_sleep_on(&sony_wait);
- if (current->signal & ~current->blocked)
- {
- return;
- }
- }
- sony_inuse = 1;
- has_cd_task = current;
- sti();
-
- /* Get drive status before doing anything. */
- while (handle_sony_cd_attention())
- ;
-
- while (1)
- {
-cdu31a_request_startover:
- /*
- * The beginning here is stolen from the hard disk driver. I hope
- * it's right.
- */
- if (!(CURRENT) || CURRENT->dev < 0)
- {
- goto end_do_cdu31a_request;
- }
-
- if (!sony_spun_up)
- {
- struct inode in;
-
- /* This is a kludge to get a valid dev in an inode that
- scd_open can take. That's the only thing scd_open()
- uses the inode for. */
- in.i_rdev = CURRENT->dev;
- scd_open(&in,NULL);
- }
-
- /* I don't use INIT_REQUEST because it calls return, which would
- return without unlocking the device. It shouldn't matter,
- but just to be safe... */
- if (MAJOR(CURRENT->dev) != MAJOR_NR)
- {
- panic(DEVICE_NAME ": request list destroyed");
- }
- if (CURRENT->bh)
- {
- if (!CURRENT->bh->b_lock)
- {
- panic(DEVICE_NAME ": block not locked");
- }
- }
-
- block = CURRENT->sector;
- nblock = CURRENT->nr_sectors;
-
- if (!sony_toc_read)
- {
- printk("CDU31A: TOC not read\n");
- end_request(0);
- goto cdu31a_request_startover;
- }
-
- /* Check for base read of multi-session disk. This will still work
- for single session disks, so just do it. Blocks less than 80
- are for the volume info, so offset them by the start track (which
- should be zero for a single-session disk). */
- if (block < 80)
- {
- /* Offset the request into the session. */
- block += (sony_toc->start_track_lba * 4);
- }
-
- switch(CURRENT->cmd)
- {
- case READ:
- /*
- * If the block address is invalid or the request goes beyond the end of
- * the media, return an error.
- */
-#if 0
- if ((block / 4) < sony_toc->start_track_lba)
- {
- printk("CDU31A: Request before beginning of media\n");
- end_request(0);
- goto cdu31a_request_startover;
- }
-#endif
- if ((block / 4) >= sony_toc->lead_out_start_lba)
- {
- printk("CDU31A: Request past end of media\n");
- end_request(0);
- goto cdu31a_request_startover;
- }
- if (((block + nblock) / 4) >= sony_toc->lead_out_start_lba)
- {
- printk("CDU31A: Request past end of media\n");
- end_request(0);
- goto cdu31a_request_startover;
- }
-
- num_retries = 0;
-
-try_read_again:
- while (handle_sony_cd_attention())
- ;
-
- if (!sony_toc_read)
- {
- printk("CDU31A: TOC not read\n");
- end_request(0);
- goto cdu31a_request_startover;
- }
-
- /* If no data is left to be read from the drive, start the
- next request. */
- if (sony_blocks_left == 0)
- {
- if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
- {
- end_request(0);
- goto cdu31a_request_startover;
- }
- }
- /* If the requested block is not the next one waiting in
- the driver, abort the current operation and start a
- new one. */
- else if (block != sony_next_block)
- {
-#if DEBUG
- printk("CDU31A Warning: Read for block %d, expected %d\n",
- block,
- sony_next_block);
-#endif
- abort_read();
- if (!sony_toc_read)
- {
- printk("CDU31A: TOC not read\n");
- end_request(0);
- goto cdu31a_request_startover;
- }
- if (start_request(block / 4, CDU31A_READAHEAD / 4, 0))
- {
- end_request(0);
- goto cdu31a_request_startover;
- }
- }
-
- read_data_block(CURRENT->buffer, block, nblock, res_reg, &res_size);
- if (res_reg[0] == 0x20)
- {
- if (num_retries > MAX_CDU31A_RETRIES)
- {
- end_request(0);
- goto cdu31a_request_startover;
- }
-
- num_retries++;
- if (res_reg[1] == SONY_NOT_SPIN_ERR)
- {
- do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
- }
- else
- {
- printk("CDU31A: Read error: 0x%2.2x\n", res_reg[1]);
- }
- goto try_read_again;
- }
- else
- {
- end_request(1);
- }
- break;
-
- case WRITE:
- end_request(0);
- break;
-
- default:
- panic("CDU31A: Unknown cmd");
- }
- }
-
-end_do_cdu31a_request:
-#if 1
- /* After finished, cancel any pending operations. */
- abort_read();
-#endif
-
- has_cd_task = NULL;
- sony_inuse = 0;
- wake_up_interruptible(&sony_wait);
-}
-
-/* Copy overlapping buffers. */
-static void
-mcovlp(char *dst,
- char *src,
- int size)
-{
- src += (size - 1);
- dst += (size - 1);
- while (size > 0)
- {
- *dst = *src;
- size--;
- dst--;
- src--;
- }
-}
-
-
-/*
- * Read the table of contents from the drive and set up TOC if
- * successful.
- */
-static void
-sony_get_toc(void)
-{
- unsigned char res_reg[2];
- unsigned int res_size;
- unsigned char parms[1];
- int session;
-
-
-#if DEBUG
- printk("Entering sony_get_toc\n");
-#endif
-
- if (!sony_toc_read)
- {
- /* The idea here is we keep asking for sessions until the command
- fails. Then we know what the last valid session on the disk is.
- No need to check session 0, since session 0 is the same as session
- 1; the command returns different information if you give it 0.
- Don't check session 1 because that is the first session, it must
- be there. */
- session = 2;
- while (1)
- {
-#if DEBUG
- printk("Trying session %d\n", session);
-#endif
- parms[0] = session;
- do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD,
- parms,
- 1,
- res_reg,
- &res_size);
-
-#if DEBUG
- printk("%2.2x %2.2x\n", res_reg[0], res_reg[1]);
-#endif
-
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- /* An error reading the TOC, this must be past the last session. */
- break;
- }
-
- session++;
-
- /* Let's not get carried away... */
- if (session > 20)
- {
- return;
- }
- }
-
- session--;
-
-#if DEBUG
- printk("Reading session %d\n", session);
-#endif
-
- parms[0] = session;
- do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
- parms,
- 1,
- (unsigned char *) sony_toc,
- &res_size);
- if ((res_size < 2) || ((sony_toc->exec_status[0] & 0xf0) == 0x20))
- {
- /* An error reading the TOC. Return without sony_toc_read
- set. */
- return;
- }
-
- sony_toc_read = 1;
-
- /* For points that do not exist, move the data over them
- to the right location. */
- if (sony_toc->pointb0 != 0xb0)
- {
- mcovlp(((char *) sony_toc) + 27,
- ((char *) sony_toc) + 18,
- res_size - 18);
- res_size += 9;
- }
- if (sony_toc->pointb1 != 0xb1)
- {
- mcovlp(((char *) sony_toc) + 36,
- ((char *) sony_toc) + 27,
- res_size - 27);
- res_size += 9;
- }
- if (sony_toc->pointb2 != 0xb2)
- {
- mcovlp(((char *) sony_toc) + 45,
- ((char *) sony_toc) + 36,
- res_size - 36);
- res_size += 9;
- }
- if (sony_toc->pointb3 != 0xb3)
- {
- mcovlp(((char *) sony_toc) + 54,
- ((char *) sony_toc) + 45,
- res_size - 45);
- res_size += 9;
- }
- if (sony_toc->pointb4 != 0xb4)
- {
- mcovlp(((char *) sony_toc) + 63,
- ((char *) sony_toc) + 54,
- res_size - 54);
- res_size += 9;
- }
- if (sony_toc->pointc0 != 0xc0)
- {
- mcovlp(((char *) sony_toc) + 72,
- ((char *) sony_toc) + 63,
- res_size - 63);
- res_size += 9;
- }
-
- sony_toc->start_track_lba = msf_to_log(sony_toc->tracks[0].track_start_msf);
- sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
-
-#if DEBUG
- printk("Disk session %d, start track: %d, stop track: %d\n",
- session,
- sony_toc->start_track_lba,
- sony_toc->lead_out_start_lba);
-#endif
- }
-#if DEBUG
- printk("Leaving sony_get_toc\n");
-#endif
-}
-
-
-/*
- * Search for a specific track in the table of contents.
- */
-static int
-find_track(int track)
-{
- int i;
- int num_tracks;
-
-
- num_tracks = sony_toc->last_track_num - sony_toc->first_track_num + 1;
- for (i = 0; i < num_tracks; i++)
- {
- if (sony_toc->tracks[i].track == track)
- {
- return i;
- }
- }
-
- return -1;
-}
-
-
-/*
- * Read the subcode and put it int last_sony_subcode for future use.
- */
-static int
-read_subcode(void)
-{
- unsigned int res_size;
-
-
- do_sony_cd_cmd(SONY_REQ_SUBCODE_ADDRESS_CMD,
- NULL,
- 0,
- (unsigned char *) last_sony_subcode,
- &res_size);
- if ((res_size < 2) || ((last_sony_subcode->exec_status[0] & 0xf0) == 0x20))
- {
- printk("Sony CDROM error 0x%2.2x (read_subcode)\n",
- last_sony_subcode->exec_status[1]);
- return -EIO;
- }
-
- return 0;
-}
-
-
-/*
- * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If
- * the drive is playing, the subchannel needs to be read (since it would be
- * changing). If the drive is paused or completed, the subcode information has
- * already been stored, just use that. The ioctl call wants things in decimal
- * (not BCD), so all the conversions are done.
- */
-static int
-sony_get_subchnl_info(long arg)
-{
- struct cdrom_subchnl schi;
-
-
- /* Get attention stuff */
- while (handle_sony_cd_attention())
- ;
-
- sony_get_toc();
- if (!sony_toc_read)
- {
- return -EIO;
- }
-
- verify_area(VERIFY_READ, (char *) arg, sizeof(schi));
- verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
-
- memcpy_fromfs(&schi, (char *) arg, sizeof(schi));
-
- switch (sony_audio_status)
- {
- case CDROM_AUDIO_PLAY:
- if (read_subcode() < 0)
- {
- return -EIO;
- }
- break;
-
- case CDROM_AUDIO_PAUSED:
- case CDROM_AUDIO_COMPLETED:
- break;
-
- case CDROM_AUDIO_NO_STATUS:
- schi.cdsc_audiostatus = sony_audio_status;
- memcpy_tofs((char *) arg, &schi, sizeof(schi));
- return 0;
- break;
-
- case CDROM_AUDIO_INVALID:
- case CDROM_AUDIO_ERROR:
- default:
- return -EIO;
- }
-
- schi.cdsc_audiostatus = sony_audio_status;
- schi.cdsc_adr = last_sony_subcode->address;
- schi.cdsc_ctrl = last_sony_subcode->control;
- schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
- schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
- if (schi.cdsc_format == CDROM_MSF)
- {
- schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
- schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
- schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
-
- schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
- schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
- schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
- }
- else if (schi.cdsc_format == CDROM_LBA)
- {
- schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
- schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
- }
-
- memcpy_tofs((char *) arg, &schi, sizeof(schi));
- return 0;
-}
-
-/* Get audio data from the drive. This is fairly complex because I
- am looking for status and data at the same time, but if I get status
- then I just look for data. I need to get the status immediately so
- the switch from audio to data tracks will happen quickly. */
-static void
-read_audio_data(char *buffer,
- unsigned char res_reg[],
- int *res_size)
-{
- unsigned int retry_count;
- int result_read;
-
-
- res_reg[0] = 0;
- res_reg[1] = 0;
- *res_size = 0;
- result_read = 0;
-
- /* Wait for the drive to tell us we have something */
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
-continue_read_audio_wait:
- while ( (retry_count > jiffies)
- && !(is_data_ready())
- && !(is_result_ready() || result_read))
- {
- while (handle_sony_cd_attention())
- ;
-
- sony_sleep();
- }
- if (!(is_data_ready()))
- {
- if (is_result_ready() && !result_read)
- {
- get_result(res_reg, res_size);
-
- /* Read block status and continue waiting for data. */
- if ((res_reg[0] & 0xf0) == 0x50)
- {
- result_read = 1;
- goto continue_read_audio_wait;
- }
- /* Invalid data from the drive. Shut down the operation. */
- else if ((res_reg[0] & 0xf0) != 0x20)
- {
- printk("CDU31A: Got result that should have been error: %d\n",
- res_reg[0]);
- res_reg[0] = 0x20;
- res_reg[1] = SONY_BAD_DATA_ERR;
- *res_size = 2;
- }
- abort_read();
- }
- else
- {
-#if DEBUG
- printk("CDU31A timeout out %d\n", __LINE__);
-#endif
- res_reg[0] = 0x20;
- res_reg[1] = SONY_TIMEOUT_OP_ERR;
- *res_size = 2;
- abort_read();
- }
- }
- else
- {
- clear_data_ready();
-
- /* If data block, then get 2340 bytes offset by 12. */
- if (sony_raw_data_mode)
- {
- insb(sony_cd_read_reg, buffer + CD_XA_HEAD, CD_FRAMESIZE_XA);
- }
- else
- {
- /* Audio gets the whole 2352 bytes. */
- insb(sony_cd_read_reg, buffer, CD_FRAMESIZE_RAW);
- }
-
- /* If I haven't already gotten the result, get it now. */
- if (!result_read)
- {
- /* Wait for the drive to tell us we have something */
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while ((retry_count > jiffies) && !(is_result_ready()))
- {
- while (handle_sony_cd_attention())
- ;
-
- sony_sleep();
- }
-
- if (!is_result_ready())
- {
-#if DEBUG
- printk("CDU31A timeout out %d\n", __LINE__);
-#endif
- res_reg[0] = 0x20;
- res_reg[1] = SONY_TIMEOUT_OP_ERR;
- *res_size = 2;
- abort_read();
- return;
- }
- else
- {
- get_result(res_reg, res_size);
- }
- }
-
- if ((res_reg[0] & 0xf0) == 0x50)
- {
- if ( (res_reg[0] == SONY_NO_CIRC_ERR_BLK_STAT)
- || (res_reg[0] == SONY_NO_LECC_ERR_BLK_STAT)
- || (res_reg[0] == SONY_RECOV_LECC_ERR_BLK_STAT)
- || (res_reg[0] == SONY_NO_ERR_DETECTION_STAT))
- {
- /* Ok, nothing to do. */
- }
- else
- {
- printk("CDU31A: Data block error: 0x%x\n", res_reg[0]);
- res_reg[0] = 0x20;
- res_reg[1] = SONY_BAD_DATA_ERR;
- *res_size = 2;
- }
- }
- else if ((res_reg[0] & 0xf0) != 0x20)
- {
- /* The drive gave me bad status, I don't know what to do.
- Reset the driver and return an error. */
- printk("CDU31A: Invalid block status: 0x%x\n", res_reg[0]);
- restart_on_error();
- res_reg[0] = 0x20;
- res_reg[1] = SONY_BAD_DATA_ERR;
- *res_size = 2;
- }
- }
-}
-
-/* Perform a raw data read. This will automatically detect the
- track type and read the proper data (audio or data). */
-static int
-read_audio(struct cdrom_read_audio *ra,
- struct inode *inode)
-{
- int retval;
- unsigned char params[2];
- unsigned char res_reg[12];
- unsigned int res_size;
- unsigned int cframe;
-
- /*
- * Make sure no one else is using the driver; wait for them
- * to finish if it is so.
- */
- cli();
- while (sony_inuse)
- {
- interruptible_sleep_on(&sony_wait);
- if (current->signal & ~current->blocked)
- {
- return -EAGAIN;
- }
- }
- sony_inuse = 1;
- has_cd_task = current;
- sti();
-
- if (!sony_spun_up)
- {
- scd_open (inode, NULL);
- }
-
- /* Set the drive to do raw operations. */
- params[0] = SONY_SD_DECODE_PARAM;
- params[1] = 0x06 | sony_raw_data_mode;
- do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
- params,
- 2,
- res_reg,
- &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]);
- return -EIO;
- }
-
- /* From here down, we have to goto exit_read_audio instead of returning
- because the drive parameters have to be set back to data before
- return. */
-
- retval = 0;
- /* start_request clears out any readahead data, so it should be safe. */
- if (start_request(ra->addr.lba, ra->nframes, 1))
- {
- retval = -EIO;
- goto exit_read_audio;
- }
-
- /* For every requested frame. */
- cframe = 0;
- while (cframe < ra->nframes)
- {
- read_audio_data(readahead_buffer, res_reg, &res_size);
- if ((res_reg[0] & 0xf0) == 0x20)
- {
- if (res_reg[1] == SONY_BAD_DATA_ERR)
- {
- printk("CDU31A: Data error on audio sector %d\n",
- ra->addr.lba + cframe);
- }
- else if (res_reg[1] == SONY_ILL_TRACK_R_ERR)
- {
- /* Illegal track type, change track types and start over. */
- sony_raw_data_mode = (sony_raw_data_mode) ? 0 : 1;
-
- /* Set the drive mode. */
- params[0] = SONY_SD_DECODE_PARAM;
- params[1] = 0x06 | sony_raw_data_mode;
- do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
- params,
- 2,
- res_reg,
- &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("CDU31A: Unable to set decode params: 0x%2.2x\n", res_reg[1]);
- retval = -EIO;
- goto exit_read_audio;
- }
-
- /* Restart the request on the current frame. */
- if (start_request(ra->addr.lba + cframe, ra->nframes - cframe, 1))
- {
- retval = -EIO;
- goto exit_read_audio;
- }
-
- /* Don't go back to the top because don't want to get into
- and infinite loop. A lot of code gets duplicated, but
- that's no big deal, I don't guess. */
- read_audio_data(readahead_buffer, res_reg, &res_size);
- if ((res_reg[0] & 0xf0) == 0x20)
- {
- if (res_reg[1] == SONY_BAD_DATA_ERR)
- {
- printk("CDU31A: Data error on audio sector %d\n",
- ra->addr.lba + cframe);
- }
- else
- {
- printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
- ra->addr.lba + cframe,
- res_reg[1]);
- retval = -EIO;
- goto exit_read_audio;
- }
- }
- else
- {
- memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)),
- (char *) readahead_buffer,
- CD_FRAMESIZE_RAW);
- }
- }
- else
- {
- printk("CDU31A: Error reading audio data on sector %d: 0x%x\n",
- ra->addr.lba + cframe,
- res_reg[1]);
- retval = -EIO;
- goto exit_read_audio;
- }
- }
- else
- {
- memcpy_tofs((char *) (ra->buf + (CD_FRAMESIZE_RAW * cframe)),
- (char *) readahead_buffer,
- CD_FRAMESIZE_RAW);
- }
-
- cframe++;
- }
-
- get_result(res_reg, &res_size);
- if ((res_reg[0] & 0xf0) == 0x20)
- {
- printk("CDU31A: Error return from audio read: 0x%x\n",
- res_reg[1]);
- retval = -EIO;
- goto exit_read_audio;
- }
-
-exit_read_audio:
-
- /* Set the drive mode back to the proper one for the disk. */
- params[0] = SONY_SD_DECODE_PARAM;
- if (!sony_xa_mode)
- {
- params[1] = 0x0f;
- }
- else
- {
- params[1] = 0x07;
- }
- do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
- params,
- 2,
- res_reg,
- &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("CDU31A: Unable to reset decode params: 0x%2.2x\n", res_reg[1]);
- return -EIO;
- }
-
- has_cd_task = NULL;
- sony_inuse = 0;
- wake_up_interruptible(&sony_wait);
-
- return(retval);
-}
-
-/*
- * The big ugly ioctl handler.
- */
-static int
-scd_ioctl(struct inode *inode,
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
-{
- unsigned char res_reg[12];
- unsigned int res_size;
- unsigned char params[7];
- int i;
-
-
- if (!inode)
- {
- return -EINVAL;
- }
-
- switch (cmd)
- {
- case CDROMSTART: /* Spin up the drive */
- do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("Sony CDROM error 0x%2.2x (CDROMSTART)\n", res_reg[1]);
- return -EIO;
- }
- return 0;
- break;
-
- case CDROMSTOP: /* Spin down the drive */
- do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
-
- /*
- * Spin the drive down, ignoring the error if the disk was
- * already not spinning.
- */
- sony_audio_status = CDROM_AUDIO_NO_STATUS;
- do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
- if ( ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- && (res_reg[1] != SONY_NOT_SPIN_ERR))
- {
- printk("Sony CDROM error 0x%2.2x (CDROMSTOP)\n", res_reg[1]);
- return -EIO;
- }
-
- return 0;
- break;
-
- case CDROMPAUSE: /* Pause the drive */
- do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("Sony CDROM error 0x%2.2x (CDROMPAUSE)\n", res_reg[1]);
- return -EIO;
- }
-
- /* Get the current position and save it for resuming */
- if (read_subcode() < 0)
- {
- return -EIO;
- }
- cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
- cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
- cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
- sony_audio_status = CDROM_AUDIO_PAUSED;
- return 0;
- break;
-
- case CDROMRESUME: /* Start the drive after being paused */
- if (sony_audio_status != CDROM_AUDIO_PAUSED)
- {
- return -EINVAL;
- }
-
- do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-
- /* Start the drive at the saved position. */
- params[1] = cur_pos_msf[0];
- params[2] = cur_pos_msf[1];
- params[3] = cur_pos_msf[2];
- params[4] = final_pos_msf[0];
- params[5] = final_pos_msf[1];
- params[6] = final_pos_msf[2];
- params[0] = 0x03;
- do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("Sony CDROM error 0x%2.2x (CDROMRESUME)\n", res_reg[1]);
- return -EIO;
- }
- sony_audio_status = CDROM_AUDIO_PLAY;
- return 0;
- break;
-
- case CDROMPLAYMSF: /* Play starting at the given MSF address. */
- verify_area(VERIFY_READ, (char *) arg, 6);
- do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
- memcpy_fromfs(&(params[1]), (void *) arg, 6);
-
- /* The parameters are given in int, must be converted */
- for (i=1; i<7; i++)
- {
- params[i] = int_to_bcd(params[i]);
- }
- params[0] = 0x03;
- do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("Sony CDROM error 0x%2.2x (CDROMPLAYMSF)\n", res_reg[1]);
- return -EIO;
- }
-
- /* Save the final position for pauses and resumes */
- final_pos_msf[0] = params[4];
- final_pos_msf[1] = params[5];
- final_pos_msf[2] = params[6];
- sony_audio_status = CDROM_AUDIO_PLAY;
- return 0;
- break;
-
- case CDROMREADTOCHDR: /* Read the table of contents header */
- {
- struct cdrom_tochdr *hdr;
- struct cdrom_tochdr loc_hdr;
-
- sony_get_toc();
- if (!sony_toc_read)
- {
- return -EIO;
- }
-
- hdr = (struct cdrom_tochdr *) arg;
- verify_area(VERIFY_WRITE, hdr, sizeof(*hdr));
- loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
- loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
- memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr));
- }
- return 0;
- break;
-
- case CDROMREADTOCENTRY: /* Read a given table of contents entry */
- {
- struct cdrom_tocentry *entry;
- struct cdrom_tocentry loc_entry;
- int track_idx;
- unsigned char *msf_val = NULL;
-
- sony_get_toc();
- if (!sony_toc_read)
- {
- return -EIO;
- }
-
- entry = (struct cdrom_tocentry *) arg;
- verify_area(VERIFY_READ, entry, sizeof(*entry));
- verify_area(VERIFY_WRITE, entry, sizeof(*entry));
-
- memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry));
-
- /* Lead out is handled separately since it is special. */
- if (loc_entry.cdte_track == CDROM_LEADOUT)
- {
- loc_entry.cdte_adr = sony_toc->address2;
- loc_entry.cdte_ctrl = sony_toc->control2;
- msf_val = sony_toc->lead_out_start_msf;
- }
- else
- {
- track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
- if (track_idx < 0)
- {
- return -EINVAL;
- }
-
- loc_entry.cdte_adr = sony_toc->tracks[track_idx].address;
- loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
- msf_val = sony_toc->tracks[track_idx].track_start_msf;
- }
-
- /* Logical buffer address or MSF format requested? */
- if (loc_entry.cdte_format == CDROM_LBA)
- {
- loc_entry.cdte_addr.lba = msf_to_log(msf_val);
- }
- else if (loc_entry.cdte_format == CDROM_MSF)
- {
- loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
- loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1));
- loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2));
- }
- memcpy_tofs(entry, &loc_entry, sizeof(*entry));
- }
- return 0;
- break;
-
- case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
- {
- struct cdrom_ti ti;
- int track_idx;
-
- sony_get_toc();
- if (!sony_toc_read)
- {
- return -EIO;
- }
-
- verify_area(VERIFY_READ, (char *) arg, sizeof(ti));
-
- memcpy_fromfs(&ti, (char *) arg, sizeof(ti));
- if ( (ti.cdti_trk0 < sony_toc->first_track_num)
- || (ti.cdti_trk0 > sony_toc->last_track_num)
- || (ti.cdti_trk1 < ti.cdti_trk0))
- {
- return -EINVAL;
- }
-
- track_idx = find_track(int_to_bcd(ti.cdti_trk0));
- if (track_idx < 0)
- {
- return -EINVAL;
- }
- params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
- params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
- params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
-
- /*
- * If we want to stop after the last track, use the lead-out
- * MSF to do that.
- */
- if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num))
- {
- log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1,
- &(params[4]));
- }
- else
- {
- track_idx = find_track(int_to_bcd(ti.cdti_trk1+1));
- if (track_idx < 0)
- {
- return -EINVAL;
- }
- log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1,
- &(params[4]));
- }
- params[0] = 0x03;
-
- do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-
- do_sony_cd_cmd(SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg, &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1],
- params[2], params[3], params[4], params[5], params[6]);
- printk("Sony CDROM error 0x%2.2x (CDROMPLAYTRKIND\n", res_reg[1]);
- return -EIO;
- }
-
- /* Save the final position for pauses and resumes */
- final_pos_msf[0] = params[4];
- final_pos_msf[1] = params[5];
- final_pos_msf[2] = params[6];
- sony_audio_status = CDROM_AUDIO_PLAY;
- return 0;
- }
-
- case CDROMSUBCHNL: /* Get subchannel info */
- return sony_get_subchnl_info(arg);
-
- case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */
- {
- struct cdrom_volctrl volctrl;
-
- verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl));
-
- memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
- params[0] = SONY_SD_AUDIO_VOLUME;
- params[1] = volctrl.channel0;
- params[2] = volctrl.channel1;
- do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, params, 3, res_reg, &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("Sony CDROM error 0x%2.2x (CDROMVOLCTRL)\n", res_reg[1]);
- return -EIO;
- }
- }
- return 0;
-
- case CDROMEJECT: /* Eject the drive */
- do_sony_cd_cmd(SONY_AUDIO_STOP_CMD, NULL, 0, res_reg, &res_size);
- do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
-
- sony_audio_status = CDROM_AUDIO_INVALID;
- do_sony_cd_cmd(SONY_EJECT_CMD, NULL, 0, res_reg, &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("Sony CDROM error 0x%2.2x (CDROMEJECT)\n", res_reg[1]);
- return -EIO;
- }
- return 0;
- break;
-
- case CDROMREADAUDIO: /* Read 2352 byte audio tracks and 2340 byte
- raw data tracks. */
- {
- struct cdrom_read_audio ra;
-
-
- sony_get_toc();
- if (!sony_toc_read)
- {
- return -EIO;
- }
-
- verify_area(VERIFY_READ, (char *) arg, sizeof(ra));
- memcpy_fromfs(&ra, (char *) arg, sizeof(ra));
-
- verify_area(VERIFY_WRITE, ra.buf, CD_FRAMESIZE_RAW * ra.nframes);
-
- if (ra.addr_format == CDROM_LBA)
- {
- if ( (ra.addr.lba >= sony_toc->lead_out_start_lba)
- || (ra.addr.lba + ra.nframes >= sony_toc->lead_out_start_lba))
- {
- return -EINVAL;
- }
- }
- else if (ra.addr_format == CDROM_MSF)
- {
- if ( (ra.addr.msf.minute >= 75)
- || (ra.addr.msf.second >= 60)
- || (ra.addr.msf.frame >= 75))
- {
- return -EINVAL;
- }
-
- ra.addr.lba = ( (ra.addr.msf.minute * 4500)
- + (ra.addr.msf.second * 75)
- + ra.addr.msf.frame);
- if ( (ra.addr.lba >= sony_toc->lead_out_start_lba)
- || (ra.addr.lba + ra.nframes >= sony_toc->lead_out_start_lba))
- {
- return -EINVAL;
- }
-
- /* I know, this can go negative on an unsigned. However,
- the first thing done to the data is to add this value,
- so this should compensate and allow direct msf access. */
- ra.addr.lba -= LOG_START_OFFSET;
- }
- else
- {
- return -EINVAL;
- }
-
- return(read_audio(&ra, inode));
- }
- return 0;
- break;
-
- default:
- return -EINVAL;
- }
-}
-
-
-/*
- * Open the drive for operations. Spin the drive up and read the table of
- * contents if these have not already been done.
- */
-static int
-scd_open(struct inode *inode,
- struct file *filp)
-{
- unsigned char res_reg[12];
- unsigned int res_size;
- int num_spin_ups;
- unsigned char params[2];
-
-
- if ((filp) && filp->f_mode & 2)
- return -EROFS;
-
- if (!sony_spun_up)
- {
- num_spin_ups = 0;
-
-respinup_on_open:
- do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
-
- /* The drive sometimes returns error 0. I don't know why, but ignore
- it. It seems to mean the drive has already done the operation. */
- if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
- {
- printk("Sony CDROM error 0x%2.2x (scd_open, spin up)\n", res_reg[1]);
- return -EIO;
- }
-
- do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
-
- /* The drive sometimes returns error 0. I don't know why, but ignore
- it. It seems to mean the drive has already done the operation. */
- if ((res_size < 2) || ((res_reg[0] != 0) && (res_reg[1] != 0)))
- {
- /* If the drive is already playing, it's ok. */
- if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR) || (res_reg[1] == 0))
- {
- goto drive_spinning;
- }
-
- /* If the drive says it is not spun up (even though we just did it!)
- then retry the operation at least a few times. */
- if ( (res_reg[1] == SONY_NOT_SPIN_ERR)
- && (num_spin_ups < MAX_CDU31A_RETRIES))
- {
- num_spin_ups++;
- goto respinup_on_open;
- }
-
- printk("Sony CDROM error 0x%2.2x (scd_open, read toc)\n", res_reg[1]);
- do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
-
- return -EIO;
- }
-
- sony_get_toc();
- if (!sony_toc_read)
- {
- do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
- return -EIO;
- }
-
- /* For XA on the CDU31A only, we have to do special reads.
- The CDU33A handles XA automagically. */
- if ( (sony_toc->disk_type == SONY_XA_DISK_TYPE)
- && (!is_double_speed))
- {
- params[0] = SONY_SD_DECODE_PARAM;
- params[1] = 0x07;
- do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
- params,
- 2,
- res_reg,
- &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("CDU31A: Unable to set XA params: 0x%2.2x\n", res_reg[1]);
- }
- sony_xa_mode = 1;
- }
- /* A non-XA disk. Set the parms back if necessary. */
- else if (sony_xa_mode)
- {
- params[0] = SONY_SD_DECODE_PARAM;
- params[1] = 0x0f;
- do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
- params,
- 2,
- res_reg,
- &res_size);
- if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20))
- {
- printk("CDU31A: Unable to reset XA params: 0x%2.2x\n", res_reg[1]);
- }
- sony_xa_mode = 0;
- }
-
- sony_spun_up = 1;
- }
-
-drive_spinning:
-
- /* If filp is not NULL (standard open), try a disk change. */
- if (filp)
- {
- check_disk_change(inode->i_rdev);
- }
-
- sony_usage++;
-
- return 0;
-}
-
-
-/*
- * Close the drive. Spin it down if no task is using it. The spin
- * down will fail if playing audio, so audio play is OK.
- */
-static void
-scd_release(struct inode *inode,
- struct file *filp)
-{
- unsigned char res_reg[12];
- unsigned int res_size;
-
-
- if (sony_usage > 0)
- {
- sony_usage--;
- }
- if (sony_usage == 0)
- {
- sync_dev(inode->i_rdev);
- do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size);
-
- sony_spun_up = 0;
- }
-}
-
-
-static struct file_operations scd_fops = {
- NULL, /* lseek - default */
- block_read, /* read - general block-dev read */
- block_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- scd_ioctl, /* ioctl */
- NULL, /* mmap */
- scd_open, /* open */
- scd_release, /* release */
- NULL, /* fsync */
- NULL, /* fasync */
- scd_disk_change, /* media_change */
- NULL /* revalidate */
-};
-
-
-/* The different types of disc loading mechanisms supported */
-static char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" };
-
-static void
-get_drive_configuration(unsigned short base_io,
- unsigned char res_reg[],
- unsigned int *res_size)
-{
- int retry_count;
-
-
- /* Set the base address */
- sony_cd_base_io = base_io;
-
- /* Set up all the register locations */
- sony_cd_cmd_reg = sony_cd_base_io + SONY_CMD_REG_OFFSET;
- sony_cd_param_reg = sony_cd_base_io + SONY_PARAM_REG_OFFSET;
- sony_cd_write_reg = sony_cd_base_io + SONY_WRITE_REG_OFFSET;
- sony_cd_control_reg = sony_cd_base_io + SONY_CONTROL_REG_OFFSET;
- sony_cd_status_reg = sony_cd_base_io + SONY_STATUS_REG_OFFSET;
- sony_cd_result_reg = sony_cd_base_io + SONY_RESULT_REG_OFFSET;
- sony_cd_read_reg = sony_cd_base_io + SONY_READ_REG_OFFSET;
- sony_cd_fifost_reg = sony_cd_base_io + SONY_FIFOST_REG_OFFSET;
-
- /*
- * Check to see if anything exists at the status register location.
- * I don't know if this is a good way to check, but it seems to work
- * ok for me.
- */
- if (read_status_register() != 0xff)
- {
- /*
- * Reset the drive and wait for attention from it (to say it's reset).
- * If you don't wait, the next operation will probably fail.
- */
- reset_drive();
- retry_count = jiffies + SONY_RESET_TIMEOUT;
- while ((retry_count > jiffies) && (!is_attention()))
- {
- sony_sleep();
- }
-
-#if 0
- /* If attention is never seen probably not a CDU31a present */
- if (!is_attention())
- {
- res_reg[0] = 0x20;
- return;
- }
-#endif
-
- /*
- * Get the drive configuration.
- */
- do_sony_cd_cmd(SONY_REQ_DRIVE_CONFIG_CMD,
- NULL,
- 0,
- (unsigned char *) res_reg,
- res_size);
- return;
- }
-
- /* Return an error */
- res_reg[0] = 0x20;
-}
-
-/*
- * Set up base I/O and interrupts, called from main.c.
- */
-void
-cdu31a_setup(char *strings,
- int *ints)
-{
- if (ints[0] > 0)
- {
- sony_cd_base_io = ints[1];
- }
- if (ints[0] > 1)
- {
- irq_used = ints[2];
- }
- if ((strings != NULL) && (*strings != '\0'))
- {
- if (strcmp(strings, "PAS") == 0)
- {
- sony_pas_init = 1;
- }
- else
- {
- printk("CDU31A: Unknown interface type: %s\n", strings);
- }
- }
-}
-
-static int cdu31a_block_size;
-
-/*
- * Initialize the driver.
- */
-unsigned long
-cdu31a_init(unsigned long mem_start, unsigned long mem_end)
-{
- struct s_sony_drive_config drive_config;
- unsigned int res_size;
- int i;
- int drive_found;
- int tmp_irq;
-
-
- /*
- * According to Alex Freed (freed@europa.orion.adobe.com), this is
- * required for the Fusion CD-16 package. If the sound driver is
- * loaded, it should work fine, but just in case...
- *
- * The following turn on the CD-ROM interface for a Fusion CD-16.
- */
- if (sony_pas_init)
- {
- outb(0xbc, 0x9a01);
- outb(0xe2, 0x9a01);
- }
-
- drive_found = 0;
-
- /* Setting the base I/O address to 0xffff will disable it. */
- if (sony_cd_base_io == 0xffff)
- {
- }
- else if (sony_cd_base_io != 0)
- {
- tmp_irq = irq_used; /* Need IRQ 0 because we can't sleep here. */
- irq_used = 0;
-
- get_drive_configuration(sony_cd_base_io,
- drive_config.exec_status,
- &res_size);
- if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00))
- {
- drive_found = 1;
- }
-
- irq_used = tmp_irq;
- }
- else
- {
- irq_used = 0;
- i = 0;
- while ( (cdu31a_addresses[i].base != 0)
- && (!drive_found))
- {
- if (check_region(cdu31a_addresses[i].base, 4)) {
- i++;
- continue;
- }
- get_drive_configuration(cdu31a_addresses[i].base,
- drive_config.exec_status,
- &res_size);
- if ((res_size > 2) && ((drive_config.exec_status[0] & 0xf0) == 0x00))
- {
- drive_found = 1;
- irq_used = cdu31a_addresses[i].int_num;
- }
- else
- {
- i++;
- }
- }
- }
-
- if (drive_found)
- {
- request_region(sony_cd_base_io, 4,"cdu31a");
-
- if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops))
- {
- printk("Unable to get major %d for CDU-31a\n", MAJOR_NR);
- return mem_start;
- }
-
- if (SONY_HWC_DOUBLE_SPEED(drive_config))
- {
- is_double_speed = 1;
- }
-
- /* A negative irq_used will attempt an autoirq. */
- if (irq_used < 0)
- {
- autoirq_setup(0);
- enable_interrupts();
- reset_drive();
- tmp_irq = autoirq_report(10);
- disable_interrupts();
-
- irq_used = 0;
- set_drive_params();
- irq_used = tmp_irq;
- }
- else
- {
- tmp_irq = irq_used; /* Need IRQ 0 because we can't sleep here. */
- irq_used = 0;
-
- set_drive_params();
-
- irq_used = tmp_irq;
- }
-
- if (irq_used > 0)
- {
- if (request_irq(irq_used, cdu31a_interrupt, SA_INTERRUPT, "cdu31a"))
- {
- printk("Unable to grab IRQ%d for the CDU31A driver\n", irq_used);
- irq_used = 0;
- }
- }
-
- printk("Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
- drive_config.vendor_id,
- drive_config.product_id,
- drive_config.product_rev_level);
- printk(" Capabilities: %s",
- load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
- if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
- {
- printk(", audio");
- }
- if (SONY_HWC_EJECT(drive_config))
- {
- printk(", eject");
- }
- if (SONY_HWC_LED_SUPPORT(drive_config))
- {
- printk(", LED");
- }
- if (SONY_HWC_ELECTRIC_VOLUME(drive_config))
- {
- printk(", elec. Vol");
- }
- if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config))
- {
- printk(", sep. Vol");
- }
- if (is_double_speed)
- {
- printk(", double speed");
- }
- if (irq_used > 0)
- {
- printk(", irq %d", irq_used);
- }
- printk("\n");
-
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- read_ahead[MAJOR_NR] = CDU31A_READAHEAD;
- cdu31a_block_size = 1024; /* 1kB default block size */
- /* use 'mount -o block=2048' */
- blksize_size[MAJOR_NR] = &cdu31a_block_size;
-
- last_sony_subcode = (struct s_sony_subcode *) mem_start;
- mem_start += sizeof(*last_sony_subcode);
- readahead_buffer = (unsigned char *) mem_start;
- mem_start += CD_FRAMESIZE_RAW;
- sony_toc = (struct s_sony_session_toc *) mem_start;
- mem_start += sizeof(struct s_sony_session_toc);
- }
-
-
- disk_changed = 1;
-
- return mem_start;
-}
diff --git a/drivers/block/cmd640.c b/drivers/block/cmd640.c
new file mode 100644
index 000000000..1110512e5
--- /dev/null
+++ b/drivers/block/cmd640.c
@@ -0,0 +1,849 @@
+/*
+ * linux/drivers/block/cmd640.c Version 1.02 Sep 01, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Original author: abramov@cecmow.enet.dec.com (Igor Abramov)
+ *
+ * Maintained by: mlord@pobox.com (Mark Lord)
+ * with fanatical support from a legion of hackers!
+ *
+ * This file provides support for the advanced features and bugs
+ * of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
+ *
+ * These chips are basically fucked by design, and getting this driver
+ * to work on every motherboard design that uses this screwed chip seems
+ * bloody well impossible. However, we're still trying.
+ *
+ * Version 0.97 worked for everybody.
+ *
+ * User feedback is essential. Many thanks to the beta test team:
+ *
+ * A.Hartgers@stud.tue.nl, JZDQC@CUNYVM.CUNY.edu, abramov@cecmow.enet.dec.com,
+ * bardj@utopia.ppp.sn.no, bart@gaga.tue.nl, bbol001@cs.auckland.ac.nz,
+ * chrisc@dbass.demon.co.uk, dalecki@namu26.Num.Math.Uni-Goettingen.de,
+ * derekn@vw.ece.cmu.edu, florian@btp2x3.phy.uni-bayreuth.de,
+ * flynn@dei.unipd.it, gadio@netvision.net.il, godzilla@futuris.net,
+ * j@pobox.com, jkemp1@mises.uni-paderborn.de, jtoppe@hiwaay.net,
+ * kerouac@ssnet.com, meskes@informatik.rwth-aachen.de, hzoli@cs.elte.hu,
+ * peter@udgaard.isgtec.com, phil@tazenda.demon.co.uk, roadcapw@cfw.com,
+ * s0033las@sun10.vsz.bme.hu, schaffer@tam.cornell.edu, sjd@slip.net,
+ * steve@ei.org, ulrpeg@bigcomm.gun.de, ism@tardis.ed.ac.uk, mack@cray.com
+ * liug@mama.indstate.edu, and others.
+ *
+ * Version 0.01 Initial version, hacked out of ide.c,
+ * and #include'd rather than compiled separately.
+ * This will get cleaned up in a subsequent release.
+ *
+ * Version 0.02 Fixes for vlb initialization code, enable prefetch
+ * for versions 'B' and 'C' of chip by default,
+ * some code cleanup.
+ *
+ * Version 0.03 Added reset of secondary interface,
+ * and black list for devices which are not compatible
+ * with prefetch mode. Separate function for setting
+ * prefetch is added, possibly it will be called some
+ * day from ioctl processing code.
+ *
+ * Version 0.04 Now configs/compiles separate from ide.c
+ *
+ * Version 0.05 Major rewrite of interface timing code.
+ * Added new function cmd640_set_mode to set PIO mode
+ * from ioctl call. New drives added to black list.
+ *
+ * Version 0.06 More code cleanup. Prefetch is enabled only for
+ * detected hard drives, not included in prefetch
+ * black list.
+ *
+ * Version 0.07 Changed to more conservative drive tuning policy.
+ * Unknown drives, which report PIO < 4 are set to
+ * (reported_PIO - 1) if it is supported, or to PIO0.
+ * List of known drives extended by info provided by
+ * CMD at their ftp site.
+ *
+ * Version 0.08 Added autotune/noautotune support.
+ *
+ * Version 0.09 Try to be smarter about 2nd port enabling.
+ * Version 0.10 Be nice and don't reset 2nd port.
+ * Version 0.11 Try to handle more wierd situations.
+ *
+ * Version 0.12 Lots of bug fixes from Laszlo Peter
+ * irq unmasking disabled for reliability.
+ * try to be even smarter about the second port.
+ * tidy up source code formatting.
+ * Version 0.13 permit irq unmasking again.
+ * Version 0.90 massive code cleanup, some bugs fixed.
+ * defaults all drives to PIO mode0, prefetch off.
+ * autotune is OFF by default, with compile time flag.
+ * prefetch can be turned OFF/ON using "hdparm -p8/-p9"
+ * (requires hdparm-3.1 or newer)
+ * Version 0.91 first release to linux-kernel list.
+ * Version 0.92 move initial reg dump to separate callable function
+ * change "readahead" to "prefetch" to avoid confusion
+ * Version 0.95 respect original BIOS timings unless autotuning.
+ * tons of code cleanup and rearrangement.
+ * added CONFIG_BLK_DEV_CMD640_ENHANCED option
+ * prevent use of unmask when prefetch is on
+ * Version 0.96 prevent use of io_32bit when prefetch is off
+ * Version 0.97 fix VLB secondary interface for sjd@slip.net
+ * other minor tune-ups: 0.96 was very good.
+ * Version 0.98 ignore PCI version when disabled by BIOS
+ * Version 0.99 display setup/active/recovery clocks with PIO mode
+ * Version 1.00 Mmm.. cannot depend on PCMD_ENA in all systems
+ * Version 1.01 slow/fast devsel can be selected with "hdparm -p6/-p7"
+ * ("fast" is necessary for 32bit I/O in some systems)
+ * Version 1.02 fix bug that resulted in slow "setup times"
+ * (patch courtesy of Zoltan Hidvegi)
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+#define CMD640_PREFETCH_MASKS 1
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "ide_modes.h"
+
+/*
+ * This flag is set in ide.c by the parameter: ide0=cmd640_vlb
+ */
+int cmd640_vlb = 0;
+
+/*
+ * CMD640 specific registers definition.
+ */
+
+#define VID 0x00
+#define DID 0x02
+#define PCMD 0x04
+#define PCMD_ENA 0x01
+#define PSTTS 0x06
+#define REVID 0x08
+#define PROGIF 0x09
+#define SUBCL 0x0a
+#define BASCL 0x0b
+#define BaseA0 0x10
+#define BaseA1 0x14
+#define BaseA2 0x18
+#define BaseA3 0x1c
+#define INTLINE 0x3c
+#define INPINE 0x3d
+
+#define CFR 0x50
+#define CFR_DEVREV 0x03
+#define CFR_IDE01INTR 0x04
+#define CFR_DEVID 0x18
+#define CFR_AT_VESA_078h 0x20
+#define CFR_DSA1 0x40
+#define CFR_DSA0 0x80
+
+#define CNTRL 0x51
+#define CNTRL_DIS_RA0 0x40
+#define CNTRL_DIS_RA1 0x80
+#define CNTRL_ENA_2ND 0x08
+
+#define CMDTIM 0x52
+#define ARTTIM0 0x53
+#define DRWTIM0 0x54
+#define ARTTIM1 0x55
+#define DRWTIM1 0x56
+#define ARTTIM23 0x57
+#define ARTTIM23_DIS_RA2 0x04
+#define ARTTIM23_DIS_RA3 0x08
+#define DRWTIM23 0x58
+#define BRST 0x59
+
+/*
+ * Registers and masks for easy access by drive index:
+ */
+static byte prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
+static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
+
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+
+static byte arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
+static byte drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23};
+
+/*
+ * Current cmd640 timing values for each drive.
+ * The defaults for each are the slowest possible timings.
+ */
+static byte setup_counts[4] = {4, 4, 4, 4}; /* Address setup count (in clocks) */
+static byte active_counts[4] = {16, 16, 16, 16}; /* Active count (encoded) */
+static byte recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */
+
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+/*
+ * These are initialized to point at the devices we control
+ */
+static ide_hwif_t *cmd_hwif0, *cmd_hwif1;
+static ide_drive_t *cmd_drives[4];
+
+/*
+ * Interface to access cmd640x registers
+ */
+static unsigned int cmd640_key;
+static void (*put_cmd640_reg)(unsigned short reg, byte val);
+static byte (*get_cmd640_reg)(unsigned short reg);
+
+/*
+ * This is read from the CFR reg, and is used in several places.
+ */
+static unsigned int cmd640_chip_version;
+
+/*
+ * The CMD640x chip does not support DWORD config write cycles, but some
+ * of the BIOSes use them to implement the config services.
+ * Therefore, we must use direct IO instead.
+ */
+
+/* PCI method 1 access */
+
+static void put_cmd640_reg_pci1 (unsigned short reg, byte val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
+ outb_p(val, (reg & 3) | 0xcfc);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci1 (unsigned short reg)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg & 0xfc) | cmd640_key, 0xcf8);
+ b = inb_p((reg & 3) | 0xcfc);
+ restore_flags(flags);
+ return b;
+}
+
+/* PCI method 2 access (from CMD datasheet) */
+
+static void put_cmd640_reg_pci2 (unsigned short reg, byte val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x10, 0xcf8);
+ outb_p(val, cmd640_key + reg);
+ outb_p(0, 0xcf8);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci2 (unsigned short reg)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x10, 0xcf8);
+ b = inb_p(cmd640_key + reg);
+ outb_p(0, 0xcf8);
+ restore_flags(flags);
+ return b;
+}
+
+/* VLB access */
+
+static void put_cmd640_reg_vlb (unsigned short reg, byte val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(reg, cmd640_key);
+ outb_p(val, cmd640_key + 4);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_vlb (unsigned short reg)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(reg, cmd640_key);
+ b = inb_p(cmd640_key + 4);
+ restore_flags(flags);
+ return b;
+}
+
+static int match_pci_cmd640_device (void)
+{
+ const byte ven_dev[4] = {0x95, 0x10, 0x40, 0x06};
+ unsigned int i;
+ for (i = 0; i < 4; i++) {
+ if (get_cmd640_reg(i) != ven_dev[i])
+ return 0;
+ }
+#ifdef STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT
+ if ((get_cmd640_reg(PCMD) & PCMD_ENA) == 0) {
+ printk("ide: cmd640 on PCI disabled by BIOS\n");
+ return 0;
+ }
+#endif /* STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT */
+ return 1; /* success */
+}
+
+/*
+ * Probe for CMD640x -- pci method 1
+ */
+static int probe_for_cmd640_pci1 (void)
+{
+ get_cmd640_reg = get_cmd640_reg_pci1;
+ put_cmd640_reg = put_cmd640_reg_pci1;
+ for (cmd640_key = 0x80000000; cmd640_key <= 0x8000f800; cmd640_key += 0x800) {
+ if (match_pci_cmd640_device())
+ return 1; /* success */
+ }
+ return 0;
+}
+
+/*
+ * Probe for CMD640x -- pci method 2
+ */
+static int probe_for_cmd640_pci2 (void)
+{
+ get_cmd640_reg = get_cmd640_reg_pci2;
+ put_cmd640_reg = put_cmd640_reg_pci2;
+ for (cmd640_key = 0xc000; cmd640_key <= 0xcf00; cmd640_key += 0x100) {
+ if (match_pci_cmd640_device())
+ return 1; /* success */
+ }
+ return 0;
+}
+
+/*
+ * Probe for CMD640x -- vlb
+ */
+static int probe_for_cmd640_vlb (void)
+{
+ byte b;
+
+ get_cmd640_reg = get_cmd640_reg_vlb;
+ put_cmd640_reg = put_cmd640_reg_vlb;
+ cmd640_key = 0x178;
+ b = get_cmd640_reg(CFR);
+ if (b == 0xff || b == 0x00 || (b & CFR_AT_VESA_078h)) {
+ cmd640_key = 0x78;
+ b = get_cmd640_reg(CFR);
+ if (b == 0xff || b == 0x00 || !(b & CFR_AT_VESA_078h))
+ return 0;
+ }
+ return 1; /* success */
+}
+
+/*
+ * Returns 1 if an IDE interface/drive exists at 0x170,
+ * Returns 0 otherwise.
+ */
+static int secondary_port_responding (void)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ outb_p(0x0a, 0x170 + IDE_SELECT_OFFSET); /* select drive0 */
+ udelay(100);
+ if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x0a) {
+ outb_p(0x1a, 0x170 + IDE_SELECT_OFFSET); /* select drive1 */
+ udelay(100);
+ if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x1a) {
+ restore_flags(flags);
+ return 0; /* nothing responded */
+ }
+ }
+ restore_flags(flags);
+ return 1; /* success */
+}
+
+#ifdef CMD640_DUMP_REGS
+/*
+ * Dump out all cmd640 registers. May be called from ide.c
+ */
+void cmd640_dump_regs (void)
+{
+ unsigned int reg = cmd640_vlb ? 0x50 : 0x00;
+
+ /* Dump current state of chip registers */
+ printk("ide: cmd640 internal register dump:");
+ for (; reg <= 0x59; reg++) {
+ if (!(reg & 0x0f))
+ printk("\n%04x:", reg);
+ printk(" %02x", get_cmd640_reg(reg));
+ }
+ printk("\n");
+}
+#endif
+
+/*
+ * Check whether prefetch is on for a drive,
+ * and initialize the unmask flags for safe operation.
+ */
+static void check_prefetch (unsigned int index)
+{
+ ide_drive_t *drive = cmd_drives[index];
+ byte b = get_cmd640_reg(prefetch_regs[index]);
+
+ if (b & prefetch_masks[index]) { /* is prefetch off? */
+ drive->no_unmask = 0;
+ drive->no_io_32bit = 1;
+ drive->io_32bit = 0;
+ } else {
+#if CMD640_PREFETCH_MASKS
+ drive->no_unmask = 1;
+ drive->unmask = 0;
+#endif
+ drive->no_io_32bit = 0;
+ }
+}
+
+/*
+ * Figure out which devices we control
+ */
+static void setup_device_ptrs (void)
+{
+ unsigned int i;
+
+ cmd_hwif0 = &ide_hwifs[0]; /* default, if not found below */
+ cmd_hwif1 = &ide_hwifs[1]; /* default, if not found below */
+ for (i = 0; i < MAX_HWIFS; i++) {
+ ide_hwif_t *hwif = &ide_hwifs[i];
+ if (hwif->chipset == ide_unknown || hwif->chipset == ide_generic) {
+ if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0)
+ cmd_hwif0 = hwif;
+ else if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170)
+ cmd_hwif1 = hwif;
+ }
+ }
+ cmd_drives[0] = &cmd_hwif0->drives[0];
+ cmd_drives[1] = &cmd_hwif0->drives[1];
+ cmd_drives[2] = &cmd_hwif1->drives[0];
+ cmd_drives[3] = &cmd_hwif1->drives[1];
+}
+
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+
+/*
+ * Sets prefetch mode for a drive.
+ */
+static void set_prefetch_mode (unsigned int index, int mode)
+{
+ ide_drive_t *drive = cmd_drives[index];
+ int reg = prefetch_regs[index];
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ b = get_cmd640_reg(reg);
+ if (mode) { /* want prefetch on? */
+#if CMD640_PREFETCH_MASKS
+ drive->no_unmask = 1;
+ drive->unmask = 0;
+#endif
+ drive->no_io_32bit = 0;
+ b &= ~prefetch_masks[index]; /* enable prefetch */
+ } else {
+ drive->no_unmask = 0;
+ drive->no_io_32bit = 1;
+ drive->io_32bit = 0;
+ b |= prefetch_masks[index]; /* disable prefetch */
+ }
+ put_cmd640_reg(reg, b);
+ restore_flags(flags);
+}
+
+/*
+ * Dump out current drive clocks settings
+ */
+static void display_clocks (unsigned int index)
+{
+ byte active_count, recovery_count;
+
+ active_count = active_counts[index];
+ if (active_count == 1)
+ ++active_count;
+ recovery_count = recovery_counts[index];
+ if (active_count > 3 && recovery_count == 1)
+ ++recovery_count;
+ if (cmd640_chip_version > 1)
+ recovery_count += 1; /* cmd640b uses (count + 1)*/
+ printk(", clocks=%d/%d/%d\n", setup_counts[index], active_count, recovery_count);
+}
+
+/*
+ * Pack active and recovery counts into single byte representation
+ * used by controller
+ */
+inline static byte pack_nibbles (byte upper, byte lower)
+{
+ return ((upper & 0x0f) << 4) | (lower & 0x0f);
+}
+
+/*
+ * This routine retrieves the initial drive timings from the chipset.
+ */
+static void retrieve_drive_counts (unsigned int index)
+{
+ byte b;
+
+ /*
+ * Get the internal setup timing, and convert to clock count
+ */
+ b = get_cmd640_reg(arttim_regs[index]) & ~0x3f;
+ switch (b) {
+ case 0x00: b = 4; break;
+ case 0x80: b = 3; break;
+ case 0x40: b = 2; break;
+ default: b = 5; break;
+ }
+ setup_counts[index] = b;
+
+ /*
+ * Get the active/recovery counts
+ */
+ b = get_cmd640_reg(drwtim_regs[index]);
+ active_counts[index] = (b >> 4) ? (b >> 4) : 0x10;
+ recovery_counts[index] = (b & 0x0f) ? (b & 0x0f) : 0x10;
+}
+
+
+/*
+ * This routine writes the prepared setup/active/recovery counts
+ * for a drive into the cmd640 chipset registers to active them.
+ */
+static void program_drive_counts (unsigned int index)
+{
+ unsigned long flags;
+ byte setup_count = setup_counts[index];
+ byte active_count = active_counts[index];
+ byte recovery_count = recovery_counts[index];
+
+ /*
+ * Set up address setup count and drive read/write timing registers.
+ * Primary interface has individual count/timing registers for
+ * each drive. Secondary interface has one common set of registers,
+ * so we merge the timings, using the slowest value for each timing.
+ */
+ if (index > 1) {
+ unsigned int mate;
+ if (cmd_drives[mate = index ^ 1]->present) {
+ if (setup_count < setup_counts[mate])
+ setup_count = setup_counts[mate];
+ if (active_count < active_counts[mate])
+ active_count = active_counts[mate];
+ if (recovery_count < recovery_counts[mate])
+ recovery_count = recovery_counts[mate];
+ }
+ }
+
+ /*
+ * Convert setup_count to internal chipset representation
+ */
+ switch (setup_count) {
+ case 4: setup_count = 0x00; break;
+ case 3: setup_count = 0x80; break;
+ case 1:
+ case 2: setup_count = 0x40; break;
+ default: setup_count = 0xc0; /* case 5 */
+ }
+
+ /*
+ * Now that everything is ready, program the new timings
+ */
+ save_flags (flags);
+ cli();
+ /*
+ * Program the address_setup clocks into ARTTIM reg,
+ * and then the active/recovery counts into the DRWTIM reg
+ * (this converts counts of 16 into counts of zero -- okay).
+ */
+ setup_count |= get_cmd640_reg(arttim_regs[index]) & 0x3f;
+ put_cmd640_reg(arttim_regs[index], setup_count);
+ put_cmd640_reg(drwtim_regs[index], pack_nibbles(active_count, recovery_count));
+ restore_flags(flags);
+}
+
+/*
+ * Set a specific pio_mode for a drive
+ */
+static void cmd640_set_mode (unsigned int index, byte pio_mode, unsigned int cycle_time)
+{
+ int setup_time, active_time, recovery_time, clock_time;
+ byte setup_count, active_count, recovery_count, recovery_count2, cycle_count;
+ int bus_speed = ide_system_bus_speed();
+
+ if (pio_mode > 5)
+ pio_mode = 5;
+ setup_time = ide_pio_timings[pio_mode].setup_time;
+ active_time = ide_pio_timings[pio_mode].active_time;
+ recovery_time = cycle_time - (setup_time + active_time);
+ clock_time = 1000 / bus_speed;
+ cycle_count = (cycle_time + clock_time - 1) / clock_time;
+
+ setup_count = (setup_time + clock_time - 1) / clock_time;
+
+ active_count = (active_time + clock_time - 1) / clock_time;
+ if (active_count < 2)
+ active_count = 2; /* minimum allowed by cmd640 */
+
+ recovery_count = (recovery_time + clock_time - 1) / clock_time;
+ recovery_count2 = cycle_count - (setup_count + active_count);
+ if (recovery_count2 > recovery_count)
+ recovery_count = recovery_count2;
+ if (recovery_count < 2)
+ recovery_count = 2; /* minimum allowed by cmd640 */
+ if (recovery_count > 17) {
+ active_count += recovery_count - 17;
+ recovery_count = 17;
+ }
+ if (active_count > 16)
+ active_count = 16; /* maximum allowed by cmd640 */
+ if (cmd640_chip_version > 1)
+ recovery_count -= 1; /* cmd640b uses (count + 1)*/
+ if (recovery_count > 16)
+ recovery_count = 16; /* maximum allowed by cmd640 */
+
+ setup_counts[index] = setup_count;
+ active_counts[index] = active_count;
+ recovery_counts[index] = recovery_count;
+
+ /*
+ * In a perfect world, we might set the drive pio mode here
+ * (using WIN_SETFEATURE) before continuing.
+ *
+ * But we do not, because:
+ * 1) this is the wrong place to do it (proper is do_special() in ide.c)
+ * 2) in practice this is rarely, if ever, necessary
+ */
+ program_drive_counts (index);
+}
+
+/*
+ * Drive PIO mode selection:
+ */
+static void cmd640_tune_drive (ide_drive_t *drive, byte mode_wanted)
+{
+ byte b;
+ ide_pio_data_t d;
+ unsigned int index = 0;
+
+ while (drive != cmd_drives[index]) {
+ if (++index > 3) {
+ printk("%s: bad news in cmd640_tune_drive\n", drive->name);
+ return;
+ }
+ }
+ switch (mode_wanted) {
+ case 6: /* set fast-devsel off */
+ case 7: /* set fast-devsel on */
+ mode_wanted &= 1;
+ b = get_cmd640_reg(CNTRL) & ~0x27;
+ if (mode_wanted)
+ b |= 0x27;
+ put_cmd640_reg(CNTRL, b);
+ printk("%s: %sabled cmd640 fast host timing (devsel)\n", drive->name, mode_wanted ? "en" : "dis");
+ return;
+
+ case 8: /* set prefetch off */
+ case 9: /* set prefetch on */
+ mode_wanted &= 1;
+ set_prefetch_mode(index, mode_wanted);
+ printk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis");
+ return;
+ }
+
+ (void) ide_get_best_pio_mode (drive, mode_wanted, 5, &d);
+ cmd640_set_mode (index, d.pio_mode, d.cycle_time);
+
+ printk ("%s: selected cmd640 PIO mode%d (%dns)%s",
+ drive->name,
+ d.pio_mode,
+ d.cycle_time,
+ d.overridden ? " (overriding vendor mode)" : "");
+ display_clocks(index);
+}
+
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+/*
+ * Probe for a cmd640 chipset, and initialize it if found. Called from ide.c
+ */
+int ide_probe_for_cmd640x (void)
+{
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ int second_port_toggled = 0;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ int second_port_cmd640 = 0;
+ const char *bus_type, *port2;
+ unsigned int index;
+ byte b, cfr;
+
+ if (cmd640_vlb && probe_for_cmd640_vlb()) {
+ bus_type = "VLB";
+ } else {
+ cmd640_vlb = 0;
+ if (probe_for_cmd640_pci1())
+ bus_type = "PCI (type1)";
+ else if (probe_for_cmd640_pci2())
+ bus_type = "PCI (type2)";
+ else
+ return 0;
+ }
+ /*
+ * Undocumented magic (there is no 0x5b reg in specs)
+ */
+ put_cmd640_reg(0x5b, 0xbd);
+ if (get_cmd640_reg(0x5b) != 0xbd) {
+ printk("ide: cmd640 init failed: wrong value in reg 0x5b\n");
+ return 0;
+ }
+ put_cmd640_reg(0x5b, 0);
+
+#ifdef CMD640_DUMP_REGS
+ CMD640_DUMP_REGS;
+#endif
+
+ /*
+ * Documented magic begins here
+ */
+ cfr = get_cmd640_reg(CFR);
+ cmd640_chip_version = cfr & CFR_DEVREV;
+ if (cmd640_chip_version == 0) {
+ printk ("ide: bad cmd640 revision: %d\n", cmd640_chip_version);
+ return 0;
+ }
+
+ /*
+ * Initialize data for primary port
+ */
+ setup_device_ptrs ();
+ printk("%s: buggy cmd640%c interface on %s, config=0x%02x\n",
+ cmd_hwif0->name, 'a' + cmd640_chip_version - 1, bus_type, cfr);
+ cmd_hwif0->chipset = ide_cmd640;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ cmd_hwif0->tuneproc = &cmd640_tune_drive;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+ /*
+ * Ensure compatibility by always using the slowest timings
+ * for access to the drive's command register block,
+ * and reset the prefetch burstsize to default (512 bytes).
+ *
+ * Maybe we need a way to NOT do these on *some* systems?
+ */
+ put_cmd640_reg(CMDTIM, 0);
+ put_cmd640_reg(BRST, 0x40);
+
+ /*
+ * Try to enable the secondary interface, if not already enabled
+ */
+ if (cmd_hwif1->noprobe) {
+ port2 = "not probed";
+ } else {
+ b = get_cmd640_reg(CNTRL);
+ if (secondary_port_responding()) {
+ if ((b & CNTRL_ENA_2ND)) {
+ second_port_cmd640 = 1;
+ port2 = "okay";
+ } else if (cmd640_vlb) {
+ second_port_cmd640 = 1;
+ port2 = "alive";
+ } else
+ port2 = "not cmd640";
+ } else {
+ put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */
+ if (secondary_port_responding()) {
+ second_port_cmd640 = 1;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ second_port_toggled = 1;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ port2 = "enabled";
+ } else {
+ put_cmd640_reg(CNTRL, b); /* restore original setting */
+ port2 = "not responding";
+ }
+ }
+ }
+
+ /*
+ * Initialize data for secondary cmd640 port, if enabled
+ */
+ if (second_port_cmd640) {
+ cmd_hwif0->serialized = 1;
+ cmd_hwif1->serialized = 1;
+ cmd_hwif1->chipset = ide_cmd640;
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ cmd_hwif1->tuneproc = &cmd640_tune_drive;
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ }
+ printk("%s: %sserialized, secondary interface %s\n", cmd_hwif1->name,
+ cmd_hwif0->serialized ? "" : "not ", port2);
+
+ /*
+ * Establish initial timings/prefetch for all drives.
+ * Do not unnecessarily disturb any prior BIOS setup of these.
+ */
+ for (index = 0; index < (2 + (second_port_cmd640 << 1)); index++) {
+ ide_drive_t *drive = cmd_drives[index];
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ if (drive->autotune || ((index > 1) && second_port_toggled)) {
+ /*
+ * Reset timing to the slowest speed and turn off prefetch.
+ * This way, the drive identify code has a better chance.
+ */
+ setup_counts [index] = 4; /* max possible */
+ active_counts [index] = 16; /* max possible */
+ recovery_counts [index] = 16; /* max possible */
+ program_drive_counts (index);
+ set_prefetch_mode (index, 0);
+ printk("cmd640: drive%d timings/prefetch cleared\n", index);
+ } else {
+ /*
+ * Record timings/prefetch without changing them.
+ * This preserves any prior BIOS setup.
+ */
+ retrieve_drive_counts (index);
+ check_prefetch (index);
+ printk("cmd640: drive%d timings/prefetch(%s) preserved",
+ index, drive->no_io_32bit ? "off" : "on");
+ display_clocks(index);
+ }
+#else
+ /*
+ * Set the drive unmask flags to match the prefetch setting
+ */
+ check_prefetch (index);
+ printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
+ index, drive->no_io_32bit ? "off" : "on");
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+ }
+
+#ifdef CMD640_DUMP_REGS
+ CMD640_DUMP_REGS;
+#endif
+ return 1;
+}
+
diff --git a/drivers/block/dtc2278.c b/drivers/block/dtc2278.c
new file mode 100644
index 000000000..25966debc
--- /dev/null
+++ b/drivers/block/dtc2278.c
@@ -0,0 +1,128 @@
+/*
+ * linux/drivers/block/dtc2278.c Version 0.02 Feb 10, 1996
+ *
+ * Copyright (C) 1996 Linus Torvalds & author (see below)
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "ide_modes.h"
+
+/*
+ * Changing this #undef to #define may solve start up problems in some systems.
+ */
+#undef ALWAYS_SET_DTC2278_PIO_MODE
+
+/*
+ * From: andy@cercle.cts.com (Dyan Wile)
+ *
+ * Below is a patch for DTC-2278 - alike software-programmable controllers
+ * The code enables the secondary IDE controller and the PIO4 (3?) timings on
+ * the primary (EIDE). You may probably have to enable the 32-bit support to
+ * get the full speed. You better get the disk interrupts disabled ( hdparm -u0
+ * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my
+ * filesystem corrupted with -u1, but under heavy disk load only :-)
+ *
+ * This card is now forced to use the "serialize" feature,
+ * and irq-unmasking is disallowed. If io_32bit is enabled,
+ * it must be done for BOTH drives on each interface.
+ *
+ * This code was written for the DTC2278E, but might work with any of these:
+ *
+ * DTC2278S has only a single IDE interface.
+ * DTC2278D has two IDE interfaces and is otherwise identical to the S version.
+ * DTC2278E also has serial ports and a printer port
+ * DTC2278EB: has onboard BIOS, and "works like a charm" -- Kent Bradford <kent@theory.caltech.edu>
+ *
+ * There may be a fourth controller type. The S and D versions use the
+ * Winbond chip, and I think the E version does also.
+ *
+ */
+
+static void sub22 (char b, char c)
+{
+ int i;
+
+ for(i = 0; i < 3; ++i) {
+ inb(0x3f6);
+ outb_p(b,0xb0);
+ inb(0x3f6);
+ outb_p(c,0xb4);
+ inb(0x3f6);
+ if(inb(0xb4) == c) {
+ outb_p(7,0xb0);
+ inb(0x3f6);
+ return; /* success */
+ }
+ }
+}
+
+static void tune_dtc2278 (ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+
+ pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+
+ if (pio >= 3) {
+ save_flags(flags);
+ cli();
+ /*
+ * This enables PIO mode4 (3?) on the first interface
+ */
+ sub22(1,0xc3);
+ sub22(0,0xa0);
+ restore_flags(flags);
+ } else {
+ /* we don't know how to set it back again.. */
+ }
+
+ /*
+ * 32bit I/O has to be enabled for *both* drives at the same time.
+ */
+ drive->io_32bit = 1;
+ HWIF(drive)->drives[!drive->select.b.unit].io_32bit = 1;
+}
+
+void init_dtc2278 (void)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ /*
+ * This enables the second interface
+ */
+ outb_p(4,0xb0);
+ inb(0x3f6);
+ outb_p(0x20,0xb4);
+ inb(0x3f6);
+#ifdef ALWAYS_SET_DTC2278_PIO_MODE
+ /*
+ * This enables PIO mode4 (3?) on the first interface
+ * and may solve start-up problems for some people.
+ */
+ sub22(1,0xc3);
+ sub22(0,0xa0);
+#endif
+ restore_flags(flags);
+
+ ide_hwifs[0].serialized = 1;
+ ide_hwifs[1].serialized = 1;
+ ide_hwifs[0].chipset = ide_dtc2278;
+ ide_hwifs[1].chipset = ide_dtc2278;
+ ide_hwifs[0].tuneproc = &tune_dtc2278;
+ ide_hwifs[0].drives[0].no_unmask = 1;
+ ide_hwifs[0].drives[1].no_unmask = 1;
+ ide_hwifs[1].drives[0].no_unmask = 1;
+ ide_hwifs[1].drives[1].no_unmask = 1;
+}
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index e1501385f..7179c6f33 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -50,7 +50,7 @@
/* 1992/9/20
* Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
- * modelled after the freeware MS/DOS program fdformat/88 V1.8 by
+ * modeled after the freeware MS-DOS program fdformat/88 V1.8 by
* Christoph H. Hochst\"atter.
* I have fixed the shift values to the ones I always use. Maybe a new
* ioctl() should be created to be able to modify them.
@@ -77,42 +77,47 @@
* format bug fixes, but unfortunately some new bugs too...
*/
-/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
+/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
* errors to allow safe writing by specialized programs.
*/
-/* 1995/8/26 -- Andreas Busse -- added Mips support.
- * Needs some more cleanup, but seems to work so far.
+/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
+ * by defining bit 1 of the "stretch" parameter to mean put sectors on the
+ * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
+ * drives are "upside-down").
+ */
+
+/*
+ * 1995/8/26 -- Andreas Busse -- added Mips support.
+ */
+
+/*
+ * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent
+ * features to asm/floppy.h.
*/
-#define CONFIG_FLOPPY_SANITY
-#undef CONFIG_FLOPPY_SILENT_DCL_CLEAR
+
+#define FLOPPY_SANITY_CHECK
+#undef FLOPPY_SILENT_DCL_CLEAR
#define REALLY_SLOW_IO
#define DEBUGT 2
#define DCL_DEBUG /* debug disk change line */
-#include <linux/config.h>
-
/* do print messages for unexpected interrupts */
static int print_unex=1;
+#include <linux/module.h>
-#ifndef FD_MODULE
/* the following is the mask of allowed drives. By default units 2 and
* 3 of both floppy controllers are disabled, because switching on the
* motor of these drives causes system hangs on some PCI computers. drive
* 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
* a drive is allowed. */
-static int ALLOWED_DRIVE_MASK=0x33;
-
-#define FLOPPY_IRQ 6
-#define FLOPPY_DMA 2
-#define FDC1 0x3f0
-static int FDC2=-1;
-#endif
-
-#define MODULE_AWARE_DRIVER
+int FLOPPY_IRQ=6;
+int FLOPPY_DMA=2;
+static int allowed_drive_mask = 0x33;
+
#include <linux/sched.h>
#include <linux/fs.h>
@@ -121,69 +126,74 @@ static int FDC2=-1;
#include <linux/tqueue.h>
#define FDPATCHES
#include <linux/fdreg.h>
+
+
#include <linux/fd.h>
+#include <linux/hdreg.h>
+
#include <linux/errno.h>
#include <linux/malloc.h>
+#include <linux/mm.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+static int use_virtual_dma=0; /* virtual DMA for Intel */
+static unsigned short virtual_dma_port=0x3f0;
+void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+static int set_dor(int fdc, char mask, char data);
+#include <asm/floppy.h>
+
#define MAJOR_NR FLOPPY_MAJOR
-#include "blk.h"
-static unsigned int fake_change = 0;
-static int initialising=1;
+#include <linux/blk.h>
+#include <linux/cdrom.h> /* for the compatibility eject ioctl */
-#define FLOPPY0_TYPE ((CMOS_READ(0x10) >> 4) & 15)
-#define FLOPPY1_TYPE (CMOS_READ(0x10) & 15)
-#define N_FDC 2
-#define N_DRIVE 8
+#ifndef FLOPPY_MOTOR_MASK
+#define FLOPPY_MOTOR_MASK 0xf0
+#endif
-/*
- * Again, the CMOS information doesn't work on the alpha..
- */
-#ifdef __alpha__
-#undef FLOPPY0_TYPE
-#undef FLOPPY1_TYPE
-#define FLOPPY0_TYPE 6
-#define FLOPPY1_TYPE 0
+#ifndef fd_get_dma_residue
+#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
#endif
-/*
- * And on Mips's it doesn't work too.
- */
-#ifdef __mips__
-#include <asm/bootinfo.h>
-#include <asm/mipsconfig.h>
-#include <asm/jazz.h>
-#include <asm/jazzdma.h>
-#include <asm/cachectl.h>
-#undef FLOPPY0_TYPE
-#undef FLOPPY1_TYPE
-#undef FDC1
-#define FLOPPY0_TYPE 4 /* this is wrong for the Olli M700, but who cares... */
-#define FLOPPY1_TYPE 0
-#define FDC1 ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000 || \
- boot_info.machtype == MACH_OLIVETTI_M700) ? \
- 0xe0003000 : PORT_BASE + 0x3f0)
-#undef N_FDC
-#define N_FDC 1 /* do you *really* want a second controller ? */
-#endif /* __mips__ */
-
-#define TYPE(x) ( ((x)>>2) & 0x1f )
-#define DRIVE(x) ( ((x)&0x03) | (((x)&0x80 ) >> 5))
-#define UNIT(x) ( (x) & 0x03 ) /* drive on fdc */
-#define FDC(x) ( ((x) & 0x04) >> 2 ) /* fdc of drive */
-#define REVDRIVE(fdc, unit) ( (unit) + ((fdc) << 2 ))
+/* Dma Memory related stuff */
+
+#ifndef fd_dma_mem_free
+#define fd_dma_mem_free(addr, size) free_pages(addr, __get_order(size))
+#endif
+
+#ifndef fd_dma_mem_alloc
+#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
+#endif
+
+/* End dma memory related stuff */
+
+static unsigned int fake_change = 0;
+static int initialising=1;
+
+static inline int TYPE(kdev_t x) {
+ return (MINOR(x)>>2) & 0x1f;
+}
+static inline int DRIVE(kdev_t x) {
+ return (MINOR(x)&0x03) | ((MINOR(x)&0x80) >> 5);
+}
+#define ITYPE(x) (((x)>>2) & 0x1f)
+#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
+#define UNIT(x) ((x) & 0x03) /* drive on fdc */
+#define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */
+#define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2))
/* reverse mapping from unit and fdc to drive */
#define DP (&drive_params[current_drive])
#define DRS (&drive_state[current_drive])
@@ -201,34 +211,33 @@ static int initialising=1;
#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
-#define DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive)
+#define DPRINT(format, args...) printk(DEVICE_NAME "%d: " format, current_drive , ## args)
-#define DPRINT1(x,x1) \
-printk(DEVICE_NAME "%d: " x,current_drive,(x1))
+#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
+#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
-#define DPRINT2(x,x1,x2) \
-printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2))
+#define CLEARSTRUCT(x) memset((x), 0, sizeof(*(x)))
-#define DPRINT3(x,x1,x2,x3) \
-printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
+#define INT_OFF save_flags(flags); cli()
+#define INT_ON restore_flags(flags)
/* read/write */
-#define COMMAND raw_cmd.cmd[0]
-#define DR_SELECT raw_cmd.cmd[1]
-#define TRACK raw_cmd.cmd[2]
-#define HEAD raw_cmd.cmd[3]
-#define SECTOR raw_cmd.cmd[4]
-#define SIZECODE raw_cmd.cmd[5]
-#define SECT_PER_TRACK raw_cmd.cmd[6]
-#define GAP raw_cmd.cmd[7]
-#define SIZECODE2 raw_cmd.cmd[8]
+#define COMMAND raw_cmd->cmd[0]
+#define DR_SELECT raw_cmd->cmd[1]
+#define TRACK raw_cmd->cmd[2]
+#define HEAD raw_cmd->cmd[3]
+#define SECTOR raw_cmd->cmd[4]
+#define SIZECODE raw_cmd->cmd[5]
+#define SECT_PER_TRACK raw_cmd->cmd[6]
+#define GAP raw_cmd->cmd[7]
+#define SIZECODE2 raw_cmd->cmd[8]
#define NR_RW 9
/* format */
-#define F_SIZECODE raw_cmd.cmd[2]
-#define F_SECT_PER_TRACK raw_cmd.cmd[3]
-#define F_GAP raw_cmd.cmd[4]
-#define F_FILL raw_cmd.cmd[5]
+#define F_SIZECODE raw_cmd->cmd[2]
+#define F_SECT_PER_TRACK raw_cmd->cmd[3]
+#define F_GAP raw_cmd->cmd[4]
+#define F_FILL raw_cmd->cmd[5]
#define NR_F 6
/*
@@ -238,25 +247,12 @@ printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
*/
#define MAX_DISK_SIZE 2 /* 3984*/
-
-
-/*
- * The DMA channel used by the floppy controller cannot access data at
- * addresses >= 16MB
- *
- * Went back to the 1MB limit, as some people had problems with the floppy
- * driver otherwise. It doesn't matter much for performance anyway, as most
- * floppy accesses go through the track buffer.
- * On MIPSes, this actually means that *all* transfers go thru the
- * track buffer since 0x1000000 is always smaller than KSEG0/1.
- */
-#define LAST_DMA_ADDR (0x1000000)
-#define K_64 (0x10000) /* 64 k */
+#define K_64 0x10000 /* 64KB */
/*
* globals used by 'result()'
*/
-#define MAX_REPLIES 10
+#define MAX_REPLIES 16
static unsigned char reply_buffer[MAX_REPLIES];
static int inr; /* size of reply buffer, when called from interrupt */
#define ST0 (reply_buffer[0])
@@ -270,13 +266,13 @@ static int inr; /* size of reply buffer, when called from interrupt */
#define SEL_DLY (2*HZ/100)
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof( (x)[0] ))
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
/*
* this struct defines the different floppy drive types.
*/
static struct {
struct floppy_drive_params params;
- char *name; /* name printed while booting */
+ const char *name; /* name printed while booting */
} default_drive_params[]= {
/* NOTE: the time values in jiffies should be in msec!
CMOS drive type
@@ -313,23 +309,30 @@ static struct {
{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
-/* | ---autodetected formats-- | | |
- read_track | | Name printed when booting
- | Native format
- Frequency of disk change checks */
+/* | --autodetected formats--- | | |
+ * read_track | | Name printed when booting
+ * | Native format
+ * Frequency of disk change checks */
};
static struct floppy_drive_params drive_params[N_DRIVE];
static struct floppy_drive_struct drive_state[N_DRIVE];
static struct floppy_write_errors write_errors[N_DRIVE];
-static struct floppy_raw_cmd raw_cmd;
+static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
/*
* This struct defines the different floppy types.
*
- * The 'stretch' tells if the tracks need to be doubled for some
- * types (ie 360kB diskette in 1.2MB drive etc). Others should
- * be self-explanatory.
+ * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
+ * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
+ * tells if the disk is in Commodore 1581 format, which means side 0 sectors
+ * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
+ * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
+ * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
+ * side 0 is on physical side 0 (but with the misnamed sector IDs).
+ * 'stretch' should probably be renamed to something more general, like
+ * 'options'. Other parameters should be self-explanatory (see also
+ * setfdprm(8)).
*/
static struct floppy_struct floppy_type[32] = {
{ 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
@@ -341,7 +344,7 @@ static struct floppy_struct floppy_type[32] = {
{ 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
- { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"CompaQ"}, /* 9 2.88MB 3.5" */
+ { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120"}, /* 9 3.12MB 3.5" */
{ 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
{ 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
@@ -370,10 +373,10 @@ static struct floppy_struct floppy_type[32] = {
};
#define NUMBER(x) (sizeof(x) / sizeof(*(x)))
-#define SECTSIZE ( _FD_SECTSIZE(*floppy))
+#define SECTSIZE (_FD_SECTSIZE(*floppy))
/* Auto-detection: Disk type used until the next media change occurs. */
-struct floppy_struct *current_type[N_DRIVE] = {
+static struct floppy_struct *current_type[N_DRIVE] = {
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL
};
@@ -382,7 +385,7 @@ struct floppy_struct *current_type[N_DRIVE] = {
* User-provided type information. current_type points to
* the respective entry of this array.
*/
-struct floppy_struct user_params[N_DRIVE];
+static struct floppy_struct user_params[N_DRIVE];
static int floppy_sizes[256];
static int floppy_blocksizes[256] = { 0, };
@@ -395,7 +398,6 @@ static int floppy_blocksizes[256] = { 0, };
static int probing = 0;
/* Synchronization of FDC access. */
-#define FD_COMMAND_DETECT -2
#define FD_COMMAND_NONE -1
#define FD_COMMAND_ERROR 2
#define FD_COMMAND_OKAY 3
@@ -403,7 +405,8 @@ static int probing = 0;
static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0;
static struct wait_queue *fdc_wait = NULL, *command_done = NULL;
#define NO_SIGNAL (!(current->signal & ~current->blocked) || !interruptible)
-#define CALL(x) if( (x) == -EINTR) return -EINTR;
+#define CALL(x) if ((x) == -EINTR) return -EINTR
+#define ECALL(x) if ((ret = (x))) return ret;
#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
#define WAIT(x) _WAIT((x),interruptible)
#define IWAIT(x) _WAIT((x),1)
@@ -424,21 +427,21 @@ static struct format_descr format_req;
* Track buffer
* Because these are written to by the DMA controller, they must
* not contain a 64k byte boundary crossing, or data will be
- * corrupted/lost. Alignment of these is enforced in boot/head.S.
- * Note that you must not change the sizes below without updating head.S.
+ * corrupted/lost.
*/
-extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS];
-#define max_buffer_sectors MAX_BUFFER_SECTORS
+static char *floppy_track_buffer=0;
+static int max_buffer_sectors=0;
-int *errors;
+static int *errors;
typedef void (*done_f)(int);
-struct cont_t {
-void (*interrupt)(void); /* this is called after the interrupt of the
- * main command */
-void (*redo)(void); /* this is called to retry the operation */
-void (*error)(void); /* this is called to tally an error */
-done_f done; /* this is called to say if the operation has succeeded/failed */
-} *cont;
+static struct cont_t {
+ void (*interrupt)(void); /* this is called after the interrupt of the
+ * main command */
+ void (*redo)(void); /* this is called to retry the operation */
+ void (*error)(void); /* this is called to tally an error */
+ done_f done; /* this is called to say if the operation has
+ * succeeded/failed */
+} *cont=NULL;
static void floppy_ready(void);
static void floppy_start(void);
@@ -456,7 +459,7 @@ static void floppy_release_irq_and_dma(void);
* reset doesn't need to be tested before sending commands, because
* output_byte is automatically disabled when reset is set.
*/
-#define CHECK_RESET { if ( FDCS->reset ){ reset_fdc(); return ; } }
+#define CHECK_RESET { if (FDCS->reset){ reset_fdc(); return; } }
static void reset_fdc(void);
/*
@@ -482,14 +485,18 @@ static int buffer_max = -1;
static struct floppy_fdc_state fdc_state[N_FDC];
static int fdc; /* current fdc */
-static struct floppy_struct * floppy = floppy_type;
+static struct floppy_struct *_floppy = floppy_type;
static unsigned char current_drive = 0;
static long current_count_sectors = 0;
-static char *current_addr = 0;
static unsigned char sector_t; /* sector in track */
+#ifndef fd_eject
+#define fd_eject(x) -EINVAL
+#endif
+
+
#ifdef DEBUGT
-long unsigned debugtimer;
+static long unsigned debugtimer;
#endif
/*
@@ -503,113 +510,88 @@ static inline void set_debugt(void)
#endif
}
-static inline void debugt(char *message)
+static inline void debugt(const char *message)
{
#ifdef DEBUGT
- if ( DP->flags & DEBUGT )
- printk("%s dtime=%lu\n", message, jiffies-debugtimer );
+ if (DP->flags & DEBUGT)
+ printk("%s dtime=%lu\n", message, jiffies-debugtimer);
#endif
}
-/*
- * Port access functions
- */
+typedef void (*timeout_fn)(unsigned long);
+static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
+ (timeout_fn) floppy_shutdown };
-#ifdef CONFIG_MIPS_JAZZ
-static inline unsigned int fd_in(unsigned int port)
-{
- if (port >= JAZZ_LOCAL_IO_SPACE) {
- return (*(volatile unsigned char *)port);
- udelay(1);
- }
- else
- return inb_p(port);
-}
-
-static inline void fd_out(unsigned char value, unsigned int port)
+static const char *timeout_message;
+
+#ifdef FLOPPY_SANITY_CHECK
+static void is_alive(const char *message)
{
- if (port >= JAZZ_LOCAL_IO_SPACE) {
- *(volatile unsigned char *)port = value;
- udelay(1);
+ /* this routine checks whether the floppy driver is "alive" */
+ if (fdc_busy && command_status < 2 && !fd_timeout.prev){
+ DPRINT("timeout handler died: %s\n",message);
}
- else
- outb_p(value, port);
}
-#else /* !CONFIG_MIPS_JAZZ */
-#define fd_in(port) inb_p(port)
-#define fd_out(port,value) outb_p(port,value)
#endif
-/*
- * DMA control stuff. If MIPS_JAZZ support is compiled in, we have to
- * check if we're running on such a board. If not, we just call the
- * standard PC macros and functions.
- */
+#ifdef FLOPPY_SANITY_CHECK
-#ifdef CONFIG_MIPS_JAZZ
-#define fd_enable_dma() ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- vdma_enable(JAZZ_FLOPPY_DMA) : \
- enable_dma(FLOPPY_DMA))
-#define fd_disable_dma() ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- vdma_disable(JAZZ_FLOPPY_DMA) : \
- disable_dma(FLOPPY_DMA))
-#define fd_request_dma() ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- 0 : request_dma(FLOPPY_DMA,"floppy"))
-#define fd_free_dma() ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- : free_dma(FLOPPY_DMA))
-#define fd_clear_dma_ff() ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- : clear_dma_ff(FLOPPY_DMA))
-#define fd_set_dma_mode(mode) ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- vdma_set_mode(JAZZ_FLOPPY_DMA,mode) : \
- set_dma_mode(FLOPPY_DMA,mode))
-#define fd_set_dma_addr(addr) ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- vdma_set_addr(JAZZ_FLOPPY_DMA, \
- vdma_phys2log(PHYSADDR(addr))) : \
- set_dma_addr(FLOPPY_DMA,addr))
-#define fd_set_dma_count(count) ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- vdma_set_count(JAZZ_FLOPPY_DMA,count) : \
- set_dma_count(FLOPPY_DMA,count))
-#define fd_get_dma_residue() ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- vdma_get_residue(JAZZ_FLOPPY_DMA) : \
- get_dma_residue(FLOPPY_DMA))
-#define fd_enable_irq() ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- : enable_irq(FLOPPY_IRQ))
-#define fd_disable_irq() ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- : disable_irq(FLOPPY_IRQ))
-#define fd_cacheflush(addr,size) \
- ((boot_info.machtype == MACH_ACER_PICA_61 || \
- boot_info.machtype == MACH_MIPS_MAGNUM_4000) ? \
- sys_cacheflush((char *)addr, size, DCACHE) : empty() )
-#else
-#define fd_enable_dma() enable_dma(FLOPPY_DMA)
-#define fd_disable_dma() disable_dma(FLOPPY_DMA)
-#define fd_request_dma() request_dma(FLOPPY_DMA,"floppy")
-#define fd_free_dma() free_dma(FLOPPY_DMA)
-#define fd_clear_dma_ff() clear_dma_ff(FLOPPY_DMA)
-#define fd_set_dma_mode(mode) set_dma_mode(FLOPPY_DMA,mode)
-#define fd_set_dma_addr(addr) set_dma_addr(FLOPPY_DMA,addr)
-#define fd_set_dma_count(count) set_dma_count(FLOPPY_DMA,count)
-#define fd_enable_irq() enable_irq(FLOPPY_IRQ)
-#define fd_disable_irq() disable_irq(FLOPPY_IRQ)
-#define fd_cacheflush(addr,size) /* nothing */
+#define OLOGSIZE 20
+
+static void (*lasthandler)(void) = NULL;
+static int interruptjiffies=0;
+static int resultjiffies=0;
+static int resultsize=0;
+static int lastredo=0;
+
+static struct output_log {
+ unsigned char data;
+ unsigned char status;
+ unsigned long jiffies;
+} output_log[OLOGSIZE];
+
+static int output_log_pos=0;
#endif
-/* Just for coherency */
+#define CURRENTD -1
+#define MAXTIMEOUT -2
+
+static void reschedule_timeout(int drive, const char *message, int marg)
+{
+ if (drive == CURRENTD)
+ drive = current_drive;
+ del_timer(&fd_timeout);
+ if (drive < 0 || drive > N_DRIVE) {
+ fd_timeout.expires = jiffies + 20*HZ;
+ drive=0;
+ } else
+ fd_timeout.expires = jiffies + UDP->timeout;
+ add_timer(&fd_timeout);
+ if (UDP->flags & FD_DEBUG){
+ DPRINT("reschedule timeout ");
+ printk(message, marg);
+ printk("\n");
+ }
+ timeout_message = message;
+}
+
+static int maximum(int a, int b)
+{
+ if(a > b)
+ return a;
+ else
+ return b;
+}
+#define INFBOUND(a,b) (a)=maximum((a),(b));
-#define fd_request_irq() request_irq(FLOPPY_IRQ, floppy_interrupt, \
- SA_INTERRUPT, "floppy")
-#define fd_free_irq() free_irq(FLOPPY_IRQ);
+static int minimum(int a, int b)
+{
+ if(a < b)
+ return a;
+ else
+ return b;
+}
+#define SUPBOUND(a,b) (a)=minimum((a),(b));
/*
@@ -648,44 +630,45 @@ static inline void fd_out(unsigned char value, unsigned int port)
static int disk_change(int drive)
{
int fdc=FDC(drive);
-#ifdef CONFIG_FLOPPY_SANITY
- if(jiffies < UDP->select_delay + UDRS->select_date)
+#ifdef FLOPPY_SANITY_CHECK
+ if (jiffies - UDRS->select_date < UDP->select_delay)
DPRINT("WARNING disk change called early\n");
- if(! (FDCS->dor & (0x10 << UNIT(drive))) ||
+ if (!(FDCS->dor & (0x10 << UNIT(drive))) ||
(FDCS->dor & 3) != UNIT(drive) ||
fdc != FDC(drive)){
DPRINT("probing disk change on unselected drive\n");
- DPRINT3("drive=%d fdc=%d dor=%x\n",drive, FDC(drive),
+ DPRINT("drive=%d fdc=%d dor=%x\n",drive, FDC(drive),
FDCS->dor);
}
#endif
#ifdef DCL_DEBUG
if (UDP->flags & FD_DEBUG){
- DPRINT1("checking disk change line for drive %d\n",drive);
- DPRINT1("jiffies=%ld\n", jiffies);
- DPRINT1("disk change line=%x\n",fd_in(FD_DIR)&0x80);
- DPRINT1("flags=%x\n",UDRS->flags);
+ DPRINT("checking disk change line for drive %d\n",drive);
+ DPRINT("jiffies=%ld\n", jiffies);
+ DPRINT("disk change line=%x\n",fd_inb(FD_DIR)&0x80);
+ DPRINT("flags=%x\n",UDRS->flags);
}
#endif
if (UDP->flags & FD_BROKEN_DCL)
return UTESTF(FD_DISK_CHANGED);
- if( (fd_in(FD_DIR) ^ UDP->flags) & 0x80){
- USETF(FD_VERIFY); /* verify write protection */
- if(UDRS->maxblock){
+ if ((fd_inb(FD_DIR) ^ UDP->flags) & 0x80){
+ USETF(FD_VERIFY); /* verify write protection */
+ if (UDRS->maxblock){
/* mark it changed */
USETF(FD_DISK_CHANGED);
+ }
- /* invalidate its geometry */
- if (UDRS->keep_data >= 0) {
- if ((UDP->flags & FTD_MSG) &&
- current_type[drive] != NULL)
- DPRINT("Disk type is undefined after "
- "disk change\n");
- current_type[drive] = NULL;
- floppy_sizes[DRIVE(current_drive) + (FDC(current_drive) << 7)] = MAX_DISK_SIZE;
- }
+ /* invalidate its geometry */
+ if (UDRS->keep_data >= 0) {
+ if ((UDP->flags & FTD_MSG) &&
+ current_type[drive] != NULL)
+ DPRINT("Disk type is undefined after "
+ "disk change\n");
+ current_type[drive] = NULL;
+ floppy_sizes[TOMINOR(current_drive)] = MAX_DISK_SIZE;
}
+
/*USETF(FD_DISK_NEWCHANGE);*/
return 1;
} else {
@@ -697,42 +680,41 @@ static int disk_change(int drive)
static inline int is_selected(int dor, int unit)
{
- return ( (dor & (0x10 << unit)) && (dor &3) == unit);
+ return ((dor & (0x10 << unit)) && (dor &3) == unit);
}
static int set_dor(int fdc, char mask, char data)
{
register unsigned char drive, unit, newdor,olddor;
- if(FDCS->address == -1)
+ if (FDCS->address == -1)
return -1;
olddor = FDCS->dor;
newdor = (olddor & mask) | data;
- if ( newdor != olddor ){
+ if (newdor != olddor){
unit = olddor & 0x3;
- if(is_selected(olddor, unit) && !is_selected(newdor,unit)){
+ if (is_selected(olddor, unit) && !is_selected(newdor,unit)){
drive = REVDRIVE(fdc,unit);
#ifdef DCL_DEBUG
- if (UDP->flags & FD_DEBUG){
- DPRINT("calling disk change from set_dor\n");
- }
+ if (UDP->flags & FD_DEBUG){
+ DPRINT("calling disk change from set_dor\n");
+ }
#endif
disk_change(drive);
}
FDCS->dor = newdor;
-/* printk("setting DOR to %02x\n",(unsigned)newdor); */
- fd_out(newdor, FD_DOR);
+ fd_outb(newdor, FD_DOR);
unit = newdor & 0x3;
- if(!is_selected(olddor, unit) && is_selected(newdor,unit)){
+ if (!is_selected(olddor, unit) && is_selected(newdor,unit)){
drive = REVDRIVE(fdc,unit);
UDRS->select_date = jiffies;
}
}
- if ( newdor & 0xf0 )
+ if (newdor & FLOPPY_MOTOR_MASK)
floppy_grab_irq_and_dma();
- if( olddor & 0xf0 )
+ if (olddor & FLOPPY_MOTOR_MASK)
floppy_release_irq_and_dma();
return olddor;
}
@@ -741,8 +723,8 @@ static void twaddle(void)
{
if (DP->select_delay)
return;
- fd_out(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
- fd_out(FDCS->dor, FD_DOR);
+ fd_outb(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
+ fd_outb(FDCS->dor, FD_DOR);
DRS->select_date = jiffies;
}
@@ -756,9 +738,9 @@ static void reset_fdc_info(int mode)
FDCS->need_configure = 1;
FDCS->perp_mode = 1;
FDCS->rawcmd = 0;
- for ( drive = 0; drive < N_DRIVE; drive++)
+ for (drive = 0; drive < N_DRIVE; drive++)
if (FDC(drive) == fdc &&
- ( mode || UDRS->track != NEED_1_RECAL))
+ (mode || UDRS->track != NEED_1_RECAL))
UDRS->track = NEED_2_RECAL;
}
@@ -769,56 +751,62 @@ static void set_fdc(int drive)
fdc = FDC(drive);
current_drive = drive;
}
+ if (fdc != 1 && fdc != 0) {
+ printk("bad fdc value\n");
+ return;
+ }
set_dor(fdc,~0,8);
+#if N_FDC > 1
set_dor(1-fdc, ~8, 0);
- if ( FDCS->rawcmd == 2 )
+#endif
+ if (FDCS->rawcmd == 2)
reset_fdc_info(1);
- if( fd_in(FD_STATUS) != STATUS_READY )
+ if (fd_inb(FD_STATUS) != STATUS_READY)
FDCS->reset = 1;
}
/* locks the driver */
static int lock_fdc(int drive, int interruptible)
{
- if(!usage_count){
+ unsigned long flags;
+
+ if (!usage_count){
printk("trying to lock fdc while usage count=0\n");
return -1;
}
floppy_grab_irq_and_dma();
- if (!current->pid)
- run_task_queue(&tq_timer);
- cli();
+ INT_OFF;
while (fdc_busy && NO_SIGNAL)
interruptible_sleep_on(&fdc_wait);
- if(fdc_busy){
- sti();
+ if (fdc_busy){
+ INT_ON;
return -EINTR;
}
fdc_busy = 1;
- sti();
+ INT_ON;
command_status = FD_COMMAND_NONE;
+ reschedule_timeout(drive, "lock fdc", 0);
set_fdc(drive);
return 0;
}
#define LOCK_FDC(drive,interruptible) \
-if(lock_fdc(drive,interruptible)) return -EINTR;
+if (lock_fdc(drive,interruptible)) return -EINTR;
-typedef void (*timeout_fn)(unsigned long);
-static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
- (timeout_fn) floppy_shutdown };
/* unlocks the driver */
static inline void unlock_fdc(void)
{
+ raw_cmd = 0;
if (!fdc_busy)
DPRINT("FDC access conflict!\n");
- if ( DEVICE_INTR )
- DPRINT1("device interrupt still active at FDC release: %p!\n",
+ if (DEVICE_INTR)
+ DPRINT("device interrupt still active at FDC release: %p!\n",
DEVICE_INTR);
command_status = FD_COMMAND_NONE;
del_timer(&fd_timeout);
+ cont = NULL;
fdc_busy = 0;
floppy_release_irq_and_dma();
wake_up(&fdc_wait);
@@ -829,7 +817,7 @@ static void motor_off_callback(unsigned long nr)
{
unsigned char mask = ~(0x10 << UNIT(nr));
- set_dor( FDC(nr), mask, 0 );
+ set_dor(FDC(nr), mask, 0);
}
static struct timer_list motor_off_timer[N_DRIVE] = {
@@ -849,18 +837,18 @@ static void floppy_off(unsigned int drive)
unsigned long volatile delta;
register int fdc=FDC(drive);
- if( !(FDCS->dor & ( 0x10 << UNIT(drive))))
+ if (!(FDCS->dor & (0x10 << UNIT(drive))))
return;
del_timer(motor_off_timer+drive);
/* make spindle stop in a position which minimizes spinup time
* next time */
- if (UDP->rps ){
+ if (UDP->rps){
delta = jiffies - UDRS->first_read_date + HZ -
UDP->spindown_offset;
- delta = (( delta * UDP->rps) % HZ ) / UDP->rps;
- motor_off_timer[drive].expires = UDP->spindown - delta;
+ delta = ((delta * UDP->rps) % HZ) / UDP->rps;
+ motor_off_timer[drive].expires = jiffies + UDP->spindown - delta;
}
add_timer(motor_off_timer+drive);
}
@@ -873,27 +861,48 @@ static void floppy_off(unsigned int drive)
static void scandrives(void)
{
int i, drive, saved_drive;
-
+
if (DP->select_delay)
return;
saved_drive = current_drive;
- for(i=0; i< N_DRIVE; i++){
- drive = (saved_drive + i + 1 ) % N_DRIVE;
- if ( UDRS->fd_ref == 0 || UDP->select_delay != 0)
+ for (i=0; i < N_DRIVE; i++){
+ drive = (saved_drive + i + 1) % N_DRIVE;
+ if (UDRS->fd_ref == 0 || UDP->select_delay != 0)
continue; /* skip closed drives */
set_fdc(drive);
- if(! (set_dor( fdc, ~3, UNIT(drive) | ( 0x10 << UNIT(drive))) &
+ if (!(set_dor(fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) &
(0x10 << UNIT(drive))))
/* switch the motor off again, if it was off to
* begin with */
- set_dor( fdc, ~( 0x10 << UNIT(drive) ), 0 );
+ set_dor(fdc, ~(0x10 << UNIT(drive)), 0);
}
set_fdc(saved_drive);
}
+static void empty(void)
+{
+}
+
+static struct tq_struct floppy_tq =
+{ 0, 0, 0, 0 };
+
+static void schedule_bh( void (*handler)(void*) )
+{
+ floppy_tq.routine = (void *)(void *) handler;
+ queue_task_irq(&floppy_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 };
+static void cancel_activity(void)
+{
+ CLEAR_INTR;
+ floppy_tq.routine = (void *)(void *) empty;
+ del_timer(&fd_timer);
+}
+
/* this function makes sure that the disk stays in the drive during the
* transfer */
static void fd_watchdog(void)
@@ -904,13 +913,15 @@ static void fd_watchdog(void)
}
#endif
- if ( disk_change(current_drive) ){
+ if (disk_change(current_drive)){
DPRINT("disk removed during i/o\n");
- floppy_shutdown();
- } else {
+ cancel_activity();
+ cont->done(0);
+ reset_fdc();
+ } else {
del_timer(&fd_timer);
fd_timer.function = (timeout_fn) fd_watchdog;
- fd_timer.expires = 10;
+ fd_timer.expires = jiffies + HZ / 10;
add_timer(&fd_timer);
}
}
@@ -924,17 +935,17 @@ static void main_command_interrupt(void)
/* waits for a delay (spinup or select) to pass */
static int wait_for_completion(int delay, timeout_fn function)
{
- if ( FDCS->reset ){
+ if (FDCS->reset){
reset_fdc(); /* do the reset during sleep to win time
* if we don't need to sleep, it's a good
* occasion anyways */
return 1;
}
- if ( jiffies < delay ){
+ if ((signed) (jiffies - delay) < 0){
del_timer(&fd_timer);
fd_timer.function = function;
- fd_timer.expires = delay - jiffies;
+ fd_timer.expires = delay;
add_timer(&fd_timer);
return 1;
}
@@ -945,149 +956,168 @@ static int hlt_disabled=0;
static void floppy_disable_hlt(void)
{
unsigned long flags;
- save_flags(flags);
- cli();
- if(!hlt_disabled){
+
+ INT_OFF;
+ if (!hlt_disabled){
hlt_disabled=1;
#ifdef HAVE_DISABLE_HLT
disable_hlt();
#endif
}
- restore_flags(flags);
+ INT_ON;
}
static void floppy_enable_hlt(void)
{
unsigned long flags;
- save_flags(flags);
- cli();
- if(hlt_disabled){
+
+ INT_OFF;
+ if (hlt_disabled){
hlt_disabled=0;
#ifdef HAVE_DISABLE_HLT
enable_hlt();
#endif
}
- restore_flags(flags);
+ INT_ON;
}
-
+
static void setup_DMA(void)
{
-#ifdef CONFIG_FLOPPY_SANITY
- if (raw_cmd.length == 0){
+ unsigned long flags;
+
+#ifdef FLOPPY_SANITY_CHECK
+ if (raw_cmd->length == 0){
int i;
printk("zero dma transfer size:");
- for(i=0; i< raw_cmd.cmd_count; i++)
- printk("%x,", raw_cmd.cmd[i]);
+ for (i=0; i < raw_cmd->cmd_count; i++)
+ printk("%x,", raw_cmd->cmd[i]);
printk("\n");
cont->done(0);
FDCS->reset = 1;
return;
}
- if ((!CURRENT ||
- CURRENT->buffer != current_addr ||
- raw_cmd.length > 512 * CURRENT->nr_sectors) &&
- (current_addr < floppy_track_buffer ||
- current_addr + raw_cmd.length >
- floppy_track_buffer + 1024 * max_buffer_sectors)){
- printk("bad address. start=%p lg=%lx tb=%p\n",
- current_addr, raw_cmd.length, floppy_track_buffer);
- if ( CURRENT ){
- printk("buffer=%p nr=%lx cnr=%lx\n",
- CURRENT->buffer, CURRENT->nr_sectors,
- CURRENT->current_nr_sectors);
- }
+ if (((unsigned long) raw_cmd->kernel_data) % 512){
+ printk("non aligned address: %p\n", raw_cmd->kernel_data);
cont->done(0);
FDCS->reset=1;
return;
}
- if ((unsigned long) current_addr % 512 ){
- printk("non aligned address: %p\n", current_addr );
- cont->done(0);
- FDCS->reset=1;
- return;
- }
- if ( ( (unsigned long)current_addr & ~(64*1024-1) ) !=
- ((unsigned long)(current_addr + raw_cmd.length-1) & ~(64*1024-1))){
+ if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
printk("DMA crossing 64-K boundary %p-%p\n",
- current_addr, current_addr + raw_cmd.length);
+ raw_cmd->kernel_data,
+ raw_cmd->kernel_data + raw_cmd->length);
cont->done(0);
FDCS->reset=1;
return;
}
-
#endif
- cli();
+ INT_OFF;
fd_disable_dma();
fd_clear_dma_ff();
- fd_set_dma_mode((raw_cmd.flags & FD_RAW_READ)?
+ fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
+ fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
DMA_MODE_READ : DMA_MODE_WRITE);
- fd_set_dma_addr((long) current_addr);
- fd_set_dma_count(raw_cmd.length);
+ fd_set_dma_addr(raw_cmd->kernel_data);
+ fd_set_dma_count(raw_cmd->length);
+ virtual_dma_port = FDCS->address;
fd_enable_dma();
- sti();
+ INT_ON;
floppy_disable_hlt();
}
+void show_floppy(void);
+
+/* waits until the fdc becomes ready */
+static int wait_til_ready(void)
+{
+ int counter, status;
+ if(FDCS->reset)
+ return -1;
+ for (counter = 0; counter < 10000; counter++) {
+ status = fd_inb(FD_STATUS);
+ if (status & STATUS_READY)
+ return status;
+ }
+ if (!initialising) {
+ DPRINT("Getstatus times out (%x) on fdc %d\n",
+ status, fdc);
+ show_floppy();
+ }
+ FDCS->reset = 1;
+ return -1;
+}
+
/* sends a command byte to the fdc */
static int output_byte(char byte)
{
- int counter;
- unsigned char status;
+ int status;
- if (FDCS->reset)
+ if ((status = wait_til_ready()) < 0)
return -1;
-/* for(counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) { */
- for(counter = 0 ; counter < 100000 && !FDCS->reset ; counter++) {
- status = fd_in(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA);
- if (!(status & STATUS_READY))
- continue;
- if (status == STATUS_READY){
- fd_out(byte,FD_DATA);
- return 0;
- } else
- break;
+ if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY){
+ fd_outb(byte,FD_DATA);
+#ifdef FLOPPY_SANITY_CHECK
+ output_log[output_log_pos].data = byte;
+ output_log[output_log_pos].status = status;
+ output_log[output_log_pos].jiffies = jiffies;
+ output_log_pos = (output_log_pos + 1) % OLOGSIZE;
+#endif
+ return 0;
}
FDCS->reset = 1;
- if ( !initialising )
- DPRINT2("Unable to send byte %x to FDC. Status=%x\n",
- byte, status);
+ if (!initialising) {
+ DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n",
+ byte, fdc, status);
+ show_floppy();
+ }
return -1;
}
-#define LAST_OUT(x) if(output_byte(x)){ reset_fdc();return;}
+#define LAST_OUT(x) if (output_byte(x)<0){ reset_fdc();return;}
/* gets the response from the fdc */
static int result(void)
{
- int i = 0, counter, status;
+ int i, status;
- if (FDCS->reset)
- return -1;
- for (counter = 0 ; counter < 10000 && !FDCS->reset ; counter++) {
- status = fd_in(FD_STATUS)&
- (STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
- if (!(status & STATUS_READY))
- continue;
- if (status == STATUS_READY)
- return i;
- if (status & STATUS_DMA )
+ for(i=0; i < MAX_REPLIES; i++) {
+ if ((status = wait_til_ready()) < 0)
break;
- if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
- if (i >= MAX_REPLIES) {
- DPRINT("floppy_stat reply overrun\n");
- break;
- }
- reply_buffer[i++] = fd_in(FD_DATA);
+ status &= STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA;
+ if ((status & ~STATUS_BUSY) == STATUS_READY){
+#ifdef FLOPPY_SANITY_CHECK
+ resultjiffies = jiffies;
+ resultsize = i;
+#endif
+ return i;
}
+ if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY))
+ reply_buffer[i] = fd_inb(FD_DATA);
+ else
+ break;
+ }
+ if(!initialising) {
+ DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n",
+ fdc, status, i);
+ show_floppy();
}
FDCS->reset = 1;
- if ( !initialising )
- DPRINT3("Getstatus times out (%x) on fdc %d [%d]\n",
- status, fdc, i);
return -1;
}
+#define MORE_OUTPUT -2
+/* does the fdc need more output? */
+static int need_more_output(void)
+{
+ int status;
+ if( (status = wait_til_ready()) < 0)
+ return -1;
+ if ((status & (STATUS_READY|STATUS_DIR|STATUS_DMA)) == STATUS_READY)
+ return MORE_OUTPUT;
+ return result();
+}
+
/* Set perpendicular mode as required, based on data rate, if supported.
* 82077 Now tested. 1Mbps data rate only possible with 82077-1.
*/
@@ -1095,30 +1125,28 @@ static inline void perpendicular_mode(void)
{
unsigned char perp_mode;
- if (!floppy)
- return;
- if (floppy->rate & 0x40){
- switch(raw_cmd.rate){
- case 0:
- perp_mode=2;
- break;
- case 3:
- perp_mode=3;
- break;
- default:
- DPRINT("Invalid data rate for perpendicular mode!\n");
- cont->done(0);
- FDCS->reset = 1; /* convenient way to return to
- * redo without to much hassle (deep
- * stack et al. */
- return;
+ if (raw_cmd->rate & 0x40){
+ switch(raw_cmd->rate & 3){
+ case 0:
+ perp_mode=2;
+ break;
+ case 3:
+ perp_mode=3;
+ break;
+ default:
+ DPRINT("Invalid data rate for perpendicular mode!\n");
+ cont->done(0);
+ FDCS->reset = 1; /* convenient way to return to
+ * redo without to much hassle (deep
+ * stack et al. */
+ return;
}
} else
perp_mode = 0;
-
- if ( FDCS->perp_mode == perp_mode )
+
+ if (FDCS->perp_mode == perp_mode)
return;
- if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) {
+ if (FDCS->version >= FDC_82077_ORIG) {
output_byte(FD_PERPENDICULAR);
output_byte(perp_mode);
FDCS->perp_mode = perp_mode;
@@ -1127,6 +1155,22 @@ static inline void perpendicular_mode(void)
}
} /* perpendicular_mode */
+static int fifo_depth = 0xa;
+static int no_fifo = 0;
+
+static int fdc_configure(void)
+{
+ /* Turn on FIFO */
+ output_byte(FD_CONFIGURE);
+ if(need_more_output() != MORE_OUTPUT)
+ return 0;
+ output_byte(0);
+ output_byte(0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf));
+ output_byte(0); /* pre-compensation from track
+ 0 upwards */
+ return 1;
+}
+
#define NOMINAL_DTR 500
/* Issue a "SPECIFY" command to set the step rate time, head unload time,
@@ -1157,33 +1201,31 @@ static void fdc_specify(void)
int hlt_max_code = 0x7f;
int hut_max_code = 0xf;
- if (FDCS->need_configure && FDCS->has_fifo) {
- if ( FDCS->reset )
- return;
- /* Turn on FIFO for 82077-class FDC (improves performance) */
- /* TODO: lock this in via LOCK during initialization */
- output_byte(FD_CONFIGURE);
- output_byte(0);
- output_byte(0x2A); /* FIFO on, polling off, 10 byte threshold */
- output_byte(0); /* precompensation from track 0 upwards */
- if ( FDCS->reset ){
- FDCS->has_fifo=0;
- return;
- }
+ if (FDCS->need_configure && FDCS->version >= FDC_82072A) {
+ fdc_configure();
FDCS->need_configure = 0;
/*DPRINT("FIFO enabled\n");*/
}
- switch (raw_cmd.rate & 0x03) {
- case 3:
- dtr = 1000;
- break;
- case 1:
- dtr = 300;
- break;
- case 2:
- dtr = 250;
- break;
+ switch (raw_cmd->rate & 0x03) {
+ case 3:
+ dtr = 1000;
+ break;
+ case 1:
+ dtr = 300;
+ if (FDCS->version >= FDC_82078) {
+ /* chose the default rate table, not the one
+ * where 1 = 2 Mbps */
+ output_byte(FD_DRIVESPEC);
+ if(need_more_output() == MORE_OUTPUT) {
+ output_byte(UNIT(current_drive));
+ output_byte(0xc0);
+ }
+ }
+ break;
+ case 2:
+ dtr = 250;
+ break;
}
if (FDCS->version >= FDC_82072) {
@@ -1194,10 +1236,8 @@ static void fdc_specify(void)
/* Convert step rate from microseconds to milliseconds and 4 bits */
srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR;
- if (srt > 0xf)
- srt = 0xf;
- else if (srt < 0)
- srt = 0;
+ SUPBOUND(srt, 0xf);
+ INFBOUND(srt, 0);
hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR;
if (hlt < 0x01)
@@ -1212,7 +1252,7 @@ static void fdc_specify(void)
hut = hut_max_code;
spec1 = (srt << 4) | hut;
- spec2 = (hlt << 1);
+ spec2 = (hlt << 1) | (use_virtual_dma & 1);
/* If these parameters did not change, just return with success */
if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) {
@@ -1230,19 +1270,19 @@ static void fdc_specify(void)
static int fdc_dtr(void)
{
/* If data rate not already set to desired value, set it. */
- if ( raw_cmd.rate == FDCS->dtr)
+ if ((raw_cmd->rate & 3) == FDCS->dtr)
return 0;
-
+
/* Set dtr */
- fd_out(raw_cmd.rate, FD_DCR);
-
+ fd_outb(raw_cmd->rate & 3, FD_DCR);
+
/* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
* need a stabilization period of several milliseconds to be
* enforced after data rate changes before R/W operations.
* Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
*/
- FDCS->dtr = raw_cmd.rate;
- return(wait_for_completion(jiffies+2,
+ FDCS->dtr = raw_cmd->rate & 3;
+ return(wait_for_completion(jiffies+2*HZ/100,
(timeout_fn) floppy_ready));
} /* fdc_dtr */
@@ -1254,7 +1294,7 @@ static void tell_sector(void)
/*
- * Ok, this error interpreting routine is called after a
+ * OK, this error interpreting routine is called after a
* DMA read/write has succeeded
* or failed, so we check the results, and copy any buffers.
* hhb: Added better error reporting.
@@ -1271,8 +1311,10 @@ static int interpret_errors(void)
}
/* check IC to find cause of interrupt */
- switch ((ST0 & ST0_INTR)>>6) {
- case 1: /* error occurred during command execution */
+ switch (ST0 & ST0_INTR) {
+ case 0x40: /* error occurred during command execution */
+ if (ST1 & ST1_EOC)
+ return 0; /* occurs with pseudo-DMA */
bad = 1;
if (ST1 & ST1_WP) {
DPRINT("Drive is write protected\n");
@@ -1282,10 +1324,10 @@ static int interpret_errors(void)
} else if (ST1 & ST1_ND) {
SETF(FD_NEED_TWADDLE);
} else if (ST1 & ST1_OR) {
- if (DP->flags & FTD_MSG )
+ if (DP->flags & FTD_MSG)
DPRINT("Over/Underrun - retrying\n");
bad = 0;
- }else if(*errors >= DP->max_errors.reporting){
+ }else if (*errors >= DP->max_errors.reporting){
DPRINT("");
if (ST0 & ST0_ECE) {
printk("Recalibrate failed!");
@@ -1312,15 +1354,15 @@ static int interpret_errors(void)
printk("\n");
}
- if ( ST2 & ST2_WC || ST2 & ST2_BC)
+ if (ST2 & ST2_WC || ST2 & ST2_BC)
/* wrong cylinder => recal */
DRS->track = NEED_2_RECAL;
return bad;
- case 2: /* invalid command given */
+ case 0x80: /* invalid command given */
DPRINT("Invalid FDC command given!\n");
cont->done(0);
return 2;
- case 3:
+ case 0xc0:
DPRINT("Abnormal termination caused by polling\n");
cont->error();
return 2;
@@ -1331,7 +1373,7 @@ static int interpret_errors(void)
/*
* This routine is called when everything should be correctly set up
- * for the transfer (ie floppy motor is on, the correct floppy is
+ * for the transfer (i.e. floppy motor is on, the correct floppy is
* selected, and the head is sitting on the right track).
*/
static void setup_rw_floppy(void)
@@ -1339,17 +1381,17 @@ static void setup_rw_floppy(void)
int i,ready_date,r, flags,dflags;
timeout_fn function;
- flags = raw_cmd.flags;
- if ( flags & ( FD_RAW_READ | FD_RAW_WRITE))
+ flags = raw_cmd->flags;
+ if (flags & (FD_RAW_READ | FD_RAW_WRITE))
flags |= FD_RAW_INTR;
if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
- ready_date = DRS->spinup_date + DP->spinup;
+ ready_date = DRS->spinup_date + DP->spinup;
/* If spinup will take a long time, rerun scandrives
* again just before spinup completion. Beware that
* after scandrives, we must again wait for selection.
*/
- if ( ready_date > jiffies + DP->select_delay){
+ if ((signed) (ready_date - jiffies) > DP->select_delay){
ready_date -= DP->select_delay;
function = (timeout_fn) floppy_start;
} else
@@ -1361,28 +1403,29 @@ static void setup_rw_floppy(void)
}
dflags = DRS->flags;
- if ( (flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
+ if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
setup_DMA();
-
- if ( flags & FD_RAW_INTR )
+
+ if (flags & FD_RAW_INTR)
SET_INTR(main_command_interrupt);
r=0;
- for(i=0; i< raw_cmd.cmd_count; i++)
- r|=output_byte( raw_cmd.cmd[i] );
+ for (i=0; i< raw_cmd->cmd_count; i++)
+ r|=output_byte(raw_cmd->cmd[i]);
#ifdef DEBUGT
debugt("rw_command: ");
#endif
- if ( r ){
+ if (r){
+ cont->error();
reset_fdc();
return;
}
- if ( ! ( flags & FD_RAW_INTR )){
+ if (!(flags & FD_RAW_INTR)){
inr = result();
cont->interrupt();
- } else if ( flags & FD_RAW_NEED_DISK )
+ } else if (flags & FD_RAW_NEED_DISK)
fd_watchdog();
}
@@ -1397,7 +1440,7 @@ static void seek_interrupt(void)
#ifdef DEBUGT
debugt("seek interrupt:");
#endif
- if (inr != 2 || (ST0 & 0xF8) != 0x20 ) {
+ if (inr != 2 || (ST0 & 0xF8) != 0x20) {
DPRINT("seek failed\n");
DRS->track = NEED_2_RECAL;
cont->error();
@@ -1408,7 +1451,7 @@ static void seek_interrupt(void)
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("clearing NEWCHANGE flag because of effective seek\n");
- DPRINT1("jiffies=%ld\n", jiffies);
+ DPRINT("jiffies=%ld\n", jiffies);
}
#endif
CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
@@ -1422,9 +1465,9 @@ static void check_wp(void)
{
if (TESTF(FD_VERIFY)) {
/* check write protection */
- output_byte( FD_GETSTATUS );
- output_byte( UNIT(current_drive) );
- if ( result() != 1 ){
+ output_byte(FD_GETSTATUS);
+ output_byte(UNIT(current_drive));
+ if (result() != 1){
FDCS->reset = 1;
return;
}
@@ -1433,10 +1476,10 @@ static void check_wp(void)
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("checking whether disk is write protected\n");
- DPRINT1("wp=%x\n",ST3 & 0x40);
+ DPRINT("wp=%x\n",ST3 & 0x40);
}
#endif
- if (!( ST3 & 0x40))
+ if (!(ST3 & 0x40))
SETF(FD_DISK_WRITABLE);
else
CLEARF(FD_DISK_WRITABLE);
@@ -1457,7 +1500,7 @@ static void seek_floppy(void)
if (!TESTF(FD_DISK_NEWCHANGE) &&
disk_change(current_drive) &&
- (raw_cmd.flags & FD_RAW_NEED_DISK)){
+ (raw_cmd->flags & FD_RAW_NEED_DISK)){
/* the media changed flag should be cleared after the seek.
* If it isn't, this means that there is really no disk in
* the drive.
@@ -1467,32 +1510,31 @@ static void seek_floppy(void)
cont->redo();
return;
}
- if ( DRS->track <= NEED_1_RECAL ){
+ if (DRS->track <= NEED_1_RECAL){
recalibrate_floppy();
return;
} else if (TESTF(FD_DISK_NEWCHANGE) &&
- (raw_cmd.flags & FD_RAW_NEED_DISK) &&
- (DRS->track <= NO_TRACK || DRS->track == raw_cmd.track)) {
+ (raw_cmd->flags & FD_RAW_NEED_DISK) &&
+ (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) {
/* we seek to clear the media-changed condition. Does anybody
* know a more elegant way, which works on all drives? */
- if ( raw_cmd.track )
- track = raw_cmd.track - 1;
+ if (raw_cmd->track)
+ track = raw_cmd->track - 1;
else {
- if(DP->flags & FD_SILENT_DCL_CLEAR){
+ if (DP->flags & FD_SILENT_DCL_CLEAR){
set_dor(fdc, ~(0x10 << UNIT(current_drive)), 0);
blind_seek = 1;
- raw_cmd.flags |= FD_RAW_NEED_SEEK;
+ raw_cmd->flags |= FD_RAW_NEED_SEEK;
}
track = 1;
}
} else {
check_wp();
- if (raw_cmd.track != DRS->track &&
- (raw_cmd.flags & FD_RAW_NEED_SEEK))
- track = raw_cmd.track;
+ if (raw_cmd->track != DRS->track &&
+ (raw_cmd->flags & FD_RAW_NEED_SEEK))
+ track = raw_cmd->track;
else {
- setup_rw_floppy();
- return;
+ setup_rw_floppy(); return;
}
}
@@ -1510,115 +1552,121 @@ static void recal_interrupt(void)
#ifdef DEBUGT
debugt("recal interrupt:");
#endif
- if (inr !=2 )
+ if (inr !=2)
FDCS->reset = 1;
else if (ST0 & ST0_ECE) {
switch(DRS->track){
- case NEED_1_RECAL:
+ case NEED_1_RECAL:
#ifdef DEBUGT
- debugt("recal interrupt need 1 recal:");
+ debugt("recal interrupt need 1 recal:");
#endif
- /* after a second recalibrate, we still haven't
- * reached track 0. Probably no drive. Raise an
- * error, as failing immediately might upset
- * computers possessed by the Devil :-) */
- cont->error();
- cont->redo();
- return;
- case NEED_2_RECAL:
+ /* after a second recalibrate, we still haven't
+ * reached track 0. Probably no drive. Raise an
+ * error, as failing immediately might upset
+ * computers possessed by the Devil :-) */
+ cont->error();
+ cont->redo();
+ return;
+ case NEED_2_RECAL:
#ifdef DEBUGT
- debugt("recal interrupt need 2 recal:");
+ debugt("recal interrupt need 2 recal:");
#endif
- /* If we already did a recalibrate, and we are not at
- * track 0, this means we have moved. (The only way
- * not to move at recalibration is to be already at
- * track 0.) Clear the new change flag
- */
+ /* If we already did a recalibrate,
+ * and we are not at track 0, this
+ * means we have moved. (The only way
+ * not to move at recalibration is to
+ * be already at track 0.) Clear the
+ * new change flag */
#ifdef DCL_DEBUG
- if (DP->flags & FD_DEBUG){
- DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
- }
+ if (DP->flags & FD_DEBUG){
+ DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
+ }
#endif
- CLEARF(FD_DISK_NEWCHANGE);
- DRS->select_date = jiffies;
- /* fall through */
- default:
+ CLEARF(FD_DISK_NEWCHANGE);
+ DRS->select_date = jiffies;
+ /* fall through */
+ default:
#ifdef DEBUGT
- debugt("recal interrupt default:");
+ debugt("recal interrupt default:");
#endif
- /* Recalibrate moves the head by at most 80 steps. If
- * after one recalibrate we don't have reached track
- * 0, this might mean that we started beyond track 80.
- * Try again.
- */
- DRS->track = NEED_1_RECAL;
- break;
+ /* Recalibrate moves the head by at
+ * most 80 steps. If after one
+ * recalibrate we don't have reached
+ * track 0, this might mean that we
+ * started beyond track 80. Try
+ * again. */
+ DRS->track = NEED_1_RECAL;
+ break;
}
} else
DRS->track = ST1;
floppy_ready();
}
-/*
- * Unexpected interrupt - Print as much debugging info as we can...
- * All bets are off...
- */
-static void unexpected_floppy_interrupt(void)
+static void print_result(char *message, int inr)
{
int i;
- if ( initialising )
- return;
- if(print_unex){
- DPRINT("unexpected interrupt\n");
- if ( inr >= 0 )
- for(i=0; i<inr; i++)
- printk("%d %x\n", i, reply_buffer[i] );
- }
- while(1){
- output_byte(FD_SENSEI);
- inr=result();
- if ( inr != 2 )
- break;
- if(print_unex){
- printk("sensei\n");
- for(i=0; i<inr; i++)
- printk("%d %x\n", i, reply_buffer[i] );
- }
- }
- FDCS->reset = 1;
-}
-struct tq_struct floppy_tq =
-{ 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
+ DPRINT("%s ", message);
+ if (inr >= 0)
+ for (i=0; i<inr; i++)
+ printk("repl[%d]=%x ", i, reply_buffer[i]);
+ printk("\n");
+}
/* interrupt handler */
-static void floppy_interrupt(int irq, struct pt_regs * regs)
+void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
void (*handler)(void) = DEVICE_INTR;
+ int do_print;
+ lasthandler = handler;
+ interruptjiffies = jiffies;
+
+ fd_disable_dma();
floppy_enable_hlt();
CLEAR_INTR;
- if ( fdc >= N_FDC || FDCS->address == -1){
+ if (fdc >= N_FDC || FDCS->address == -1){
/* we don't even know which FDC is the culprit */
printk("DOR0=%x\n", fdc_state[0].dor);
printk("floppy interrupt on bizarre fdc %d\n",fdc);
printk("handler=%p\n", handler);
+ is_alive("bizarre fdc");
return;
}
+
+ FDCS->reset = 0;
+ /* We have to clear the reset flag here, because apparently on boxes
+ * with level triggered interrupts (PS/2, Sparc, ...), it is needed to
+ * emit SENSEI's to clear the interrupt line. And FDCS->reset blocks the
+ * emission of the SENSEI's.
+ * It is OK to emit floppy commands because we are in an interrupt
+ * handler here, and thus we have to fear no interference of other
+ * activity.
+ */
+
+ do_print = !handler && print_unex && !initialising;
+
inr = result();
- if (!handler){
- unexpected_floppy_interrupt();
- return;
- }
- if ( inr == 0 ){
+ if(do_print)
+ print_result("unexpected interrupt", inr);
+ if (inr == 0){
do {
output_byte(FD_SENSEI);
inr = result();
- } while ( (ST0 & 0x83) != UNIT(current_drive) && inr == 2);
+ if(do_print)
+ print_result("sensei", inr);
+ } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2);
}
- floppy_tq.routine = (void *)(void *) handler;
- queue_task_irq(&floppy_tq, &tq_timer);
+ if (handler) {
+ if(intr_count >= 2)
+ schedule_bh( (void *)(void *) handler);
+ else
+ handler();
+ } else
+ FDCS->reset = 1;
+ is_alive("normal interrupt end");
}
static void recalibrate_floppy(void)
@@ -1639,36 +1687,37 @@ static void reset_interrupt(void)
#ifdef DEBUGT
debugt("reset interrupt:");
#endif
- fdc_specify(); /* reprogram fdc */
result(); /* get the status ready for set_fdc */
- if ( FDCS->reset )
+ if (FDCS->reset) {
+ printk("reset set in interrupt, calling %p\n", cont->error);
cont->error(); /* a reset just after a reset. BAD! */
+ }
cont->redo();
}
/*
- * reset is done by pulling bit 2 of DOR low for a while (old FDC's),
- * or by setting the self clearing bit 7 of STATUS (newer FDC's)
+ * reset is done by pulling bit 2 of DOR low for a while (old FDCs),
+ * or by setting the self clearing bit 7 of STATUS (newer FDCs)
*/
static void reset_fdc(void)
{
-/* printk("in reset_fdc()\n"); */
SET_INTR(reset_interrupt);
FDCS->reset = 0;
reset_fdc_info(0);
- if ( FDCS->version >= FDC_82077 )
- fd_out(0x80 | ( FDCS->dtr &3), FD_STATUS);
+
+ /* Pseudo-DMA may intercept 'reset finished' interrupt. */
+ /* Irrelevant for systems with true DMA (i386). */
+ fd_disable_dma();
+
+ if (FDCS->version >= FDC_82077)
+ fd_outb(0x80 | (FDCS->dtr &3), FD_STATUS);
else {
- fd_out(FDCS->dor & ~0x04, FD_DOR);
+ fd_outb(FDCS->dor & ~0x04, FD_DOR);
udelay(FD_RESET_DELAY);
- fd_out(FDCS->dor, FD_DOR);
+ fd_outb(FDCS->dor, FD_DOR);
}
}
-static void empty(void)
-{
-}
-
void show_floppy(void)
{
int i;
@@ -1676,24 +1725,37 @@ void show_floppy(void)
printk("\n");
printk("floppy driver state\n");
printk("-------------------\n");
- for(i=0; i<N_FDC; i++){
- if(FDCS->address != -1){
- printk("dor %d = %x\n", i, fdc_state[i].dor );
- fd_out(fdc_state[i].address+2, fdc_state[i].dor);
- udelay(1000); /* maybe we'll catch an interrupt... */
- }
+ printk("now=%ld last interrupt=%d last called handler=%p\n",
+ jiffies, interruptjiffies, lasthandler);
+
+
+#ifdef FLOPPY_SANITY_CHECK
+ printk("timeout_message=%s\n", timeout_message);
+ printk("last output bytes:\n");
+ for (i=0; i < OLOGSIZE; i++)
+ printk("%2x %2x %ld\n",
+ output_log[(i+output_log_pos) % OLOGSIZE].data,
+ output_log[(i+output_log_pos) % OLOGSIZE].status,
+ output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
+ printk("last result at %d\n", resultjiffies);
+ printk("last redo_fd_request at %d\n", lastredo);
+ for (i=0; i<resultsize; i++){
+ printk("%2x ", reply_buffer[i]);
}
- printk("status=%x\n", fd_in(FD_STATUS));
+ printk("\n");
+#endif
+
+ printk("status=%x\n", fd_inb(FD_STATUS));
printk("fdc_busy=%d\n", fdc_busy);
- if( DEVICE_INTR)
+ if (DEVICE_INTR)
printk("DEVICE_INTR=%p\n", DEVICE_INTR);
- if(floppy_tq.sync)
+ if (floppy_tq.sync)
printk("floppy_tq.routine=%p\n", floppy_tq.routine);
- if(fd_timer.prev)
+ if (fd_timer.prev)
printk("fd_timer.function=%p\n", fd_timer.function);
- if(fd_timeout.prev){
+ if (fd_timeout.prev){
printk("timer_table=%p\n",fd_timeout.function);
- printk("expires=%ld\n",fd_timeout.expires);
+ printk("expires=%ld\n",fd_timeout.expires-jiffies);
printk("now=%ld\n",jiffies);
}
printk("cont=%p\n", cont);
@@ -1704,31 +1766,38 @@ void show_floppy(void)
static void floppy_shutdown(void)
{
- CLEAR_INTR;
- floppy_tq.routine = (void *)(void *) empty;
- del_timer( &fd_timer);
+ if (!initialising)
+ show_floppy();
+ cancel_activity();
+ sti();
floppy_enable_hlt();
fd_disable_dma();
/* avoid dma going to a random drive after shutdown */
- if(!initialising)
- DPRINT("floppy timeout\n");
+ if (!initialising)
+ DPRINT("floppy timeout called\n");
FDCS->reset = 1;
- cont->done(0);
- cont->redo(); /* this will recall reset when needed */
+ if (cont){
+ cont->done(0);
+ cont->redo(); /* this will recall reset when needed */
+ } else {
+ printk("no cont in shutdown!\n");
+ process_fd_request();
+ }
+ is_alive("floppy shutdown");
}
/*typedef void (*timeout_fn)(unsigned long);*/
/* start motor, check media-changed condition and write protection */
-static int start_motor( void (*function)(void) )
+static int start_motor(void (*function)(void) )
{
int mask, data;
mask = 0xfc;
data = UNIT(current_drive);
- if (!(raw_cmd.flags & FD_RAW_NO_MOTOR)){
- if(!(FDCS->dor & ( 0x10 << UNIT(current_drive) ) )){
+ if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)){
+ if (!(FDCS->dor & (0x10 << UNIT(current_drive)))){
set_debugt();
/* no read since this drive is running */
DRS->first_read_date = 0;
@@ -1737,12 +1806,12 @@ static int start_motor( void (*function)(void) )
data |= (0x10 << UNIT(current_drive));
}
} else
- if (FDCS->dor & ( 0x10 << UNIT(current_drive) ) )
+ if (FDCS->dor & (0x10 << UNIT(current_drive)))
mask &= ~(0x10 << UNIT(current_drive));
/* starts motor and selects floppy */
del_timer(motor_off_timer + current_drive);
- set_dor( fdc, mask, data);
+ set_dor(fdc, mask, data);
/* wait_for_completion also schedules reset if needed. */
return(wait_for_completion(DRS->select_date+DP->select_delay,
@@ -1752,8 +1821,8 @@ static int start_motor( void (*function)(void) )
static void floppy_ready(void)
{
CHECK_RESET;
- if(start_motor(floppy_ready)) return;
- if(fdc_dtr()) return;
+ if (start_motor(floppy_ready)) return;
+ if (fdc_dtr()) return;
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
@@ -1761,13 +1830,13 @@ static void floppy_ready(void)
}
#endif
- if(!(raw_cmd.flags & FD_RAW_NO_MOTOR) &&
+ if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
disk_change(current_drive) &&
!DP->select_delay)
twaddle(); /* this clears the dcl on certain drive/controller
* combinations */
- if ( raw_cmd.flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
+ if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
perpendicular_mode();
fdc_specify(); /* must be done here because of hut, hlt ... */
seek_floppy();
@@ -1777,15 +1846,13 @@ static void floppy_ready(void)
static void floppy_start(void)
{
- del_timer(&fd_timeout);
- fd_timeout.expires = DP->timeout;
- add_timer(&fd_timeout);
+ reschedule_timeout(CURRENTD, "floppy start", 0);
scandrives();
#ifdef DCL_DEBUG
- if (DP->flags & FD_DEBUG){
- DPRINT("setting NEWCHANGE in floppy_start\n");
- }
+ if (DP->flags & FD_DEBUG){
+ DPRINT("setting NEWCHANGE in floppy_start\n");
+ }
#endif
SETF(FD_DISK_NEWCHANGE);
floppy_ready();
@@ -1794,9 +1861,9 @@ static void floppy_start(void)
/*
* ========================================================================
* here ends the bottom half. Exported routines are:
- * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
+ * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
* start_motor, reset_fdc, reset_fdc_info, interpret_errors.
- * Initialisation also uses output_byte, result, set_dor, floppy_interrupt
+ * Initialization also uses output_byte, result, set_dor, floppy_interrupt
* and set_dor.
* ========================================================================
*/
@@ -1807,7 +1874,7 @@ static void floppy_start(void)
static void do_wakeup(void)
{
- del_timer(&fd_timeout);
+ reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
cont = 0;
command_status += 2;
wake_up(&command_done);
@@ -1820,33 +1887,40 @@ static struct cont_t wakeup_cont={
(done_f)empty
};
-static int wait_til_done( void (*handler)(void ), int interruptible )
+
+static struct cont_t intr_cont={
+ empty,
+ process_fd_request,
+ empty,
+ (done_f) empty
+};
+
+static int wait_til_done(void (*handler)(void), int interruptible)
{
int ret;
+ unsigned long flags;
- floppy_tq.routine = (void *)(void *) handler;
- queue_task(&floppy_tq, &tq_timer);
-
- cli();
- while(command_status < 2 && NO_SIGNAL)
- if (current->pid)
+ schedule_bh((void *)(void *)handler);
+ INT_OFF;
+ while(command_status < 2 && NO_SIGNAL){
+ is_alive("wait_til_done");
+ if (interruptible)
interruptible_sleep_on(&command_done);
- else {
- sti();
- run_task_queue(&tq_timer);
- cli();
- }
- if(command_status < 2){
- sti();
- floppy_shutdown();
- process_fd_request();
+ else
+ sleep_on(&command_done);
+ }
+ if (command_status < 2){
+ cancel_activity();
+ cont = &intr_cont;
+ reset_fdc();
+ INT_ON;
return -EINTR;
}
- sti();
+ INT_ON;
- if ( FDCS->reset )
+ if (FDCS->reset)
command_status = FD_COMMAND_ERROR;
- if ( command_status == FD_COMMAND_OKAY )
+ if (command_status == FD_COMMAND_OKAY)
ret=0;
else
ret=-EIO;
@@ -1862,25 +1936,20 @@ static void generic_done(int result)
static void generic_success(void)
{
- generic_done(1);
+ cont->done(1);
}
static void generic_failure(void)
{
- generic_done(0);
+ cont->done(0);
}
static void success_and_wakeup(void)
{
generic_success();
- do_wakeup();
+ cont->redo();
}
-static void failure_and_wakeup(void)
-{
- generic_failure();
- do_wakeup();
-}
/*
* formatting and rw support.
@@ -1893,12 +1962,12 @@ static int next_valid_format(void)
probed_format = DRS->probed_format;
while(1){
- if ( probed_format >= 8 ||
- ! DP->autodetect[probed_format] ){
+ if (probed_format >= 8 ||
+ !DP->autodetect[probed_format]){
DRS->probed_format = 0;
return 1;
}
- if ( floppy_type[DP->autodetect[probed_format]].sect ){
+ if (floppy_type[DP->autodetect[probed_format]].sect){
DRS->probed_format = probed_format;
return 0;
}
@@ -1908,14 +1977,13 @@ static int next_valid_format(void)
static void bad_flp_intr(void)
{
- if ( probing ){
+ if (probing){
DRS->probed_format++;
- if ( !next_valid_format())
+ if (!next_valid_format())
return;
}
(*errors)++;
- if (*errors > DRWE->badness)
- DRWE->badness = *errors;
+ INFBOUND(DRWE->badness, *errors);
if (*errors > DP->max_errors.abort)
cont->done(0);
if (*errors > DP->max_errors.reset)
@@ -1924,35 +1992,35 @@ static void bad_flp_intr(void)
DRS->track = NEED_2_RECAL;
}
-static void set_floppy(int device)
+static void set_floppy(kdev_t device)
{
if (TYPE(device))
- floppy = TYPE(device) + floppy_type;
+ _floppy = TYPE(device) + floppy_type;
else
- floppy = current_type[ DRIVE(device) ];
+ _floppy = current_type[ DRIVE(device) ];
}
/*
- * formatting and support.
- * =======================
+ * formatting support.
+ * ===================
*/
static void format_interrupt(void)
{
switch (interpret_errors()){
- case 1:
- cont->error();
- case 2:
- break;
- case 0:
- cont->done(1);
+ case 1:
+ cont->error();
+ case 2:
+ break;
+ case 0:
+ cont->done(1);
}
cont->redo();
}
-#define CODE2SIZE (ssize = ( ( 1 << SIZECODE ) + 3 ) >> 2)
-#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80 ) >>1))
-#define CT(x) ( (x) | 0x40 )
-static void setup_format_params(void)
+#define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2)
+#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1))
+#define CT(x) ((x) | 0x40)
+static void setup_format_params(int track)
{
struct fparm {
unsigned char track,head,sect,size;
@@ -1960,33 +2028,36 @@ static void setup_format_params(void)
int il,n;
int count,head_shift,track_shift;
- raw_cmd.flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
- /*FD_RAW_NEED_DISK |*/ FD_RAW_NEED_SEEK;
- raw_cmd.rate = floppy->rate & 0x3;
- raw_cmd.cmd_count = NR_F;
- COMMAND = FM_MODE(floppy,FD_FORMAT);
- DR_SELECT = UNIT(current_drive) + ( format_req.head << 2 );
- F_SIZECODE = FD_SIZECODE(floppy);
- F_SECT_PER_TRACK = floppy->sect << 2 >> F_SIZECODE;
- F_GAP = floppy->fmt_gap;
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->track = track;
+
+ raw_cmd->flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
+ FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
+ raw_cmd->rate = _floppy->rate & 0x43;
+ raw_cmd->cmd_count = NR_F;
+ COMMAND = FM_MODE(_floppy,FD_FORMAT);
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy,format_req.head);
+ F_SIZECODE = FD_SIZECODE(_floppy);
+ F_SECT_PER_TRACK = _floppy->sect << 2 >> F_SIZECODE;
+ F_GAP = _floppy->fmt_gap;
F_FILL = FD_FILL_BYTE;
- current_addr = floppy_track_buffer;
- raw_cmd.length = 4 * F_SECT_PER_TRACK;
+ raw_cmd->kernel_data = floppy_track_buffer;
+ raw_cmd->length = 4 * F_SECT_PER_TRACK;
/* allow for about 30ms for data transport per track */
head_shift = (F_SECT_PER_TRACK + 5) / 6;
/* a ``cylinder'' is two tracks plus a little stepping time */
- track_shift = 2 * head_shift + 1;
+ track_shift = 2 * head_shift + 3;
/* position of logical sector 1 on this track */
- n = (track_shift * format_req.track + head_shift * format_req.head )
+ n = (track_shift * format_req.track + head_shift * format_req.head)
% F_SECT_PER_TRACK;
/* determine interleave */
il = 1;
- if (floppy->sect > DP->interleave_sect && F_SIZECODE == 2)
+ if (_floppy->fmt_gap < 0x22)
il++;
/* initialize field */
@@ -2012,9 +2083,8 @@ static void setup_format_params(void)
static void redo_format(void)
{
- raw_cmd.track = format_req.track << floppy->stretch;
buffer_track = -1;
- setup_format_params();
+ setup_format_params(format_req.track << STRETCH(_floppy));
floppy_start();
#ifdef DEBUGT
debugt("queue format request");
@@ -2027,19 +2097,19 @@ static struct cont_t format_cont={
bad_flp_intr,
generic_done };
-static int do_format(int device, struct format_descr *tmp_format_req)
+static int do_format(kdev_t device, struct format_descr *tmp_format_req)
{
int ret;
int drive=DRIVE(device);
LOCK_FDC(drive,1);
set_floppy(device);
- if (!floppy ||
- floppy->track > DP->tracks ||
- tmp_format_req->track >= floppy->track ||
- tmp_format_req->head >= floppy->head ||
- (floppy->sect << 2) % (1 << FD_SIZECODE(floppy)) ||
- !floppy->fmt_gap) {
+ if (!_floppy ||
+ _floppy->track > DP->tracks ||
+ tmp_format_req->track >= _floppy->track ||
+ tmp_format_req->head >= _floppy->head ||
+ (_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) ||
+ !_floppy->fmt_gap) {
process_fd_request();
return -EINVAL;
}
@@ -2064,30 +2134,30 @@ static void request_done(int uptodate)
int block;
probing = 0;
- del_timer(&fd_timeout);
+ reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
if (!CURRENT){
DPRINT("request list destroyed in floppy request done\n");
return;
}
+
if (uptodate){
/* maintain values for invalidation on geometry
- change */
+ * change */
block = current_count_sectors + CURRENT->sector;
- if (block > DRS->maxblock)
- DRS->maxblock=block;
- if ( block > floppy->sect)
+ INFBOUND(DRS->maxblock, block);
+ if (block > _floppy->sect)
DRS->maxtrack = 1;
/* unlock chained buffers */
while (current_count_sectors && CURRENT &&
- current_count_sectors >= CURRENT->current_nr_sectors ){
+ current_count_sectors >= CURRENT->current_nr_sectors){
current_count_sectors -= CURRENT->current_nr_sectors;
CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
CURRENT->sector += CURRENT->current_nr_sectors;
end_request(1);
}
- if ( current_count_sectors && CURRENT){
+ if (current_count_sectors && CURRENT){
/* "unlock" last subsector */
CURRENT->buffer += current_count_sectors <<9;
CURRENT->current_nr_sectors -= current_count_sectors;
@@ -2096,14 +2166,14 @@ static void request_done(int uptodate)
return;
}
- if ( current_count_sectors && ! CURRENT )
+ if (current_count_sectors && !CURRENT)
DPRINT("request list destroyed in floppy request done\n");
} else {
- if(CURRENT->cmd == WRITE) {
+ if (CURRENT->cmd == WRITE) {
/* record write error information */
DRWE->write_errors++;
- if(DRWE->write_errors == 1) {
+ if (DRWE->write_errors == 1) {
DRWE->first_error_sector = CURRENT->sector;
DRWE->first_error_generation = DRS->generation;
}
@@ -2117,22 +2187,27 @@ static void request_done(int uptodate)
/* Interrupt handler evaluating the result of the r/w operation */
static void rw_interrupt(void)
{
- int nr_sectors, ssize;
+ int nr_sectors, ssize, eoc;
- if ( ! DRS->first_read_date )
+ if (!DRS->first_read_date)
DRS->first_read_date = jiffies;
nr_sectors = 0;
CODE2SIZE;
- nr_sectors = ((R_TRACK-TRACK)*floppy->head+R_HEAD-HEAD) *
- floppy->sect + ((R_SECTOR-SECTOR) << SIZECODE >> 2) -
- (sector_t % floppy->sect) % ssize;
-
-#ifdef CONFIG_FLOPPY_SANITY
- if ( nr_sectors > current_count_sectors + ssize -
- (current_count_sectors + sector_t) % ssize +
- sector_t % ssize){
- DPRINT2("long rw: %x instead of %lx\n",
+
+ if(ST1 & ST1_EOC)
+ eoc = 1;
+ else
+ eoc = 0;
+ nr_sectors = ((R_TRACK-TRACK)*_floppy->head+R_HEAD-HEAD) *
+ _floppy->sect + ((R_SECTOR-SECTOR+eoc) << SIZECODE >> 2) -
+ (sector_t % _floppy->sect) % ssize;
+
+#ifdef FLOPPY_SANITY_CHECK
+ if (nr_sectors > current_count_sectors + ssize -
+ (current_count_sectors + sector_t) % ssize +
+ sector_t % ssize){
+ DPRINT("long rw: %x instead of %lx\n",
nr_sectors, current_count_sectors);
printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
printk("rh=%d h=%d\n", R_HEAD, HEAD);
@@ -2141,51 +2216,47 @@ static void rw_interrupt(void)
sector_t, ssize);
}
#endif
- if ( nr_sectors < 0 )
- nr_sectors = 0;
- if ( nr_sectors < current_count_sectors )
- current_count_sectors = nr_sectors;
+ INFBOUND(nr_sectors,0);
+ SUPBOUND(current_count_sectors, nr_sectors);
switch (interpret_errors()){
- case 2:
- cont->redo();
- return;
- case 1:
- if ( !current_count_sectors){
- cont->error();
+ case 2:
cont->redo();
return;
- }
- break;
- case 0:
- if ( !current_count_sectors){
- cont->redo();
- return;
- }
- current_type[current_drive] = floppy;
- floppy_sizes[DRIVE(current_drive) + (FDC(current_drive) << 7)] =
- floppy->size >> 1;
- break;
+ case 1:
+ if (!current_count_sectors){
+ cont->error();
+ cont->redo();
+ return;
+ }
+ break;
+ case 0:
+ if (!current_count_sectors){
+ cont->redo();
+ return;
+ }
+ current_type[current_drive] = _floppy;
+ floppy_sizes[TOMINOR(current_drive) ]= _floppy->size>>1;
+ break;
}
if (probing) {
if (DP->flags & FTD_MSG)
- DPRINT2("Auto-detected floppy type %s in fd%d\n",
- floppy->name,current_drive);
- current_type[current_drive] = floppy;
- floppy_sizes[DRIVE(current_drive) + (FDC(current_drive) << 7)] =
- floppy->size >> 1;
+ DPRINT("Auto-detected floppy type %s in fd%d\n",
+ _floppy->name,current_drive);
+ current_type[current_drive] = _floppy;
+ floppy_sizes[TOMINOR(current_drive)] = _floppy->size >> 1;
probing = 0;
}
- if ( CT(COMMAND) != FD_READ || current_addr == CURRENT->buffer ){
+ if (CT(COMMAND) != FD_READ ||
+ raw_cmd->kernel_data == CURRENT->buffer){
/* transfer directly from buffer */
cont->done(1);
- } else if ( CT(COMMAND) == FD_READ){
- buffer_track = raw_cmd.track;
+ } else if (CT(COMMAND) == FD_READ){
+ buffer_track = raw_cmd->track;
buffer_drive = current_drive;
- if ( nr_sectors + sector_t > buffer_max )
- buffer_max = nr_sectors + sector_t;
+ INFBOUND(buffer_max, nr_sectors + sector_t);
}
cont->redo();
}
@@ -2203,7 +2274,7 @@ static int buffer_chain_size(void)
if (bh){
bh = bh->b_reqnext;
- while ( bh && bh->b_data == base + size ){
+ while (bh && bh->b_data == base + size){
size += bh->b_size;
bh = bh->b_reqnext;
}
@@ -2214,11 +2285,10 @@ static int buffer_chain_size(void)
/* Compute the maximal transfer size */
static int transfer_size(int ssize, int max_sector, int max_size)
{
- if ( max_sector > sector_t + max_size)
- max_sector = sector_t + max_size;
+ SUPBOUND(max_sector, sector_t + max_size);
/* alignment */
- max_sector -= (max_sector % floppy->sect ) % ssize;
+ max_sector -= (max_sector % _floppy->sect) % ssize;
/* transfer size, beginning not aligned */
current_count_sectors = max_sector - sector_t ;
@@ -2236,21 +2306,19 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
char *buffer, *dma_buffer;
int size;
- if ( max_sector > max_sector_2 )
- max_sector = max_sector_2;
-
- max_sector = transfer_size(ssize, max_sector, CURRENT->nr_sectors);
+ max_sector = transfer_size(ssize,
+ minimum(max_sector, max_sector_2),
+ CURRENT->nr_sectors);
if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
- buffer_max > sector_t + CURRENT->nr_sectors){
- current_count_sectors = buffer_max - sector_t;
- if ( current_count_sectors > CURRENT->nr_sectors )
- current_count_sectors = CURRENT->nr_sectors;
- }
+ buffer_max > sector_t + CURRENT->nr_sectors)
+ current_count_sectors = minimum(buffer_max - sector_t,
+ CURRENT->nr_sectors);
+
remaining = current_count_sectors << 9;
-#ifdef CONFIG_FLOPPY_SANITY
- if ((remaining >> 9) > CURRENT->nr_sectors &&
- CT(COMMAND) == FD_WRITE ){
+#ifdef FLOPPY_SANITY_CHECK
+ if ((remaining >> 9) > CURRENT->nr_sectors &&
+ CT(COMMAND) == FD_WRITE){
DPRINT("in copy buffer\n");
printk("current_count_sectors=%ld\n", current_count_sectors);
printk("remaining=%d\n", remaining >> 9);
@@ -2262,8 +2330,7 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
}
#endif
- if ( max_sector > buffer_max )
- buffer_max = max_sector;
+ buffer_max = maximum(max_sector, buffer_max);
dma_buffer = floppy_track_buffer + ((sector_t - buffer_min) << 9);
@@ -2271,44 +2338,39 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
size = CURRENT->current_nr_sectors << 9;
buffer = CURRENT->buffer;
- while ( remaining > 0){
- if ( size > remaining )
- size = remaining;
-#ifdef CONFIG_FLOPPY_SANITY
+ while (remaining > 0){
+ SUPBOUND(size, remaining);
+#ifdef FLOPPY_SANITY_CHECK
if (dma_buffer + size >
floppy_track_buffer + (max_buffer_sectors << 10) ||
- dma_buffer < floppy_track_buffer ){
- DPRINT1("buffer overrun in copy buffer %d\n",
+ dma_buffer < floppy_track_buffer){
+ DPRINT("buffer overrun in copy buffer %d\n",
(int) ((floppy_track_buffer - dma_buffer) >>9));
printk("sector_t=%d buffer_min=%d\n",
sector_t, buffer_min);
printk("current_count_sectors=%ld\n",
current_count_sectors);
- if ( CT(COMMAND) == FD_READ )
+ if (CT(COMMAND) == FD_READ)
printk("read\n");
- if ( CT(COMMAND) == FD_READ )
+ if (CT(COMMAND) == FD_READ)
printk("write\n");
break;
}
- if ( ((unsigned long)buffer) % 512 )
- DPRINT1("%p buffer not aligned\n", buffer);
+ if (((unsigned long)buffer) % 512)
+ DPRINT("%p buffer not aligned\n", buffer);
#endif
- if ( CT(COMMAND) == FD_READ ) {
- fd_cacheflush(dma_buffer, size);
- memcpy( buffer, dma_buffer, size);
- }
- else {
- memcpy( dma_buffer, buffer, size);
- fd_cacheflush(dma_buffer, size);
- }
+ if (CT(COMMAND) == FD_READ)
+ memcpy(buffer, dma_buffer, size);
+ else
+ memcpy(dma_buffer, buffer, size);
remaining -= size;
- if ( !remaining)
+ if (!remaining)
break;
dma_buffer += size;
bh = bh->b_reqnext;
-#ifdef CONFIG_FLOPPY_SANITY
- if ( !bh){
+#ifdef FLOPPY_SANITY_CHECK
+ if (!bh){
DPRINT("bh=null in copy buffer after copy\n");
break;
}
@@ -2316,15 +2378,26 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
size = bh->b_size;
buffer = bh->b_data;
}
-#ifdef CONFIG_FLOPPY_SANITY
- if ( remaining ){
- if ( remaining > 0 )
+#ifdef FLOPPY_SANITY_CHECK
+ if (remaining){
+ if (remaining > 0)
max_sector -= remaining >> 9;
- DPRINT1("weirdness: remaining %d\n", remaining>>9);
+ DPRINT("weirdness: remaining %d\n", remaining>>9);
}
#endif
}
+static inline int check_dma_crossing(char *start,
+ unsigned long length, char *message)
+{
+ if (CROSS_64KB(start, length)) {
+ printk("DMA xfer crosses 64KB boundary in %s %p-%p\n",
+ message, start, start+length);
+ return 1;
+ } else
+ return 0;
+}
+
/*
* Formulate a read/write request.
* this routine decides where to load the data (directly to buffer, or to
@@ -2340,80 +2413,83 @@ static int make_raw_rw_request(void)
int aligned_sector_t;
int max_sector, max_size, tracksize, ssize;
- set_fdc(DRIVE(CURRENT->dev));
+ set_fdc(DRIVE(CURRENT->rq_dev));
- raw_cmd.flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
FD_RAW_NEED_SEEK;
- raw_cmd.cmd_count = NR_RW;
+ raw_cmd->cmd_count = NR_RW;
if (CURRENT->cmd == READ){
- raw_cmd.flags |= FD_RAW_READ;
- COMMAND = FM_MODE(floppy,FD_READ);
+ raw_cmd->flags |= FD_RAW_READ;
+ COMMAND = FM_MODE(_floppy,FD_READ);
} else if (CURRENT->cmd == WRITE){
- raw_cmd.flags |= FD_RAW_WRITE;
- COMMAND = FM_MODE(floppy,FD_WRITE);
+ raw_cmd->flags |= FD_RAW_WRITE;
+ COMMAND = FM_MODE(_floppy,FD_WRITE);
} else {
DPRINT("make_raw_rw_request: unknown command\n");
return 0;
}
- max_sector = floppy->sect * floppy->head;
+ max_sector = _floppy->sect * _floppy->head;
+
TRACK = CURRENT->sector / max_sector;
sector_t = CURRENT->sector % max_sector;
- if ( floppy->track && TRACK >= floppy->track )
+ if (_floppy->track && TRACK >= _floppy->track)
return 0;
- HEAD = sector_t / floppy->sect;
+ HEAD = sector_t / _floppy->sect;
- if ( TESTF( FD_NEED_TWADDLE) && sector_t < floppy->sect )
- max_sector = floppy->sect;
+ if (((_floppy->stretch & FD_SWAPSIDES) || TESTF(FD_NEED_TWADDLE)) &&
+ sector_t < _floppy->sect)
+ max_sector = _floppy->sect;
/* 2M disks have phantom sectors on the first track */
- if ( (floppy->rate & FD_2M ) && (!TRACK) && (!HEAD)){
- max_sector = 2 * floppy->sect / 3;
+ if ((_floppy->rate & FD_2M) && (!TRACK) && (!HEAD)){
+ max_sector = 2 * _floppy->sect / 3;
if (sector_t >= max_sector){
- current_count_sectors = (floppy->sect - sector_t);
- if ( current_count_sectors > CURRENT->nr_sectors )
- current_count_sectors = CURRENT->nr_sectors;
+ current_count_sectors = minimum(_floppy->sect - sector_t,
+ CURRENT->nr_sectors);
return 1;
}
SIZECODE = 2;
} else
- SIZECODE = FD_SIZECODE(floppy);
- raw_cmd.rate = floppy->rate & 3;
- if ((floppy->rate & FD_2M) &&
- (TRACK || HEAD ) &&
- raw_cmd.rate == 2)
- raw_cmd.rate = 1;
-
- if ( SIZECODE )
+ SIZECODE = FD_SIZECODE(_floppy);
+ raw_cmd->rate = _floppy->rate & 0x43;
+ if ((_floppy->rate & FD_2M) &&
+ (TRACK || HEAD) &&
+ raw_cmd->rate == 2)
+ raw_cmd->rate = 1;
+
+ if (SIZECODE)
SIZECODE2 = 0xff;
else
SIZECODE2 = 0x80;
- raw_cmd.track = TRACK << floppy->stretch;
- DR_SELECT = UNIT(current_drive) + ( HEAD << 2 );
- GAP = floppy->gap;
+ raw_cmd->track = TRACK << STRETCH(_floppy);
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(_floppy,HEAD);
+ GAP = _floppy->gap;
CODE2SIZE;
- SECT_PER_TRACK = floppy->sect << 2 >> SIZECODE;
- SECTOR = ((sector_t % floppy->sect) << 2 >> SIZECODE) + 1;
- tracksize = floppy->sect - floppy->sect % ssize;
- if ( tracksize < floppy->sect ){
+ SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE;
+ SECTOR = ((sector_t % _floppy->sect) << 2 >> SIZECODE) + 1;
+ tracksize = _floppy->sect - _floppy->sect % ssize;
+ if (tracksize < _floppy->sect){
SECT_PER_TRACK ++;
- if ( tracksize <= sector_t % floppy->sect)
+ if (tracksize <= sector_t % _floppy->sect)
SECTOR--;
- while ( tracksize <= sector_t % floppy->sect){
- while( tracksize + ssize > floppy->sect ){
+ while (tracksize <= sector_t % _floppy->sect){
+ while(tracksize + ssize > _floppy->sect){
SIZECODE--;
ssize >>= 1;
}
SECTOR++; SECT_PER_TRACK ++;
tracksize += ssize;
}
- max_sector = HEAD * floppy->sect + tracksize;
- } else if ( !TRACK && !HEAD && !( floppy->rate & FD_2M ) && probing)
- max_sector = floppy->sect;
+ max_sector = HEAD * _floppy->sect + tracksize;
+ } else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing)
+ max_sector = _floppy->sect;
- aligned_sector_t = sector_t - ( sector_t % floppy->sect ) % ssize;
+ aligned_sector_t = sector_t - (sector_t % _floppy->sect) % ssize;
max_size = CURRENT->nr_sectors;
- if ((raw_cmd.track == buffer_track) && (current_drive == buffer_drive) &&
+ if ((raw_cmd->track == buffer_track) &&
+ (current_drive == buffer_drive) &&
(sector_t >= buffer_min) && (sector_t < buffer_max)) {
/* data already in track buffer */
if (CT(COMMAND) == FD_READ) {
@@ -2422,28 +2498,35 @@ static int make_raw_rw_request(void)
}
} else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
if (CT(COMMAND) == FD_WRITE){
- if(sector_t + CURRENT->nr_sectors > ssize &&
- sector_t + CURRENT->nr_sectors < ssize + ssize)
+ if (sector_t + CURRENT->nr_sectors > ssize &&
+ sector_t + CURRENT->nr_sectors < ssize + ssize)
max_size = ssize + ssize;
else
max_size = ssize;
}
- raw_cmd.flags &= ~FD_RAW_WRITE;
- raw_cmd.flags |= FD_RAW_READ;
- COMMAND = FM_MODE(floppy,FD_READ);
- } else if ((unsigned long)CURRENT->buffer <= LAST_DMA_ADDR ) {
+ raw_cmd->flags &= ~FD_RAW_WRITE;
+ raw_cmd->flags |= FD_RAW_READ;
+ COMMAND = FM_MODE(_floppy,FD_READ);
+ } else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS) {
+ unsigned long dma_limit;
int direct, indirect;
indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
sector_t;
+ /*
+ * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide
+ * on a 64 bit machine!
+ */
max_size = buffer_chain_size();
- if ( max_size > ( LAST_DMA_ADDR - ((unsigned long) CURRENT->buffer))>>9)
- max_size=(LAST_DMA_ADDR - ((unsigned long)CURRENT->buffer))>>9;
+ dma_limit = (MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer)) >> 9;
+ if ((unsigned long) max_size > dma_limit) {
+ max_size = dma_limit;
+ }
/* 64 kb boundaries */
- if ( ((max_size << 9) + ((unsigned long) CURRENT->buffer)) / K_64 !=
- ((unsigned long) CURRENT->buffer ) / K_64 )
- max_size = ( K_64 - ((unsigned long) CURRENT->buffer) % K_64)>>9;
+ if (CROSS_64KB(CURRENT->buffer, max_size << 9))
+ max_size = (K_64 -
+ ((unsigned long)CURRENT->buffer) % K_64)>>9;
direct = transfer_size(ssize,max_sector,max_size) - sector_t;
/*
* We try to read tracks, but if we get too many errors, we
@@ -2455,51 +2538,55 @@ static int make_raw_rw_request(void)
if (!direct ||
(indirect * 2 > direct * 3 &&
*errors < DP->max_errors.read_track &&
- /*!TESTF( FD_NEED_TWADDLE) &&*/
+ /*!TESTF(FD_NEED_TWADDLE) &&*/
((!probing || (DP->read_track&(1<<DRS->probed_format)))))){
max_size = CURRENT->nr_sectors;
} else {
- current_addr = CURRENT->buffer;
- raw_cmd.length = current_count_sectors << 9;
- if (raw_cmd.length == 0){
+ raw_cmd->kernel_data = CURRENT->buffer;
+ raw_cmd->length = current_count_sectors << 9;
+ if (raw_cmd->length == 0){
DPRINT("zero dma transfer attempted from make_raw_request\n");
- DPRINT3("indirect=%d direct=%d sector_t=%d",
+ DPRINT("indirect=%d direct=%d sector_t=%d",
indirect, direct, sector_t);
return 0;
}
+ check_dma_crossing(raw_cmd->kernel_data,
+ raw_cmd->length,
+ "end of make_raw_request [1]");
return 2;
}
}
- if ( CT(COMMAND) == FD_READ )
+ if (CT(COMMAND) == FD_READ)
max_size = max_sector; /* unbounded */
/* claim buffer track if needed */
- if (buffer_track != raw_cmd.track || /* bad track */
+ if (buffer_track != raw_cmd->track || /* bad track */
buffer_drive !=current_drive || /* bad drive */
sector_t > buffer_max ||
sector_t < buffer_min ||
((CT(COMMAND) == FD_READ ||
- (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize ))&&
+ (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize))&&
max_sector > 2 * max_buffer_sectors + buffer_min &&
max_size + sector_t > 2 * max_buffer_sectors + buffer_min)
- /* not enough space */ ){
+ /* not enough space */){
buffer_track = -1;
buffer_drive = current_drive;
buffer_max = buffer_min = aligned_sector_t;
}
- current_addr = floppy_track_buffer +((aligned_sector_t-buffer_min )<<9);
+ raw_cmd->kernel_data = floppy_track_buffer +
+ ((aligned_sector_t-buffer_min)<<9);
- if ( CT(COMMAND) == FD_WRITE ){
+ if (CT(COMMAND) == FD_WRITE){
/* copy write buffer to track buffer.
* if we get here, we know that the write
* is either aligned or the data already in the buffer
* (buffer will be overwritten) */
-#ifdef CONFIG_FLOPPY_SANITY
- if (sector_t != aligned_sector_t && buffer_track == -1 )
+#ifdef FLOPPY_SANITY_CHECK
+ if (sector_t != aligned_sector_t && buffer_track == -1)
DPRINT("internal error offset !=0 on write\n");
#endif
- buffer_track = raw_cmd.track;
+ buffer_track = raw_cmd->track;
buffer_drive = current_drive;
copy_buffer(ssize, max_sector, 2*max_buffer_sectors+buffer_min);
} else
@@ -2507,22 +2594,25 @@ static int make_raw_rw_request(void)
2*max_buffer_sectors+buffer_min-aligned_sector_t);
/* round up current_count_sectors to get dma xfer size */
- raw_cmd.length = sector_t+current_count_sectors-aligned_sector_t;
- raw_cmd.length = ((raw_cmd.length -1)|(ssize-1))+1;
- raw_cmd.length <<= 9;
-#ifdef CONFIG_FLOPPY_SANITY
- if ((raw_cmd.length < current_count_sectors << 9) ||
- (current_addr != CURRENT->buffer &&
+ raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t;
+ raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
+ raw_cmd->length <<= 9;
+#ifdef FLOPPY_SANITY_CHECK
+ check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length,
+ "end of make_raw_request");
+ if ((raw_cmd->length < current_count_sectors << 9) ||
+ (raw_cmd->kernel_data != CURRENT->buffer &&
CT(COMMAND) == FD_WRITE &&
- (aligned_sector_t + (raw_cmd.length >> 9) > buffer_max ||
- aligned_sector_t < buffer_min )) ||
- raw_cmd.length % ( 128 << SIZECODE ) ||
- raw_cmd.length <= 0 || current_count_sectors <= 0){
- DPRINT2("fractionary current count b=%lx s=%lx\n",
- raw_cmd.length, current_count_sectors);
- if ( current_addr != CURRENT->buffer )
+ (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
+ aligned_sector_t < buffer_min)) ||
+ raw_cmd->length % (128 << SIZECODE) ||
+ raw_cmd->length <= 0 || current_count_sectors <= 0){
+ DPRINT("fractionary current count b=%lx s=%lx\n",
+ raw_cmd->length, current_count_sectors);
+ if (raw_cmd->kernel_data != CURRENT->buffer)
printk("addr=%d, length=%ld\n",
- (int) ((current_addr - floppy_track_buffer ) >> 9),
+ (int) ((raw_cmd->kernel_data -
+ floppy_track_buffer) >> 9),
current_count_sectors);
printk("st=%d ast=%d mse=%d msi=%d\n",
sector_t, aligned_sector_t, max_sector, max_size);
@@ -2531,39 +2621,39 @@ static int make_raw_rw_request(void)
COMMAND, SECTOR, HEAD, TRACK);
printk("buffer drive=%d\n", buffer_drive);
printk("buffer track=%d\n", buffer_track);
- printk("buffer_min=%d\n", buffer_min );
- printk("buffer_max=%d\n", buffer_max );
+ printk("buffer_min=%d\n", buffer_min);
+ printk("buffer_max=%d\n", buffer_max);
return 0;
}
- if (current_addr != CURRENT->buffer ){
- if (current_addr < floppy_track_buffer ||
+ if (raw_cmd->kernel_data != CURRENT->buffer){
+ if (raw_cmd->kernel_data < floppy_track_buffer ||
current_count_sectors < 0 ||
- raw_cmd.length < 0 ||
- current_addr + raw_cmd.length >
+ raw_cmd->length < 0 ||
+ raw_cmd->kernel_data + raw_cmd->length >
floppy_track_buffer + (max_buffer_sectors << 10)){
DPRINT("buffer overrun in schedule dma\n");
printk("sector_t=%d buffer_min=%d current_count=%ld\n",
sector_t, buffer_min,
- raw_cmd.length >> 9 );
+ raw_cmd->length >> 9);
printk("current_count_sectors=%ld\n",
current_count_sectors);
- if ( CT(COMMAND) == FD_READ )
+ if (CT(COMMAND) == FD_READ)
printk("read\n");
- if ( CT(COMMAND) == FD_READ )
+ if (CT(COMMAND) == FD_READ)
printk("write\n");
return 0;
}
- } else if (raw_cmd.length > CURRENT->nr_sectors << 9 ||
+ } else if (raw_cmd->length > CURRENT->nr_sectors << 9 ||
current_count_sectors > CURRENT->nr_sectors){
DPRINT("buffer overrun in direct transfer\n");
return 0;
- } else if ( raw_cmd.length < current_count_sectors << 9 ){
+ } else if (raw_cmd->length < current_count_sectors << 9){
DPRINT("more sectors than bytes\n");
- printk("bytes=%ld\n", raw_cmd.length >> 9 );
+ printk("bytes=%ld\n", raw_cmd->length >> 9);
printk("sectors=%ld\n", current_count_sectors);
}
- if (raw_cmd.length == 0){
+ if (raw_cmd->length == 0){
DPRINT("zero dma transfer attempted from make_raw_request\n");
return 0;
}
@@ -2574,15 +2664,18 @@ static int make_raw_rw_request(void)
static void redo_fd_request(void)
{
#define REPEAT {request_done(0); continue; }
- int device;
+ kdev_t device;
int tmp;
- int error;
- error = -1;
+ lastredo = jiffies;
if (current_drive < N_DRIVE)
floppy_off(current_drive);
- if (CURRENT && CURRENT->dev < 0) return;
+ if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
+ CLEAR_INTR;
+ unlock_fdc();
+ return;
+ }
while(1){
if (!CURRENT) {
@@ -2590,57 +2683,48 @@ static void redo_fd_request(void)
unlock_fdc();
return;
}
- if (MAJOR(CURRENT->dev) != MAJOR_NR)
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
panic(DEVICE_NAME ": request list destroyed");
- if (CURRENT->bh && !CURRENT->bh->b_lock)
+ if (CURRENT->bh && !buffer_locked(CURRENT->bh))
panic(DEVICE_NAME ": block not locked");
-#if 0
- if (!CURRENT->bh->b_count &&
- (CURRENT->errors || error == CURRENT->dev)){
- error=CURRENT->dev;
- DPRINT("skipping read ahead buffer\n");
- REPEAT;
- }
-#endif
- error=-1;
- device = CURRENT->dev;
- set_fdc( DRIVE(device));
- del_timer(&fd_timeout);
- fd_timeout.expires = DP->timeout;
- add_timer(&fd_timeout);
+ device = CURRENT->rq_dev;
+ set_fdc(DRIVE(device));
+ reschedule_timeout(CURRENTD, "redo fd request", 0);
set_floppy(device);
- if(start_motor(redo_fd_request)) return;
- if(test_bit(current_drive, &fake_change) ||
+ raw_cmd = & default_raw_cmd;
+ raw_cmd->flags = 0;
+ if (start_motor(redo_fd_request)) return;
+ disk_change(current_drive);
+ if (test_bit(current_drive, &fake_change) ||
TESTF(FD_DISK_CHANGED)){
DPRINT("disk absent or changed during operation\n");
REPEAT;
}
- if (!floppy) { /* Autodetection */
+ if (!_floppy) { /* Autodetection */
if (!probing){
DRS->probed_format = 0;
- if ( next_valid_format() ){
+ if (next_valid_format()){
DPRINT("no autodetectable formats\n");
- floppy = NULL;
+ _floppy = NULL;
REPEAT;
}
}
probing = 1;
- floppy = floppy_type+DP->autodetect[DRS->probed_format];
+ _floppy = floppy_type+DP->autodetect[DRS->probed_format];
} else
probing = 0;
errors = & (CURRENT->errors);
tmp = make_raw_rw_request();
- if ( tmp < 2 ){
+ if (tmp < 2){
request_done(tmp);
continue;
}
if (TESTF(FD_NEED_TWADDLE))
twaddle();
- floppy_tq.routine = (void *)(void *) floppy_start;
- queue_task(&floppy_tq, &tq_timer);
+ schedule_bh( (void *)(void *) floppy_start);
#ifdef DEBUGT
debugt("queue fd request");
#endif
@@ -2655,25 +2739,29 @@ static struct cont_t rw_cont={
bad_flp_intr,
request_done };
-struct tq_struct request_tq =
-{ 0, 0, (void *) (void *) redo_fd_request, 0 };
-
static void process_fd_request(void)
{
cont = &rw_cont;
- queue_task(&request_tq, &tq_timer);
+ schedule_bh( (void *)(void *) redo_fd_request);
}
static void do_fd_request(void)
{
- if (fdc_busy)
+ if(usage_count == 0) {
+ printk("warning: usage count=0, CURRENT=%p exiting\n", CURRENT);
+ printk("sect=%ld cmd=%d\n", CURRENT->sector, CURRENT->cmd);
+ return;
+ }
+ sti();
+ if (fdc_busy){
/* fdc busy, this new request will be treated when the
current one is done */
+ is_alive("do fd request, old request running");
return;
- /* fdc_busy cannot be set by an interrupt or a bh */
- floppy_grab_irq_and_dma();
- fdc_busy=1;
+ }
+ lock_fdc(MAXTIMEOUT,0);
process_fd_request();
+ is_alive("do fd request");
}
static struct cont_t poll_cont={
@@ -2682,17 +2770,19 @@ static struct cont_t poll_cont={
generic_failure,
generic_done };
-static int poll_drive(int interruptible, int flag){
+static int poll_drive(int interruptible, int flag)
+{
int ret;
/* no auto-sense, just clear dcl */
- raw_cmd.flags= flag;
- raw_cmd.track=0;
- raw_cmd.cmd_count=0;
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->flags= flag;
+ raw_cmd->track=0;
+ raw_cmd->cmd_count=0;
cont = &poll_cont;
#ifdef DCL_DEBUG
- if (DP->flags & FD_DEBUG){
- DPRINT("setting NEWCHANGE in poll_drive\n");
- }
+ if (DP->flags & FD_DEBUG){
+ DPRINT("setting NEWCHANGE in poll_drive\n");
+ }
#endif
SETF(FD_DISK_NEWCHANGE);
WAIT(floppy_ready);
@@ -2720,16 +2810,11 @@ static int user_reset_fdc(int drive, int arg, int interruptible)
int ret;
ret=0;
- if(arg == FD_RESET_IF_NEEDED && !FDCS->reset)
- return 0;
LOCK_FDC(drive,interruptible);
- if(arg == FD_RESET_ALWAYS)
+ if (arg == FD_RESET_ALWAYS)
FDCS->reset=1;
- if ( FDCS->reset ){
+ if (FDCS->reset){
cont = &reset_cont;
- del_timer(&fd_timeout);
- fd_timeout.expires = DP->timeout;
- add_timer(&fd_timeout);
WAIT(reset_fdc);
}
process_fd_request();
@@ -2740,276 +2825,291 @@ static int user_reset_fdc(int drive, int arg, int interruptible)
* Misc Ioctl's and support
* ========================
*/
-static int fd_copyout(void *param, volatile void *address, int size)
+static inline int fd_copyout(void *param, const void *address, int size)
{
- int i;
+ return copy_to_user(param,address, size) ? -EFAULT : 0;
+}
- i = verify_area(VERIFY_WRITE,param,size);
- if (i)
- return i;
- fd_cacheflush(address, size); /* is this necessary ??? */
- /* Ralf: Yes; only the l2 cache is completly chipset
- controlled */
- memcpy_tofs(param,(void *) address, size);
- return 0;
+static inline int fd_copyin(void *param, void *address, int size)
+{
+ return copy_from_user(address, param, size) ? -EFAULT : 0;
}
-#define COPYOUT(x) (fd_copyout( (void *)param, &(x), sizeof(x)))
-#define COPYIN(x) (memcpy_fromfs( &(x), (void *) param, sizeof(x)),0)
+#define _COPYOUT(x) (copy_to_user((void *)param, &(x), sizeof(x)) ? -EFAULT : 0)
+#define _COPYIN(x) (copy_from_user(&(x), (void *)param, sizeof(x)) ? -EFAULT : 0)
+
+#define COPYOUT(x) ECALL(_COPYOUT(x))
+#define COPYIN(x) ECALL(_COPYIN(x))
-static char *drive_name(int type, int drive )
+static inline const char *drive_name(int type, int drive)
{
- struct floppy_struct *floppy;
+ struct floppy_struct *floppy;
- if ( type )
+ if (type)
floppy = floppy_type + type;
else {
- if ( UDP->native_format )
+ if (UDP->native_format)
floppy = floppy_type + UDP->native_format;
else
return "(null)";
}
- if ( floppy->name )
+ if (floppy->name)
return floppy->name;
else
return "(null)";
}
+
/* raw commands */
+static void raw_cmd_done(int flag)
+{
+ int i;
+
+ if (!flag) {
+ raw_cmd->flags |= FD_RAW_FAILURE;
+ raw_cmd->flags |= FD_RAW_HARDFAILURE;
+ } else {
+ raw_cmd->reply_count = inr;
+ for (i=0; i< raw_cmd->reply_count; i++)
+ raw_cmd->reply[i] = reply_buffer[i];
+
+ if (raw_cmd->flags & (FD_RAW_READ | FD_RAW_WRITE))
+ raw_cmd->length = fd_get_dma_residue();
+
+ if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
+ (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
+ raw_cmd->flags |= FD_RAW_FAILURE;
+
+ if (disk_change(current_drive))
+ raw_cmd->flags |= FD_RAW_DISK_CHANGE;
+ else
+ raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
+ if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
+ motor_off_callback(current_drive);
+
+ if (raw_cmd->next &&
+ (!(raw_cmd->flags & FD_RAW_FAILURE) ||
+ !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
+ ((raw_cmd->flags & FD_RAW_FAILURE) ||
+ !(raw_cmd->flags &FD_RAW_STOP_IF_SUCCESS))) {
+ raw_cmd = raw_cmd->next;
+ return;
+ }
+ }
+ generic_done(flag);
+}
+
+
static struct cont_t raw_cmd_cont={
success_and_wakeup,
- failure_and_wakeup,
+ floppy_start,
generic_failure,
- generic_done };
+ raw_cmd_done
+};
+
+static inline int raw_cmd_copyout(int cmd, char *param,
+ struct floppy_raw_cmd *ptr)
+{
+ int ret;
+
+ while(ptr) {
+ COPYOUT(*ptr);
+ param += sizeof(struct floppy_raw_cmd);
+ if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length){
+ if (ptr->length>=0 && ptr->length<=ptr->buffer_length)
+ ECALL(fd_copyout(ptr->data,
+ ptr->kernel_data,
+ ptr->buffer_length -
+ ptr->length));
+ }
+ ptr = ptr->next;
+ }
+ return 0;
+}
-static int raw_cmd_ioctl(void *param)
+
+static void raw_cmd_free(struct floppy_raw_cmd **ptr)
{
- int i, drive, count, ret;
+ struct floppy_raw_cmd *next,*this;
+
+ this = *ptr;
+ *ptr = 0;
+ while(this) {
+ if (this->buffer_length) {
+ fd_dma_mem_free((unsigned long)this->kernel_data,
+ this->buffer_length);
+ this->buffer_length = 0;
+ }
+ next = this->next;
+ kfree(this);
+ this = next;
+ }
+}
+
- if ( FDCS->rawcmd <= 1 )
+static inline int raw_cmd_copyin(int cmd, char *param,
+ struct floppy_raw_cmd **rcmd)
+{
+ struct floppy_raw_cmd *ptr;
+ int ret;
+ int i;
+
+ *rcmd = 0;
+ while(1) {
+ ptr = (struct floppy_raw_cmd *)
+ kmalloc(sizeof(struct floppy_raw_cmd), GFP_USER);
+ if (!ptr)
+ return -ENOMEM;
+ *rcmd = ptr;
+ COPYIN(*ptr);
+ ptr->next = 0;
+ ptr->buffer_length = 0;
+ param += sizeof(struct floppy_raw_cmd);
+ if (ptr->cmd_count > 33)
+ /* the command may now also take up the space
+ * initially intended for the reply & the
+ * reply count. Needed for long 82078 commands
+ * such as RESTORE, which takes ... 17 command
+ * bytes. Murphy's law #137: When you reserve
+ * 16 bytes for a structure, you'll one day
+ * discover that you really need 17...
+ */
+ return -EINVAL;
+
+ for (i=0; i< 16; i++)
+ ptr->reply[i] = 0;
+ ptr->resultcode = 0;
+ ptr->kernel_data = 0;
+
+ if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
+ if (ptr->length <= 0)
+ return -EINVAL;
+ ptr->kernel_data =(char*)fd_dma_mem_alloc(ptr->length);
+ if (!ptr->kernel_data)
+ return -ENOMEM;
+ ptr->buffer_length = ptr->length;
+ }
+ if (ptr->flags & FD_RAW_WRITE)
+ ECALL(fd_copyin(ptr->data, ptr->kernel_data,
+ ptr->length));
+ rcmd = & (ptr->next);
+ if (!(ptr->flags & FD_RAW_MORE))
+ return 0;
+ ptr->rate &= 0x43;
+ }
+}
+
+
+static int raw_cmd_ioctl(int cmd, void *param)
+{
+ int drive, ret, ret2;
+ struct floppy_raw_cmd *my_raw_cmd;
+
+ if (FDCS->rawcmd <= 1)
FDCS->rawcmd = 1;
- for ( drive= 0; drive < N_DRIVE; drive++){
- if ( FDC(drive) != fdc)
+ for (drive= 0; drive < N_DRIVE; drive++){
+ if (FDC(drive) != fdc)
continue;
- if ( drive == current_drive ){
- if ( UDRS->fd_ref > 1 ){
+ if (drive == current_drive){
+ if (UDRS->fd_ref > 1){
FDCS->rawcmd = 2;
break;
}
- } else if ( UDRS->fd_ref ){
+ } else if (UDRS->fd_ref){
FDCS->rawcmd = 2;
break;
}
}
- if(FDCS->reset)
+ if (FDCS->reset)
return -EIO;
- COPYIN(raw_cmd);
- raw_cmd.rate &= 0x03;
- count = raw_cmd.length;
- if (raw_cmd.flags & (FD_RAW_WRITE | FD_RAW_READ)){
- if(count > max_buffer_sectors * 1024 )
- return -ENOMEM;
- if(count == 0){
- printk("attempt to do a 0 byte dma transfer\n");
- return -EINVAL;
- }
- buffer_track = -1;
- }
- if ( raw_cmd.flags & FD_RAW_WRITE ){
- i = verify_area(VERIFY_READ, raw_cmd.data, count );
- if (i)
- return i;
- memcpy_fromfs(floppy_track_buffer, raw_cmd.data, count);
- fd_cacheflush(floppy_track_buffer, count);
+ ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
+ if (ret) {
+ raw_cmd_free(&my_raw_cmd);
+ return ret;
}
- current_addr = floppy_track_buffer;
+ raw_cmd = my_raw_cmd;
cont = &raw_cmd_cont;
- IWAIT(floppy_start);
+ ret=wait_til_done(floppy_start,1);
#ifdef DCL_DEBUG
if (DP->flags & FD_DEBUG){
DPRINT("calling disk change from raw_cmd ioctl\n");
}
#endif
- if( disk_change(current_drive) )
- raw_cmd.flags |= FD_RAW_DISK_CHANGE;
- else
- raw_cmd.flags &= ~FD_RAW_DISK_CHANGE;
- if(raw_cmd.flags & FD_RAW_NO_MOTOR_AFTER)
- motor_off_callback(current_drive);
-
- if ( !ret && !FDCS->reset ){
- raw_cmd.reply_count = inr;
- for( i=0; i< raw_cmd.reply_count; i++)
- raw_cmd.reply[i] = reply_buffer[i];
- if ( raw_cmd.flags & ( FD_RAW_READ | FD_RAW_WRITE ))
- raw_cmd.length = get_dma_residue(FLOPPY_DMA);
- } else
+
+ if (ret != -EINTR && FDCS->reset)
ret = -EIO;
+
DRS->track = NO_TRACK;
- if ( ret )
- return ret;
- if ( raw_cmd.flags & FD_RAW_READ ){
- fd_cacheflush(floppy_track_buffer, count);
- i=fd_copyout( raw_cmd.data, floppy_track_buffer, count);
- if (i)
- return i;
- }
-
- return COPYOUT(raw_cmd);
+ ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
+ if (!ret)
+ ret = ret2;
+ raw_cmd_free(&my_raw_cmd);
+ return ret;
}
-static int invalidate_drive(int rdev)
+static int invalidate_drive(kdev_t rdev)
{
/* invalidate the buffer track to force a reread */
- set_bit( DRIVE(rdev), &fake_change);
+ set_bit(DRIVE(rdev), &fake_change);
process_fd_request();
check_disk_change(rdev);
return 0;
}
-static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
- unsigned long param)
-{
-#define IOCTL_MODE_BIT 8
-#define OPEN_WRITE_BIT 16
-#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
- struct floppy_struct newparams;
- struct format_descr tmp_format_req;
- int i,device,drive,type,cnt;
- struct floppy_struct *this_floppy;
- char *name;
+static inline void clear_write_error(int drive)
+{
+ CLEARSTRUCT(UDRWE);
+}
- device = inode->i_rdev;
- switch (cmd) {
- RO_IOCTLS(device,param);
- }
- type = TYPE(device);
- drive = DRIVE(device);
- switch (cmd) {
- case FDGETDRVTYP:
- i=verify_area(VERIFY_WRITE,(void *) param,16);
- if (i)
- return i;
- name = drive_name(type,drive);
- for ( cnt=0; cnt<16; cnt++){
- put_fs_byte(name[cnt],
- ((char*)param)+cnt);
- if ( ! *name )
- break;
- }
- return 0;
- case FDGETMAXERRS:
- return COPYOUT(UDP->max_errors);
- case FDGETPRM:
- if (type)
- this_floppy = &floppy_type[type];
- else if ((this_floppy = current_type[drive]) ==
- NULL)
- return -ENODEV;
- return COPYOUT(this_floppy[0]);
- case FDPOLLDRVSTAT:
- LOCK_FDC(drive,1);
- CALL(poll_drive(1, FD_RAW_NEED_DISK));
- process_fd_request();
- /* fall through */
- case FDGETDRVSTAT:
- return COPYOUT(*UDRS);
- case FDGETFDCSTAT:
- return COPYOUT(*UFDCS);
- case FDGETDRVPRM:
- return COPYOUT(*UDP);
- case FDWERRORGET:
- return COPYOUT(*UDRWE);
- }
- if (!IOCTL_ALLOWED)
- return -EPERM;
- switch (cmd) {
- case FDWERRORCLR:
- UDRWE->write_errors = 0;
- UDRWE->first_error_sector = 0;
- UDRWE->first_error_generation = 0;
- UDRWE->last_error_sector = 0;
- UDRWE->last_error_generation = 0;
- UDRWE->badness = 0;
- return 0;
- case FDRAWCMD:
- if (type)
- return -EINVAL;
+static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
+ int drive, int type, kdev_t device)
+{
+ int cnt;
+
+ /* sanity checking for parameters.*/
+ if (g->sect <= 0 ||
+ g->head <= 0 ||
+ g->track <= 0 ||
+ g->track > UDP->tracks>>STRETCH(g) ||
+ /* check if reserved bits are set */
+ (g->stretch&~(FD_STRETCH|FD_SWAPSIDES)) != 0)
+ return -EINVAL;
+ if (type){
+ if (!suser())
+ return -EPERM;
LOCK_FDC(drive,1);
- set_floppy(device);
- CALL(i = raw_cmd_ioctl((void *) param));
+ for (cnt = 0; cnt < N_DRIVE; cnt++){
+ if (ITYPE(drive_state[cnt].fd_device) == type &&
+ drive_state[cnt].fd_ref)
+ set_bit(drive, &fake_change);
+ }
+ floppy_type[type] = *g;
+ floppy_type[type].name="user format";
+ for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
+ floppy_sizes[cnt]= floppy_sizes[cnt+0x80]=
+ floppy_type[type].size>>1;
process_fd_request();
- return i;
- case FDFMTTRK:
- if (UDRS->fd_ref != 1)
- return -EBUSY;
- COPYIN(tmp_format_req);
- return do_format(device, &tmp_format_req);
- case FDSETMAXERRS:
- return COPYIN(UDP->max_errors);
- case FDFMTBEG:
- return 0;
- case FDCLRPRM:
- LOCK_FDC(drive,1);
- current_type[drive] = NULL;
- floppy_sizes[drive] = MAX_DISK_SIZE;
- UDRS->keep_data = 0;
- return invalidate_drive(device);
- case FDFMTEND:
- case FDFLUSH:
- LOCK_FDC(drive,1);
- return invalidate_drive(device);
- case FDSETPRM:
- case FDDEFPRM:
- COPYIN(newparams);
- /* sanity checking for parameters.*/
- if(newparams.sect <= 0 ||
- newparams.head <= 0 ||
- newparams.track <= 0 ||
- newparams.track >
- UDP->tracks>>newparams.stretch)
- return -EINVAL;
- if ( type){
- if ( !suser() )
- return -EPERM;
- LOCK_FDC(drive,1);
- for ( cnt = 0; cnt < N_DRIVE; cnt++){
- if (TYPE(drive_state[cnt].fd_device) == type &&
- drive_state[cnt].fd_ref)
- set_bit(drive, &fake_change);
- }
- floppy_type[type] = newparams;
- floppy_type[type].name="user format";
- for (cnt = type << 2 ;
- cnt < (type << 2 ) + 4 ;
- cnt++)
- floppy_sizes[cnt]=
- floppy_sizes[cnt+0x80]=
- floppy_type[type].size>>1;
- process_fd_request();
- for ( cnt = 0; cnt < N_DRIVE; cnt++){
- if (TYPE(drive_state[cnt].fd_device) == type &&
- drive_state[cnt].fd_ref)
- check_disk_change(drive_state[cnt].
- fd_device);
- }
- return 0;
+ for (cnt = 0; cnt < N_DRIVE; cnt++){
+ if (ITYPE(drive_state[cnt].fd_device) == type &&
+ drive_state[cnt].fd_ref)
+ check_disk_change(
+ MKDEV(FLOPPY_MAJOR,
+ drive_state[cnt].fd_device));
}
-
+ } else {
LOCK_FDC(drive,1);
- if ( cmd != FDDEFPRM )
+ if (cmd != FDDEFPRM)
/* notice a disk change immediately, else
* we loose our settings immediately*/
- CALL(poll_drive(1,0));
- user_params[drive] = newparams;
- if (buffer_drive == drive &&
- buffer_max > user_params[drive].sect)
- buffer_max=user_params[drive].sect;
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ user_params[drive] = *g;
+ if (buffer_drive == drive)
+ SUPBOUND(buffer_max, user_params[drive].sect);
current_type[drive] = &user_params[drive];
floppy_sizes[drive] = user_params[drive].size >> 1;
if (cmd == FDDEFPRM)
@@ -3021,40 +3121,281 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
* whose number will change. This is useful, because
* mtools often changes the geometry of the disk after
* looking at the boot block */
- if (DRS->maxblock >
- user_params[drive].sect ||
- DRS->maxtrack )
+ if (DRS->maxblock > user_params[drive].sect || DRS->maxtrack)
invalidate_drive(device);
else
process_fd_request();
- return 0;
- case FDRESET:
- return user_reset_fdc( drive, (int)param, 1);
- case FDMSGON:
- UDP->flags |= FTD_MSG;
- return 0;
- case FDMSGOFF:
- UDP->flags &= ~FTD_MSG;
- return 0;
- case FDSETEMSGTRESH:
- UDP->max_errors.reporting =
- (unsigned short) (param & 0x0f);
- return 0;
- case FDTWADDLE:
- LOCK_FDC(drive,1);
- twaddle();
- process_fd_request();
}
- if ( ! suser() )
- return -EPERM;
- switch(cmd){
- case FDSETDRVPRM:
- return COPYIN(*UDP);
- default:
- return -EINVAL;
+ return 0;
+}
+
+/* handle obsolete ioctl's */
+int ioctl_table[]= {
+ FDCLRPRM,
+ FDSETPRM,
+ FDDEFPRM,
+ FDGETPRM,
+ FDMSGON,
+ FDMSGOFF,
+ FDFMTBEG,
+ FDFMTTRK,
+ FDFMTEND,
+ FDSETEMSGTRESH,
+ FDFLUSH,
+ FDSETMAXERRS,
+ FDGETMAXERRS,
+ FDGETDRVTYP,
+ FDSETDRVPRM,
+ FDGETDRVPRM,
+ FDGETDRVSTAT,
+ FDPOLLDRVSTAT,
+ FDRESET,
+ FDGETFDCSTAT,
+ FDWERRORCLR,
+ FDWERRORGET,
+ FDRAWCMD,
+ FDEJECT,
+ FDTWADDLE
+};
+
+static inline int normalize_ioctl(int *cmd, int *size)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(ioctl_table); i++) {
+ if ((*cmd & 0xffff) == (ioctl_table[i] & 0xffff)){
+ *size = _IOC_SIZE(*cmd);
+ *cmd = ioctl_table[i];
+ if (*size > _IOC_SIZE(*cmd)) {
+ printk("ioctl not yet supported\n");
+ return -EFAULT;
+ }
+ return 0;
+ }
}
+ return -EINVAL;
+}
+
+static int get_floppy_geometry(int drive, int type, struct floppy_struct **g)
+{
+ if (type)
+ *g = &floppy_type[type];
+ else {
+ LOCK_FDC(drive,0);
+ CALL(poll_drive(0,0));
+ process_fd_request();
+ *g = current_type[drive];
+ }
+ if(!*g)
+ return -ENODEV;
return 0;
+}
+
+static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long param)
+{
+#define IOCTL_MODE_BIT 8
+#define OPEN_WRITE_BIT 16
+#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
+#define OUT(c,x) case c: outparam = (const char *) (x); break
+#define IN(c,x,tag) case c: *(x) = inparam. tag ; return 0
+
+ int i,drive,type;
+ kdev_t device;
+ int ret;
+ int size;
+ union inparam {
+ struct floppy_struct g; /* geometry */
+ struct format_descr f;
+ struct floppy_max_errors max_errors;
+ struct floppy_drive_params dp;
+ } inparam; /* parameters coming from user space */
+ const char *outparam; /* parameters passed back to user space */
+
+ device = inode->i_rdev;
+ switch (cmd) {
+ RO_IOCTLS(device,param);
+ }
+ type = TYPE(device);
+ drive = DRIVE(device);
+
+ /* convert compatibility eject ioctls into floppy eject ioctl.
+ * We do this in order to provide a means to eject floppy disks before
+ * installing the new fdutils package */
+ if(cmd == CDROMEJECT || /* CD-ROM eject */
+ cmd == 0x6470 /* SunOS floppy eject */) {
+ DPRINT("obsolete eject ioctl\n");
+ DPRINT("please use floppycontrol --eject\n");
+ cmd = FDEJECT;
+ }
+
+ /* generic block device ioctls */
+ switch(cmd) {
+ /* the following have been inspired by the corresponding
+ * code for other block devices. */
+ struct floppy_struct *g;
+ case HDIO_GETGEO:
+ {
+ struct hd_geometry loc;
+ ECALL(get_floppy_geometry(drive, type, &g));
+ loc.heads = g->head;
+ loc.sectors = g->sect;
+ loc.cylinders = g->track;
+ loc.start = 0;
+ return _COPYOUT(loc);
+ }
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(param > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = param;
+ return 0;
+ case BLKRAGET:
+ return put_user(read_ahead[MAJOR(inode->i_rdev)],
+ (int *) param);
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ case BLKGETSIZE:
+ ECALL(get_floppy_geometry(drive, type, &g));
+ return put_user(g->size, (int *) param);
+ /* BLKRRPART is not defined as floppies don't have
+ * partition tables */
+ }
+
+ /* convert the old style command into a new style command */
+ if ((cmd & 0xff00) == 0x0200) {
+ ECALL(normalize_ioctl(&cmd, &size));
+ } else
+ return -EINVAL;
+
+ /* permission checks */
+ if (((cmd & 0x80) && !suser()) ||
+ ((cmd & 0x40) && !IOCTL_ALLOWED))
+ return -EPERM;
+
+ /* copyin */
+ CLEARSTRUCT(&inparam);
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ ECALL(fd_copyin((void *)param, &inparam, size))
+
+ switch (cmd) {
+ case FDEJECT:
+ if(UDRS->fd_ref != 1)
+ /* somebody else has this drive open */
+ return -EBUSY;
+ LOCK_FDC(drive,1);
+
+ /* do the actual eject. Fails on
+ * non-Sparc architectures */
+ ret=fd_eject(UNIT(drive));
+
+ USETF(FD_DISK_CHANGED);
+ USETF(FD_VERIFY);
+ process_fd_request();
+ return ret;
+ case FDCLRPRM:
+ LOCK_FDC(drive,1);
+ current_type[drive] = NULL;
+ floppy_sizes[drive] = MAX_DISK_SIZE;
+ UDRS->keep_data = 0;
+ return invalidate_drive(device);
+ case FDSETPRM:
+ case FDDEFPRM:
+ return set_geometry(cmd, & inparam.g,
+ drive, type, device);
+ case FDGETPRM:
+ ECALL(get_floppy_geometry(drive, type,
+ (struct floppy_struct**)
+ &outparam));
+ break;
+
+ case FDMSGON:
+ UDP->flags |= FTD_MSG;
+ return 0;
+ case FDMSGOFF:
+ UDP->flags &= ~FTD_MSG;
+ return 0;
+
+ case FDFMTBEG:
+ LOCK_FDC(drive,1);
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ ret = UDRS->flags;
+ process_fd_request();
+ if(ret & FD_VERIFY)
+ return -ENODEV;
+ if(!(ret & FD_DISK_WRITABLE))
+ return -EROFS;
+ return 0;
+ case FDFMTTRK:
+ if (UDRS->fd_ref != 1)
+ return -EBUSY;
+ return do_format(device, &inparam.f);
+ case FDFMTEND:
+ case FDFLUSH:
+ LOCK_FDC(drive,1);
+ return invalidate_drive(device);
+
+ case FDSETEMSGTRESH:
+ UDP->max_errors.reporting =
+ (unsigned short) (param & 0x0f);
+ return 0;
+ OUT(FDGETMAXERRS, &UDP->max_errors);
+ IN(FDSETMAXERRS, &UDP->max_errors, max_errors);
+
+ case FDGETDRVTYP:
+ outparam = drive_name(type,drive);
+ SUPBOUND(size,strlen(outparam)+1);
+ break;
+
+ IN(FDSETDRVPRM, UDP, dp);
+ OUT(FDGETDRVPRM, UDP);
+
+ case FDPOLLDRVSTAT:
+ LOCK_FDC(drive,1);
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ process_fd_request();
+ /* fall through */
+ OUT(FDGETDRVSTAT, UDRS);
+
+ case FDRESET:
+ return user_reset_fdc(drive, (int)param, 1);
+
+ OUT(FDGETFDCSTAT,UFDCS);
+
+ case FDWERRORCLR:
+ CLEARSTRUCT(UDRWE);
+ return 0;
+ OUT(FDWERRORGET,UDRWE);
+
+ case FDRAWCMD:
+ if (type)
+ return -EINVAL;
+ LOCK_FDC(drive,1);
+ set_floppy(device);
+ CALL(i = raw_cmd_ioctl(cmd,(void *) param));
+ process_fd_request();
+ return i;
+
+ case FDTWADDLE:
+ LOCK_FDC(drive,1);
+ twaddle();
+ process_fd_request();
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ return fd_copyout((void *)param, outparam, size);
+ else
+ return 0;
#undef IOCTL_ALLOWED
+#undef OUT
+#undef IN
}
static void config_types(void)
@@ -3062,9 +3403,9 @@ static void config_types(void)
int first=1;
int drive;
- /* read drive info out of physical cmos */
+ /* read drive info out of physical CMOS */
drive=0;
- if (!UDP->cmos )
+ if (!UDP->cmos)
UDP->cmos= FLOPPY0_TYPE;
drive=1;
if (!UDP->cmos && FLOPPY1_TYPE)
@@ -3074,18 +3415,20 @@ static void config_types(void)
/* additional physical CMOS drive detection should go here */
for (drive=0; drive < N_DRIVE; drive++){
+ if (UDP->cmos >= 16)
+ UDP->cmos = 0;
if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
memcpy((char *) UDP,
(char *) (&default_drive_params[(int)UDP->cmos].params),
sizeof(struct floppy_drive_params));
if (UDP->cmos){
if (first)
- printk("Floppy drive(s): ");
+ printk(KERN_INFO "Floppy drive(s): ");
else
printk(", ");
first=0;
- if (UDP->cmos > 0 ){
- ALLOWED_DRIVE_MASK |= 1 << drive;
+ if (UDP->cmos > 0){
+ allowed_drive_mask |= 1 << drive;
printk("fd%d is %s", drive,
default_drive_params[(int)UDP->cmos].name);
} else
@@ -3093,12 +3436,12 @@ static void config_types(void)
UDP->cmos);
}
}
- if(!first)
+ if (!first)
printk("\n");
}
-static int floppy_read(struct inode * inode, struct file * filp,
- char * buf, int count)
+static long floppy_read(struct inode * inode, struct file * filp,
+ char * buf, unsigned long count)
{
int drive = DRIVE(inode->i_rdev);
@@ -3108,23 +3451,22 @@ static int floppy_read(struct inode * inode, struct file * filp,
return block_read(inode, filp, buf, count);
}
-static int floppy_write(struct inode * inode, struct file * filp,
- char * buf, int count)
+static long floppy_write(struct inode * inode, struct file * filp,
+ const char * buf, unsigned long count)
{
int block;
int ret;
int drive = DRIVE(inode->i_rdev);
- if(!UDRS->maxblock)
+ if (!UDRS->maxblock)
UDRS->maxblock=1;/* make change detectable */
check_disk_change(inode->i_rdev);
- if (UTESTF(FD_DISK_CHANGED))
+ if (UTESTF(FD_DISK_CHANGED))
return -ENXIO;
- if(!UTESTF(FD_DISK_WRITABLE))
+ if (!UTESTF(FD_DISK_WRITABLE))
return -EROFS;
block = (filp->f_pos + count) >> 9;
- if(block > UDRS->maxblock)
- UDRS->maxblock = block;
+ INFBOUND(UDRS->maxblock, block);
ret= block_write(inode, filp, buf, count);
return ret;
}
@@ -3132,14 +3474,14 @@ static int floppy_write(struct inode * inode, struct file * filp,
static void floppy_release(struct inode * inode, struct file * filp)
{
int drive;
-
+
drive = DRIVE(inode->i_rdev);
- if( !filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+ if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
/* if the file is mounted OR (writable now AND writable at
* open time) Linus: Does this cover all cases? */
block_fsync(inode,filp);
-
+
if (UDRS->fd_ref < 0)
UDRS->fd_ref=0;
else if (!UDRS->fd_ref--) {
@@ -3154,14 +3496,14 @@ static void floppy_release(struct inode * inode, struct file * filp)
* /dev/PS0 etc), and disallows simultaneous access to the same
* drive with different device numbers.
*/
-#define RETERR(x) \
- do{floppy_release(inode,filp); \
- return -(x);}while(0)
+#define RETERR(x) do{floppy_release(inode,filp); return -(x);}while(0)
static int floppy_open(struct inode * inode, struct file * filp)
{
int drive;
int old_dev;
+ int try;
+ char *tmp;
if (!filp) {
DPRINT("Weird, open called with filp=0\n");
@@ -3169,23 +3511,23 @@ static int floppy_open(struct inode * inode, struct file * filp)
}
drive = DRIVE(inode->i_rdev);
- if (drive >= N_DRIVE ||
- !( ALLOWED_DRIVE_MASK & ( 1 << drive)) ||
+ if (drive >= N_DRIVE ||
+ !(allowed_drive_mask & (1 << drive)) ||
fdc_state[FDC(drive)].version == FDC_NONE)
return -ENXIO;
if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
return -ENXIO;
old_dev = UDRS->fd_device;
- if (UDRS->fd_ref && old_dev != inode->i_rdev)
+ if (UDRS->fd_ref && old_dev != MINOR(inode->i_rdev))
return -EBUSY;
- if(!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
+ if (!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
USETF(FD_DISK_CHANGED);
USETF(FD_VERIFY);
}
- if(UDRS->fd_ref == -1 ||
+ if (UDRS->fd_ref == -1 ||
(UDRS->fd_ref && (filp->f_flags & O_EXCL)))
return -EBUSY;
@@ -3197,12 +3539,38 @@ static int floppy_open(struct inode * inode, struct file * filp)
else
UDRS->fd_ref++;
- UDRS->fd_device = inode->i_rdev;
+ if (!floppy_track_buffer){
+ /* if opening an ED drive, reserve a big buffer,
+ * else reserve a small one */
+ if ((UDP->cmos == 6) || (UDP->cmos == 5))
+ try = 64; /* Only 48 actually useful */
+ else
+ try = 32; /* Only 24 actually useful */
- if (old_dev && old_dev != inode->i_rdev) {
+ tmp=(char *)fd_dma_mem_alloc(1024 * try);
+ if (!tmp) {
+ try >>= 1; /* buffer only one side */
+ INFBOUND(try, 16);
+ tmp= (char *)fd_dma_mem_alloc(1024*try);
+ }
+ if (!tmp) {
+ DPRINT("Unable to allocate DMA memory\n");
+ RETERR(ENXIO);
+ }
+ if (floppy_track_buffer)
+ fd_dma_mem_free((unsigned long)tmp,try*1024);
+ else {
+ buffer_min = buffer_max = -1;
+ floppy_track_buffer = tmp;
+ max_buffer_sectors = try;
+ }
+ }
+
+ UDRS->fd_device = MINOR(inode->i_rdev);
+ if (old_dev != -1 && old_dev != MINOR(inode->i_rdev)) {
if (buffer_drive == drive)
buffer_track = -1;
- invalidate_buffers(old_dev);
+ invalidate_buffers(MKDEV(FLOPPY_MAJOR,old_dev));
}
/* Allow ioctls if we have write-permissions even if read-only open */
@@ -3212,7 +3580,7 @@ static int floppy_open(struct inode * inode, struct file * filp)
filp->f_mode |= OPEN_WRITE_BIT;
if (UFDCS->rawcmd == 1)
- UFDCS->rawcmd = 2;
+ UFDCS->rawcmd = 2;
if (filp->f_flags & O_NDELAY)
return 0;
@@ -3231,25 +3599,26 @@ static int floppy_open(struct inode * inode, struct file * filp)
/*
* Check if the disk has been changed or if a change has been faked.
*/
-static int check_floppy_change(dev_t dev)
+static int check_floppy_change(kdev_t dev)
{
- int drive = DRIVE( dev );
+ int drive = DRIVE(dev);
if (MAJOR(dev) != MAJOR_NR) {
- DPRINT("floppy_changed: not a floppy\n");
+ DPRINT("check_floppy_change: not a floppy\n");
return 0;
}
- if (UTESTF(FD_DISK_CHANGED))
+ if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
return 1;
- if(UDRS->last_checked + UDP->checkfreq < jiffies){
+ if (UDP->checkfreq < jiffies - UDRS->last_checked){
lock_fdc(drive,0);
poll_drive(0,0);
process_fd_request();
}
-
- if(UTESTF(FD_DISK_CHANGED) ||
+
+ if (UTESTF(FD_DISK_CHANGED) ||
+ UTESTF(FD_VERIFY) ||
test_bit(drive, &fake_change) ||
(!TYPE(dev) && !current_type[drive]))
return 1;
@@ -3260,29 +3629,32 @@ static int check_floppy_change(dev_t dev)
* the bootblock (block 0). "Autodetection" is also needed to check whether
* there is a disk in the drive at all... Thus we also do it for fixed
* geometry formats */
-static int floppy_revalidate(dev_t dev)
+static int floppy_revalidate(kdev_t dev)
{
#define NO_GEOM (!current_type[drive] && !TYPE(dev))
struct buffer_head * bh;
int drive=DRIVE(dev);
int cf;
- if(UTESTF(FD_DISK_CHANGED) || test_bit(drive, &fake_change) || NO_GEOM){
+ if (UTESTF(FD_DISK_CHANGED) ||
+ UTESTF(FD_VERIFY) ||
+ test_bit(drive, &fake_change) ||
+ NO_GEOM){
lock_fdc(drive,0);
- cf = UTESTF(FD_DISK_CHANGED);
- if(! (cf || test_bit(drive, &fake_change) || NO_GEOM)){
+ cf = UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY);
+ if (!(cf || test_bit(drive, &fake_change) || NO_GEOM)){
process_fd_request(); /*already done by another thread*/
return 0;
}
UDRS->maxblock = 0;
UDRS->maxtrack = 0;
- if ( buffer_drive == drive)
+ if (buffer_drive == drive)
buffer_track = -1;
clear_bit(drive, &fake_change);
UCLEARF(FD_DISK_CHANGED);
- if(cf)
+ if (cf)
UDRS->generation++;
- if(NO_GEOM){
+ if (NO_GEOM){
/* auto-sensing */
int size = floppy_blocksizes[MINOR(dev)];
if (!size)
@@ -3291,15 +3663,15 @@ static int floppy_revalidate(dev_t dev)
process_fd_request();
return 1;
}
- if ( bh && ! bh->b_uptodate)
+ if (bh && !buffer_uptodate(bh))
ll_rw_block(READ, 1, &bh);
process_fd_request();
wait_on_buffer(bh);
brelse(bh);
return 0;
- }
- if(cf)
- poll_drive(0, FD_RAW_NEED_DISK);
+ }
+ if (cf)
+ poll_drive(0, FD_RAW_NEED_DISK);
process_fd_request();
}
return 0;
@@ -3322,7 +3694,7 @@ static struct file_operations floppy_fops = {
};
/*
- * Floppy Driver initialisation
+ * Floppy Driver initialization
* =============================
*/
@@ -3333,51 +3705,86 @@ static char get_fdc_version(void)
int r;
output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */
- if ( FDCS->reset )
+ if (FDCS->reset)
return FDC_NONE;
- if ( (r = result()) <= 0x00)
+ if ((r = result()) <= 0x00)
return FDC_NONE; /* No FDC present ??? */
if ((r==1) && (reply_buffer[0] == 0x80)){
- printk("FDC %d is a 8272A\n",fdc);
- return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
+ printk(KERN_INFO "FDC %d is an 8272A\n",fdc);
+ return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
}
if (r != 10) {
- printk("FDC init: DUMPREGS: unexpected return of %d bytes.\n", r);
+ printk("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n",
+ fdc, r);
return FDC_UNKNOWN;
}
- output_byte(FD_VERSION);
- r = result();
- if ((r == 1) && (reply_buffer[0] == 0x80)){
- printk("FDC %d is a 82072\n",fdc);
- return FDC_82072; /* 82072 doesn't know VERSION */
+
+ if(!fdc_configure()) {
+ printk(KERN_INFO "FDC %d is an 82072\n",fdc);
+ return FDC_82072; /* 82072 doesn't know CONFIGURE */
}
- if ((r != 1) || (reply_buffer[0] != 0x90)) {
- printk("FDC init: VERSION: unexpected return of %d bytes.\n", r);
- return FDC_UNKNOWN;
+
+ output_byte(FD_PERPENDICULAR);
+ if(need_more_output() == MORE_OUTPUT) {
+ output_byte(0);
+ } else {
+ printk(KERN_INFO "FDC %d is an 82072A\n", fdc);
+ return FDC_82072A; /* 82072A as found on Sparcs. */
}
+
output_byte(FD_UNLOCK);
r = result();
if ((r == 1) && (reply_buffer[0] == 0x80)){
- printk("FDC %d is a pre-1991 82077\n", fdc);
- return FDC_82077_ORIG; /* Pre-1991 82077 doesn't know LOCK/UNLOCK */
+ printk(KERN_INFO "FDC %d is a pre-1991 82077\n", fdc);
+ return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know
+ * LOCK/UNLOCK */
}
if ((r != 1) || (reply_buffer[0] != 0x00)) {
- printk("FDC init: UNLOCK: unexpected return of %d bytes.\n", r);
+ printk("FDC %d init: UNLOCK: unexpected return of %d bytes.\n",
+ fdc, r);
return FDC_UNKNOWN;
}
- printk("FDC %d is a post-1991 82077\n",fdc);
- return FDC_82077; /* Revised 82077AA passes all the tests */
+ output_byte(FD_PARTID);
+ r = result();
+ if (r != 1) {
+ printk("FDC %d init: PARTID: unexpected return of %d bytes.\n",
+ fdc, r);
+ return FDC_UNKNOWN;
+ }
+ if (reply_buffer[0] == 0x80) {
+ printk(KERN_INFO "FDC %d is a post-1991 82077\n",fdc);
+ return FDC_82077; /* Revised 82077AA passes all the tests */
+ }
+ switch (reply_buffer[0] >> 5) {
+ case 0x0:
+ /* Either a 82078-1 or a 82078SL running at 5Volt */
+ printk(KERN_INFO "FDC %d is an 82078.\n",fdc);
+ return FDC_82078;
+ case 0x1:
+ printk(KERN_INFO "FDC %d is a 44pin 82078\n",fdc);
+ return FDC_82078;
+ case 0x2:
+ printk(KERN_INFO "FDC %d is a S82078B\n", fdc);
+ return FDC_S82078B;
+ case 0x3:
+ printk(KERN_INFO "FDC %d is a National Semiconductor PC87306\n", fdc);
+ return FDC_87306;
+ default:
+ printk(KERN_INFO "FDC %d init: 82078 variant with unknown PARTID=%d.\n",
+ fdc, reply_buffer[0] >> 5);
+ return FDC_82078_UNKN;
+ }
} /* get_fdc_version */
/* lilo configuration */
/* we make the invert_dcl function global. One day, somebody might
-want to centralize all thinkpad related options into one lilo option,
-there are just so many thinkpad related quirks! */
+ * want to centralize all thinkpad related options into one lilo option,
+ * there are just so many thinkpad related quirks! */
void floppy_invert_dcl(int *ints,int param)
{
int i;
-
+
for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
if (param)
default_drive_params[i].params.flags |= 0x80;
@@ -3400,168 +3807,163 @@ static void daring(int *ints,int param)
default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR;
}
}
- DPRINT1("Assuming %s floppy hardware\n", param ? "standard" : "broken");
-}
-
-static void allow_drives(int *ints, int param)
-{
- ALLOWED_DRIVE_MASK=param;
- DPRINT1("setting allowed_drive_mask to 0x%x\n", param);
-}
-
-static void fdc2_adr(int *ints, int param)
-{
- FDC2 = param;
- if(param)
- DPRINT1("enabling second fdc at address 0x%3x\n", FDC2);
- else
- DPRINT("disabling second fdc\n");
-}
-
-static void unex(int *ints,int param)
-{
- print_unex = param;
- DPRINT1("%sprinting messages for unexpected interrupts\n",
- param ? "" : "not ");
+ DPRINT("Assuming %s floppy hardware\n", param ? "standard" : "broken");
}
static void set_cmos(int *ints, int dummy)
{
int current_drive=0;
- if ( ints[0] != 2 ){
+ if (ints[0] != 2){
DPRINT("wrong number of parameter for cmos\n");
return;
}
current_drive = ints[1];
- if (current_drive < 0 || current_drive >= 8 ){
+ if (current_drive < 0 || current_drive >= 8){
DPRINT("bad drive for set_cmos\n");
return;
}
- if(ints[2] <= 0 || ints[2] >= NUMBER(default_drive_params)){
- DPRINT1("bad cmos code %d\n", ints[2]);
+ if (current_drive >= 4 && !FDC2)
+ FDC2 = 0x370;
+ if (ints[2] <= 0 ||
+ (ints[2] >= NUMBER(default_drive_params) && ints[2] != 16)){
+ DPRINT("bad cmos code %d\n", ints[2]);
return;
}
DP->cmos = ints[2];
- DPRINT1("setting cmos code to %d\n", ints[2]);
+ DPRINT("setting cmos code to %d\n", ints[2]);
}
-
+
static struct param_table {
- char *name;
+ const char *name;
void (*fn)(int *ints, int param);
+ int *var;
int def_param;
} config_params[]={
-{ "allowed_drive_mask", allow_drives, 0xff },
-{ "all_drives", allow_drives, 0xff },
-{ "asus_pci", allow_drives, 0x33 },
+ { "allowed_drive_mask", 0, &allowed_drive_mask, 0xff },
+ { "all_drives", 0, &allowed_drive_mask, 0xff },
+ { "asus_pci", 0, &allowed_drive_mask, 0x33 },
+
+ { "daring", daring, 0, 1},
-{ "daring", daring, 1},
+ { "two_fdc", 0, &FDC2, 0x370 },
+ { "one_fdc", 0, &FDC2, 0 },
-{ "two_fdc", fdc2_adr, 0x370 },
-{ "one_fdc", fdc2_adr, 0 },
+ { "thinkpad", floppy_invert_dcl, 0, 1 },
-{ "thinkpad", floppy_invert_dcl, 1 },
+ { "nodma", 0, &use_virtual_dma, 1 },
+ { "omnibook", 0, &use_virtual_dma, 1 },
+ { "dma", 0, &use_virtual_dma, 0 },
-{ "cmos", set_cmos, 0 },
+ { "fifo_depth", 0, &fifo_depth, 0xa },
+ { "nofifo", 0, &no_fifo, 0x20 },
+ { "usefifo", 0, &no_fifo, 0 },
-{ "unexpected_interrupts", unex, 1 },
-{ "no_unexpected_interrupts", unex, 0 },
-{ "L40SX", unex, 0 } };
+ { "cmos", set_cmos, 0, 0 },
+
+ { "unexpected_interrupts", 0, &print_unex, 1 },
+ { "no_unexpected_interrupts", 0, &print_unex, 0 },
+ { "L40SX", 0, &print_unex, 0 } };
#define FLOPPY_SETUP
void floppy_setup(char *str, int *ints)
{
int i;
int param;
- if(!str)
- return;
- for(i=0; i< ARRAY_SIZE(config_params); i++){
- if (strcmp(str,config_params[i].name) == 0 ){
- if (ints[0] )
- param = ints[1];
- else
- param = config_params[i].def_param;
- config_params[i].fn(ints,param);
- return;
+ if (str)
+ for (i=0; i< ARRAY_SIZE(config_params); i++){
+ if (strcmp(str,config_params[i].name) == 0){
+ if (ints[0])
+ param = ints[1];
+ else
+ param = config_params[i].def_param;
+ if(config_params[i].fn)
+ config_params[i].fn(ints,param);
+ if(config_params[i].var) {
+ DPRINT("%s=%d\n", str, param);
+ *config_params[i].var = param;
+ }
+ return;
+ }
}
- }
- DPRINT1("unknown floppy option %s\n", str);
- DPRINT("allowed options are:");
- for(i=0; i< ARRAY_SIZE(config_params); i++)
- printk(" %s",config_params[i].name);
- printk("\n");
+ if (str) {
+ DPRINT("unknown floppy option [%s]\n", str);
+
+ DPRINT("allowed options are:");
+ for (i=0; i< ARRAY_SIZE(config_params); i++)
+ printk(" %s",config_params[i].name);
+ printk("\n");
+ } else
+ DPRINT("botched floppy option\n");
DPRINT("Read linux/drivers/block/README.fd\n");
}
-#ifdef FD_MODULE
-static
-#endif
-int new_floppy_init(void)
+int floppy_init(void)
{
- int i,drive;
- int have_no_fdc=0;
+ int i,unit,drive;
+ int have_no_fdc= -EIO;
- sti();
+ raw_cmd = 0;
if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
printk("Unable to get major %d for floppy\n",MAJOR_NR);
return -EBUSY;
}
- for(i=0; i<256; i++)
- if ( TYPE(i))
- floppy_sizes[i] = floppy_type[TYPE(i)].size >> 1;
+ for (i=0; i<256; i++)
+ if (ITYPE(i))
+ floppy_sizes[i] = floppy_type[ITYPE(i)].size >> 1;
else
floppy_sizes[i] = MAX_DISK_SIZE;
blk_size[MAJOR_NR] = floppy_sizes;
blksize_size[MAJOR_NR] = floppy_blocksizes;
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- del_timer(&fd_timeout);
+ reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
config_types();
- fdc_state[0].address = FDC1;
- fdc_state[0].dor = 0;
-#if N_FDC > 1
- fdc_state[1].address = FDC2;
- fdc_state[1].dor = 0;
-#endif
-
- for (i = 0 ; i < N_FDC ; i++) {
+ for (i = 0; i < N_FDC; i++) {
fdc = i;
+ CLEARSTRUCT(FDCS);
FDCS->dtr = -1;
FDCS->dor = 0x4;
- FDCS->reset = 0;
- FDCS->version = FDC_NONE;
+#ifdef __sparc__
+ /*sparcs don't have a DOR reset which we can fall back on to*/
+ FDCS->version = FDC_82072A;
+#endif
}
- if(floppy_grab_irq_and_dma()){
+ fdc_state[0].address = FDC1;
+#if N_FDC > 1
+ fdc_state[1].address = FDC2;
+#endif
+
+ if (floppy_grab_irq_and_dma()){
unregister_blkdev(MAJOR_NR,"fd");
return -EBUSY;
}
/* initialise drive state */
- for (drive = 0; drive < N_DRIVE ; drive++) {
+ for (drive = 0; drive < N_DRIVE; drive++) {
+ CLEARSTRUCT(UDRS);
+ CLEARSTRUCT(UDRWE);
UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
- UDRS->generation = 0;
- UDRS->keep_data = 0;
- UDRS->fd_ref = 0;
- UDRS->fd_device = 0;
- UDRWE->write_errors = 0;
- UDRWE->first_error_sector = 0;
- UDRWE->first_error_generation = 0;
- UDRWE->last_error_sector = 0;
- UDRWE->last_error_generation = 0;
- UDRWE->badness = 0;
+ UDRS->fd_device = -1;
+ floppy_track_buffer = NULL;
+ max_buffer_sectors = 0;
}
- for (i = 0 ; i < N_FDC ; i++) {
+ for (i = 0; i < N_FDC; i++) {
fdc = i;
- if (FDCS->address == -1 )
+ FDCS->driver_version = FD_DRIVER_VERSION;
+ for (unit=0; unit<4; unit++)
+ FDCS->track[unit] = 0;
+ if (FDCS->address == -1)
continue;
FDCS->rawcmd = 2;
- if(user_reset_fdc(-1,FD_RESET_IF_NEEDED,0)){
+ if (user_reset_fdc(-1,FD_RESET_ALWAYS,0)){
FDCS->address = -1;
+ FDCS->version = FDC_NONE;
continue;
}
/* Try to determine the floppy controller type */
@@ -3571,92 +3973,90 @@ int new_floppy_init(void)
continue;
}
+ request_region(FDCS->address, 6, "floppy");
+ request_region(FDCS->address+7, 1, "floppy DIR");
+ /* address + 6 is reserved, and may be taken by IDE.
+ * Unfortunately, Adaptec doesn't know this :-(, */
+
have_no_fdc = 0;
/* Not all FDCs seem to be able to handle the version command
* properly, so force a reset for the standard FDC clones,
* to avoid interrupt garbage.
*/
- FDCS->has_fifo = FDCS->version >= FDC_82077_ORIG;
user_reset_fdc(-1,FD_RESET_ALWAYS,0);
}
fdc=0;
+ del_timer(&fd_timeout);
current_drive = 0;
floppy_release_irq_and_dma();
initialising=0;
- if(have_no_fdc)
+ if (have_no_fdc) {
+ DPRINT("no floppy controllers found\n");
unregister_blkdev(MAJOR_NR,"fd");
-#ifdef CONFIG_MIPS_JAZZ
- else {
- if (boot_info.machtype == MACH_ACER_PICA_61 ||
- boot_info.machtype == MACH_MIPS_MAGNUM_4000)
- vdma_alloc(PHYSADDR(floppy_track_buffer),
- 512*2*MAX_BUFFER_SECTORS);
}
-#endif
return have_no_fdc;
}
-/* stupid compatibility hack... */
-void floppy_init(void)
-{
- new_floppy_init();
-}
-
static int floppy_grab_irq_and_dma(void)
{
int i;
- cli();
+ unsigned long flags;
+
+ INT_OFF;
if (usage_count++){
- sti();
+ INT_ON;
return 0;
}
- sti();
-#ifdef FD_MODULE
+ INT_ON;
MOD_INC_USE_COUNT;
-#endif
- for(i=0; i< N_FDC; i++){
- if(FDCS->address != -1){
+ for (i=0; i< N_FDC; i++){
+ if (fdc_state[i].address != -1){
fdc = i;
reset_fdc_info(1);
- fd_out(FDCS->dor, FD_DOR);
+ fd_outb(FDCS->dor, FD_DOR);
}
}
+ fdc = 0;
set_dor(0, ~0, 8); /* avoid immediate interrupt */
if (fd_request_irq()) {
- DPRINT1("Unable to grab IRQ%d for the floppy driver\n",
+ DPRINT("Unable to grab IRQ%d for the floppy driver\n",
FLOPPY_IRQ);
+ MOD_DEC_USE_COUNT;
+ usage_count--;
return -1;
}
if (fd_request_dma()) {
- DPRINT1("Unable to grab DMA%d for the floppy driver\n",
+ DPRINT("Unable to grab DMA%d for the floppy driver\n",
FLOPPY_DMA);
fd_free_irq();
+ MOD_DEC_USE_COUNT;
+ usage_count--;
return -1;
}
- for(fdc = 0; fdc < N_FDC ; fdc++)
- if(FDCS->address != -1)
- fd_out(FDCS->dor, FD_DOR);
+ for (fdc = 0; fdc < N_FDC; fdc++)
+ if (FDCS->address != -1)
+ fd_outb(FDCS->dor, FD_DOR);
fdc = 0;
-/* printk("enable irq %d\n",FLOPPY_IRQ); */
fd_enable_irq();
return 0;
}
static void floppy_release_irq_and_dma(void)
{
-#ifdef CONFIG_FLOPPY_SANITY
+#ifdef FLOPPY_SANITY_CHECK
int drive;
#endif
- cli();
+ long tmpsize;
+ unsigned long tmpaddr;
+ unsigned long flags;
+
+ INT_OFF;
if (--usage_count){
- sti();
+ INT_ON;
return;
}
- sti();
-#ifdef FD_MODULE
- MOD_DEC_USE_COUNT;
-#endif
+ INT_ON;
fd_disable_dma();
fd_free_dma();
fd_disable_irq();
@@ -3667,17 +4067,144 @@ static void floppy_release_irq_and_dma(void)
set_dor(1, ~8, 0);
#endif
floppy_enable_hlt();
-#ifdef CONFIG_FLOPPY_SANITY
- for(drive=0; drive < N_FDC * 4; drive++)
- if( motor_off_timer[drive].next )
+
+ if (floppy_track_buffer && max_buffer_sectors) {
+ tmpsize = max_buffer_sectors*1024;
+ tmpaddr = (unsigned long)floppy_track_buffer;
+ floppy_track_buffer = 0;
+ max_buffer_sectors = 0;
+ buffer_min = buffer_max = -1;
+ fd_dma_mem_free(tmpaddr, tmpsize);
+ }
+
+#ifdef FLOPPY_SANITY_CHECK
+ for (drive=0; drive < N_FDC * 4; drive++)
+ if (motor_off_timer[drive].next)
printk("motor off timer %d still active\n", drive);
-
- if(fd_timeout.next)
- printk("floppy timer still active\n");
+
+ if (fd_timeout.next)
+ printk("floppy timer still active:%s\n", timeout_message);
if (fd_timer.next)
printk("auxiliary floppy timer still active\n");
- if(floppy_tq.sync)
+ if (floppy_tq.sync)
printk("task queue still active\n");
#endif
+ MOD_DEC_USE_COUNT;
+}
+
+
+#ifdef MODULE
+
+extern char *get_options(char *str, int *ints);
+
+char *floppy=NULL;
+
+static void parse_floppy_cfg_string(char *cfg)
+{
+ char *ptr;
+ int ints[11];
+
+ while(*cfg) {
+ for(ptr = cfg;*cfg && *cfg != ' ' && *cfg != '\t'; cfg++);
+ if(*cfg) {
+ *cfg = '\0';
+ cfg++;
+ }
+ if(*ptr)
+ floppy_setup(get_options(ptr,ints),ints);
+ }
+}
+
+static void mod_setup(char *pattern, void (*setup)(char *, int *))
+{
+ unsigned long i;
+ char c;
+ int j;
+ int match;
+ char buffer[100];
+ int ints[11];
+ int length = strlen(pattern)+1;
+
+ match=0;
+ j=1;
+
+ for (i=current->mm->env_start; i< current->mm->env_end; i ++){
+ get_user(c, (char *)i);
+ if (match){
+ if (j==99)
+ c='\0';
+ buffer[j] = c;
+ if (!c || c == ' ' || c == '\t'){
+ if (j){
+ buffer[j] = '\0';
+ setup(get_options(buffer,ints),ints);
+ }
+ j=0;
+ } else
+ j++;
+ if (!c)
+ break;
+ continue;
+ }
+ if ((!j && !c) || (j && c == pattern[j-1]))
+ j++;
+ else
+ j=0;
+ if (j==length){
+ match=1;
+ j=0;
+ }
+ }
+}
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int init_module(void)
+{
+ printk(KERN_INFO "inserting floppy driver for %s\n", kernel_version);
+
+ if(floppy)
+ parse_floppy_cfg_string(floppy);
+ else
+ mod_setup("floppy=", floppy_setup);
+
+ return floppy_init();
+}
+
+void cleanup_module(void)
+{
+ int fdc, dummy;
+
+ for (fdc=0; fdc<2; fdc++)
+ if (FDCS->address != -1){
+ release_region(FDCS->address, 6);
+ release_region(FDCS->address+7, 1);
+ }
+
+ unregister_blkdev(MAJOR_NR, "fd");
+
+ blk_dev[MAJOR_NR].request_fn = 0;
+ /* eject disk, if any */
+ dummy = fd_eject(0);
}
+#ifdef __cplusplus
+}
+#endif
+
+#else
+/* eject the boot floppy (if we need the drive for a different root floppy) */
+/* This should only be called at boot time when we're sure that there's no
+ * resource contention. */
+void floppy_eject(void)
+{
+ int dummy;
+ floppy_grab_irq_and_dma();
+ lock_fdc(MAXTIMEOUT,0);
+ dummy=fd_eject(0);
+ process_fd_request();
+ floppy_release_irq_and_dma();
+}
+#endif
diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c
index 1108288fe..198e4a483 100644
--- a/drivers/block/genhd.c
+++ b/drivers/block/genhd.c
@@ -3,38 +3,109 @@
* linux/kernel/hd.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
- */
-
-/*
+ *
+ *
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
* in the early extended-partition checks and added DM partitions
+ *
+ * Support for DiskManager v6.0x added by Mark Lord,
+ * with information provided by OnTrack. This now works for linux fdisk
+ * and LILO, as well as loadlin and bootln. Note that disks other than
+ * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
+ *
+ * More flexible handling of extended partitions - aeb, 950831
+ *
+ * Support for systems without PC-style consoles - <dfrick@dial.eunet.ch>, 960+ *
+ *
+ * Check partition table on IDE disks for common CHS translations
*/
+#include <linux/config.h>
#include <linux/fs.h>
#include <linux/genhd.h>
#include <linux/kernel.h>
#include <linux/major.h>
+#include <linux/string.h>
+#ifdef CONFIG_BLK_DEV_INITRD
+#include <linux/blk.h>
+#endif
+
+#include <asm/system.h>
+
+/*
+ * Many architectures don't like unaligned accesses, which is
+ * frequently the case with the nr_sects and start_sect partition
+ * table entries.
+ */
+#include <asm/unaligned.h>
+
+#define SYS_IND(p) get_unaligned(&p->sys_ind)
+#define NR_SECTS(p) get_unaligned(&p->nr_sects)
+#define START_SECT(p) get_unaligned(&p->start_sect)
+
struct gendisk *gendisk_head = NULL;
static int current_minor = 0;
extern int *blk_size[];
extern void rd_load(void);
-extern int ramdisk_size;
+extern void initrd_load(void);
+
+extern int chr_dev_init(void);
+extern int blk_dev_init(void);
+extern int scsi_dev_init(void);
+extern int net_dev_init(void);
-static char minor_name (struct gendisk *hd, int minor)
+/*
+ * disk_name() is used by genhd.c and md.c.
+ * It formats the devicename of the indicated disk
+ * into the supplied buffer, and returns a pointer
+ * to that same buffer (for convenience).
+ */
+char *disk_name (struct gendisk *hd, int minor, char *buf)
{
- char base_name = (hd->major == IDE1_MAJOR) ? 'c' : 'a';
- return base_name + (minor >> hd->minor_shift);
+ unsigned int part;
+ const char *maj = hd->major_name;
+ char unit = (minor >> hd->minor_shift) + 'a';
+
+ /*
+ * IDE devices use multiple major numbers, but the drives
+ * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
+ * This requires special handling here.
+ */
+ switch (hd->major) {
+ case IDE3_MAJOR:
+ unit += 2;
+ case IDE2_MAJOR:
+ unit += 2;
+ case IDE1_MAJOR:
+ unit += 2;
+ case IDE0_MAJOR:
+ maj = "hd";
+ }
+ part = minor & ((1 << hd->minor_shift) - 1);
+ if (part)
+ sprintf(buf, "%s%c%d", maj, unit, part);
+ else
+ sprintf(buf, "%s%c", maj, unit);
+ return buf;
}
static void add_partition (struct gendisk *hd, int minor, int start, int size)
{
+ char buf[8];
hd->part[minor].start_sect = start;
hd->part[minor].nr_sects = size;
- printk(" %s%c%d", hd->major_name, minor_name(hd, minor),
- minor & ((1 << hd->minor_shift) - 1));
+ printk(" %s", disk_name(hd, minor, buf));
+}
+
+static inline int is_extended_partition(struct partition *p)
+{
+ return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
+ SYS_IND(p) == LINUX_EXTENDED_PARTITION);
}
+
+#ifdef CONFIG_MSDOS_PARTITION
/*
* Create devices for each logical partition in an extended partition.
* The logical partitions form a linked list, with each entry being
@@ -46,18 +117,20 @@ static void add_partition (struct gendisk *hd, int minor, int start, int size)
* only for the actual data partitions.
*/
-static void extended_partition(struct gendisk *hd, int dev)
+static void extended_partition(struct gendisk *hd, kdev_t dev)
{
struct buffer_head *bh;
struct partition *p;
- unsigned long first_sector, this_sector;
+ unsigned long first_sector, first_size, this_sector, this_size;
int mask = (1 << hd->minor_shift) - 1;
+ int i;
first_sector = hd->part[MINOR(dev)].start_sect;
+ first_size = hd->part[MINOR(dev)].nr_sects;
this_sector = first_sector;
while (1) {
- if ((current_minor & mask) >= (4 + hd->max_p))
+ if ((current_minor & mask) == 0)
return;
if (!(bh = bread(dev,0,1024)))
return;
@@ -65,50 +138,511 @@ static void extended_partition(struct gendisk *hd, int dev)
* This block is from a device that we're about to stomp on.
* So make sure nobody thinks this block is usable.
*/
- bh->b_dirt = 0;
- bh->b_uptodate = 0;
- bh->b_req = 0;
- if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
- p = (struct partition *) (0x1BE + bh->b_data);
+ bh->b_state = 0;
+
+ if (*(unsigned short *) (bh->b_data+510) != 0xAA55)
+ goto done;
+
+ p = (struct partition *) (0x1BE + bh->b_data);
+
+ this_size = hd->part[MINOR(dev)].nr_sects;
+
/*
- * Process the first entry, which should be the real
- * data partition.
+ * Usually, the first entry is the real data partition,
+ * the 2nd entry is the next extended partition, or empty,
+ * and the 3rd and 4th entries are unused.
+ * However, DRDOS sometimes has the extended partition as
+ * the first entry (when the data partition is empty),
+ * and OS/2 seems to use all four entries.
*/
- if (p->sys_ind == EXTENDED_PARTITION || !p->nr_sects)
- goto done; /* shouldn't happen */
- add_partition(hd, current_minor, this_sector+p->start_sect, p->nr_sects);
- current_minor++;
- p++;
+
+ /*
+ * First process the data partition(s)
+ */
+ for (i=0; i<4; i++, p++) {
+ if (!NR_SECTS(p) || is_extended_partition(p))
+ continue;
+
+ /* Check the 3rd and 4th entries -
+ these sometimes contain random garbage */
+ if (i >= 2
+ && START_SECT(p) + NR_SECTS(p) > this_size
+ && (this_sector + START_SECT(p) < first_sector ||
+ this_sector + START_SECT(p) + NR_SECTS(p) >
+ first_sector + first_size))
+ continue;
+
+ add_partition(hd, current_minor, this_sector+START_SECT(p), NR_SECTS(p));
+ current_minor++;
+ if ((current_minor & mask) == 0)
+ goto done;
+ }
/*
- * Process the second entry, which should be a link
- * to the next logical partition. Create a minor
- * for this just long enough to get the next partition
- * table. The minor will be reused for the real
+ * Next, process the (first) extended partition, if present.
+ * (So far, there seems to be no reason to make
+ * extended_partition() recursive and allow a tree
+ * of extended partitions.)
+ * It should be a link to the next logical partition.
+ * Create a minor for this just long enough to get the next
+ * partition table. The minor will be reused for the next
* data partition.
*/
- if (p->sys_ind != EXTENDED_PARTITION ||
- !(hd->part[current_minor].nr_sects = p->nr_sects))
- goto done; /* no more logicals in this partition */
- hd->part[current_minor].start_sect = first_sector + p->start_sect;
- hd->sizes[current_minor] = p->nr_sects >> (BLOCK_SIZE_BITS - 9);
- this_sector = first_sector + p->start_sect;
- dev = ((hd->major) << 8) | current_minor;
- brelse(bh);
- } else
- goto done;
+ p -= 4;
+ for (i=0; i<4; i++, p++)
+ if(NR_SECTS(p) && is_extended_partition(p))
+ break;
+ if (i == 4)
+ goto done; /* nothing left to do */
+
+ hd->part[current_minor].nr_sects = NR_SECTS(p);
+ hd->part[current_minor].start_sect = first_sector + START_SECT(p);
+ this_sector = first_sector + START_SECT(p);
+ dev = MKDEV(hd->major, current_minor);
+ brelse(bh);
}
done:
brelse(bh);
}
-static void check_partition(struct gendisk *hd, unsigned int dev)
+#ifdef CONFIG_BSD_DISKLABEL
+/*
+ * Create devices for BSD partitions listed in a disklabel, under a
+ * dos-like partition. See extended_partition() for more information.
+ */
+static void bsd_disklabel_partition(struct gendisk *hd, kdev_t dev)
+{
+ struct buffer_head *bh;
+ struct bsd_disklabel *l;
+ struct bsd_partition *p;
+ int mask = (1 << hd->minor_shift) - 1;
+
+ if (!(bh = bread(dev,0,1024)))
+ return;
+ bh->b_state = 0;
+ l = (struct bsd_disklabel *) (bh->b_data+512);
+ if (l->d_magic != BSD_DISKMAGIC) {
+ brelse(bh);
+ return;
+ }
+
+ p = &l->d_partitions[0];
+ while (p - &l->d_partitions[0] <= BSD_MAXPARTITIONS) {
+ if ((current_minor & mask) >= (4 + hd->max_p))
+ break;
+
+ if (p->p_fstype != BSD_FS_UNUSED) {
+ add_partition(hd, current_minor, p->p_offset, p->p_size);
+ current_minor++;
+ }
+ p++;
+ }
+ brelse(bh);
+
+}
+#endif
+
+static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
{
- static int first_time = 1;
int i, minor = current_minor;
struct buffer_head *bh;
+ struct partition ptable[4];
struct partition *p;
- unsigned long first_sector;
+ unsigned char *data;
+ int mask = (1 << hd->minor_shift) - 1;
+#ifdef CONFIG_BLK_DEV_IDE
+ int tested_for_xlate = 0;
+
+read_mbr:
+#endif
+ if (!(bh = bread(dev,0,1024))) {
+ printk(" unable to read partition table\n");
+ return -1;
+ }
+ data = bh->b_data;
+ /* In some cases we modify the geometry */
+ /* of the drive (below), so ensure that */
+ /* nobody else tries to re-use this data. */
+ bh->b_state = 0;
+#ifdef CONFIG_BLK_DEV_IDE
+check_table:
+#endif
+ if (*(unsigned short *) (0x1fe + data) != 0xAA55) {
+ brelse(bh);
+ return 0;
+ }
+ memcpy(ptable, (struct partition *)(data + 0x1be), sizeof(struct partition) * 4);
+ p = ptable;
+
+#ifdef CONFIG_BLK_DEV_IDE
+ if (!tested_for_xlate++) { /* Do this only once per disk */
+ /*
+ * Look for various forms of IDE disk geometry translation
+ */
+ extern int ide_xlate_1024(kdev_t, int, const char *);
+ unsigned int sig = *(unsigned short *)(data + 2);
+ if (SYS_IND(p) == EZD_PARTITION) {
+ /*
+ * The remainder of the disk must be accessed using
+ * a translated geometry that reduces the number of
+ * apparent cylinders to less than 1024 if possible.
+ *
+ * ide_xlate_1024() will take care of the necessary
+ * adjustments to fool fdisk/LILO and partition check.
+ */
+ if (ide_xlate_1024(dev, -1, " [EZD]")) {
+ data += 512;
+ goto check_table;
+ }
+ } else if (SYS_IND(p) == DM6_PARTITION) {
+
+ /*
+ * Everything on the disk is offset by 63 sectors,
+ * including a "new" MBR with its own partition table,
+ * and the remainder of the disk must be accessed using
+ * a translated geometry that reduces the number of
+ * apparent cylinders to less than 1024 if possible.
+ *
+ * ide_xlate_1024() will take care of the necessary
+ * adjustments to fool fdisk/LILO and partition check.
+ */
+ if (ide_xlate_1024(dev, 1, " [DM6:DDO]")) {
+ brelse(bh);
+ goto read_mbr; /* start over with new MBR */
+ }
+ } else if (sig <= 0x1ae && *(unsigned short *)(data + sig) == 0x55AA
+ && (1 & *(unsigned char *)(data + sig + 2)) )
+ {
+ /*
+ * DM6 signature in MBR, courtesy of OnTrack
+ */
+ (void) ide_xlate_1024 (dev, 0, " [DM6:MBR]");
+ } else if (SYS_IND(p) == DM6_AUX1PARTITION || SYS_IND(p) == DM6_AUX3PARTITION) {
+ /*
+ * DM6 on other than the first (boot) drive
+ */
+ (void) ide_xlate_1024(dev, 0, " [DM6:AUX]");
+ } else {
+ /*
+ * Examine the partition table for common translations.
+ * This is necessary for drives for situations where
+ * the translated geometry is unavailable from the BIOS.
+ */
+ for (i = 0; i < 4 ; i++) {
+ struct partition *q = &p[i];
+ if (NR_SECTS(q)
+ && (q->sector & 63) == 1
+ && (q->end_sector & 63) == 63) {
+ unsigned int heads = q->end_head + 1;
+ if (heads == 32 || heads == 64 || heads == 128) {
+
+ (void) ide_xlate_1024(dev, heads, " [PTBL]");
+ break;
+ }
+ }
+ }
+ }
+ }
+#endif /* CONFIG_BLK_DEV_IDE */
+
+ current_minor += 4; /* first "extra" minor (for extended partitions) */
+ for (i=1 ; i<=4 ; minor++,i++,p++) {
+ if (!NR_SECTS(p))
+ continue;
+ add_partition(hd, minor, first_sector+START_SECT(p), NR_SECTS(p));
+ if (is_extended_partition(p)) {
+ printk(" <");
+ /*
+ * If we are rereading the partition table, we need
+ * to set the size of the partition so that we will
+ * be able to bread the block containing the extended
+ * partition info.
+ */
+ hd->sizes[minor] = hd->part[minor].nr_sects
+ >> (BLOCK_SIZE_BITS - 9);
+ extended_partition(hd, MKDEV(hd->major, minor));
+ printk(" >");
+ /* prevent someone doing mkfs or mkswap on an
+ extended partition, but leave room for LILO */
+ if (hd->part[minor].nr_sects > 2)
+ hd->part[minor].nr_sects = 2;
+ }
+#ifdef CONFIG_BSD_DISKLABEL
+ if (SYS_IND(p) == BSD_PARTITION) {
+ printk(" <");
+ bsd_disklabel_partition(hd, MKDEV(hd->major, minor));
+ printk(" >");
+ }
+#endif
+ }
+ /*
+ * Check for old-style Disk Manager partition table
+ */
+ if (*(unsigned short *) (data+0xfc) == 0x55AA) {
+ p = ptable;
+ for (i = 4 ; i < 16 ; i++, current_minor++) {
+ p--;
+ if ((current_minor & mask) == 0)
+ break;
+ if (!(START_SECT(p) && NR_SECTS(p)))
+ continue;
+ add_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
+ }
+ }
+ printk("\n");
+ brelse(bh);
+ return 1;
+}
+
+#endif /* CONFIG_MSDOS_PARTITION */
+
+#ifdef CONFIG_OSF_PARTITION
+
+static int osf_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector)
+{
+ int i;
int mask = (1 << hd->minor_shift) - 1;
+ struct buffer_head *bh;
+ struct disklabel {
+ u32 d_magic;
+ u16 d_type,d_subtype;
+ u8 d_typename[16];
+ u8 d_packname[16];
+ u32 d_secsize;
+ u32 d_nsectors;
+ u32 d_ntracks;
+ u32 d_ncylinders;
+ u32 d_secpercyl;
+ u32 d_secprtunit;
+ u16 d_sparespertrack;
+ u16 d_sparespercyl;
+ u32 d_acylinders;
+ u16 d_rpm, d_interleave, d_trackskew, d_cylskew;
+ u32 d_headswitch, d_trkseek, d_flags;
+ u32 d_drivedata[5];
+ u32 d_spare[5];
+ u32 d_magic2;
+ u16 d_checksum;
+ u16 d_npartitions;
+ u32 d_bbsize, d_sbsize;
+ struct d_partition {
+ u32 p_size;
+ u32 p_offset;
+ u32 p_fsize;
+ u8 p_fstype;
+ u8 p_frag;
+ u16 p_cpg;
+ } d_partitions[8];
+ } * label;
+ struct d_partition * partition;
+#define DISKLABELMAGIC (0x82564557UL)
+
+ if (!(bh = bread(dev,0,1024))) {
+ printk("unable to read partition table\n");
+ return -1;
+ }
+ label = (struct disklabel *) (bh->b_data+64);
+ partition = label->d_partitions;
+ if (label->d_magic != DISKLABELMAGIC) {
+ printk("magic: %08x\n", label->d_magic);
+ brelse(bh);
+ return 0;
+ }
+ if (label->d_magic2 != DISKLABELMAGIC) {
+ printk("magic2: %08x\n", label->d_magic2);
+ brelse(bh);
+ return 0;
+ }
+ for (i = 0 ; i < label->d_npartitions; i++, partition++) {
+ if ((current_minor & mask) == 0)
+ break;
+ if (partition->p_size)
+ add_partition(hd, current_minor,
+ first_sector+partition->p_offset,
+ partition->p_size);
+ current_minor++;
+ }
+ printk("\n");
+ brelse(bh);
+ return 1;
+}
+
+#endif /* CONFIG_OSF_PARTITION */
+
+#ifdef CONFIG_SUN_PARTITION
+
+static int sun_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
+{
+ int i, csum;
+ unsigned short *ush;
+ struct buffer_head *bh;
+ struct sun_disklabel {
+ unsigned char info[128]; /* Informative text string */
+ unsigned char spare[292]; /* Boot information etc. */
+ unsigned short rspeed; /* Disk rotational speed */
+ unsigned short pcylcount; /* Physical cylinder count */
+ unsigned short sparecyl; /* extra sects per cylinder */
+ unsigned char spare2[4]; /* More magic... */
+ unsigned short ilfact; /* Interleave factor */
+ unsigned short ncyl; /* Data cylinder count */
+ unsigned short nacyl; /* Alt. cylinder count */
+ unsigned short ntrks; /* Tracks per cylinder */
+ unsigned short nsect; /* Sectors per track */
+ unsigned char spare3[4]; /* Even more magic... */
+ struct sun_partition {
+ __u32 start_cylinder;
+ __u32 num_sectors;
+ } partitions[8];
+ unsigned short magic; /* Magic number */
+ unsigned short csum; /* Label xor'd checksum */
+ } * label;
+ struct sun_partition *p;
+ int other_endian;
+ unsigned long spc;
+#define SUN_LABEL_MAGIC 0xDABE
+#define SUN_LABEL_MAGIC_SWAPPED 0xBEDA
+/* No need to optimize these macros since they are called only when reading
+ * the partition table. This occurs only at each disk change. */
+#define SWAP16(x) (other_endian ? (((__u16)(x) & 0xFF) << 8) \
+ | (((__u16)(x) & 0xFF00) >> 8) \
+ : (__u16)(x))
+#define SWAP32(x) (other_endian ? (((__u32)(x) & 0xFF) << 24) \
+ | (((__u32)(x) & 0xFF00) << 8) \
+ | (((__u32)(x) & 0xFF0000) >> 8) \
+ | (((__u32)(x) & 0xFF000000) >> 24) \
+ : (__u32)(x))
+
+ if(!(bh = bread(dev, 0, 1024))) {
+ printk("Dev %s: unable to read partition table\n",
+ kdevname(dev));
+ return -1;
+ }
+ label = (struct sun_disklabel *) bh->b_data;
+ p = label->partitions;
+ if (label->magic != SUN_LABEL_MAGIC && label->magic != SUN_LABEL_MAGIC_SWAPPED) {
+ printk("Dev %s Sun disklabel: bad magic %04x\n",
+ kdevname(dev), label->magic);
+ brelse(bh);
+ return 0;
+ }
+ other_endian = (label->magic == SUN_LABEL_MAGIC_SWAPPED);
+ /* Look at the checksum */
+ ush = ((unsigned short *) (label+1)) - 1;
+ for(csum = 0; ush >= ((unsigned short *) label);)
+ csum ^= *ush--;
+ if(csum) {
+ printk("Dev %s Sun disklabel: Csum bad, label corrupted\n",
+ kdevname(dev));
+ brelse(bh);
+ return 0;
+ }
+ /* All Sun disks have 8 partition entries */
+ spc = SWAP16(label->ntrks) * SWAP16(label->nsect);
+ for(i=0; i < 8; i++, p++) {
+ unsigned long st_sector;
+
+ /* We register all partitions, even if zero size, so that
+ * the minor numbers end up ok as per SunOS interpretation.
+ */
+ st_sector = first_sector + SWAP32(p->start_cylinder) * spc;
+ add_partition(hd, current_minor, st_sector, SWAP32(p->num_sectors));
+ current_minor++;
+ }
+ printk("\n");
+ brelse(bh);
+ return 1;
+#undef SWAP16
+#undef SWAP32
+}
+
+#endif /* CONFIG_SUN_PARTITION */
+
+#ifdef CONFIG_AMIGA_PARTITION
+#include <asm/byteorder.h>
+#include <linux/affs_hardblocks.h>
+
+static __inline__ __u32
+checksum_block(__u32 *m, int size)
+{
+ __u32 sum = 0;
+
+ while (size--)
+ sum += htonl(*m++);
+ return sum;
+}
+
+static int
+amiga_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector)
+{
+ struct buffer_head *bh;
+ struct RigidDiskBlock *rdb;
+ struct PartitionBlock *pb;
+ int start_sect;
+ int nr_sects;
+ int blk;
+ int part, res;
+
+ set_blocksize(dev,512);
+ res = 0;
+
+ for (blk = 0; blk < RDB_ALLOCATION_LIMIT; blk++) {
+ if(!(bh = bread(dev,blk,512))) {
+ printk("Dev %d: unable to read RDB block %d\n",dev,blk);
+ goto rdb_done;
+ }
+ if (*(__u32 *)bh->b_data == htonl(IDNAME_RIGIDDISK)) {
+ rdb = (struct RigidDiskBlock *)bh->b_data;
+ if (checksum_block((__u32 *)bh->b_data,htonl(rdb->rdb_SummedLongs) & 0x7F)) {
+ printk("Dev %d: RDB in block %d has bad checksum\n",dev,blk);
+ brelse(bh);
+ continue;
+ }
+ printk(" RDSK");
+ blk = htonl(rdb->rdb_PartitionList);
+ brelse(bh);
+ for (part = 1; blk > 0 && part <= 16; part++) {
+ if (!(bh = bread(dev,blk,512))) {
+ printk("Dev %d: unable to read partition block %d\n",
+ dev,blk);
+ goto rdb_done;
+ }
+ pb = (struct PartitionBlock *)bh->b_data;
+ blk = htonl(pb->pb_Next);
+ if (pb->pb_ID == htonl(IDNAME_PARTITION) && checksum_block(
+ (__u32 *)pb,htonl(pb->pb_SummedLongs) & 0x7F) == 0 ) {
+
+ /* Tell Kernel about it */
+
+ if (!(nr_sects = (htonl(pb->pb_Environment[10]) + 1 -
+ htonl(pb->pb_Environment[9])) *
+ htonl(pb->pb_Environment[3]) *
+ htonl(pb->pb_Environment[5]))) {
+ continue;
+ }
+ start_sect = htonl(pb->pb_Environment[9]) *
+ htonl(pb->pb_Environment[3]) *
+ htonl(pb->pb_Environment[5]);
+ add_partition(hd,current_minor,start_sect,nr_sects);
+ current_minor++;
+ res = 1;
+ }
+ brelse(bh);
+ }
+ printk("\n");
+ break;
+ }
+ }
+
+rdb_done:
+ set_blocksize(dev,BLOCK_SIZE);
+ return res;
+}
+#endif /* CONFIG_AMIGA_PARTITION */
+
+static void check_partition(struct gendisk *hd, kdev_t dev)
+{
+ static int first_time = 1;
+ unsigned long first_sector;
+ char buf[8];
if (first_time)
printk("Partition check:\n");
@@ -117,51 +651,31 @@ static void check_partition(struct gendisk *hd, unsigned int dev)
/*
* This is a kludge to allow the partition check to be
- * skipped for specific drives (ie. IDE cd-rom drives)
+ * skipped for specific drives (e.g. IDE cd-rom drives)
*/
if ((int)first_sector == -1) {
hd->part[MINOR(dev)].start_sect = 0;
return;
}
- if (!(bh = bread(dev,0,1024))) {
- printk(" unable to read partition table of device %04x\n",dev);
+ printk(" %s:", disk_name(hd, MINOR(dev), buf));
+#ifdef CONFIG_MSDOS_PARTITION
+ if (msdos_partition(hd, dev, first_sector))
return;
- }
- printk(" %s%c:", hd->major_name, minor_name(hd, minor));
- current_minor += 4; /* first "extra" minor (for extended partitions) */
- if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
- p = (struct partition *) (0x1BE + bh->b_data);
- for (i=1 ; i<=4 ; minor++,i++,p++) {
- if (!p->nr_sects)
- continue;
- add_partition(hd, minor, first_sector+p->start_sect, p->nr_sects);
- if ((current_minor & 0x3f) >= 60)
- continue;
- if (p->sys_ind == EXTENDED_PARTITION) {
- printk(" <");
- extended_partition(hd, (hd->major << 8) | minor);
- printk(" >");
- }
- }
- /*
- * check for Disk Manager partition table
- */
- if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) {
- p = (struct partition *) (0x1BE + bh->b_data);
- for (i = 4 ; i < 16 ; i++, current_minor++) {
- p--;
- if ((current_minor & mask) >= mask-2)
- break;
- if (!(p->start_sect && p->nr_sects))
- continue;
- add_partition(hd, current_minor, p->start_sect, p->nr_sects);
- }
- }
- } else
- printk(" bad partition table");
- printk("\n");
- brelse(bh);
+#endif
+#ifdef CONFIG_OSF_PARTITION
+ if (osf_partition(hd, dev, first_sector))
+ return;
+#endif
+#ifdef CONFIG_SUN_PARTITION
+ if(sun_partition(hd, dev, first_sector))
+ return;
+#endif
+#ifdef CONFIG_AMIGA_PARTITION
+ if(amiga_partition(hd, dev, first_sector))
+ return;
+#endif
+ printk(" unknown partition table\n");
}
/* This function is used to re-read partition tables for removable disks.
@@ -176,49 +690,75 @@ can start using the device while this function is being executed. */
void resetup_one_dev(struct gendisk *dev, int drive)
{
int i;
- int start = drive<<dev->minor_shift;
- int j = start + dev->max_p;
- int major = dev->major << 8;
+ int first_minor = drive << dev->minor_shift;
+ int end_minor = first_minor + dev->max_p;
- current_minor = 1+(drive<<dev->minor_shift);
- check_partition(dev, major+(drive<<dev->minor_shift));
+ blk_size[dev->major] = NULL;
+ current_minor = 1 + first_minor;
+ check_partition(dev, MKDEV(dev->major, first_minor));
- for (i=start ; i < j ; i++)
- dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ /*
+ * We need to set the sizes array before we will be able to access
+ * any of the partitions on this device.
+ */
+ if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
+ for (i = first_minor; i < end_minor; i++)
+ dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ blk_size[dev->major] = dev->sizes;
+ }
}
static void setup_dev(struct gendisk *dev)
{
- int i;
- int j = dev->max_nr * dev->max_p;
- int major = dev->major << 8;
- int drive;
-
+ int i, drive;
+ int end_minor = dev->max_nr * dev->max_p;
- for (i = 0 ; i < j; i++) {
+ blk_size[dev->major] = NULL;
+ for (i = 0 ; i < end_minor; i++) {
dev->part[i].start_sect = 0;
dev->part[i].nr_sects = 0;
}
- dev->init();
- for (drive=0 ; drive<dev->nr_real ; drive++) {
- current_minor = 1+(drive<<dev->minor_shift);
- check_partition(dev, major+(drive<<dev->minor_shift));
+ dev->init(dev);
+ for (drive = 0 ; drive < dev->nr_real ; drive++) {
+ int first_minor = drive << dev->minor_shift;
+ current_minor = 1 + first_minor;
+ check_partition(dev, MKDEV(dev->major, first_minor));
+ }
+ if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
+ for (i = 0; i < end_minor; i++)
+ dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ blk_size[dev->major] = dev->sizes;
}
- for (i=0 ; i < j ; i++)
- dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
- blk_size[dev->major] = dev->sizes;
}
-
+
void device_setup(void)
{
+ extern void console_map_init(void);
struct gendisk *p;
int nr=0;
+ chr_dev_init();
+ blk_dev_init();
+ sti();
+#ifdef CONFIG_SCSI
+ scsi_dev_init();
+#endif
+#ifdef CONFIG_INET
+ net_dev_init();
+#endif
+#ifndef CONFIG_SERIAL_ONLY_CONSOLE
+ console_map_init();
+#endif
+
for (p = gendisk_head ; p ; p=p->next) {
setup_dev(p);
nr += p->nr_real;
}
-
- if (ramdisk_size)
- rd_load();
+#ifdef CONFIG_BLK_DEV_RAM
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start && mount_initrd) initrd_load();
+ else
+#endif
+ rd_load();
+#endif
}
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index f17aa86d9..2bd6d46fe 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -16,7 +16,7 @@
* in the early extended-partition checks and added DM partitions
*
* IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- * and general streamlining by mlord@bnr.ca (Mark Lord).
+ * and general streamlining by Mark Lord.
*/
#define DEFAULT_MULT_COUNT 0 /* set to 0 to disable multiple mode at boot */
@@ -39,14 +39,12 @@
#define REALLY_SLOW_IO
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#define MAJOR_NR HD_MAJOR
-#include "blk.h"
+#include <linux/blk.h>
-#define HD_IRQ 14
-
-static int revalidate_hddisk(int, int);
+static int revalidate_hddisk(kdev_t, int);
#define HD_DELAY 0
@@ -128,12 +126,12 @@ void hd_setup(char *str, int *ints)
NR_HD = hdind+1;
}
-static void dump_status (char *msg, unsigned int stat)
+static void dump_status (const char *msg, unsigned int stat)
{
unsigned long flags;
char devc;
- devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->dev) : '?';
+ devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
save_flags (flags);
sti();
printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
@@ -282,7 +280,7 @@ static void fixstring (unsigned char *s, int bytecount)
static void identify_intr(void)
{
- unsigned int dev = DEVICE_NR(CURRENT->dev);
+ unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
unsigned short stat = inb_p(HD_STATUS);
struct hd_driveid *id = hd_ident_info[dev];
@@ -335,7 +333,7 @@ static void identify_intr(void)
static void set_multmode_intr(void)
{
- unsigned int dev = DEVICE_NR(CURRENT->dev), stat = inb_p(HD_STATUS);
+ unsigned int dev = DEVICE_NR(CURRENT->rq_dev), stat = inb_p(HD_STATUS);
if (unmask_intr[dev])
sti();
@@ -375,9 +373,9 @@ static void reset_controller(void)
int i;
outb_p(4,HD_CMD);
- for(i = 0; i < 1000; i++) nop();
+ for(i = 0; i < 1000; i++) barrier();
outb_p(hd_info[0].ctl & 0x0f,HD_CMD);
- for(i = 0; i < 1000; i++) nop();
+ for(i = 0; i < 1000; i++) barrier();
if (drive_busy())
printk("hd: controller still busy\n");
else if ((hd_error = inb(HD_ERROR)) != 1)
@@ -449,7 +447,7 @@ static void bad_rw_intr(void)
if (!CURRENT)
return;
- dev = DEVICE_NR(CURRENT->dev);
+ dev = DEVICE_NR(CURRENT->rq_dev);
if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
end_request(0);
special_op[dev] = recalibrate[dev] = 1;
@@ -473,7 +471,7 @@ static inline int wait_DRQ(void)
static void read_intr(void)
{
- unsigned int dev = DEVICE_NR(CURRENT->dev);
+ unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
int i, retries = 100000, msect = mult_count[dev], nsect;
if (unmask_intr[dev])
@@ -548,7 +546,7 @@ static inline void multwrite (unsigned int dev)
static void multwrite_intr(void)
{
int i;
- unsigned int dev = DEVICE_NR(WCURRENT.dev);
+ unsigned int dev = DEVICE_NR(WCURRENT.rq_dev);
if (unmask_intr[dev])
sti();
@@ -584,7 +582,7 @@ static void write_intr(void)
int i;
int retries = 100000;
- if (unmask_intr[DEVICE_NR(WCURRENT.dev)])
+ if (unmask_intr[DEVICE_NR(WCURRENT.rq_dev)])
sti();
do {
i = (unsigned) inb_p(HD_STATUS);
@@ -642,7 +640,7 @@ static void hd_times_out(void)
disable_irq(HD_IRQ);
sti();
reset = 1;
- dev = DEVICE_NR(CURRENT->dev);
+ dev = DEVICE_NR(CURRENT->rq_dev);
printk("hd%c: timeout\n", dev+'a');
if (++CURRENT->errors >= MAX_ERRORS) {
#ifdef DEBUG
@@ -697,7 +695,7 @@ static void hd_request(void)
{
unsigned int dev, block, nsect, sec, track, head, cyl;
- if (CURRENT && CURRENT->dev < 0) return;
+ if (CURRENT && CURRENT->rq_status == RQ_INACTIVE) return;
if (DEVICE_INTR)
return;
repeat:
@@ -709,16 +707,17 @@ repeat:
reset_hd();
return;
}
- dev = MINOR(CURRENT->dev);
+ dev = MINOR(CURRENT->rq_dev);
block = CURRENT->sector;
nsect = CURRENT->nr_sectors;
if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) {
#ifdef DEBUG
if (dev >= (NR_HD<<6))
- printk("hd: bad minor number: device=0x%04x\n", CURRENT->dev);
+ printk("hd: bad minor number: device=%s\n",
+ kdevname(CURRENT->rq_dev));
else
printk("hd%c: bad access: block=%d, count=%d\n",
- (CURRENT->dev>>6)+'a', block, nsect);
+ (MINOR(CURRENT->rq_dev)>>6)+'a', block, nsect);
#endif
end_request(0);
goto repeat;
@@ -783,7 +782,7 @@ static int hd_ioctl(struct inode * inode, struct file * file,
int dev, err;
unsigned long flags;
- if ((!inode) || (!inode->i_rdev))
+ if ((!inode) || !(inode->i_rdev))
return -EINVAL;
dev = DEVICE_NR(inode->i_rdev);
if (dev >= NR_HD)
@@ -794,13 +793,13 @@ static int hd_ioctl(struct inode * inode, struct file * file,
err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
if (err)
return err;
- put_fs_byte(bios_info[dev].head,
+ put_user(bios_info[dev].head,
(char *) &loc->heads);
- put_fs_byte(bios_info[dev].sect,
+ put_user(bios_info[dev].sect,
(char *) &loc->sectors);
- put_fs_word(bios_info[dev].cyl,
+ put_user(bios_info[dev].cyl,
(short *) &loc->cylinders);
- put_fs_long(hd[MINOR(inode->i_rdev)].start_sect,
+ put_user(hd[MINOR(inode->i_rdev)].start_sect,
(long *) &loc->start);
return 0;
case BLKRASET:
@@ -813,14 +812,14 @@ static int hd_ioctl(struct inode * inode, struct file * file,
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
- put_fs_long(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+ put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
return 0;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
- put_fs_long(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
+ put_user(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
return 0;
case BLKFLSBUF:
if(!suser()) return -EACCES;
@@ -843,7 +842,7 @@ static int hd_ioctl(struct inode * inode, struct file * file,
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
- put_fs_long(unmask_intr[dev], (long *) arg);
+ put_user(unmask_intr[dev], (long *) arg);
return 0;
case HDIO_GET_MULTCOUNT:
@@ -851,7 +850,7 @@ static int hd_ioctl(struct inode * inode, struct file * file,
err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
if (err)
return err;
- put_fs_long(mult_count[dev], (long *) arg);
+ put_user(mult_count[dev], (long *) arg);
return 0;
case HDIO_SET_MULTCOUNT:
@@ -879,7 +878,7 @@ static int hd_ioctl(struct inode * inode, struct file * file,
err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct hd_driveid));
if (err)
return err;
- memcpy_tofs((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid));
+ copy_to_user((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid));
return 0;
RO_IOCTLS(inode->i_rdev,arg);
@@ -915,7 +914,7 @@ static void hd_release(struct inode * inode, struct file * file)
}
-static void hd_geninit(void);
+static void hd_geninit(struct gendisk *);
static struct gendisk hd_gendisk = {
MAJOR_NR, /* Major number */
@@ -931,7 +930,7 @@ static struct gendisk hd_gendisk = {
NULL /* next */
};
-static void hd_interrupt(int irq, struct pt_regs *regs)
+static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
void (*handler)(void) = DEVICE_INTR;
@@ -952,14 +951,16 @@ static void hd_interrupt(int irq, struct pt_regs *regs)
* We enable interrupts in some of the routines after making sure it's
* safe.
*/
-static void hd_geninit(void)
+static void hd_geninit(struct gendisk *ignored)
{
- int drive, i;
- extern struct drive_info drive_info;
- unsigned char *BIOS = (unsigned char *) &drive_info;
- int cmos_disks;
+ int i;
+
+#ifdef __i386__
+ if (!NR_HD) {
+ extern struct drive_info drive_info;
+ unsigned char *BIOS = (unsigned char *) &drive_info;
+ int cmos_disks, drive;
- if (!NR_HD) {
for (drive=0 ; drive<2 ; drive++) {
bios_info[drive].cyl = hd_info[drive].cyl = *(unsigned short *) BIOS;
bios_info[drive].head = hd_info[drive].head = *(2+BIOS);
@@ -1002,6 +1003,7 @@ static void hd_geninit(void)
else
NR_HD = 1;
}
+#endif /* __i386__ */
i = NR_HD;
while (i-- > 0) {
/*
@@ -1018,7 +1020,7 @@ static void hd_geninit(void)
special_op[i] = 1;
}
if (NR_HD) {
- if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd")) {
+ if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
printk("hd: unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
NR_HD = 0;
} else {
@@ -1045,18 +1047,18 @@ static struct file_operations hd_fops = {
block_fsync /* fsync */
};
-unsigned long hd_init(unsigned long mem_start, unsigned long mem_end)
+int hd_init(void)
{
if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
printk("hd: unable to get major %d for harddisk\n",MAJOR_NR);
- return mem_start;
+ return -1;
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
hd_gendisk.next = gendisk_head;
gendisk_head = &hd_gendisk;
timer_table[HD_TIMER].fn = hd_times_out;
- return mem_start;
+ return 0;
}
#define DEVICE_BUSY busy[target]
@@ -1075,16 +1077,16 @@ unsigned long hd_init(unsigned long mem_start, unsigned long mem_end)
* usage == 1 (we need an open channel to use an ioctl :-), so this
* is our limit.
*/
-static int revalidate_hddisk(int dev, int maxusage)
+static int revalidate_hddisk(kdev_t dev, int maxusage)
{
- int target, major;
+ int target;
struct gendisk * gdev;
int max_p;
int start;
int i;
long flags;
- target = DEVICE_NR(dev);
+ target = DEVICE_NR(dev);
gdev = &GENDISK_STRUCT;
save_flags(flags);
@@ -1098,14 +1100,15 @@ static int revalidate_hddisk(int dev, int maxusage)
max_p = gdev->max_p;
start = target << gdev->minor_shift;
- major = MAJOR_NR << 8;
for (i=max_p - 1; i >=0 ; i--) {
- sync_dev(major | start | i);
- invalidate_inodes(major | start | i);
- invalidate_buffers(major | start | i);
- gdev->part[start+i].start_sect = 0;
- gdev->part[start+i].nr_sects = 0;
+ int minor = start + i;
+ kdev_t devi = MKDEV(MAJOR_NR, minor);
+ sync_dev(devi);
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+ gdev->part[minor].start_sect = 0;
+ gdev->part[minor].nr_sects = 0;
};
#ifdef MAYBE_REINIT
diff --git a/drivers/block/ht6560b.c b/drivers/block/ht6560b.c
new file mode 100644
index 000000000..ec4f0f89f
--- /dev/null
+++ b/drivers/block/ht6560b.c
@@ -0,0 +1,233 @@
+/*
+ * linux/drivers/block/ht6580.c Version 0.04 Mar 19, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ *
+ * Version 0.01 Initial version hacked out of ide.c
+ *
+ * Version 0.02 Added support for PIO modes, auto-tune
+ *
+ * Version 0.03 Some cleanups
+ *
+ * I reviewed some assembler source listings of htide drivers and found
+ * out how they setup those cycle time interfacing values, as they at Holtek
+ * call them. IDESETUP.COM that is supplied with the drivers figures out
+ * optimal values and fetches those values to drivers. I found out that
+ * they use IDE_SELECT_REG to fetch timings to the ide board right after
+ * interface switching. After that it was quite easy to add code to
+ * ht6560b.c.
+ *
+ * IDESETUP.COM gave me values 0x24, 0x45, 0xaa, 0xff that worked fine
+ * for hda and hdc. But hdb needed higher values to work, so I guess
+ * that sometimes it is necessary to give higher value than IDESETUP
+ * gives. [see cmd640.c for an extreme example of this. -ml]
+ *
+ * Perhaps I should explain something about these timing values:
+ * The higher nibble of value is the Recovery Time (rt) and the lower nibble
+ * of the value is the Active Time (at). Minimum value 2 is the fastest and
+ * the maximum value 15 is the slowest. Default values should be 15 for both.
+ * So 0x24 means 2 for rt and 4 for at. Each of the drives should have
+ * both values, and IDESETUP gives automatically rt=15 st=15 for cdroms or
+ * similar. If value is too small there will be all sorts of failures.
+ *
+ * Port 0x3e6 bit 0x20 sets these timings on/off. If 0x20 bit is set
+ * these timings are disabled.
+ *
+ * Mikko Ala-Fossi
+ *
+ * More notes:
+ *
+ * There's something still missing from the initialization code, though.
+ * If I have booted to dos sometime after power on, I can get smaller
+ * timing values working. Perhaps I could soft-ice the initialization.
+ *
+ * -=- malafoss@snakemail.hut.fi -=- searching the marvels of universe -=-
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "ide_modes.h"
+
+/*
+ * This routine handles interface switching for the peculiar hardware design
+ * on the F.G.I./Holtek HT-6560B VLB IDE interface.
+ * The HT-6560B can only enable one IDE port at a time, and requires a
+ * silly sequence (below) whenever we switch between primary and secondary.
+ *
+ * This stuff is courtesy of malafoss@snakemail.hut.fi
+ * (or maf@nemesis.tky.hut.fi)
+ *
+ * At least one user has reported that this code can confuse the floppy
+ * controller and/or driver -- perhaps this should be changed to use
+ * a read-modify-write sequence, so as not to disturb other bits in the reg?
+ */
+
+/*
+ * The special i/o-port that HT-6560B uses to select interfaces:
+ */
+#define HT_SELECT_PORT 0x3e6
+
+/*
+ * We don't know what all of the bits are for, but we *do* know about these:
+ * bit5 (0x20): "1" selects slower speed by disabling use of timing values
+ * bit0 (0x01): "1" selects second interface
+ */
+static byte ht6560b_selects [2][MAX_DRIVES] = {{0x3c,0x3c}, {0x3d,0x3d}};
+
+/*
+ * VLB ht6560b Timing values:
+ *
+ * Timing byte consists of
+ * High nibble: Recovery Time (rt)
+ * The valid values range from 2 to 15. The default is 15.
+ *
+ * Low nibble: Active Time (at)
+ * The valid values range from 2 to 15. The default is 15.
+ *
+ * You can obtain optimized timing values by running Holtek IDESETUP.COM
+ * for DOS. DOS drivers get their timing values from command line, where
+ * the first value is the Recovery Time and the second value is the
+ * Active Time for each drive. Smaller value gives higher speed.
+ * In case of failures you should probably fall back to a higher value.
+ *
+ * Hopefully this example will make it clearer:
+ *
+ * DOS: DEVICE=C:\bin\HTIDE\HTIDE.SYS /D0=2,4 /D1=4,5 /D2=10,10 /D3=15,15
+ * Linux: byte ht6560b_timings [][] = {{0x24, 0x45}, {0xaa, 0xff}};
+ *
+ * Note: There are no ioctls to change these values directly,
+ * but settings can be approximated as PIO modes, using "hdparm":
+ *
+ * rc.local: hdparm -p3 /dev/hda -p2 /dev/hdb -p1 /dev/hdc -p0 /dev/hdd
+ */
+
+static byte ht6560b_timings [2][MAX_DRIVES] = {{0xff,0xff}, {0xff,0xff}};
+
+static byte pio_to_timings[6] = {0xff, 0xaa, 0x45, 0x24, 0x13, 0x12};
+
+/*
+ * This routine is invoked from ide.c to prepare for access to a given drive.
+ */
+static void ht6560b_selectproc (ide_drive_t *drive)
+{
+ byte t;
+ unsigned long flags;
+ static byte current_select = 0;
+ static byte current_timing = 0;
+ byte select = ht6560b_selects[HWIF(drive)->index][drive->select.b.unit];
+ byte timing = ht6560b_timings[HWIF(drive)->index][drive->select.b.unit];
+
+ if (select != current_select || timing != current_timing) {
+ current_select = select;
+ current_timing = timing;
+ save_flags (flags);
+ cli();
+ (void) inb(HT_SELECT_PORT);
+ (void) inb(HT_SELECT_PORT);
+ (void) inb(HT_SELECT_PORT);
+ /*
+ * Note: input bits are reversed to output bits!!
+ */
+ t = inb(HT_SELECT_PORT) ^ 0x3f;
+ t &= (~0x21);
+ t |= (current_select & 0x21);
+ outb(t, HT_SELECT_PORT);
+ /*
+ * Set timing for this drive:
+ */
+ outb (timing, IDE_SELECT_REG);
+ (void) inb (IDE_STATUS_REG);
+ restore_flags (flags);
+#ifdef DEBUG
+ printk("ht6560b: %s: select=%#x timing=%#x\n", drive->name, t, timing);
+#endif
+ }
+ OUT_BYTE(drive->select.all,IDE_SELECT_REG);
+}
+
+/*
+ * Autodetection and initialization of ht6560b
+ */
+int try_to_init_ht6560b(void)
+{
+ byte orig_value;
+ int i;
+
+ /* Autodetect ht6560b */
+ if ((orig_value=inb(HT_SELECT_PORT)) == 0xff)
+ return 0;
+
+ for (i=3;i>0;i--) {
+ outb(0x00, HT_SELECT_PORT);
+ if (!( (~inb(HT_SELECT_PORT)) & 0x3f )) {
+ outb(orig_value, HT_SELECT_PORT);
+ return 0;
+ }
+ }
+ outb(0x00, HT_SELECT_PORT);
+ if ((~inb(HT_SELECT_PORT))& 0x3f) {
+ outb(orig_value, HT_SELECT_PORT);
+ return 0;
+ }
+ /*
+ * Ht6560b autodetected:
+ * reverse input bits to output bits
+ * initialize bit1 to 0
+ */
+ outb((orig_value ^ 0x3f) & 0xfd, HT_SELECT_PORT);
+
+ printk("\nht6560b: detected and initialized");
+ return 1;
+}
+
+static void tune_ht6560b (ide_drive_t *drive, byte pio)
+{
+ unsigned int hwif, unit;
+
+ if (pio == 255) { /* auto-tune */
+ if (drive->media != ide_disk)
+ pio = 0; /* some cdroms don't like fast modes (?) */
+ else
+ pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
+ }
+ unit = drive->select.b.unit;
+ hwif = HWIF(drive)->index;
+ ht6560b_timings[hwif][unit] = pio_to_timings[pio];
+ if (pio == 0)
+ ht6560b_selects[hwif][unit] |= 0x20;
+ else
+ ht6560b_selects[hwif][unit] &= ~0x20;
+}
+
+void init_ht6560b (void)
+{
+ if (check_region(HT_SELECT_PORT,1)) {
+ printk("\nht6560b: PORT 0x3e6 ALREADY IN USE\n");
+ } else {
+ if (try_to_init_ht6560b()) {
+ request_region(HT_SELECT_PORT, 1, ide_hwifs[0].name);
+ ide_hwifs[0].chipset = ide_ht6560b;
+ ide_hwifs[1].chipset = ide_ht6560b;
+ ide_hwifs[0].selectproc = &ht6560b_selectproc;
+ ide_hwifs[1].selectproc = &ht6560b_selectproc;
+ ide_hwifs[0].tuneproc = &tune_ht6560b;
+ ide_hwifs[1].tuneproc = &tune_ht6560b;
+ ide_hwifs[0].serialized = 1;
+ ide_hwifs[1].serialized = 1;
+ } else
+ printk("\nht6560b: not found\n");
+ }
+}
diff --git a/drivers/block/ide-cd.c b/drivers/block/ide-cd.c
index 1aef40dcf..a62184d9d 100644
--- a/drivers/block/ide-cd.c
+++ b/drivers/block/ide-cd.c
@@ -1,12 +1,21 @@
+/* #define VERBOSE_IDE_CD_ERRORS 1 */
/*
* linux/drivers/block/ide-cd.c
+ * ATAPI cd-rom driver. To be used with ide.c.
+ * See Documentation/cdrom/ide-cd for usage information.
+ *
+ * Copyright (C) 1994, 1995, 1996 scott snyder <snyder@fnald0.fnal.gov>
+ * Copyright (C) 1996 Erik Andersen <andersee@et.byu.edu>
+ *
+ * May be copied or modified under the terms of the GNU General Public License
+ * see linux/COPYING for more information.
*
* 1.00 Oct 31, 1994 -- Initial version.
* 1.01 Nov 2, 1994 -- Fixed problem with starting request in
* cdrom_check_status.
* 1.03 Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks)
* (from mlord) -- minor changes to cdrom_setup()
- * -- renamed ide_dev_s to ide_dev_t, enable irq on command
+ * -- renamed ide_dev_s to ide_drive_t, enable irq on command
* 2.00 Nov 27, 1994 -- Generalize packet command interface;
* add audio ioctls.
* 2.01 Dec 3, 1994 -- Rework packet command interface to handle devices
@@ -39,407 +48,284 @@
* Properly supply the page number field in the
* MODE_SELECT command.
* PLAYAUDIO12 is broken on the Aztech; work around it.
+ * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c
+ * (my apologies to Scott, but now ide-cd.c is independent)
+ * 3.00 Aug 22, 1995 -- Implement CDROMMULTISESSION ioctl.
+ * Implement CDROMREADAUDIO ioctl (UNTESTED).
+ * Use input_ide_data() and output_ide_data().
+ * Add door locking.
+ * Fix usage count leak in cdrom_open, which happened
+ * when a read-write mount was attempted.
+ * Try to load the disk on open.
+ * Implement CDROMEJECT_SW ioctl (off by default).
+ * Read total cdrom capacity during open.
+ * Rearrange logic in cdrom_decode_status. Issue
+ * request sense commands for failed packet commands
+ * from here instead of from cdrom_queue_packet_command.
+ * Fix a race condition in retrieving error information.
+ * Suppress printing normal unit attention errors and
+ * some drive not ready errors.
+ * Implement CDROMVOLREAD ioctl.
+ * Implement CDROMREADMODE1/2 ioctls.
+ * Fix race condition in setting up interrupt handlers
+ * when the `serialize' option is used.
+ * 3.01 Sep 2, 1995 -- Fix ordering of reenabling interrupts in
+ * cdrom_queue_request.
+ * Another try at using ide_[input,output]_data.
+ * 3.02 Sep 16, 1995 -- Stick total disk capacity in partition table as well.
+ * Make VERBOSE_IDE_CD_ERRORS dump failed command again.
+ * Dump out more information for ILLEGAL REQUEST errs.
+ * Fix handling of errors occurring before the
+ * packet command is transferred.
+ * Fix transfers with odd bytelengths.
+ * 3.03 Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
+ * `DCI-2S10' drives are broken too.
+ * 3.04 Nov 20, 1995 -- So are Vertos drives.
+ * 3.05 Dec 1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
+ * 3.06 Dec 16, 1995 -- Add support needed for partitions.
+ * More workarounds for Vertos bugs (based on patches
+ * from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
+ * Try to eliminate byteorder assumptions.
+ * Use atapi_cdrom_subchnl struct definition.
+ * Add STANDARD_ATAPI compilation option.
+ * 3.07 Jan 29, 1996 -- More twiddling for broken drives: Sony 55D,
+ * Vertos 300.
+ * Add NO_DOOR_LOCKING configuration option.
+ * Handle drive_cmd requests w/NULL args (for hdparm -t).
+ * Work around sporadic Sony55e audio play problem.
+ * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
+ * problem with "hde=cdrom" with no drive present. -ml
+ * 3.08 Mar 6, 1996 -- More Vertos workarounds.
+ * 3.09 Apr 5, 1996 -- Add CDROMCLOSETRAY ioctl.
+ * Switch to using MSF addressing for audio commands.
+ * Reformat to match kernel tabbing style.
+ * Add CDROM_GET_UPC ioctl.
+ * 3.10 Apr 10, 1996 -- Fix compilation error with STANDARD_ATAPI.
+ * 3.11 Apr 29, 1996 -- Patch from Heiko Eissfeldt <heiko@colossus.escape.de>
+ * to remove redundant verify_area calls.
+ * 3.12 May 7, 1996 -- Rudimentary changer support. Based on patches
+ * from Gerhard Zuber <zuber@berlin.snafu.de>.
+ * Let open succeed even if there's no loaded disc.
+ * 3.13 May 19, 1996 -- Fixes for changer code.
+ * 3.14 May 29, 1996 -- Add work-around for Vertos 600.
+ * (From Hennus Bergman <hennus@sky.ow.nl>.)
+ * 3.15 July 2, 1996 -- Added support for Sanyo 3 CD changers
+ * from Ben Galliart <bgallia@luc.edu> with
+ * special help from Jeff Lightfoot
+ * <jeffml@netcom.com>
+ * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification
+ * 3.16 Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl.
+ * 3.17 Sep 17, 1996 -- Tweak audio reads for some drives.
+ * Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC.
+ * 3.18 Oct 31, 1996 -- Added module and DMA support.
*
+ *
+ * 4.00 Nov 5, 1996 -- New ide-cd maintainer,
+ * Erik B. Andersen <andersee@et.byu.edu>
+ * -- Newer Creative drives don't always set the error
+ * register correctly. Make sure we see media changes
+ * regardless.
+ * -- Integrate with generic cdrom driver.
+ * -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on
+ * a patch from Ciro Cattuto <>.
+ * -- Call set_device_ro.
+ * -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ * ioctls, based on patch by Erik Andersen
+ * -- Add some probes of drive capability during setup.
*
- * ATAPI cd-rom driver. To be used with ide.c.
+ * 4.01 Nov 11, 1996 -- Split into ide-cd.c and ide-cd.h
+ * -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE
+ * ioctls in favor of a generalized approach
+ * using the generic cdrom driver.
+ * -- Fully integrated with the 2.1.X kernel.
+ * -- Other stuff that I forgot (lots of changes)
+ *
+ * 4.02 Dec 01, 1996 -- Applied patch from Gadi Oxman <gadio@netvision.net.il>
+ * to fix the drive door locking problems.
+ *
+ *
+ * MOSTLY DONE LIST:
+ * Query the drive to find what features are available
+ * before trying to use them.
+ *
+ * TO DO LIST:
+ * Avoid printing error messages for expected errors from the drive.
+ * (If you are using a cd changer, you may get errors in the kernel
+ logs that are completly expected. Don't complpain to me about this,
+ unless you have a patch to fix it. I am working on it...)
+ * Reset unlocks drive?
+ * Implement ide_cdrom_disc_status using the generic cdrom interface
+ * Implement ide_cdrom_select_speed using the generic cdrom interface
+ * Fix ide_cdrom_reset so that it works (it does nothing right now)
+ *
+ * -- Suggestions are welcome. Patches that work are more welcome though.
+ * For those wishing to work on this driver, please be sure you download
+ * and comply with the latest ATAPI standard. This document can
+ * obtained by anonymous ftp from fission.dt.wdc.com in directory:
+ * /pub/standards/atapi/spec/SFF8020-r2.6/PDF/8020r26.pdf
*
- * Copyright (C) 1994, 1995 scott snyder <snyder@fnald0.fnal.gov>
- * May be copied or modified under the terms of the GNU General Public License
- * (../../COPYING).
*/
-/* Turn this on to have the driver print out the meanings of the
- ATAPI error codes. This will use up additional kernel-space
- memory, though. */
-
-#ifndef VERBOSE_IDE_CD_ERRORS
-#define VERBOSE_IDE_CD_ERRORS 0
-#endif
-
/***************************************************************************/
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
#include <linux/cdrom.h>
+#include <linux/ucdrom.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
-#define SECTOR_SIZE 512
-#define SECTOR_BITS 9
-#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE)
-
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#include "ide.h"
+#include "ide-cd.h"
-#if 1 /* "old" method */
-#define OUT_WORDS(b,n) outsw (IDE_PORT (HD_DATA, DEV_HWIF), (b), (n))
-#define IN_WORDS(b,n) insw (IDE_PORT (HD_DATA, DEV_HWIF), (b), (n))
-#else /* "new" method -- should really fix each instance instead of this */
-#define OUT_WORDS(b,n) output_ide_data(dev,b,(n)/2)
-#define IN_WORDS(b,n) input_ide_data(dev,b,(n)/2)
-#endif
-
-/* special command codes for strategy routine. */
-#define PACKET_COMMAND 4315
-#define REQUEST_SENSE_COMMAND 4316
-
-#define WIN_PACKETCMD 0xa0 /* Send a packet command. */
-
-/* Some ATAPI command opcodes (just like SCSI).
- (Some other cdrom-specific codes are in cdrom.h.) */
-#define TEST_UNIT_READY 0x00
-#define REQUEST_SENSE 0x03
-#define START_STOP 0x1b
-#define ALLOW_MEDIUM_REMOVAL 0x1e
-#define READ_10 0x28
-#define MODE_SENSE_10 0x5a
-#define MODE_SELECT_10 0x55
-
-
-/* ATAPI sense keys (mostly copied from scsi.h). */
-
-#define NO_SENSE 0x00
-#define RECOVERED_ERROR 0x01
-#define NOT_READY 0x02
-#define MEDIUM_ERROR 0x03
-#define HARDWARE_ERROR 0x04
-#define ILLEGAL_REQUEST 0x05
-#define UNIT_ATTENTION 0x06
-#define DATA_PROTECT 0x07
-#define ABORTED_COMMAND 0x0b
-#define MISCOMPARE 0x0e
-
-
-struct packet_command {
- char *buffer;
- int buflen;
- int stat;
- unsigned char c[12];
-};
-
-
-struct atapi_request_sense {
- unsigned char error_code : 7;
- unsigned char valid : 1;
- byte reserved1;
- unsigned char sense_key : 4;
- unsigned char reserved2 : 1;
- unsigned char ili : 1;
- unsigned char reserved3 : 2;
- byte info[4];
- byte sense_len;
- byte command_info[4];
- byte asc;
- byte ascq;
- byte fru;
- byte sense_key_specific[3];
-};
-
-/* We want some additional flags for cd-rom drives.
- To save space in the ide_dev_t struct, use one of the fields which
- doesn't make sense for cd-roms -- `bios_sect'. */
-
-struct ide_cd_flags {
- unsigned drq_interrupt : 1; /* Device sends an interrupt when ready
- for a packet command. */
- unsigned no_playaudio12: 1; /* The PLAYAUDIO12 command is not supported. */
-
- unsigned media_changed : 1; /* Driver has noticed a media change. */
- unsigned toc_valid : 1; /* Saved TOC information is current. */
- unsigned no_lba_toc : 1; /* Drive cannot return TOC info in LBA format. */
- unsigned msf_as_bcd : 1; /* Drive uses BCD in PLAYAUDIO_MSF. */
- unsigned reserved : 2;
-};
-
-#define CDROM_FLAGS(dev) ((struct ide_cd_flags *)&((dev)->bios_sect))
-
-
-/* Space to hold the disk TOC. */
-
-#define MAX_TRACKS 99
-struct atapi_toc_header {
- unsigned short toc_length;
- byte first_track;
- byte last_track;
-};
-struct atapi_toc_entry {
- byte reserved1;
- unsigned control : 4;
- unsigned adr : 4;
- byte track;
- byte reserved2;
- unsigned lba;
-};
-
-struct atapi_toc {
- struct atapi_toc_header hdr;
- struct atapi_toc_entry ent[MAX_TRACKS+1]; /* One extra for the leadout. */
-};
-
-
-#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
-
-/* Extra per-device info for cdrom drives. */
-struct cdrom_info {
-
- /* Buffer for table of contents. NULL if we haven't allocated
- a TOC buffer for this device yet. */
-
- struct atapi_toc *toc;
-
- /* Sector buffer. If a read request wants only the first part of a cdrom
- block, we cache the rest of the block here, in the expectation that that
- data is going to be wanted soon. SECTOR_BUFFERED is the number of the
- first buffered sector, and NSECTORS_BUFFERED is the number of sectors
- in the buffer. Before the buffer is allocated, we should have
- SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
-
- unsigned long sector_buffered;
- unsigned long nsectors_buffered;
- char *sector_buffer;
-
- /* The result of the last successful request sense command
- on this device. */
- struct atapi_request_sense sense_data;
-};
-
-
-static struct cdrom_info cdrom_info[2][MAX_DRIVES];
-
-/* Statically allocate one request packet and one packet command struct
- for each interface for retrieving sense data during error recovery. */
-
-static struct request request_sense_request[2];
-static struct packet_command request_sense_pc[2];
-
-
-
/****************************************************************************
- * Descriptions of ATAPI error codes.
+ * Generic packet command support and error handling routines.
*/
-#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
-
-#if VERBOSE_IDE_CD_ERRORS
-
-/* From Table 124 of the ATAPI 1.2 spec. */
-
-char *sense_key_texts[16] = {
- "No sense data",
- "Recovered error",
- "Not ready",
- "Medium error",
- "Hardware error",
- "Illegal request",
- "Unit attention",
- "Data protect",
- "(reserved)",
- "(reserved)",
- "(reserved)",
- "Aborted command",
- "(reserved)",
- "(reserved)",
- "Miscompare",
- "(reserved)",
-};
-
-
-/* From Table 125 of the ATAPI 1.2 spec. */
-
-struct {
- short asc_ascq;
- char *text;
-} sense_data_texts[] = {
- { 0x0000, "No additional sense information" },
- { 0x0011, "Audio play operation in progress" },
- { 0x0012, "Audio play operation paused" },
- { 0x0013, "Audio play operation successfully completed" },
- { 0x0014, "Audio play operation stopped due to error" },
- { 0x0015, "No current audio status to return" },
-
- { 0x0200, "No seek complete" },
-
- { 0x0400, "Logical unit not ready - cause not reportable" },
- { 0x0401, "Logical unit not ready - in progress (sic) of becoming ready" },
- { 0x0402, "Logical unit not ready - initializing command required" },
- { 0x0403, "Logical unit not ready - manual intervention required" },
-
- { 0x0600, "No reference position found" },
-
- { 0x0900, "Track following error" },
- { 0x0901, "Tracking servo failure" },
- { 0x0902, "Focus servo failure" },
- { 0x0903, "Spindle servo failure" },
-
- { 0x1100, "Unrecovered read error" },
- { 0x1106, "CIRC unrecovered error" },
- { 0x1500, "Random positioning error" },
- { 0x1501, "Mechanical positioning error" },
- { 0x1502, "Positioning error detected by read of medium" },
-
- { 0x1700, "Recovered data with no error correction applied" },
- { 0x1701, "Recovered data with retries" },
- { 0x1702, "Recovered data with positive head offset" },
- { 0x1703, "Recovered data with negative head offset" },
- { 0x1704, "Recovered data with retries and/or CIRC applied" },
- { 0x1705, "Recovered data using previous sector ID" },
-
- { 0x1800, "Recovered data with error correction applied" },
- { 0x1801, "Recovered data with error correction and retries applied" },
- { 0x1802, "Recovered data - the data was auto-reallocated" },
- { 0x1803, "Recovered data with CIRC" },
- { 0x1804, "Recovered data with L-EC" },
- { 0x1805, "Recovered data - recommend reassignment" },
- { 0x1806, "Recovered data - recommend rewrite" },
-
- { 0x1a00, "Parameter list length error" },
-
- { 0x2000, "Invalid command operation code" },
-
- { 0x2100, "Logical block address out of range" },
-
- { 0x2400, "Invalid field in command packet" },
-
- { 0x2600, "Invalid field in parameter list" },
- { 0x2601, "Parameter not supported" },
- { 0x2602, "Parameter value invalid" },
- { 0x2603, "Threshold parameters not supported" },
-
- { 0x2800, "Not ready to ready transition, medium may have changed" },
-
- { 0x2900, "Power on, reset or bus device reset occurred" },
-
- { 0x2a00, "Parameters changed" },
- { 0x2a01, "Mode parameters changed" },
-
- { 0x3000, "Incompatible medium installed" },
- { 0x3001, "Cannot read medium - unknown format" },
- { 0x3002, "Cannot read medium - incompatible format" },
-
- { 0x3700, "Rounded parameter" },
-
- { 0x3900, "Saving parameters not supported" },
-
- { 0x3a00, "Medium not present" },
-
- { 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
- { 0x3f01, "Microcode has been changed" },
- { 0x3f02, "Changed operating definition" },
- { 0x3f03, "Inquiry data has changed" },
-
- { 0x4000, "Diagnostic failure on component (ASCQ)" },
-
- { 0x4400, "Internal ATAPI CD-ROM drive failure" },
-
- { 0x4e00, "Overlapped commands attempted" },
-
- { 0x5300, "Media load or eject failed" },
- { 0x5302, "Medium removal prevented" },
-
- { 0x5700, "Unable to recover table of contents" },
-
- { 0x5a00, "Operator request or state change input (unspecified)" },
- { 0x5a01, "Operator medium removal request" },
-
- { 0x5b00, "Threshold condition met" },
-
- { 0x5c00, "Status change" },
-
- { 0x6300, "End of user area encountered on this track" },
-
- { 0x6400, "Illegal mode for this track" },
-
- { 0xbf00, "Loss of streaming" },
-};
-#endif
-
-
-
-/****************************************************************************
- * Generic packet command support routines.
- */
+/* Mark that we've seen a media change, and invalidate our internal
+ buffers. */
+static void cdrom_saw_media_change (ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+
+ CDROM_STATE_FLAGS (drive)->media_changed = 1;
+ CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+ info->nsectors_buffered = 0;
+}
static
-void cdrom_analyze_sense_data (ide_dev_t *dev,
+void cdrom_analyze_sense_data (ide_drive_t *drive,
struct atapi_request_sense *reqbuf,
struct packet_command *failed_command)
{
- /* Don't print not ready or unit attention errors for READ_SUBCHANNEL.
- Workman (and probably other programs) uses this command to poll
- the drive, and we don't want to fill the syslog with useless errors. */
- if (failed_command &&
- failed_command->c[0] == SCMD_READ_SUBCHANNEL &&
- (reqbuf->sense_key == 2 || reqbuf->sense_key == 6))
- return;
+ if (reqbuf->sense_key == NOT_READY ||
+ reqbuf->sense_key == UNIT_ATTENTION) {
+ /* Make good and sure we've seen this potential media change.
+ Some drives (i.e. Creative) fail to present the correct
+ sense key in the error register. */
+ cdrom_saw_media_change (drive);
+
+
+ /* Don't print not ready or unit attention errors for
+ READ_SUBCHANNEL. Workman (and probably other programs)
+ uses this command to poll the drive, and we don't want
+ to fill the syslog with useless errors. */
+ if (failed_command &&
+ failed_command->c[0] == SCMD_READ_SUBCHANNEL)
+ return;
+ }
#if VERBOSE_IDE_CD_ERRORS
- {
- int i;
- char *s;
- char buf[80];
-
- printk ("ATAPI device %s:\n", dev->name);
-
- printk (" Error code: %x\n", reqbuf->error_code);
-
- if (reqbuf->sense_key >= 0 &&
- reqbuf->sense_key < ARY_LEN (sense_key_texts))
- s = sense_key_texts[reqbuf->sense_key];
- else
- s = "(bad sense key)";
-
- printk (" Sense key: %x - %s\n", reqbuf->sense_key, s);
-
- if (reqbuf->asc == 0x40) {
- sprintf (buf, "Diagnostic failure on component %x", reqbuf->ascq);
- s = buf;
- }
-
- else {
- int lo, hi;
- int key = (reqbuf->asc << 8);
- if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
- key |= reqbuf->ascq;
-
- lo = 0;
- hi = ARY_LEN (sense_data_texts);
- s = NULL;
-
- while (hi > lo) {
- int mid = (lo + hi) / 2;
- if (sense_data_texts[mid].asc_ascq == key) {
- s = sense_data_texts[mid].text;
- break;
- }
- else if (sense_data_texts[mid].asc_ascq > key)
- hi = mid;
- else
- lo = mid+1;
- }
- }
-
- if (s == NULL) {
- if (reqbuf->asc > 0x80)
- s = "(vendor-specific error)";
- else
- s = "(reserved error code)";
- }
-
- printk (" Additional sense data: %x, %x - %s\n",
- reqbuf->asc, reqbuf->ascq, s);
-
- if (failed_command != NULL) {
- printk (" Failed packet command: ");
- for (i=0; i<sizeof (failed_command->c); i++)
- printk ("%02x ", failed_command->c[i]);
- printk ("\n");
- }
- }
+ {
+ int i;
+ char *s;
+ char buf[80];
+
+ printk ("ATAPI device %s:\n", drive->name);
+
+ printk (" Error code: 0x%02x\n", reqbuf->error_code);
+
+ if (reqbuf->sense_key >= 0 &&
+ reqbuf->sense_key < ARY_LEN (sense_key_texts))
+ s = sense_key_texts[reqbuf->sense_key];
+ else
+ s = "(bad sense key)";
+
+ printk (" Sense key: 0x%02x - %s\n", reqbuf->sense_key, s);
+
+ if (reqbuf->asc == 0x40) {
+ sprintf (buf, "Diagnostic failure on component 0x%02x",
+ reqbuf->ascq);
+ s = buf;
+ } else {
+ int lo, hi;
+ int key = (reqbuf->asc << 8);
+ if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
+ key |= reqbuf->ascq;
+
+ lo = 0;
+ hi = ARY_LEN (sense_data_texts);
+ s = NULL;
+
+ while (hi > lo) {
+ int mid = (lo + hi) / 2;
+ if (sense_data_texts[mid].asc_ascq == key) {
+ s = sense_data_texts[mid].text;
+ break;
+ }
+ else if (sense_data_texts[mid].asc_ascq > key)
+ hi = mid;
+ else
+ lo = mid+1;
+ }
+ }
+
+ if (s == NULL) {
+ if (reqbuf->asc > 0x80)
+ s = "(vendor-specific error)";
+ else
+ s = "(reserved error code)";
+ }
+
+ printk (" Additional sense data: 0x%02x, 0x%02x - %s\n",
+ reqbuf->asc, reqbuf->ascq, s);
+
+ if (failed_command != NULL) {
+ printk (" Failed packet command: ");
+ for (i=0; i<sizeof (failed_command->c); i++)
+ printk ("%02x ", failed_command->c[i]);
+ printk ("\n");
+ }
+
+ if (reqbuf->sense_key == ILLEGAL_REQUEST &&
+ (reqbuf->sense_key_specific[0] & 0x80) != 0) {
+ printk (" Error in %s byte %d",
+ (reqbuf->sense_key_specific[0] & 0x40) != 0
+ ? "command packet"
+ : "command data",
+ (reqbuf->sense_key_specific[1] << 8) +
+ reqbuf->sense_key_specific[2]);
+
+ if ((reqbuf->sense_key_specific[0] & 0x40) != 0) {
+ printk (" bit %d",
+ reqbuf->sense_key_specific[0] & 0x07);
+ }
+
+ printk ("\n");
+ }
+ }
-#else
- printk ("%s: code: %x key: %x asc: %x ascq: %x\n",
- dev->name,
- reqbuf->error_code, reqbuf->sense_key, reqbuf->asc, reqbuf->ascq);
-#endif
+#else /* not VERBOSE_IDE_CD_ERRORS */
+
+ /* Suppress printing unit attention and `in progress of becoming ready'
+ errors when we're not being verbose. */
+
+ if (reqbuf->sense_key == UNIT_ATTENTION ||
+ (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 ||
+ reqbuf->asc == 0x3a)))
+ return;
+
+ printk ("%s: code: 0x%02x key: 0x%02x asc: 0x%02x ascq: 0x%02x\n",
+ drive->name,
+ reqbuf->error_code, reqbuf->sense_key,
+ reqbuf->asc, reqbuf->ascq);
+#endif /* not VERBOSE_IDE_CD_ERRORS */
}
@@ -447,285 +333,297 @@ void cdrom_analyze_sense_data (ide_dev_t *dev,
start it over entirely, or even put it back on the request queue. */
static void restore_request (struct request *rq)
{
- if (rq->buffer != rq->bh->b_data)
- {
- int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
- rq->buffer = rq->bh->b_data;
- rq->nr_sectors += n;
- rq->sector -= n;
- }
- rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
+ if (rq->buffer != rq->bh->b_data) {
+ int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
+ rq->buffer = rq->bh->b_data;
+ rq->nr_sectors += n;
+ rq->sector -= n;
+ }
+ rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
}
-static void cdrom_queue_request_sense (ide_dev_t *dev)
+static void cdrom_queue_request_sense (ide_drive_t *drive,
+ struct semaphore *sem,
+ struct atapi_request_sense *reqbuf,
+ struct packet_command *failed_command)
{
- struct request *rq;
- struct packet_command *pc;
- struct atapi_request_sense *reqbuf;
- unsigned long flags;
-
- int major = ide_major[DEV_HWIF];
-
- save_flags (flags);
- cli (); /* safety */
-
- rq = ide_cur_rq[DEV_HWIF];
-
- /* If we're processing a request, put it back on the request queue. */
- if (rq != NULL)
- {
- restore_request (rq);
- rq->next = blk_dev[major].current_request;
- blk_dev[major].current_request = rq;
- ide_cur_rq[DEV_HWIF] = NULL;
- }
-
- restore_flags (flags);
-
- /* Make up a new request to retrieve sense information. */
- reqbuf = &cdrom_info[DEV_HWIF][dev->select.b.drive].sense_data;
-
- pc = &request_sense_pc[DEV_HWIF];
- memset (pc, 0, sizeof (*pc));
-
- pc->c[0] = REQUEST_SENSE;
- pc->c[4] = sizeof (*reqbuf);
- pc->buffer = (char *)reqbuf;
- pc->buflen = sizeof (*reqbuf);
-
- rq = &request_sense_request[DEV_HWIF];
- rq->dev = MKDEV (major, (dev->select.b.drive) << PARTN_BITS);
- rq->cmd = REQUEST_SENSE_COMMAND;
- rq->errors = 0;
- rq->sector = 0;
- rq->nr_sectors = 0;
- rq->current_nr_sectors = 0;
- rq->buffer = (char *)pc;
- rq->sem = NULL;
- rq->bh = NULL;
- rq->bhtail = NULL;
- rq->next = NULL;
-
- save_flags (flags);
- cli (); /* safety */
-
- /* Stick it onto the front of the queue. */
- rq->next = blk_dev[major].current_request;
- blk_dev[major].current_request = rq;
-
- restore_flags (flags);
+ struct cdrom_info *info = drive->driver_data;
+ struct request *rq;
+ struct packet_command *pc;
+ int len;
+
+ /* If the request didn't explicitly specify where
+ to put the sense data, use the statically allocated structure. */
+ if (reqbuf == NULL)
+ reqbuf = &info->sense_data;
+
+ /* Make up a new request to retrieve sense information. */
+
+ pc = &info->request_sense_pc;
+ memset (pc, 0, sizeof (*pc));
+
+ /* The request_sense structure has an odd number of (16-bit) words,
+ which won't work well with 32-bit transfers. However, we don't care
+ about the last two bytes, so just truncate the structure down
+ to an even length. */
+ len = sizeof (*reqbuf) / 4;
+ len *= 4;
+
+ pc->c[0] = REQUEST_SENSE;
+ pc->c[4] = len;
+ pc->buffer = (char *)reqbuf;
+ pc->buflen = len;
+ pc->sense_data = (struct atapi_request_sense *)failed_command;
+
+ /* stuff the sense request in front of our current request */
+
+ rq = &info->request_sense_request;
+ ide_init_drive_cmd (rq);
+ rq->cmd = REQUEST_SENSE_COMMAND;
+ rq->buffer = (char *)pc;
+ rq->sem = sem;
+ (void) ide_do_drive_cmd (drive, rq, ide_preempt);
}
-static void cdrom_end_request (int uptodate, ide_dev_t *dev)
+static void cdrom_end_request (int uptodate, ide_drive_t *drive)
{
- struct request *rq = ide_cur_rq[DEV_HWIF];
-
- /* The code in blk.h can screw us up on error recovery if the block
- size is larger than 1k. Fix that up here. */
- if (!uptodate && rq->bh != 0)
- {
- int adj = rq->current_nr_sectors - 1;
- rq->current_nr_sectors -= adj;
- rq->sector += adj;
- }
-
- if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate)
- {
- struct atapi_request_sense *reqbuf;
- reqbuf = &cdrom_info[DEV_HWIF][dev->select.b.drive].sense_data;
- cdrom_analyze_sense_data (dev, reqbuf, NULL);
- }
-
- end_request (uptodate, DEV_HWIF);
-}
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* The code in blk.h can screw us up on error recovery if the block
+ size is larger than 1k. Fix that up here. */
+ if (!uptodate && rq->bh != 0) {
+ int adj = rq->current_nr_sectors - 1;
+ rq->current_nr_sectors -= adj;
+ rq->sector += adj;
+ }
+ if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) {
+ struct packet_command *pc = (struct packet_command *)
+ rq->buffer;
+ cdrom_analyze_sense_data (drive,
+ (struct atapi_request_sense *)
+ (pc->buffer - pc->c[4]),
+ (struct packet_command *)
+ pc->sense_data);
+ }
-/* Mark that we've seen a media change, and invalidate our internal
- buffers. */
-static void cdrom_saw_media_change (ide_dev_t *dev)
-{
- CDROM_FLAGS (dev)->media_changed = 1;
- CDROM_FLAGS (dev)->toc_valid = 0;
- cdrom_info[DEV_HWIF][dev->select.b.drive].nsectors_buffered = 0;
+ ide_end_request (uptodate, HWGROUP(drive));
}
/* Returns 0 if the request should be continued.
Returns 1 if the request was ended. */
-static int cdrom_decode_status (ide_dev_t *dev, int good_stat, int *stat_ret)
+static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
+ int *stat_ret)
{
- struct request *rq = ide_cur_rq[DEV_HWIF];
- int stat, err, sense_key, cmd;
-
- /* Check for errors. */
- stat = GET_STAT (DEV_HWIF);
- *stat_ret = stat;
-
- if (OK_STAT (stat, good_stat, BAD_R_STAT))
- return 0;
-
- /* Got an error. */
- err = IN_BYTE (HD_ERROR, DEV_HWIF);
- sense_key = err >> 4;
-
- if (rq == NULL)
- printk ("%s : missing request in cdrom_decode_status\n", dev->name);
- else
- {
- cmd = rq->cmd;
-
- /* Check for tray open */
- if (sense_key == NOT_READY)
- {
- struct packet_command *pc;
- cdrom_saw_media_change (dev);
-
- /* Fail the request if this is a read command. */
- if (cmd == READ)
- {
- printk ("%s : tray open\n", dev->name);
- cdrom_end_request (0, dev);
- }
-
- else
- {
- /* Otherwise, it's some other packet command.
- Print an error message to the syslog.
- Exception: don't print anything if this is a read subchannel
- command. This is because workman constantly polls the drive
- with this command, and we don't want to uselessly fill up
- the syslog. */
- pc = (struct packet_command *)rq->buffer;
- if (pc->c[0] != SCMD_READ_SUBCHANNEL)
- printk ("%s : tray open\n", dev->name);
-
- /* Set the error flag and complete the request. */
- pc->stat = 1;
- cdrom_end_request (1, dev);
- }
- }
-
- /* Check for media change. */
- else if (sense_key == UNIT_ATTENTION)
- {
- cdrom_saw_media_change (dev);
- printk ("%s: media changed\n", dev->name);
-
- /* Return failure for a packet command, so that
- cdrom_queue_packet_command can do a request sense before
- the command gets retried. */
-
- if (cmd == PACKET_COMMAND)
- {
- struct packet_command *pc = (struct packet_command *)rq->buffer;
- pc->stat = 1;
- cdrom_end_request (1, dev);
- }
-
- /* Otherwise, it's a block read. Arrange to retry it.
- But be sure to give up if we've retried too many times. */
- else if ((++rq->errors > ERROR_MAX))
- {
- cdrom_end_request (0, dev);
- }
- }
-
- /* Don't attempt to retry if this was a packet command. */
- else if (cmd == PACKET_COMMAND)
- {
- struct packet_command *pc = (struct packet_command *)rq->buffer;
- dump_status (DEV_HWIF, "packet command error", stat);
- pc->stat = 1; /* signal error */
- cdrom_end_request (1, dev);
+ struct request *rq = HWGROUP(drive)->rq;
+ int stat, err, sense_key, cmd;
+
+ /* Check for errors. */
+ stat = GET_STAT();
+ *stat_ret = stat;
+
+ if (OK_STAT (stat, good_stat, BAD_R_STAT))
+ return 0;
+
+ /* Got an error. */
+ err = IN_BYTE (IDE_ERROR_REG);
+ sense_key = err >> 4;
+
+ if (rq == NULL)
+ printk ("%s : missing request in cdrom_decode_status\n",
+ drive->name);
+ else {
+ cmd = rq->cmd;
+
+ if (cmd == REQUEST_SENSE_COMMAND) {
+ /* We got an error trying to get sense info
+ from the drive (probably while trying
+ to recover from a former error). Just give up. */
+
+ struct packet_command *pc = (struct packet_command *)
+ rq->buffer;
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+ ide_error (drive, "request sense failure", stat);
+ return 1;
+
+ } else if (cmd == PACKET_COMMAND) {
+ /* All other functions, except for READ. */
+
+ struct packet_command *pc = (struct packet_command *)
+ rq->buffer;
+ struct semaphore *sem = NULL;
+
+ /* Check for tray open. */
+ if (sense_key == NOT_READY) {
+ cdrom_saw_media_change (drive);
+
+ /* Print an error message to the syslog.
+ Exception: don't print anything if this
+ is a read subchannel command. This is
+ because workman constantly polls the drive
+ with this command, and we don't want
+ to uselessly fill up the syslog. */
+ if (pc->c[0] != SCMD_READ_SUBCHANNEL)
+ printk ("%s: tray open or drive not ready\n",
+ drive->name);
+ } else if (sense_key == UNIT_ATTENTION) {
+ /* Check for media change. */
+ cdrom_saw_media_change (drive);
+ printk ("%s: media changed\n", drive->name);
+ } else {
+ /* Otherwise, print an error. */
+ ide_dump_status (drive, "packet command error",
+ stat);
+ }
+
+ /* Set the error flag and complete the request.
+ Then, if we have a CHECK CONDITION status,
+ queue a request sense command. We must be careful,
+ though: we don't want the thread in
+ cdrom_queue_packet_command to wake up until
+ the request sense has completed. We do this
+ by transferring the semaphore from the packet
+ command request to the request sense request. */
+
+ if ((stat & ERR_STAT) != 0) {
+ sem = rq->sem;
+ rq->sem = NULL;
+ }
+
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+
+ if ((stat & ERR_STAT) != 0)
+ cdrom_queue_request_sense (drive, sem,
+ pc->sense_data, pc);
+ } else {
+ /* Handle errors from READ requests. */
+
+ if (sense_key == NOT_READY) {
+ /* Tray open. */
+ cdrom_saw_media_change (drive);
+
+ /* Fail the request. */
+ printk ("%s : tray open\n", drive->name);
+ cdrom_end_request (0, drive);
+ } else if (sense_key == UNIT_ATTENTION) {
+ /* Media change. */
+ cdrom_saw_media_change (drive);
+
+ /* Arrange to retry the request.
+ But be sure to give up if we've retried
+ too many times. */
+ if (++rq->errors > ERROR_MAX)
+ cdrom_end_request (0, drive);
+ } else if (sense_key == ILLEGAL_REQUEST ||
+ sense_key == DATA_PROTECT) {
+ /* No point in retrying after an illegal
+ request or data protect error.*/
+ ide_dump_status (drive, "command error", stat);
+ cdrom_end_request (0, drive);
+ } else if ((err & ~ABRT_ERR) != 0) {
+ /* Go to the default handler
+ for other errors. */
+ ide_error (drive, "cdrom_decode_status", stat);
+ return 1;
+ } else if ((++rq->errors > ERROR_MAX)) {
+ /* We've racked up too many retries. Abort. */
+ cdrom_end_request (0, drive);
+ }
+
+ /* If we got a CHECK_CONDITION status,
+ queue a request sense command. */
+ if ((stat & ERR_STAT) != 0)
+ cdrom_queue_request_sense (drive,
+ NULL, NULL, NULL);
+ }
}
- /* No point in retrying after an illegal request or data protect error.*/
- else if (sense_key == ILLEGAL_REQUEST || sense_key == DATA_PROTECT)
- {
- dump_status (DEV_HWIF, "command error", stat);
- cdrom_end_request (0, dev);
- }
+ /* Retry, or handle the next request. */
+ return 1;
+}
- /* If there were other errors, go to the default handler. */
- else if ((err & ~ABRT_ERR) != 0)
- {
- ide_error (dev, "cdrom_decode_status", stat);
- }
- /* Else, abort if we've racked up too many retries. */
- else if ((++rq->errors > ERROR_MAX))
- {
- cdrom_end_request (0, dev);
- }
+/* Set up the device registers for transferring a packet command on DEV,
+ expecting to later transfer XFERLEN bytes. HANDLER is the routine
+ which actually transfers the command to the drive. If this is a
+ drq_interrupt device, this routine will arrange for HANDLER to be
+ called when the interrupt from the drive arrives. Otherwise, HANDLER
+ will be called immediately after the drive is prepared for the transfer. */
+
+static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
+ ide_handler_t *handler)
+{
+ struct cdrom_info *info = drive->driver_data;
- /* If we got a CHECK_STATUS condition, and this was a READ request,
- queue a request sense command to try to find out more about
- what went wrong (and clear a unit attention)? For packet commands,
- this is done separately in cdrom_queue_packet_command. */
- if ((stat & ERR_STAT) != 0 && cmd == READ)
- cdrom_queue_request_sense (dev);
- }
+ /* Wait for the controller to be idle. */
+ if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1;
- /* Retry, or handle the next request. */
- DO_REQUEST;
- return 1;
-}
+ if (info->dma)
+ info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive);
+ /* Set up the controller registers. */
+ OUT_BYTE (info->dma, IDE_FEATURE_REG);
+ OUT_BYTE (0, IDE_NSECTOR_REG);
+ OUT_BYTE (0, IDE_SECTOR_REG);
-/* Set up the device registers for transferring a packet command on DEV,
- expecting to later transfer XFERLEN bytes. This should be followed
- by a call to cdrom_transfer_packet_command; however, if this is a
- drq_interrupt device, one must wait for an interrupt first. */
-static int cdrom_start_packet_command (ide_dev_t *dev, int xferlen)
-{
- /* Wait for the controller to be idle. */
- if (wait_stat (dev, 0, BUSY_STAT, WAIT_READY)) return 1;
+ OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
+ OUT_BYTE (xferlen >> 8 , IDE_HCYL_REG);
+ OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
+
+ if (info->dma)
+ (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
- /* Set up the controller registers. */
- OUT_BYTE (0, HD_FEATURE);
- OUT_BYTE (0, HD_NSECTOR);
- OUT_BYTE (0, HD_SECTOR);
- OUT_BYTE (xferlen & 0xff, HD_LCYL);
- OUT_BYTE (xferlen >> 8 , HD_HCYL);
- OUT_BYTE (dev->ctl, HD_CMD);
- OUT_BYTE (WIN_PACKETCMD, HD_COMMAND); /* packet command */
+ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+ ide_set_handler (drive, handler, WAIT_CMD);
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+ } else {
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+ (*handler) (drive);
+ }
- return 0;
+ return 0;
}
-/* Send a packet command to DEV described by CMD_BUF and CMD_LEN.
+/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN.
The device registers must have already been prepared
- by cdrom_start_packet_command. */
-static int cdrom_transfer_packet_command (ide_dev_t *dev,
- char *cmd_buf, int cmd_len)
+ by cdrom_start_packet_command.
+ HANDLER is the interrupt handler to call when the command completes
+ or there's data ready. */
+static int cdrom_transfer_packet_command (ide_drive_t *drive,
+ char *cmd_buf, int cmd_len,
+ ide_handler_t *handler)
{
- if (CDROM_FLAGS (dev)->drq_interrupt)
- {
- /* Here we should have been called after receiving an interrupt
- from the device. DRQ should how be set. */
- int stat_dum;
+ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
+ /* Here we should have been called after receiving an interrupt
+ from the device. DRQ should how be set. */
+ int stat_dum;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) return 1;
+ } else {
+ /* Otherwise, we must wait for DRQ to get set. */
+ if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY))
+ return 1;
+ }
- /* Check for errors. */
- if (cdrom_decode_status (dev, DRQ_STAT, &stat_dum)) return 1;
- }
- else
- {
- /* Otherwise, we must wait for DRQ to get set. */
- if (wait_stat (dev, DRQ_STAT, BUSY_STAT, WAIT_READY)) return 1;
- }
+ /* Arm the interrupt handler. */
+ ide_set_handler (drive, handler, WAIT_CMD);
- /* Send the command to the device. */
- OUT_WORDS (cmd_buf, cmd_len/2);
+ /* Send the command to the device. */
+ atapi_output_bytes (drive, cmd_buf, cmd_len);
- return 0;
+ return 0;
}
-
+
/****************************************************************************
* Block read functions.
*/
@@ -737,52 +635,51 @@ static int cdrom_transfer_packet_command (ide_dev_t *dev,
* sector added, SECTOR is its sector number. (SECTOR is then ignored until
* the buffer is cleared.)
*/
-static void cdrom_buffer_sectors (ide_dev_t *dev, unsigned long sector,
+static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
int sectors_to_transfer)
{
- struct cdrom_info *info = &cdrom_info[DEV_HWIF][dev->select.b.drive];
-
- /* Number of sectors to read into the buffer. */
- int sectors_to_buffer = MIN (sectors_to_transfer,
- (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
- info->nsectors_buffered);
-
- char *dest;
-
- /* If we don't yet have a sector buffer, try to allocate one.
- If we can't get one atomically, it's not fatal -- we'll just throw
- the data away rather than caching it. */
- if (info->sector_buffer == NULL)
- {
- info->sector_buffer = (char *) kmalloc (SECTOR_BUFFER_SIZE, GFP_ATOMIC);
-
- /* If we couldn't get a buffer, don't try to buffer anything... */
- if (info->sector_buffer == NULL)
- sectors_to_buffer = 0;
- }
-
- /* If this is the first sector in the buffer, remember its number. */
- if (info->nsectors_buffered == 0)
- info->sector_buffered = sector;
+ struct cdrom_info *info = drive->driver_data;
+
+ /* Number of sectors to read into the buffer. */
+ int sectors_to_buffer = MIN (sectors_to_transfer,
+ (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
+ info->nsectors_buffered);
+
+ char *dest;
+
+ /* If we don't yet have a sector buffer, try to allocate one.
+ If we can't get one atomically, it's not fatal -- we'll just throw
+ the data away rather than caching it. */
+ if (info->sector_buffer == NULL) {
+ info->sector_buffer = (char *) kmalloc (SECTOR_BUFFER_SIZE,
+ GFP_ATOMIC);
+
+ /* If we couldn't get a buffer,
+ don't try to buffer anything... */
+ if (info->sector_buffer == NULL)
+ sectors_to_buffer = 0;
+ }
- /* Read the data into the buffer. */
- dest = info->sector_buffer + info->nsectors_buffered * SECTOR_SIZE;
- while (sectors_to_buffer > 0)
- {
- IN_WORDS (dest, SECTOR_SIZE / 2);
- --sectors_to_buffer;
- --sectors_to_transfer;
- ++info->nsectors_buffered;
- dest += SECTOR_SIZE;
- }
+ /* If this is the first sector in the buffer, remember its number. */
+ if (info->nsectors_buffered == 0)
+ info->sector_buffered = sector;
+
+ /* Read the data into the buffer. */
+ dest = info->sector_buffer + info->nsectors_buffered * SECTOR_SIZE;
+ while (sectors_to_buffer > 0) {
+ atapi_input_bytes (drive, dest, SECTOR_SIZE);
+ --sectors_to_buffer;
+ --sectors_to_transfer;
+ ++info->nsectors_buffered;
+ dest += SECTOR_SIZE;
+ }
- /* Throw away any remaining data. */
- while (sectors_to_transfer > 0)
- {
- char dum[SECTOR_SIZE];
- IN_WORDS (dum, sizeof (dum) / 2);
- --sectors_to_transfer;
- }
+ /* Throw away any remaining data. */
+ while (sectors_to_transfer > 0) {
+ char dum[SECTOR_SIZE];
+ atapi_input_bytes (drive, dum, sizeof (dum));
+ --sectors_to_transfer;
+ }
}
@@ -792,152 +689,163 @@ static void cdrom_buffer_sectors (ide_dev_t *dev, unsigned long sector,
* ok; nonzero if the request has been terminated.
*/
static inline
-int cdrom_read_check_ireason (ide_dev_t *dev, int len, int ireason)
+int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
{
- ireason &= 3;
- if (ireason == 2) return 0;
-
- if (ireason == 0)
- {
- /* Whoops... The drive is expecting to receive data from us! */
- printk ("%s: cdrom_read_intr: "
- "Drive wants to transfer data the wrong way!\n",
- dev->name);
-
- /* Throw some data at the drive so it doesn't hang
- and quit this request. */
- while (len > 0)
- {
- short dum = 0;
- OUT_WORDS (&dum, 1);
- len -= 2;
- }
- }
-
- else
- {
- /* Drive wants a command packet, or invalid ireason... */
- printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
- dev->name, ireason);
- }
+ ireason &= 3;
+ if (ireason == 2) return 0;
+
+ if (ireason == 0) {
+ /* Whoops... The drive is expecting to receive data from us! */
+ printk ("%s: cdrom_read_intr: "
+ "Drive wants to transfer data the wrong way!\n",
+ drive->name);
+
+ /* Throw some data at the drive so it doesn't hang
+ and quit this request. */
+ while (len > 0) {
+ int dum = 0;
+ atapi_output_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+ } else {
+ /* Drive wants a command packet, or invalid ireason... */
+ printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
+ drive->name, ireason);
+ }
- cdrom_end_request (0, dev);
- DO_REQUEST;
- return -1;
+ cdrom_end_request (0, drive);
+ return -1;
}
/*
* Interrupt routine. Called when a read request has completed.
*/
-static void cdrom_read_intr (ide_dev_t *dev)
-{
- int stat;
- int ireason, len, sectors_to_transfer, nskip;
-
- struct request *rq = ide_cur_rq[DEV_HWIF];
-
- /* Check for errors. */
- if (cdrom_decode_status (dev, 0, &stat)) return;
-
- /* Read the interrupt reason and the transfer length. */
- ireason = IN_BYTE (HD_NSECTOR, DEV_HWIF);
- len = IN_BYTE (HD_LCYL, DEV_HWIF) + 256 * IN_BYTE (HD_HCYL, DEV_HWIF);
-
- /* If DRQ is clear, the command has completed. */
- if ((stat & DRQ_STAT) == 0)
- {
- /* If we're not done filling the current buffer, complain.
- Otherwise, complete the command normally. */
- if (rq->current_nr_sectors > 0)
- {
- printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
- dev->name, rq->current_nr_sectors);
- cdrom_end_request (0, dev);
- }
- else
- cdrom_end_request (1, dev);
-
- DO_REQUEST;
- return;
- }
-
- /* Check that the drive is expecting to do the same thing that we are. */
- if (cdrom_read_check_ireason (dev, len, ireason)) return;
-
- /* Assume that the drive will always provide data in multiples of at least
- SECTOR_SIZE, as it gets hairy to keep track of the transfers otherwise. */
- if ((len % SECTOR_SIZE) != 0)
- {
- printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
- dev->name, len);
- printk (" This drive is not supported by this version of the driver\n");
- cdrom_end_request (0, dev);
- DO_REQUEST;
- return;
- }
-
- /* The number of sectors we need to read from the drive. */
- sectors_to_transfer = len / SECTOR_SIZE;
-
- /* First, figure out if we need to bit-bucket any of the leading sectors. */
- nskip = MIN ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)),
- sectors_to_transfer);
-
- while (nskip > 0)
- {
- /* We need to throw away a sector. */
- char dum[SECTOR_SIZE];
- IN_WORDS (dum, sizeof (dum) / 2);
-
- --rq->current_nr_sectors;
- --nskip;
- --sectors_to_transfer;
- }
-
- /* Now loop while we still have data to read from the drive. */
- while (sectors_to_transfer > 0)
- {
- int this_transfer;
-
- /* If we've filled the present buffer but there's another chained
- buffer after it, move on. */
- if (rq->current_nr_sectors == 0 &&
- rq->nr_sectors > 0)
- cdrom_end_request (1, dev);
-
- /* If the buffers are full, cache the rest of the data in our
- internal buffer. */
- if (rq->current_nr_sectors == 0)
- {
- cdrom_buffer_sectors (dev, rq->sector, sectors_to_transfer);
- sectors_to_transfer = 0;
- }
- else
- {
- /* Transfer data to the buffers.
- Figure out how many sectors we can transfer
- to the current buffer. */
- this_transfer = MIN (sectors_to_transfer,
- rq->current_nr_sectors);
-
- /* Read this_transfer sectors into the current buffer. */
- while (this_transfer > 0)
- {
- IN_WORDS (rq->buffer, SECTOR_SIZE / 2);
- rq->buffer += SECTOR_SIZE;
- --rq->nr_sectors;
- --rq->current_nr_sectors;
- ++rq->sector;
- --this_transfer;
- --sectors_to_transfer;
- }
- }
- }
-
- /* Done moving data!
- Wait for another interrupt. */
- ide_handler[DEV_HWIF] = cdrom_read_intr;
+static void cdrom_read_intr (ide_drive_t *drive)
+{
+ int stat;
+ int ireason, len, sectors_to_transfer, nskip;
+ struct cdrom_info *info = drive->driver_data;
+ int i, dma = info->dma, dma_error = 0;
+
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* Check for errors. */
+ if (dma) {
+ info->dma = 0;
+ if ((dma_error = HWIF(drive)->dmaproc(ide_dma_status_bad, drive))) {
+ printk ("%s: disabled DMA\n", drive->name);
+ drive->using_dma = 0;
+ }
+ (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));
+ }
+
+ if (cdrom_decode_status (drive, 0, &stat)) return;
+
+ if (dma) {
+ if (!dma_error) {
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, HWGROUP(drive));
+ }
+ } else
+ ide_error (drive, "dma error", stat);
+ return;
+ }
+
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (IDE_NSECTOR_REG);
+ len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+ /* If DRQ is clear, the command has completed. */
+ if ((stat & DRQ_STAT) == 0) {
+ /* If we're not done filling the current buffer, complain.
+ Otherwise, complete the command normally. */
+ if (rq->current_nr_sectors > 0) {
+ printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
+ drive->name, rq->current_nr_sectors);
+ cdrom_end_request (0, drive);
+ } else
+ cdrom_end_request (1, drive);
+
+ return;
+ }
+
+ /* Check that the drive is expecting to do the same thing we are. */
+ if (cdrom_read_check_ireason (drive, len, ireason)) return;
+
+ /* Assume that the drive will always provide data in multiples
+ of at least SECTOR_SIZE, as it gets hairy to keep track
+ of the transfers otherwise. */
+ if ((len % SECTOR_SIZE) != 0) {
+ printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
+ drive->name, len);
+ printk (" This drive is not supported by this version of the driver\n");
+ cdrom_end_request (0, drive);
+ return;
+ }
+
+ /* The number of sectors we need to read from the drive. */
+ sectors_to_transfer = len / SECTOR_SIZE;
+
+ /* First, figure out if we need to bit-bucket
+ any of the leading sectors. */
+ nskip = MIN ((int)(rq->current_nr_sectors -
+ (rq->bh->b_size >> SECTOR_BITS)),
+ sectors_to_transfer);
+
+ while (nskip > 0) {
+ /* We need to throw away a sector. */
+ char dum[SECTOR_SIZE];
+ atapi_input_bytes (drive, dum, sizeof (dum));
+
+ --rq->current_nr_sectors;
+ --nskip;
+ --sectors_to_transfer;
+ }
+
+ /* Now loop while we still have data to read from the drive. */
+ while (sectors_to_transfer > 0) {
+ int this_transfer;
+
+ /* If we've filled the present buffer but there's another
+ chained buffer after it, move on. */
+ if (rq->current_nr_sectors == 0 &&
+ rq->nr_sectors > 0)
+ cdrom_end_request (1, drive);
+
+ /* If the buffers are full, cache the rest of the data in our
+ internal buffer. */
+ if (rq->current_nr_sectors == 0) {
+ cdrom_buffer_sectors (drive,
+ rq->sector, sectors_to_transfer);
+ sectors_to_transfer = 0;
+ } else {
+ /* Transfer data to the buffers.
+ Figure out how many sectors we can transfer
+ to the current buffer. */
+ this_transfer = MIN (sectors_to_transfer,
+ rq->current_nr_sectors);
+
+ /* Read this_transfer sectors
+ into the current buffer. */
+ while (this_transfer > 0) {
+ atapi_input_bytes (drive,
+ rq->buffer, SECTOR_SIZE);
+ rq->buffer += SECTOR_SIZE;
+ --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ ++rq->sector;
+ --this_transfer;
+ --sectors_to_transfer;
+ }
+ }
+ }
+
+ /* Done moving data!
+ Wait for another interrupt. */
+ ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD);
}
@@ -945,58 +853,56 @@ static void cdrom_read_intr (ide_dev_t *dev)
* Try to satisfy some of the current read request from our cached data.
* Returns nonzero if the request has been completed, zero otherwise.
*/
-static int cdrom_read_from_buffer (ide_dev_t *dev)
-{
- struct cdrom_info *info = &cdrom_info[DEV_HWIF][dev->select.b.drive];
- struct request *rq = ide_cur_rq[DEV_HWIF];
-
- /* Can't do anything if there's no buffer. */
- if (info->sector_buffer == NULL) return 0;
-
- /* Loop while this request needs data and the next block is present
- in our cache. */
- while (rq->nr_sectors > 0 &&
- rq->sector >= info->sector_buffered &&
- rq->sector < info->sector_buffered + info->nsectors_buffered)
- {
- if (rq->current_nr_sectors == 0)
- cdrom_end_request (1, dev);
-
- memcpy (rq->buffer,
- info->sector_buffer +
- (rq->sector - info->sector_buffered) * SECTOR_SIZE,
- SECTOR_SIZE);
- rq->buffer += SECTOR_SIZE;
- --rq->current_nr_sectors;
- --rq->nr_sectors;
- ++rq->sector;
- }
-
- /* If we've satisfied the current request, terminate it successfully. */
- if (rq->nr_sectors == 0)
- {
- cdrom_end_request (1, dev);
- return -1;
- }
-
- /* Move on to the next buffer if needed. */
- if (rq->current_nr_sectors == 0)
- cdrom_end_request (1, dev);
-
- /* If this condition does not hold, then the kluge i use to
- represent the number of sectors to skip at the start of a transfer
- will fail. I think that this will never happen, but let's be
- paranoid and check. */
- if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
- (rq->sector % SECTORS_PER_FRAME) != 0)
- {
- printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
- dev->name, rq->sector);
- cdrom_end_request (0, dev);
- return -1;
- }
-
- return 0;
+static int cdrom_read_from_buffer (ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* Can't do anything if there's no buffer. */
+ if (info->sector_buffer == NULL) return 0;
+
+ /* Loop while this request needs data and the next block is present
+ in our cache. */
+ while (rq->nr_sectors > 0 &&
+ rq->sector >= info->sector_buffered &&
+ rq->sector < info->sector_buffered + info->nsectors_buffered) {
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, drive);
+
+ memcpy (rq->buffer,
+ info->sector_buffer +
+ (rq->sector - info->sector_buffered) * SECTOR_SIZE,
+ SECTOR_SIZE);
+ rq->buffer += SECTOR_SIZE;
+ --rq->current_nr_sectors;
+ --rq->nr_sectors;
+ ++rq->sector;
+ }
+
+ /* If we've satisfied the current request,
+ terminate it successfully. */
+ if (rq->nr_sectors == 0) {
+ cdrom_end_request (1, drive);
+ return -1;
+ }
+
+ /* Move on to the next buffer if needed. */
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, drive);
+
+ /* If this condition does not hold, then the kluge i use to
+ represent the number of sectors to skip at the start of a transfer
+ will fail. I think that this will never happen, but let's be
+ paranoid and check. */
+ if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
+ (rq->sector % SECTORS_PER_FRAME) != 0) {
+ printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+ drive->name, rq->sector);
+ cdrom_end_request (0, drive);
+ return -1;
+ }
+
+ return 0;
}
@@ -1007,1040 +913,1640 @@ static int cdrom_read_from_buffer (ide_dev_t *dev)
* However, for drq_interrupt devices, it is called from an interrupt
* when the drive is ready to accept the command.
*/
-static int cdrom_start_read_continuation (ide_dev_t *dev)
-{
- struct packet_command pc;
- struct request *rq = ide_cur_rq[DEV_HWIF];
-
- int nsect, sector, nframes, frame, nskip;
-
- /* Number of sectors to transfer. */
- nsect = rq->nr_sectors;
-
- /* Starting sector. */
- sector = rq->sector;
-
- /* If the requested sector doesn't start on a cdrom block boundary,
- we must adjust the start of the transfer so that it does,
- and remember to skip the first few sectors. If the CURRENT_NR_SECTORS
- field is larger than the size of the buffer, it will mean that
- we're to skip a number of sectors equal to the amount by which
- CURRENT_NR_SECTORS is larger than the buffer size. */
- nskip = (sector % SECTORS_PER_FRAME);
- if (nskip > 0)
- {
- /* Sanity check... */
- if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS))
- {
- printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
- dev->name, rq->current_nr_sectors);
- cdrom_end_request (0, dev);
- DO_REQUEST;
- return 1;
- }
-
- sector -= nskip;
- nsect += nskip;
- rq->current_nr_sectors += nskip;
- }
-
- /* Convert from sectors to cdrom blocks, rounding up the transfer
- length if needed. */
- nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
- frame = sector / SECTORS_PER_FRAME;
-
- /* Largest number of frames was can transfer at once is 64k-1. */
- nframes = MIN (nframes, 65535);
-
- /* Set up the command */
- memset (&pc.c, 0, sizeof (pc.c));
- pc.c[0] = READ_10;
- pc.c[7] = (nframes >> 8);
- pc.c[8] = (nframes & 0xff);
-
- /* Write the sector address into the command image. */
- {
- union {
- struct {unsigned char b0, b1, b2, b3;} b;
- struct {unsigned long l0;} l;
- } conv;
- conv.l.l0 = frame;
- pc.c[2] = conv.b.b3;
- pc.c[3] = conv.b.b2;
- pc.c[4] = conv.b.b1;
- pc.c[5] = conv.b.b0;
- }
-
- if (cdrom_transfer_packet_command (dev, pc.c, sizeof (pc.c)))
- return 1;
-
- /* Set up our interrupt handler and return. */
- ide_handler[DEV_HWIF] = cdrom_read_intr;
-
- return 0;
+static void cdrom_start_read_continuation (ide_drive_t *drive)
+{
+ struct packet_command pc;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ int nsect, sector, nframes, frame, nskip;
+
+ /* Number of sectors to transfer. */
+ nsect = rq->nr_sectors;
+
+ /* Starting sector. */
+ sector = rq->sector;
+
+ /* If the requested sector doesn't start on a cdrom block boundary,
+ we must adjust the start of the transfer so that it does,
+ and remember to skip the first few sectors.
+ If the CURRENT_NR_SECTORS field is larger than the size
+ of the buffer, it will mean that we're to skip a number
+ of sectors equal to the amount by which CURRENT_NR_SECTORS
+ is larger than the buffer size. */
+ nskip = (sector % SECTORS_PER_FRAME);
+ if (nskip > 0) {
+ /* Sanity check... */
+ if (rq->current_nr_sectors !=
+ (rq->bh->b_size >> SECTOR_BITS)) {
+ printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
+ drive->name, rq->current_nr_sectors);
+ cdrom_end_request (0, drive);
+ return;
+ }
+
+ sector -= nskip;
+ nsect += nskip;
+ rq->current_nr_sectors += nskip;
+ }
+
+ /* Convert from sectors to cdrom blocks, rounding up the transfer
+ length if needed. */
+ nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
+ frame = sector / SECTORS_PER_FRAME;
+
+ /* Largest number of frames was can transfer at once is 64k-1. */
+ nframes = MIN (nframes, 65535);
+
+ /* Set up the command */
+ memset (&pc.c, 0, sizeof (pc.c));
+ pc.c[0] = READ_10;
+ pc.c[7] = (nframes >> 8);
+ pc.c[8] = (nframes & 0xff);
+ put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
+
+ /* Send the command to the drive and return. */
+ (void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
+ &cdrom_read_intr);
}
/*
* Start a read request from the CD-ROM.
- * Returns 0 if the request was started successfully,
- * 1 if there was an error and we should either retry or move on to the
- * next request.
*/
-static int cdrom_start_read (ide_dev_t *dev, unsigned int block)
+static void cdrom_start_read (ide_drive_t *drive, unsigned int block)
{
- struct request *rq = ide_cur_rq[DEV_HWIF];
-
- /* We may be retrying this request after an error.
- Fix up any weirdness which might be present in the request packet. */
- restore_request (rq);
+ struct cdrom_info *info = drive->driver_data;
+ struct request *rq = HWGROUP(drive)->rq;
+ int minor = MINOR (rq->rq_dev);
+
+ /* If the request is relative to a partition, fix it up to refer to the
+ absolute address. */
+ if ((minor & PARTN_MASK) != 0) {
+ rq->sector = block;
+ minor &= ~PARTN_MASK;
+ rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor);
+ }
- /* Satisfy whatever we can of this request from our cached sector. */
- if (cdrom_read_from_buffer (dev))
- return 1;
+ /* We may be retrying this request after an error. Fix up
+ any weirdness which might be present in the request packet. */
+ restore_request (rq);
- /* Clear the local sector buffer. */
- cdrom_info[DEV_HWIF][dev->select.b.drive].nsectors_buffered = 0;
+ /* Satisfy whatever we can of this request from our cached sector. */
+ if (cdrom_read_from_buffer (drive))
+ return;
- if (cdrom_start_packet_command (dev, 32768))
- return 1;
+ /* Clear the local sector buffer. */
+ info->nsectors_buffered = 0;
- if (CDROM_FLAGS (dev)->drq_interrupt)
- ide_handler[DEV_HWIF] = (void (*)(ide_dev_t *))cdrom_start_read_continuation;
- else
- {
- if (cdrom_start_read_continuation (dev))
- return 1;
- }
+ if (drive->using_dma && (rq->sector % SECTORS_PER_FRAME == 0) && (rq->nr_sectors % SECTORS_PER_FRAME == 0))
+ info->dma = 1;
+ else
+ info->dma = 0;
- return 0;
+ /* Start sending the read request to the drive. */
+ cdrom_start_packet_command (drive, 32768,
+ cdrom_start_read_continuation);
}
-
+
/****************************************************************************
* Execute all other packet commands.
*/
-/* Forward declaration */
+/* Forward declarations. */
static int
-cdrom_request_sense (ide_dev_t *dev, struct atapi_request_sense *reqbuf);
+cdrom_lockdoor (ide_drive_t *drive, int lockflag,
+ struct atapi_request_sense *reqbuf);
+
/* Interrupt routine for packet command completion. */
-static void cdrom_pc_intr (ide_dev_t *dev)
-{
- int ireason, len, stat, thislen;
- struct request *rq = ide_cur_rq[DEV_HWIF];
- struct packet_command *pc = (struct packet_command *)rq->buffer;
-
- /* Check for errors. */
- if (cdrom_decode_status (dev, 0, &stat)) return;
-
- /* Read the interrupt reason and the transfer length. */
- ireason = IN_BYTE (HD_NSECTOR, DEV_HWIF);
- len = IN_BYTE (HD_LCYL, DEV_HWIF) + 256 * IN_BYTE (HD_HCYL, DEV_HWIF);
-
- /* If DRQ is clear, the command has completed.
- Complain if we still have data left to transfer. */
- if ((stat & DRQ_STAT) == 0)
- {
- /* Some of the trailing request sense fields are optional, and
- some drives don't send them. Sigh. */
- if (pc->c[0] == REQUEST_SENSE && pc->buflen > 0 && pc->buflen <= 5) {
- while (pc->buflen > 0) {
- *pc->buffer++ = 0;
- --pc->buflen;
- }
- }
-
- if (pc->buflen == 0)
- cdrom_end_request (1, dev);
- else
- {
- printk ("%s: cdrom_pc_intr: data underrun %d\n",
- dev->name, pc->buflen);
- pc->stat = 1;
- cdrom_end_request (1, dev);
- }
- DO_REQUEST;
- return;
- }
-
- /* Figure out how much data to transfer. */
- thislen = pc->buflen;
- if (thislen < 0) thislen = -thislen;
- if (thislen > len) thislen = len;
-
- /* The drive wants to be written to. */
- if ((ireason & 3) == 0)
- {
- /* Check that we want to write. */
- if (pc->buflen > 0)
- {
- printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
- dev->name);
- pc->stat = 1;
- thislen = 0;
- }
-
- /* Transfer the data. */
- OUT_WORDS (pc->buffer, thislen / 2);
-
- /* If we haven't moved enough data to satisfy the drive,
- add some padding. */
- while (len > thislen)
- {
- short dum = 0;
- OUT_WORDS (&dum, 1);
- len -= 2;
- }
-
- /* Keep count of how much data we've moved. */
- pc->buffer += thislen;
- pc->buflen += thislen;
- }
-
- /* Same drill for reading. */
- else if ((ireason & 3) == 2)
- {
- /* Check that we want to read. */
- if (pc->buflen < 0)
- {
- printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
- dev->name);
- pc->stat = 1;
- thislen = 0;
- }
-
- /* Transfer the data. */
- IN_WORDS (pc->buffer, thislen / 2);
-
- /* If we haven't moved enough data to satisfy the drive,
- add some padding. */
- while (len > thislen)
- {
- short dum = 0;
- IN_WORDS (&dum, 1);
- len -= 2;
- }
-
- /* Keep count of how much data we've moved. */
- pc->buffer += thislen;
- pc->buflen -= thislen;
- }
-
- else
- {
- printk ("%s: cdrom_pc_intr: The drive appears confused (ireason = 0x%2x)\n",
- dev->name, ireason);
- pc->stat = 1;
- }
-
- /* Now we wait for another interrupt. */
- ide_handler[DEV_HWIF] = cdrom_pc_intr;
-}
-
-
-static int cdrom_do_pc_continuation (ide_dev_t *dev)
-{
- struct request *rq = ide_cur_rq[DEV_HWIF];
- struct packet_command *pc = (struct packet_command *)rq->buffer;
-
- if (cdrom_transfer_packet_command (dev, pc->c, sizeof (pc->c)))
- return 1;
-
- /* Set up our interrupt handler and return. */
- ide_handler[DEV_HWIF] = cdrom_pc_intr;
-
- return 0;
-}
-
-
-static int cdrom_do_packet_command (ide_dev_t *dev)
-{
- int len;
- struct request *rq = ide_cur_rq[DEV_HWIF];
- struct packet_command *pc = (struct packet_command *)rq->buffer;
+static void cdrom_pc_intr (ide_drive_t *drive)
+{
+ int ireason, len, stat, thislen;
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (drive, 0, &stat)) return;
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (IDE_NSECTOR_REG);
+ len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+ /* If DRQ is clear, the command has completed.
+ Complain if we still have data left to transfer. */
+ if ((stat & DRQ_STAT) == 0) {
+ /* Some of the trailing request sense fields are optional, and
+ some drives don't send them. Sigh. */
+ if (pc->c[0] == REQUEST_SENSE &&
+ pc->buflen > 0 &&
+ pc->buflen <= 5) {
+ while (pc->buflen > 0) {
+ *pc->buffer++ = 0;
+ --pc->buflen;
+ }
+ }
+
+ if (pc->buflen == 0)
+ cdrom_end_request (1, drive);
+ else {
+ printk ("%s: cdrom_pc_intr: data underrun %d\n",
+ drive->name, pc->buflen);
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+ }
+ return;
+ }
+
+ /* Figure out how much data to transfer. */
+ thislen = pc->buflen;
+ if (thislen < 0) thislen = -thislen;
+ if (thislen > len) thislen = len;
+
+ /* The drive wants to be written to. */
+ if ((ireason & 3) == 0) {
+ /* Check that we want to write. */
+ if (pc->buflen > 0) {
+ printk ("%s: cdrom_pc_intr: Drive wants "
+ "to transfer data the wrong way!\n",
+ drive->name);
+ pc->stat = 1;
+ thislen = 0;
+ }
+
+ /* Transfer the data. */
+ atapi_output_bytes (drive, pc->buffer, thislen);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen) {
+ int dum = 0;
+ atapi_output_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen += thislen;
+ }
+
+ /* Same drill for reading. */
+ else if ((ireason & 3) == 2) {
+ /* Check that we want to read. */
+ if (pc->buflen < 0) {
+ printk ("%s: cdrom_pc_intr: Drive wants to "
+ "transfer data the wrong way!\n",
+ drive->name);
+ pc->stat = 1;
+ thislen = 0;
+ }
+
+ /* Transfer the data. */
+ atapi_input_bytes (drive, pc->buffer, thislen);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen) {
+ int dum = 0;
+ atapi_input_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen -= thislen;
+ } else {
+ printk ("%s: cdrom_pc_intr: The drive "
+ "appears confused (ireason = 0x%2x)\n",
+ drive->name, ireason);
+ pc->stat = 1;
+ }
+
+ /* Now we wait for another interrupt. */
+ ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD);
+}
+
+
+static void cdrom_do_pc_continuation (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ /* Send the command to the drive and return. */
+ cdrom_transfer_packet_command (drive, pc->c,
+ sizeof (pc->c), &cdrom_pc_intr);
+}
- len = pc->buflen;
- if (len < 0) len = -len;
- pc->stat = 0;
+static void cdrom_do_packet_command (ide_drive_t *drive)
+{
+ int len;
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ struct cdrom_info *info = drive->driver_data;
+
+ info->dma = 0;
- if (cdrom_start_packet_command (dev, len))
- return 1;
+ len = pc->buflen;
+ if (len < 0) len = -len;
- if (CDROM_FLAGS (dev)->drq_interrupt)
- ide_handler[DEV_HWIF] = (void (*)(ide_dev_t *))cdrom_do_pc_continuation;
- else
- {
- if (cdrom_do_pc_continuation (dev))
- return 1;
- }
+ pc->stat = 0;
- return 0;
+ /* Start sending the command to the drive. */
+ cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
}
+/* Sleep for TIME jiffies.
+ Not to be called from an interrupt handler. */
static
-int cdrom_queue_packet_command (ide_dev_t *dev, struct packet_command *pc)
-{
- int retries = 3;
- unsigned long flags;
- struct request req, **p, **pfirst;
- struct semaphore sem = MUTEX_LOCKED;
- int major = ide_major[DEV_HWIF];
-
- retry:
- req.dev = MKDEV (major, (dev->select.b.drive) << PARTN_BITS);
- req.cmd = PACKET_COMMAND;
- req.errors = 0;
- req.sector = 0;
- req.nr_sectors = 0;
- req.current_nr_sectors = 0;
- req.buffer = (char *)pc;
- req.sem = &sem;
- req.bh = NULL;
- req.bhtail = NULL;
- req.next = NULL;
-
- save_flags (flags);
- cli ();
-
- p = &blk_dev[major].current_request;
- pfirst = p;
- while ((*p) != NULL)
- {
- p = &((*p)->next);
- }
- *p = &req;
- if (p == pfirst)
- blk_dev[major].request_fn ();
-
- restore_flags (flags);
-
- down (&sem);
-
- if (pc->stat != 0)
- {
- /* The request failed. Try to do a request sense to get more information
- about the error; store the result in the cdrom_info struct
- for this drive. Check to be sure that it wasn't a request sense
- request that failed, though, to prevent infinite loops. */
-
- struct atapi_request_sense *reqbuf =
- &cdrom_info[DEV_HWIF][dev->select.b.drive].sense_data;
-
- if (pc->c[0] == REQUEST_SENSE || cdrom_request_sense (dev, reqbuf))
- {
- memset (reqbuf, 0, sizeof (*reqbuf));
- reqbuf->asc = 0xff;
- }
- cdrom_analyze_sense_data (dev, reqbuf, pc);
+void cdrom_sleep (int time)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + time;
+ schedule ();
+}
- /* If the error was a unit attention (usually means media was changed),
- retry the command. */
- if (reqbuf->sense_key == UNIT_ATTENTION && retries > 0)
- {
- --retries;
- goto retry;
+static
+int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
+{
+ struct atapi_request_sense my_reqbuf;
+ int retries = 10;
+ struct request req;
+
+ /* If our caller has not provided a place to stick any sense data,
+ use our own area. */
+ if (pc->sense_data == NULL)
+ pc->sense_data = &my_reqbuf;
+ pc->sense_data->sense_key = 0;
+
+ /* Start of retry loop. */
+ do {
+ ide_init_drive_cmd (&req);
+ req.cmd = PACKET_COMMAND;
+ req.buffer = (char *)pc;
+ (void) ide_do_drive_cmd (drive, &req, ide_wait);
+
+ if (pc->stat != 0) {
+ /* The request failed. Retry if it was due to a unit
+ attention status
+ (usually means media was changed). */
+ struct atapi_request_sense *reqbuf = pc->sense_data;
+
+ if (reqbuf->sense_key == UNIT_ATTENTION)
+ ;
+ else if (reqbuf->sense_key == NOT_READY &&
+ reqbuf->asc == 4) {
+ /* The drive is in the process of loading
+ a disk. Retry, but wait a little to give
+ the drive time to complete the load. */
+ cdrom_sleep (HZ);
+ } else
+ /* Otherwise, don't retry. */
+ retries = 0;
+
+ --retries;
+ }
+
+ /* End of retry loop. */
+ } while (pc->stat != 0 && retries >= 0);
+
+
+ /* Return an error if the command failed. */
+ if (pc->stat != 0)
+ return -EIO;
+ else {
+ /* The command succeeded. If it was anything other than
+ a request sense, eject, or door lock command,
+ and we think that the door is presently, lock it again.
+ (The door was probably unlocked via an explicit
+ CDROMEJECT ioctl.) */
+ if (CDROM_STATE_FLAGS (drive)->door_locked == 0 && drive->usage &&
+ (pc->c[0] != REQUEST_SENSE &&
+ pc->c[0] != ALLOW_MEDIUM_REMOVAL &&
+ pc->c[0] != START_STOP)) {
+ (void) cdrom_lockdoor (drive, 1, NULL);
+ }
+ return 0;
}
-
- return -EIO;
- }
- else
- return 0;
}
-
/****************************************************************************
* cdrom driver request routine.
*/
-static int do_rw_cdrom (ide_dev_t *dev, unsigned long block)
+void ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
{
- struct request *rq = ide_cur_rq[DEV_HWIF];
-
- if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
- return cdrom_do_packet_command (dev);
-
- if (rq -> cmd != READ)
- {
- printk ("ide-cd: bad cmd %d\n", rq -> cmd);
- cdrom_end_request (0, dev);
- return 1;
- }
-
- return cdrom_start_read (dev, block);
+ if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
+ cdrom_do_packet_command (drive);
+ else if (rq -> cmd == RESET_DRIVE_COMMAND) {
+ cdrom_end_request (1, drive);
+ ide_do_reset (drive);
+ return;
+ } else if (rq -> cmd != READ) {
+ printk ("ide-cd: bad cmd %d\n", rq -> cmd);
+ cdrom_end_request (0, drive);
+ } else
+ cdrom_start_read (drive, block);
}
-
+
/****************************************************************************
- * ioctl handling.
+ * Ioctl handling.
+ *
+ * Routines which queue packet commands take as a final argument a pointer
+ * to an atapi_request_sense struct. If execution of the command results
+ * in an error with a CHECK CONDITION status, this structure will be filled
+ * with the results of the subsequent request sense command. The pointer
+ * can also be NULL, in which case no sense information is returned.
*/
+#if ! STANDARD_ATAPI
static inline
-void byte_swap_word (unsigned short *x)
+int bin2bcd (int x)
{
- char *c = (char *)x;
- char d = c[0];
- c[0] = c[1];
- c[1] = d;
+ return (x%10) | ((x/10) << 4);
}
static inline
-void byte_swap_long (unsigned *x)
+int bcd2bin (int x)
{
- char *c = (char *)x;
- char d = c[0];
- c[0] = c[3];
- c[3] = d;
- d = c[1];
- c[1] = c[2];
- c[2] = d;
+ return (x >> 4) * 10 + (x & 0x0f);
}
-
static
-int bin2bcd (int x)
+void msf_from_bcd (struct atapi_msf *msf)
{
- return (x%10) | ((x/10) << 4);
+ msf->minute = bcd2bin (msf->minute);
+ msf->second = bcd2bin (msf->second);
+ msf->frame = bcd2bin (msf->frame);
}
+#endif /* not STANDARD_ATAPI */
+
static inline
void lba_to_msf (int lba, byte *m, byte *s, byte *f)
{
- lba += CD_BLOCK_OFFSET;
- lba &= 0xffffff; /* negative lbas use only 24 bits */
- *m = lba / (CD_SECS * CD_FRAMES);
- lba %= (CD_SECS * CD_FRAMES);
- *s = lba / CD_FRAMES;
- *f = lba % CD_FRAMES;
+ lba += CD_BLOCK_OFFSET;
+ lba &= 0xffffff; /* negative lbas use only 24 bits */
+ *m = lba / (CD_SECS * CD_FRAMES);
+ lba %= (CD_SECS * CD_FRAMES);
+ *s = lba / CD_FRAMES;
+ *f = lba % CD_FRAMES;
}
static inline
int msf_to_lba (byte m, byte s, byte f)
{
- return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_BLOCK_OFFSET;
+ return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_BLOCK_OFFSET;
}
-static void
-cdrom_check_status (ide_dev_t *dev)
+static int
+cdrom_check_status (ide_drive_t *drive,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
- memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+ pc.c[0] = TEST_UNIT_READY;
- pc.c[0] = TEST_UNIT_READY;
+#if ! STANDARD_ATAPI
+ /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to
+ switch CDs instead of supporting the LOAD_UNLOAD opcode */
- (void) cdrom_queue_packet_command (dev, &pc);
+ pc.c[7] = CDROM_STATE_FLAGS (drive)->sanyo_slot % 3;
+#endif /* not STANDARD_ATAPI */
+
+ return cdrom_queue_packet_command (drive, &pc);
}
+/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
static int
-cdrom_request_sense (ide_dev_t *dev, struct atapi_request_sense *reqbuf)
+cdrom_lockdoor (ide_drive_t *drive, int lockflag,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ struct atapi_request_sense my_reqbuf;
+ int stat;
+ struct packet_command pc;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ /* If the drive cannot lock the door, just pretend. */
+ if (CDROM_CONFIG_FLAGS (drive)->no_doorlock)
+ stat = 0;
+ else {
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = ALLOW_MEDIUM_REMOVAL;
+ pc.c[4] = (lockflag != 0);
+ stat = cdrom_queue_packet_command (drive, &pc);
+ }
- memset (&pc, 0, sizeof (pc));
+ /* If we got an illegal field error, the drive
+ probably cannot lock the door. */
+ if (stat != 0 &&
+ reqbuf->sense_key == ILLEGAL_REQUEST &&
+ reqbuf->asc == 0x24) {
+ printk ("%s: door locking not supported\n",
+ drive->name);
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+ stat = 0;
+ }
- pc.c[0] = REQUEST_SENSE;
- pc.c[4] = sizeof (*reqbuf);
- pc.buffer = (char *)reqbuf;
- pc.buflen = sizeof (*reqbuf);
+ if (stat == 0)
+ CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
- return cdrom_queue_packet_command (dev, &pc);
+ return stat;
}
-#if 0
-/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
+/* Eject the disk if EJECTFLAG is 0.
+ If EJECTFLAG is 1, try to reload the disk. */
static int
-cdrom_lockdoor (ide_dev_t *dev, int lockflag)
+cdrom_eject (ide_drive_t *drive, int ejectflag,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ struct packet_command pc;
- memset (&pc, 0, sizeof (pc));
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
- pc.c[0] = ALLOW_MEDIUM_REMOVAL;
- pc.c[4] = (lockflag != 0);
- return cdrom_queue_packet_command (dev, &pc);
+ pc.c[0] = START_STOP;
+ pc.c[4] = 2 + (ejectflag != 0);
+ return cdrom_queue_packet_command (drive, &pc);
}
-#endif
-/* Eject the disk if EJECTFLAG is 0.
- If EJECTFLAG is 1, try to reload the disk. */
static int
-cdrom_eject (ide_dev_t *dev, int ejectflag)
+cdrom_pause (ide_drive_t *drive, int pauseflag,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ struct packet_command pc;
- memset (&pc, 0, sizeof (pc));
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
- pc.c[0] = START_STOP;
- pc.c[4] = 2 + (ejectflag != 0);
- return cdrom_queue_packet_command (dev, &pc);
+ pc.c[0] = SCMD_PAUSE_RESUME;
+ pc.c[8] = !pauseflag;
+ return cdrom_queue_packet_command (drive, &pc);
}
static int
-cdrom_pause (ide_dev_t *dev, int pauseflag)
+cdrom_startstop (ide_drive_t *drive, int startflag,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ struct packet_command pc;
- memset (&pc, 0, sizeof (pc));
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
- pc.c[0] = SCMD_PAUSE_RESUME;
- pc.c[8] = !pauseflag;
- return cdrom_queue_packet_command (dev, &pc);
+ pc.c[0] = START_STOP;
+ pc.c[1] = 1;
+ pc.c[4] = startflag;
+ return cdrom_queue_packet_command (drive, &pc);
}
static int
-cdrom_startstop (ide_dev_t *dev, int startflag)
+cdrom_read_capacity (ide_drive_t *drive, unsigned *capacity,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ struct {
+ unsigned lba;
+ unsigned blocklen;
+ } capbuf;
+
+ int stat;
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = READ_CAPACITY;
+ pc.buffer = (char *)&capbuf;
+ pc.buflen = sizeof (capbuf);
- memset (&pc, 0, sizeof (pc));
+ stat = cdrom_queue_packet_command (drive, &pc);
+ if (stat == 0)
+ *capacity = ntohl (capbuf.lba);
- pc.c[0] = START_STOP;
- pc.c[1] = 1;
- pc.c[4] = startflag;
- return cdrom_queue_packet_command (dev, &pc);
+ return stat;
}
static int
-cdrom_read_tocentry (ide_dev_t *dev, int trackno, int msf_flag,
- char *buf, int buflen)
+cdrom_read_tocentry (ide_drive_t *drive, int trackno, int msf_flag,
+ int format, char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
-
- memset (&pc, 0, sizeof (pc));
-
- pc.buffer = buf;
- pc.buflen = buflen;
- pc.c[0] = SCMD_READ_TOC;
- pc.c[6] = trackno;
- pc.c[7] = (buflen >> 8);
- pc.c[8] = (buflen & 0xff);
- if (msf_flag) pc.c[1] = 2;
- return cdrom_queue_packet_command (dev, &pc);
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = SCMD_READ_TOC;
+ pc.c[6] = trackno;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ pc.c[9] = (format << 6);
+ if (msf_flag) pc.c[1] = 2;
+ return cdrom_queue_packet_command (drive, &pc);
}
/* Try to read the entire TOC for the disk into our internal buffer. */
static int
-cdrom_read_toc (ide_dev_t *dev)
-{
- int msf_flag;
- int stat, ntracks, i;
- struct atapi_toc *toc = cdrom_info[DEV_HWIF][dev->select.b.drive].toc;
-
- if (toc == NULL)
- {
- /* Try to allocate space. */
- toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
- GFP_KERNEL);
- cdrom_info[DEV_HWIF][dev->select.b.drive].toc = toc;
- }
-
- if (toc == NULL)
- {
- printk ("%s: No cdrom TOC buffer!\n", dev->name);
- return -EIO;
- }
-
- /* Check to see if the existing data is still valid.
- If it is, just return. */
- if (CDROM_FLAGS (dev)->toc_valid)
- cdrom_check_status (dev);
-
- if (CDROM_FLAGS (dev)->toc_valid) return 0;
-
- /* Some drives can't return TOC data in LBA format. */
- msf_flag = (CDROM_FLAGS (dev)->no_lba_toc);
-
- /* First read just the header, so we know how long the TOC is. */
- stat = cdrom_read_tocentry (dev, 0, msf_flag, (char *)toc,
- sizeof (struct atapi_toc_header) +
- sizeof (struct atapi_toc_entry));
- if (stat) return stat;
-
- ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
- if (ntracks <= 0) return -EIO;
- if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
-
- /* Now read the whole schmeer. */
- stat = cdrom_read_tocentry (dev, 0, msf_flag, (char *)toc,
- sizeof (struct atapi_toc_header) +
- (ntracks+1) * sizeof (struct atapi_toc_entry));
- if (stat) return stat;
- byte_swap_word (&toc->hdr.toc_length);
- for (i=0; i<=ntracks; i++)
- {
- if (msf_flag)
- {
- byte *adr = (byte *)&(toc->ent[i].lba);
- toc->ent[i].lba = msf_to_lba (adr[1], adr[2], adr[3]);
+cdrom_read_toc (ide_drive_t *drive,
+ struct atapi_request_sense *reqbuf)
+{
+ int stat, ntracks, i;
+ struct cdrom_info *info = drive->driver_data;
+ struct atapi_toc *toc = info->toc;
+ struct {
+ struct atapi_toc_header hdr;
+ struct atapi_toc_entry ent;
+ } ms_tmp;
+
+ if (toc == NULL) {
+ /* Try to allocate space. */
+ toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
+ GFP_KERNEL);
+ info->toc = toc;
}
- else
- byte_swap_long (&toc->ent[i].lba);
- }
- /* Remember that we've read this stuff. */
- CDROM_FLAGS (dev)->toc_valid = 1;
+ if (toc == NULL) {
+ printk ("%s: No cdrom TOC buffer!\n", drive->name);
+ return -EIO;
+ }
+
+ /* Check to see if the existing data is still valid.
+ If it is, just return. */
+ if (CDROM_STATE_FLAGS (drive)->toc_valid)
+ (void) cdrom_check_status (drive, NULL);
+
+ if (CDROM_STATE_FLAGS (drive)->toc_valid) return 0;
+
+ /* First read just the header, so we know how long the TOC is. */
+ stat = cdrom_read_tocentry (drive, 0, 1, 0, (char *)&toc->hdr,
+ sizeof (struct atapi_toc_header) +
+ sizeof (struct atapi_toc_entry),
+ reqbuf);
+ if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+ toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+ toc->hdr.last_track = bcd2bin (toc->hdr.last_track);
+ }
+#endif /* not STANDARD_ATAPI */
+
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (ntracks <= 0) return -EIO;
+ if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
+
+ /* Now read the whole schmeer. */
+ stat = cdrom_read_tocentry (drive, 0, 1, 0, (char *)&toc->hdr,
+ sizeof (struct atapi_toc_header) +
+ (ntracks+1) *
+ sizeof (struct atapi_toc_entry),
+ reqbuf);
+ if (stat) return stat;
+ toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) {
+ toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+ toc->hdr.last_track = bcd2bin (toc->hdr.last_track);
+ }
+#endif /* not STANDARD_ATAPI */
+
+ for (i=0; i<=ntracks; i++) {
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd) {
+ if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd)
+ toc->ent[i].track = bcd2bin (toc->ent[i].track);
+ msf_from_bcd (&toc->ent[i].addr.msf);
+ }
+#endif /* not STANDARD_ATAPI */
+ toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.minute,
+ toc->ent[i].addr.msf.second,
+ toc->ent[i].addr.msf.frame);
+ }
- return 0;
+ /* Read the multisession information. */
+ stat = cdrom_read_tocentry (drive, 0, 1, 1,
+ (char *)&ms_tmp, sizeof (ms_tmp),
+ reqbuf);
+ if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
+ msf_from_bcd (&ms_tmp.ent.addr.msf);
+#endif /* not STANDARD_ATAPI */
+
+ toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.minute,
+ ms_tmp.ent.addr.msf.second,
+ ms_tmp.ent.addr.msf.frame);
+
+ toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+
+ /* Now try to get the total cdrom capacity. */
+ stat = cdrom_read_capacity (drive, &toc->capacity, reqbuf);
+ if (stat) toc->capacity = 0x1fffff;
+
+ HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS]
+ = toc->capacity * SECTORS_PER_FRAME;
+ drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
+
+ /* Remember that we've read this stuff. */
+ CDROM_STATE_FLAGS (drive)->toc_valid = 1;
+
+ return 0;
}
static int
-cdrom_read_subchannel (ide_dev_t *dev,
- char *buf, int buflen)
+cdrom_read_subchannel (ide_drive_t *drive, int format,
+ char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = SCMD_READ_SUBCHANNEL;
+ pc.c[1] = 2; /* MSF addressing */
+ pc.c[2] = 0x40; /* request subQ data */
+ pc.c[3] = format;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
+}
- memset (&pc, 0, sizeof (pc));
- pc.buffer = buf;
- pc.buflen = buflen;
- pc.c[0] = SCMD_READ_SUBCHANNEL;
- pc.c[2] = 0x40; /* request subQ data */
- pc.c[3] = 0x01; /* Format 1: current position */
- pc.c[7] = (buflen >> 8);
- pc.c[8] = (buflen & 0xff);
- return cdrom_queue_packet_command (dev, &pc);
+/* modeflag: 0 = current, 1 = changeable mask, 2 = default, 3 = saved */
+static int
+cdrom_mode_sense (ide_drive_t *drive, int pageno, int modeflag,
+ char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = MODE_SENSE_10;
+ pc.c[2] = pageno | (modeflag << 6);
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
}
-/* modeflag: 0 = current, 1 = changeable mask, 2 = default, 3 = saved */
static int
-cdrom_mode_sense (ide_dev_t *dev, int pageno, int modeflag,
- char *buf, int buflen)
+cdrom_mode_select (ide_drive_t *drive, int pageno, char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = - buflen;
+ pc.c[0] = MODE_SELECT_10;
+ pc.c[1] = 0x10;
+ pc.c[2] = pageno;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
+}
- memset (&pc, 0, sizeof (pc));
- pc.buffer = buf;
- pc.buflen = buflen;
- pc.c[0] = MODE_SENSE_10;
- pc.c[2] = pageno | (modeflag << 6);
- pc.c[7] = (buflen >> 8);
- pc.c[8] = (buflen & 0xff);
- return cdrom_queue_packet_command (dev, &pc);
+static int
+cdrom_play_lba_range_1 (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = SCMD_PLAYAUDIO_MSF;
+ lba_to_msf (lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
+ lba_to_msf (lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd) {
+ pc.c[3] = bin2bcd (pc.c[3]);
+ pc.c[4] = bin2bcd (pc.c[4]);
+ pc.c[5] = bin2bcd (pc.c[5]);
+ pc.c[6] = bin2bcd (pc.c[6]);
+ pc.c[7] = bin2bcd (pc.c[7]);
+ pc.c[8] = bin2bcd (pc.c[8]);
+ }
+#endif /* not STANDARD_ATAPI */
+
+ return cdrom_queue_packet_command (drive, &pc);
}
+/* Play audio starting at LBA LBA_START and finishing with the
+ LBA before LBA_END. */
static int
-cdrom_mode_select (ide_dev_t *dev, int pageno, char *buf, int buflen)
+cdrom_play_lba_range (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ int i, stat;
+ struct atapi_request_sense my_reqbuf;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ /* Some drives, will, for certain audio cds,
+ give an error if you ask them to play the entire cd using the
+ values which are returned in the TOC. The play will succeed,
+ however, if the ending address is adjusted downwards
+ by a few frames. */
+ for (i=0; i<75; i++) {
+ stat = cdrom_play_lba_range_1 (drive, lba_start, lba_end,
+ reqbuf);
+
+ if (stat == 0 ||
+ !(reqbuf->sense_key == ILLEGAL_REQUEST &&
+ reqbuf->asc == 0x24))
+ return stat;
+
+ --lba_end;
+ if (lba_end <= lba_start) break;
+ }
+
+ return stat;
+}
- memset (&pc, 0, sizeof (pc));
- pc.buffer = buf;
- pc.buflen = - buflen;
- pc.c[0] = MODE_SELECT_10;
- pc.c[1] = 0x10;
- pc.c[2] = pageno;
- pc.c[7] = (buflen >> 8);
- pc.c[8] = (buflen & 0xff);
- return cdrom_queue_packet_command (dev, &pc);
+static
+int cdrom_get_toc_entry (ide_drive_t *drive, int track,
+ struct atapi_toc_entry **ent,
+ struct atapi_request_sense *reqbuf)
+{
+ struct cdrom_info *info = drive->driver_data;
+ int stat, ntracks;
+ struct atapi_toc *toc;
+
+ /* Make sure our saved TOC is valid. */
+ stat = cdrom_read_toc (drive, reqbuf);
+ if (stat) return stat;
+
+ toc = info->toc;
+
+ /* Check validity of requested track number. */
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (track == CDROM_LEADOUT)
+ *ent = &toc->ent[ntracks];
+ else if (track < toc->hdr.first_track ||
+ track > toc->hdr.last_track)
+ return -EINVAL;
+ else
+ *ent = &toc->ent[track - toc->hdr.first_track];
+
+ return 0;
}
static int
-cdrom_play_lba_range_play12 (ide_dev_t *dev, int lba_start, int lba_end)
+cdrom_read_block (ide_drive_t *drive, int format, int lba, int nblocks,
+ char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+ struct packet_command pc;
+ struct atapi_request_sense my_reqbuf;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
- memset (&pc, 0, sizeof (pc));
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
- pc.c[0] = SCMD_PLAYAUDIO12;
- *(int *)(&pc.c[2]) = lba_start;
- *(int *)(&pc.c[6]) = lba_end - lba_start;
- byte_swap_long ((int *)(&pc.c[2]));
- byte_swap_long ((int *)(&pc.c[6]));
+ pc.buffer = buf;
+ pc.buflen = buflen;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->nec260)
+ pc.c[0] = 0xd4;
+ else
+#endif /* not STANDARD_ATAPI */
+ pc.c[0] = READ_CD;
+
+ pc.c[1] = (format << 2);
+ put_unaligned(htonl(lba), (unsigned int *) &pc.c[2]);
+ pc.c[8] = (nblocks & 0xff);
+ pc.c[7] = ((nblocks>>8) & 0xff);
+ pc.c[6] = ((nblocks>>16) & 0xff);
+ if (format <= 1)
+ pc.c[9] = 0xf0;
+ else
+ pc.c[9] = 0x10;
- return cdrom_queue_packet_command (dev, &pc);
+ return cdrom_queue_packet_command (drive, &pc);
}
+/* If SLOT<0, unload the current slot. Otherwise, try to load SLOT. */
static int
-cdrom_play_lba_range_msf (ide_dev_t *dev, int lba_start, int lba_end)
+cdrom_load_unload (ide_drive_t *drive, int slot,
+ struct atapi_request_sense *reqbuf)
{
- struct packet_command pc;
+#if ! STANDARD_ATAPI
+ /* if the drive is a Sanyo 3 CD changer then TEST_UNIT_READY
+ (used in the cdrom_check_status function) is used to
+ switch CDs instead of LOAD_UNLOAD */
- memset (&pc, 0, sizeof (pc));
+ if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
- pc.c[0] = SCMD_PLAYAUDIO_MSF;
- lba_to_msf (lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
- lba_to_msf (lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
+ if ((slot == 1) || (slot == 2))
+ CDROM_STATE_FLAGS (drive)->sanyo_slot = slot;
+ else if (slot >= 0)
+ CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
+ else
+ return 0;
- if (CDROM_FLAGS (dev)->msf_as_bcd)
- {
- pc.c[3] = bin2bcd (pc.c[3]);
- pc.c[4] = bin2bcd (pc.c[4]);
- pc.c[5] = bin2bcd (pc.c[5]);
- pc.c[6] = bin2bcd (pc.c[6]);
- pc.c[7] = bin2bcd (pc.c[7]);
- pc.c[8] = bin2bcd (pc.c[8]);
- }
+ return cdrom_check_status (drive, reqbuf);
- return cdrom_queue_packet_command (dev, &pc);
+ }
+ else
+#endif /*not STANDARD_ATAPI */
+ {
+
+ /* ATAPI Rev. 2.2+ standard for requesting switching of
+ CDs in a multiplatter device */
+
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = LOAD_UNLOAD;
+ pc.c[4] = 2 + (slot >= 0);
+ pc.c[8] = slot;
+ return cdrom_queue_packet_command (drive, &pc);
+
+ }
}
-/* Play audio starting at LBA LBA_START and finishing with the
- LBA before LBA_END. */
+/* This gets the mechanism status per ATAPI draft spec 2.6 */
+static int
+cdrom_read_mech_status (ide_drive_t *drive, char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = MECHANISM_STATUS;
+ pc.c[8] = (buflen >> 8);
+ pc.c[9] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* Read the drive mechanism status and slot table into our internal buffer.
+ If the buffer does not yet exist, allocate it. */
static int
-cdrom_play_lba_range (ide_dev_t *dev, int lba_start, int lba_end)
-{
- /* This is rather annoying.
- My NEC-260 won't recognize group 5 commands such as PLAYAUDIO12;
- the only way to get it to play more than 64k of blocks at once
- seems to be the PLAYAUDIO_MSF command. However, the parameters
- the NEC 260 wants for the PLAYMSF command are incompatible with
- the new version of the spec.
-
- So what i'll try is this. First try for PLAYAUDIO12. If it works,
- great. Otherwise, if the drive reports an illegal command code,
- try PLAYAUDIO_MSF using the NEC 260-style bcd parameters. */
-
- if (CDROM_FLAGS (dev)->no_playaudio12)
- return cdrom_play_lba_range_msf (dev, lba_start, lba_end);
- else
- {
- int stat;
- struct atapi_request_sense *reqbuf;
-
- stat = cdrom_play_lba_range_play12 (dev, lba_start, lba_end);
- if (stat == 0) return 0;
-
- /* It failed. Try to find out why. */
- reqbuf = &cdrom_info[DEV_HWIF][dev->select.b.drive].sense_data;
- if (reqbuf->sense_key == 0x05 && reqbuf->asc == 0x20)
- {
- /* The drive didn't recognize the command.
- Retry with the MSF variant. */
- printk ("%s: Drive does not support PLAYAUDIO12; "
- "trying PLAYAUDIO_MSF\n", dev->name);
- CDROM_FLAGS (dev)->no_playaudio12 = 1;
- CDROM_FLAGS (dev)->msf_as_bcd = 1;
- return cdrom_play_lba_range_msf (dev, lba_start, lba_end);
- }
-
- /* Failed for some other reason. Give up. */
- return stat;
- }
+cdrom_read_changer_info (ide_drive_t *drive)
+{
+ int nslots;
+ struct cdrom_info *info = drive->driver_data;
+
+ if (info->changer_info)
+ nslots = info->changer_info->hdr.nslots;
+
+ else {
+ struct atapi_mechstat_header mechbuf;
+ int stat;
+
+ stat = cdrom_read_mech_status (drive,
+ (char *)&mechbuf,
+ sizeof (mechbuf),
+ NULL);
+ if (stat)
+ return stat;
+
+ nslots = mechbuf.nslots;
+ info->changer_info =
+ (struct atapi_changer_info *)
+ kmalloc (sizeof (struct atapi_changer_info) +
+ nslots * sizeof (struct atapi_slot),
+ GFP_KERNEL);
+
+ if (info->changer_info == NULL)
+ return -ENOMEM;
+ }
+
+ return cdrom_read_mech_status
+ (drive,
+ (char *)&info->changer_info->hdr,
+ sizeof (struct atapi_mechstat_header) +
+ nslots * sizeof (struct atapi_slot),
+ NULL);
}
static
-int cdrom_get_toc_entry (ide_dev_t *dev, int track,
- struct atapi_toc_entry **ent)
+int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi,
+ unsigned int cmd, unsigned long arg)
+
{
- int stat, ntracks;
- struct atapi_toc *toc;
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_info *info = drive->driver_data;
+
+
+ switch (cmd) {
+ case CDROMREADMODE1:
+ case CDROMREADMODE2: {
+ struct cdrom_msf msf;
+ int blocksize, format, stat, lba;
+ struct atapi_toc *toc;
+ char *buf;
+
+ if (cmd == CDROMREADMODE1) {
+ blocksize = CD_FRAMESIZE;
+ format = 2;
+ } else {
+ blocksize = CD_FRAMESIZE_RAW0;
+ format = 3;
+ }
+
+ stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
+ if (stat) return stat;
+
+ copy_from_user (&msf, (void *)arg, sizeof (msf));
+
+ lba = msf_to_lba (msf.cdmsf_min0,
+ msf.cdmsf_sec0,
+ msf.cdmsf_frame0);
+
+ /* Make sure the TOC is up to date. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = info->toc;
+
+ if (lba < 0 || lba >= toc->capacity)
+ return -EINVAL;
+
+ buf = (char *) kmalloc (CD_FRAMESIZE_RAW0, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ stat = cdrom_read_block (drive, format, lba, 1, buf, blocksize,
+ NULL);
+ if (stat == 0)
+ copy_to_user ((char *)arg, buf, blocksize);
+
+ kfree (buf);
+ return stat;
+ }
- /* Make sure our saved TOC is valid. */
- stat = cdrom_read_toc (dev);
- if (stat) return stat;
+ /* Read 2352 byte blocks from audio tracks. */
+ case CDROMREADAUDIO: {
+ int stat, lba;
+ struct atapi_toc *toc;
+ struct cdrom_read_audio ra;
+ char *buf;
+
+ /* Make sure the TOC is up to date. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = info->toc;
+
+ stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
+ if (stat) return stat;
+
+ copy_from_user (&ra, (void *)arg, sizeof (ra));
+
+ if (ra.nframes < 0 || ra.nframes > toc->capacity)
+ return -EINVAL;
+ else if (ra.nframes == 0)
+ return 0;
+
+ stat = verify_area (VERIFY_WRITE, (char *)ra.buf,
+ ra.nframes * CD_FRAMESIZE_RAW);
+ if (stat) return stat;
+
+ if (ra.addr_format == CDROM_MSF)
+ lba = msf_to_lba (ra.addr.msf.minute,
+ ra.addr.msf.second,
+ ra.addr.msf.frame);
+ else if (ra.addr_format == CDROM_LBA)
+ lba = ra.addr.lba;
+ else
+ return -EINVAL;
+
+ if (lba < 0 || lba >= toc->capacity)
+ return -EINVAL;
+
+ buf = (char *) kmalloc (CDROM_NBLOCKS_BUFFER*CD_FRAMESIZE_RAW,
+ GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ while (ra.nframes > 0) {
+ int this_nblocks = ra.nframes;
+ if (this_nblocks > CDROM_NBLOCKS_BUFFER)
+ this_nblocks = CDROM_NBLOCKS_BUFFER;
+ stat = cdrom_read_block
+ (drive, 1, lba, this_nblocks,
+ buf, this_nblocks * CD_FRAMESIZE_RAW, NULL);
+ if (stat) break;
+
+ copy_to_user (ra.buf, buf,
+ this_nblocks * CD_FRAMESIZE_RAW);
+ ra.buf += this_nblocks * CD_FRAMESIZE_RAW;
+ ra.nframes -= this_nblocks;
+ lba += this_nblocks;
+ }
+
+ kfree (buf);
+ return stat;
+ }
- toc = cdrom_info[DEV_HWIF][dev->select.b.drive].toc;
+ case CDROMSETSPINDOWN: {
+ char spindown;
+ char buffer[16];
+ int stat;
+
+ stat = verify_area (VERIFY_READ, (void *) arg,
+ sizeof (char));
+ if (stat) return stat;
+
+ copy_from_user (&spindown, (void *) arg, sizeof(char));
+
+ stat = cdrom_mode_sense (drive, PAGE_CDROM, 0, buffer,
+ sizeof (buffer), NULL);
+ if (stat) return stat;
+
+ buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f);
+
+ return cdrom_mode_select (drive, PAGE_CDROM, buffer,
+ sizeof (buffer), NULL);
+ }
+
+ case CDROMGETSPINDOWN: {
+ char spindown;
+ char buffer[16];
+ int stat;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg,
+ sizeof (char));
+ if (stat) return stat;
+
+ stat = cdrom_mode_sense (drive, PAGE_CDROM, 0, buffer,
+ sizeof (buffer), NULL);
+ if (stat) return stat;
+
+ spindown = buffer[11] & 0x0f;
+
+ copy_to_user ((void *) arg, &spindown, sizeof (char));
+
+ return 0;
+ }
+
+#ifdef ALLOW_TEST_PACKETS
+ case 0x1234: {
+ int stat;
+ struct packet_command pc;
+ int len, lena;
+
+ memset (&pc, 0, sizeof (pc));
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
+ if (stat) return stat;
+ copy_from_user (&pc.c, (void *) arg, sizeof (pc.c));
+ arg += sizeof (pc.c);
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
+ if (stat) return stat;
+ copy_from_user (&len, (void *) arg , sizeof (len));
+ arg += sizeof (len);
+
+ lena = len;
+ if (lena < 0) lena = -lena;
+
+ {
+ char buf[lena];
+ if (len > 0) {
+ stat = verify_area (VERIFY_WRITE,
+ (void *) arg, len);
+ if (stat) return stat;
+ }
+ else if (len < 0) {
+ stat = verify_area (VERIFY_READ,
+ (void *) arg, -len);
+ if (stat) return stat;
+ copy_from_user (buf, (void*)arg, -len);
+ }
+
+ if (len != 0) {
+ pc.buflen = len;
+ pc.buffer = buf;
+ }
+
+ stat = cdrom_queue_packet_command (drive, &pc);
+
+ if (len > 0)
+ copy_to_user ((void *)arg, buf, len);
+ }
+
+ return stat;
+ }
+#endif
- /* Check validity of requested track number. */
- ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
- if (track == CDROM_LEADOUT)
- *ent = &toc->ent[ntracks];
- else if (track < toc->hdr.first_track ||
- track > toc->hdr.last_track)
- return -EINVAL;
- else
- *ent = &toc->ent[track - toc->hdr.first_track];
+ default:
+ return -EINVAL;
+ }
- return 0;
}
-static int ide_cdrom_ioctl (ide_dev_t *dev, struct inode *inode,
- struct file *file, unsigned int cmd, unsigned long arg)
+
+static
+int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi,
+ unsigned int cmd, void *arg)
+
{
- switch (cmd)
- {
- case CDROMEJECT:
- return cdrom_eject (dev, 0);
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_info *info = drive->driver_data;
+
+ switch (cmd) {
+ case CDROMSUBCHNL: {
+ struct atapi_cdrom_subchnl scbuf;
+ int stat;
+ struct cdrom_subchnl *subchnl = (struct cdrom_subchnl *)arg;
+
+ stat = cdrom_read_subchannel (drive, 1, /* current position */
+ (char *)&scbuf, sizeof (scbuf),
+ NULL);
+ if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd) {
+ msf_from_bcd (&scbuf.acdsc_absaddr.msf);
+ msf_from_bcd (&scbuf.acdsc_reladdr.msf);
+ }
+ if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd)
+ scbuf.acdsc_trk = bcd2bin (scbuf.acdsc_trk);
+#endif /* not STANDARD_ATAPI */
+
+ subchnl->cdsc_absaddr.msf.minute =
+ scbuf.acdsc_absaddr.msf.minute;
+ subchnl->cdsc_absaddr.msf.second =
+ scbuf.acdsc_absaddr.msf.second;
+ subchnl->cdsc_absaddr.msf.frame =
+ scbuf.acdsc_absaddr.msf.frame;
+
+ subchnl->cdsc_reladdr.msf.minute =
+ scbuf.acdsc_reladdr.msf.minute;
+ subchnl->cdsc_reladdr.msf.second =
+ scbuf.acdsc_reladdr.msf.second;
+ subchnl->cdsc_reladdr.msf.frame =
+ scbuf.acdsc_reladdr.msf.frame;
+
+ subchnl->cdsc_audiostatus = scbuf.acdsc_audiostatus;
+ subchnl->cdsc_ctrl = scbuf.acdsc_ctrl;
+ subchnl->cdsc_trk = scbuf.acdsc_trk;
+ subchnl->cdsc_ind = scbuf.acdsc_ind;
+
+ return 0;
+ }
- case CDROMPAUSE:
- return cdrom_pause (dev, 1);
+ case CDROMREADTOCHDR: {
+ int stat;
+ struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg;
+ struct atapi_toc *toc;
- case CDROMRESUME:
- return cdrom_pause (dev, 0);
+ /* Make sure our saved TOC is valid. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
- case CDROMSTART:
- return cdrom_startstop (dev, 1);
+ toc = info->toc;
+ tochdr->cdth_trk0 = toc->hdr.first_track;
+ tochdr->cdth_trk1 = toc->hdr.last_track;
- case CDROMSTOP:
- return cdrom_startstop (dev, 0);
+ return 0;
+ }
- case CDROMPLAYMSF:
- {
- struct cdrom_msf msf;
- int stat, lba_start, lba_end;
+ case CDROMREADTOCENTRY: {
+ int stat;
+ struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg;
+ struct atapi_toc_entry *toce;
- stat = verify_area (VERIFY_READ, (void *)arg, sizeof (msf));
- if (stat) return stat;
+ stat = cdrom_get_toc_entry (drive, tocentry->cdte_track, &toce,
+ NULL);
+ if (stat) return stat;
- memcpy_fromfs (&msf, (void *) arg, sizeof(msf));
+ tocentry->cdte_ctrl = toce->control;
+ tocentry->cdte_adr = toce->adr;
+ tocentry->cdte_format = CDROM_LBA;
+ tocentry->cdte_addr.lba = toce->addr.lba;
- lba_start = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0,
- msf.cdmsf_frame0);
- lba_end = msf_to_lba (msf.cdmsf_min1, msf.cdmsf_sec1,
- msf.cdmsf_frame1) + 1;
+ return 0;
+ }
+
+ case CDROMPLAYMSF: {
+ struct cdrom_msf *msf = (struct cdrom_msf *) arg;
+ int lba_start, lba_end;
+
+ lba_start = msf_to_lba (msf->cdmsf_min0, msf->cdmsf_sec0,
+ msf->cdmsf_frame0);
+ lba_end = msf_to_lba (msf->cdmsf_min1, msf->cdmsf_sec1,
+ msf->cdmsf_frame1) + 1;
+
+ if (lba_end <= lba_start) return -EINVAL;
- if (lba_end <= lba_start) return -EINVAL;
+ return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+ }
- return cdrom_play_lba_range (dev, lba_start, lba_end);
- }
+ /* Like just about every other Linux cdrom driver, we ignore the
+ index part of the request here. */
+ case CDROMPLAYTRKIND: {
+ int stat, lba_start, lba_end;
+ struct cdrom_ti *ti = (struct cdrom_ti *)arg;
+ struct atapi_toc_entry *first_toc, *last_toc;
- /* Like just about every other Linux cdrom driver, we ignore the
- index part of the request here. */
- case CDROMPLAYTRKIND:
- {
- int stat, lba_start, lba_end;
- struct cdrom_ti ti;
- struct atapi_toc_entry *first_toc, *last_toc;
+ stat = cdrom_get_toc_entry (drive, ti->cdti_trk0, &first_toc,
+ NULL);
+ if (stat) return stat;
+ stat = cdrom_get_toc_entry (drive, ti->cdti_trk1, &last_toc,
+ NULL);
+ if (stat) return stat;
- stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ti));
- if (stat) return stat;
+ if (ti->cdti_trk1 != CDROM_LEADOUT) ++last_toc;
+ lba_start = first_toc->addr.lba;
+ lba_end = last_toc->addr.lba;
- memcpy_fromfs (&ti, (void *) arg, sizeof(ti));
+ if (lba_end <= lba_start) return -EINVAL;
- stat = cdrom_get_toc_entry (dev, ti.cdti_trk0, &first_toc);
- if (stat) return stat;
- stat = cdrom_get_toc_entry (dev, ti.cdti_trk1, &last_toc);
- if (stat) return stat;
+ return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+ }
- if (ti.cdti_trk1 != CDROM_LEADOUT) ++last_toc;
- lba_start = first_toc->lba;
- lba_end = last_toc->lba;
+ case CDROMVOLCTRL: {
+ struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
+ char buffer[24], mask[24];
+ int stat;
- if (lba_end <= lba_start) return -EINVAL;
+ stat = cdrom_mode_sense (drive, PAGE_AUDIO, 0, buffer,
+ sizeof (buffer), NULL);
+ if (stat) return stat;
+ stat = cdrom_mode_sense (drive, PAGE_AUDIO, 1, mask,
+ sizeof (buffer), NULL);
+ if (stat) return stat;
- return cdrom_play_lba_range (dev, lba_start, lba_end);
- }
+ buffer[1] = buffer[2] = 0;
- case CDROMREADTOCHDR:
- {
- int stat;
- struct cdrom_tochdr tochdr;
- struct atapi_toc *toc;
+ buffer[17] = volctrl->channel0 & mask[17];
+ buffer[19] = volctrl->channel1 & mask[19];
+ buffer[21] = volctrl->channel2 & mask[21];
+ buffer[23] = volctrl->channel3 & mask[23];
- stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tochdr));
- if (stat) return stat;
+ return cdrom_mode_select (drive, PAGE_AUDIO, buffer,
+ sizeof (buffer), NULL);
+ }
- /* Make sure our saved TOC is valid. */
- stat = cdrom_read_toc (dev);
- if (stat) return stat;
+ case CDROMVOLREAD: {
+ struct cdrom_volctrl *volctrl = (struct cdrom_volctrl *) arg;
+ char buffer[24];
+ int stat;
- toc = cdrom_info[DEV_HWIF][dev->select.b.drive].toc;
- tochdr.cdth_trk0 = toc->hdr.first_track;
- tochdr.cdth_trk1 = toc->hdr.last_track;
+ stat = cdrom_mode_sense (drive, PAGE_AUDIO, 0, buffer,
+ sizeof (buffer), NULL);
+ if (stat) return stat;
- memcpy_tofs ((void *) arg, &tochdr, sizeof (tochdr));
-
- return stat;
- }
+ volctrl->channel0 = buffer[17];
+ volctrl->channel1 = buffer[19];
+ volctrl->channel2 = buffer[21];
+ volctrl->channel3 = buffer[23];
- case CDROMREADTOCENTRY:
- {
- int stat;
- struct cdrom_tocentry tocentry;
- struct atapi_toc_entry *toce;
+ return 0;
+ }
- stat = verify_area (VERIFY_READ, (void *) arg, sizeof (tocentry));
- if (stat) return stat;
- stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tocentry));
- if (stat) return stat;
-
- memcpy_fromfs (&tocentry, (void *) arg, sizeof (tocentry));
-
- stat = cdrom_get_toc_entry (dev, tocentry.cdte_track, &toce);
- if (stat) return stat;
-
- tocentry.cdte_ctrl = toce->control;
- tocentry.cdte_adr = toce->adr;
-
- if (tocentry.cdte_format == CDROM_MSF)
- {
- /* convert to MSF */
- lba_to_msf (toce->lba,
- &tocentry.cdte_addr.msf.minute,
- &tocentry.cdte_addr.msf.second,
- &tocentry.cdte_addr.msf.frame);
- }
- else
- tocentry.cdte_addr.lba = toce->lba;
-
- memcpy_tofs ((void *) arg, &tocentry, sizeof (tocentry));
-
- return stat;
- }
+ case CDROMSTART:
+ return cdrom_startstop (drive, 1, NULL);
- case CDROMSUBCHNL:
- {
- char buffer[16];
- int stat, abs_lba, rel_lba;
- struct cdrom_subchnl subchnl;
-
- stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (subchnl));
- if (stat) return stat;
- stat = verify_area (VERIFY_READ, (void *) arg, sizeof (subchnl));
- if (stat) return stat;
-
- memcpy_fromfs (&subchnl, (void *) arg, sizeof (subchnl));
-
- stat = cdrom_read_subchannel (dev, buffer, sizeof (buffer));
- if (stat) return stat;
-
- abs_lba = *(int *)&buffer[8];
- rel_lba = *(int *)&buffer[12];
- byte_swap_long (&abs_lba);
- byte_swap_long (&rel_lba);
-
- if (subchnl.cdsc_format == CDROM_MSF)
- {
- lba_to_msf (abs_lba,
- &subchnl.cdsc_absaddr.msf.minute,
- &subchnl.cdsc_absaddr.msf.second,
- &subchnl.cdsc_absaddr.msf.frame);
- lba_to_msf (rel_lba,
- &subchnl.cdsc_reladdr.msf.minute,
- &subchnl.cdsc_reladdr.msf.second,
- &subchnl.cdsc_reladdr.msf.frame);
- }
- else
- {
- subchnl.cdsc_absaddr.lba = abs_lba;
- subchnl.cdsc_reladdr.lba = rel_lba;
- }
-
- subchnl.cdsc_audiostatus = buffer[1];
- subchnl.cdsc_ctrl = buffer[5] & 0xf;
- subchnl.cdsc_trk = buffer[6];
- subchnl.cdsc_ind = buffer[7];
-
- memcpy_tofs ((void *) arg, &subchnl, sizeof (subchnl));
-
- return stat;
- }
-
- case CDROMVOLCTRL:
- {
- struct cdrom_volctrl volctrl;
- char buffer[24], mask[24];
- int stat;
-
- stat = verify_area (VERIFY_READ, (void *) arg, sizeof (volctrl));
- if (stat) return stat;
- memcpy_fromfs (&volctrl, (void *) arg, sizeof (volctrl));
-
- stat = cdrom_mode_sense (dev, 0x0e, 0, buffer, sizeof (buffer));
- if (stat) return stat;
- stat = cdrom_mode_sense (dev, 0x0e, 1, mask , sizeof (buffer));
- if (stat) return stat;
-
- buffer[1] = buffer[2] = 0;
-
- buffer[17] = volctrl.channel0 & mask[17];
- buffer[19] = volctrl.channel1 & mask[19];
- buffer[21] = volctrl.channel2 & mask[21];
- buffer[23] = volctrl.channel3 & mask[23];
-
- return cdrom_mode_select (dev, 0x0e, buffer, sizeof (buffer));
- }
-
-#ifdef TEST
- case 0x1234:
- {
- int stat;
- struct packet_command pc;
-
- memset (&pc, 0, sizeof (pc));
-
- stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
- if (stat) return stat;
- memcpy_fromfs (&pc.c, (void *) arg, sizeof (pc.c));
-
- return cdrom_queue_packet_command (dev, &pc);
- }
-
- case 0x1235:
- {
- int stat;
- struct atapi_request_sense reqbuf;
-
- stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (reqbuf));
- if (stat) return stat;
-
- stat = cdrom_request_sense (dev, &reqbuf);
-
- memcpy_tofs ((void *) arg, &reqbuf, sizeof (reqbuf));
-
- return stat;
- }
+ case CDROMSTOP: {
+ int stat;
+
+ stat = cdrom_startstop (drive, 0, NULL);
+ if (stat) return stat;
+ /* pit says the Dolphin needs this. */
+ return cdrom_eject (drive, 1, NULL);
+ }
+
+ case CDROMPAUSE:
+ return cdrom_pause (drive, 1, NULL);
+
+ case CDROMRESUME:
+ return cdrom_pause (drive, 0, NULL);
+
+
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static
+int ide_cdrom_reset (struct cdrom_device_info *cdi)
+{
+
+/* This doesn't work reliably yet, and so it is currently just a stub. */
+
+#if 0
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct request req;
+ ide_init_drive_cmd (&req);
+ req.cmd = RESET_DRIVE_COMMAND;
+ return ide_do_drive_cmd (drive, &req, ide_wait);
#endif
- default:
- return -EPERM;
- }
+/* For now, just return 0, as if things worked... */
+ return 0;
+
+
+}
+
+
+static
+int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position)
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+ if (position) {
+ int stat = cdrom_lockdoor (drive, 0, NULL);
+ if (stat) return stat;
+ }
+
+ return cdrom_eject (drive, !position, NULL);
+}
+
+static
+int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock)
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ return cdrom_lockdoor (drive, lock, NULL);
}
-
+static
+int ide_cdrom_select_disc (struct cdrom_device_info *cdi, int slot)
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_info *info = drive->driver_data;
+
+ struct atapi_request_sense my_reqbuf;
+ int stat;
+ int nslots, curslot;
+
+ if ( ! CDROM_CONFIG_FLAGS (drive)->is_changer) {
+ printk ("%s: Not a changer.", drive->name);
+ return -EINVAL;
+ }
+
+#if ! STANDARD_ATAPI
+ if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+ nslots = 3;
+ curslot = CDROM_STATE_FLAGS (drive)->sanyo_slot;
+ if (curslot == 3)
+ curslot = 0;
+ }
+ else
+#endif /* not STANDARD_ATAPI */
+ {
+ stat = cdrom_read_changer_info (drive);
+ if (stat)
+ return stat;
+
+ nslots = info->changer_info->hdr.nslots;
+ curslot = info->changer_info->hdr.curslot;
+ }
+
+ if (slot == curslot)
+ return curslot;
+
+ if (slot == CDSL_CURRENT)
+ return curslot;
+
+ if (slot != CDSL_NONE && (slot < 0 || slot >= nslots))
+ return -EINVAL;
+
+ if (drive->usage > 1)
+ return -EBUSY;
+
+ stat = cdrom_check_status (drive, &my_reqbuf);
+ if (stat && my_reqbuf.sense_key == NOT_READY)
+ return (-ENOENT);
+
+ if (slot == CDSL_NONE) {
+ (void) cdrom_load_unload (drive, -1, NULL);
+ cdrom_saw_media_change (drive);
+ (void) cdrom_lockdoor (drive, 0, NULL);
+ return 0;
+ }
+ else {
+ if (
+#if ! STANDARD_ATAPI
+ CDROM_STATE_FLAGS (drive)->sanyo_slot == 0 &&
+#endif
+ info->changer_info->slots[slot].disc_present
+ == 0) {
+ printk ("%s: Requested slot does not contain a CD.\n",
+ drive->name);
+ return (-ENOENT);
+ }
+
+ stat = cdrom_load_unload (drive, slot, NULL);
+ cdrom_saw_media_change (drive);
+ if (stat)
+ return stat;
+
+ stat = cdrom_check_status (drive, &my_reqbuf);
+ if (stat && my_reqbuf.sense_key == NOT_READY)
+ return -ENOENT;
+
+ if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION) {
+ stat = cdrom_read_toc (drive, &my_reqbuf);
+ if (stat)
+ return stat;
+ return slot;
+ }
+ else
+ return stat;
+ }
+}
+
+
+static
+int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr)
+{
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_info *info = drive->driver_data;
+
+ if (slot_nr == CDSL_CURRENT) {
+
+ struct atapi_request_sense my_reqbuf;
+ int stat = cdrom_check_status (drive, &my_reqbuf);
+ if (stat == 0 || my_reqbuf.sense_key == UNIT_ATTENTION)
+ return CDS_DISC_OK;
+
+ if (my_reqbuf.sense_key == NOT_READY) {
+ /* With my NEC260, at least, we can't distinguish
+ between tray open and tray closed but no disc
+ inserted. */
+ return CDS_TRAY_OPEN;
+ }
+
+ return CDS_DRIVE_NOT_READY;
+ }
+
+#if ! STANDARD_ATAPI
+ else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0)
+ return CDS_NO_INFO;
+#endif /* not STANDARD_ATAPI */
+
+ else {
+ struct atapi_changer_info *ci;
+ int stat = cdrom_read_changer_info (drive);
+ if (stat < 0)
+ return stat;
+ ci = info->changer_info;
+
+ if (ci->slots[slot_nr].disc_present)
+ return CDS_DISC_OK;
+ else
+ return CDS_NO_DISC;
+ }
+}
+
+
+static
+int ide_cdrom_get_last_session (struct cdrom_device_info *cdi,
+ struct cdrom_multisession *ms_info)
+{
+ int stat;
+ struct atapi_toc *toc;
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_info *info = drive->driver_data;
+
+ /* Make sure the TOC information is valid. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = info->toc;
+ ms_info->addr.lba = toc->last_session_lba;
+ ms_info->xa_flag = toc->xa_flag;
+
+ return 0;
+}
+
+
+static
+int ide_cdrom_get_mcn (struct cdrom_device_info *cdi,
+ struct cdrom_mcn *mcn_info)
+{
+ int stat;
+ char mcnbuf[24];
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+
+ stat = cdrom_read_subchannel (drive, 2, /* get MCN */
+ mcnbuf, sizeof (mcnbuf),
+ NULL);
+ if (stat) return stat;
+
+ memcpy (mcn_info->medium_catalog_number, mcnbuf+9,
+ sizeof (mcn_info->medium_catalog_number)-1);
+ mcn_info->medium_catalog_number[sizeof (mcn_info->medium_catalog_number)-1]
+ = '\0';
+
+ return 0;
+}
+
+
+
/****************************************************************************
* Other driver requests (open, close, check media change).
*/
-static int cdrom_check_media_change (ide_dev_t *dev)
+static
+int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi,
+ int slot_nr)
{
- int retval;
+ ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+ struct cdrom_info *info = drive->driver_data;
- cdrom_check_status (dev);
+ int retval;
- retval = CDROM_FLAGS (dev)->media_changed;
- CDROM_FLAGS (dev)->media_changed = 0;
+ if (slot_nr == CDSL_CURRENT) {
+ (void) cdrom_check_status (drive, NULL);
+ retval = CDROM_STATE_FLAGS (drive)->media_changed;
+ CDROM_STATE_FLAGS (drive)->media_changed = 0;
+ }
- return retval;
-}
+#if ! STANDARD_ATAPI
+ else if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+ retval = 0;
+ }
+#endif /* not STANDARD_ATAPI */
+ else {
+ struct atapi_changer_info *ci;
+ int stat = cdrom_read_changer_info (drive);
+ if (stat < 0)
+ return stat;
+ ci = info->changer_info;
-static int
-cdrom_open (struct inode *ip, struct file *fp, ide_dev_t *dev)
-{
- /* no write access */
- if (fp->f_mode & 2) return -EROFS;
+ /* This test may be redundant with cdrom.c. */
+ if (slot_nr < 0 || slot_nr >= ci->hdr.nslots)
+ return -EINVAL;
-#if 0 /* With this, one cannot eject a disk with workman */
- /* If this is the first open, lock the door. */
- if (dev->usage == 1)
- (void) cdrom_lockdoor (dev, 1);
-#endif
+ retval = ci->slots[slot_nr].change;
+ }
- /* Should check that there's a disk in the drive? */
- return 0;
+ return retval;
+}
+
+
+static
+int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose)
+{
+ return 0;
}
@@ -2048,79 +2554,356 @@ cdrom_open (struct inode *ip, struct file *fp, ide_dev_t *dev)
* Close down the device. Invalidate all cached blocks.
*/
-static void
-cdrom_release (struct inode *inode, struct file *file, ide_dev_t *dev)
+static
+void ide_cdrom_release_real (struct cdrom_device_info *cdi)
{
- if (dev->usage == 0)
- {
- invalidate_buffers (inode->i_rdev);
-
-#if 0
- /* Unlock the door. */
- (void) cdrom_lockdoor (dev, 0);
-#endif
- }
}
-
+
/****************************************************************************
* Device initialization.
*/
+static
+struct cdrom_device_ops ide_cdrom_dops = {
+ ide_cdrom_open_real, /* open */
+ ide_cdrom_release_real, /* release */
+ ide_cdrom_drive_status, /* drive_status */
+ 0, /* disc_status */
+ ide_cdrom_check_media_change_real, /* media_changed */
+ ide_cdrom_tray_move, /* tray_move */
+ ide_cdrom_lock_door, /* lock_door */
+ 0, /* select_speed */
+ ide_cdrom_select_disc, /* select_disc */
+ ide_cdrom_get_last_session, /* get_last_session */
+ ide_cdrom_get_mcn, /* get_mcn */
+ ide_cdrom_reset, /* reset */
+ ide_cdrom_audio_ioctl, /* audio_ioctl */
+ ide_cdrom_dev_ioctl, /* dev_ioctl */
+ CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK
+ | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN
+ | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO, /* capability */
+ 0 /* n_minors */
+};
+
+
+static int ide_cdrom_register (ide_drive_t *drive, int nslots)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct cdrom_device_info *devinfo = &info->devinfo;
+ int minor = (drive->select.b.unit)<<PARTN_BITS;
+
+ devinfo->dev = MKDEV (HWIF(drive)->major, minor);
+ devinfo->ops = &ide_cdrom_dops;
+ devinfo->mask = 0;
+ *(int *)&devinfo->speed = 0;
+ *(int *)&devinfo->capacity = nslots;
+ devinfo->handle = (void *) drive;
+
+ return register_cdrom (devinfo, drive->name);
+}
+
+
+static
+int ide_cdrom_probe_capabilities (ide_drive_t *drive)
+{
+ int stat, nslots;
+ struct {
+ char pad[8];
+ struct atapi_capabilities_page cap;
+ } buf;
+
+ nslots = 0;
+
+ if (CDROM_CONFIG_FLAGS (drive)->nec260)
+ return nslots;
+
+ stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0,
+ (char *)&buf, sizeof (buf), NULL);
+ if (stat)
+ return nslots;
+
+ if (buf.cap.lock == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_STATE_FLAGS (drive)->sanyo_slot > 0) {
+ CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+ nslots = 3;
+ }
-static void cdrom_setup (ide_dev_t *dev)
+ else
+#endif /* not STANDARD_ATAPI */
+ if (buf.cap.mechtype == mechtype_individual_changer ||
+ buf.cap.mechtype == mechtype_cartridge_changer) {
+ struct atapi_mechstat_header mechbuf;
+
+ stat = cdrom_read_mech_status (drive, (char*)&mechbuf,
+ sizeof (mechbuf), NULL);
+ if (!stat) {
+ CDROM_CONFIG_FLAGS (drive)->is_changer = 1;
+ CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 1;
+ nslots = mechbuf.nslots;
+ }
+ }
+
+ if (CDROM_CONFIG_FLAGS (drive)->is_changer)
+ printk (" %s: ATAPI CDROM changer with %d slots\n",
+ drive->name, nslots);
+
+ return nslots;
+}
+
+static
+int ide_cdrom_setup (ide_drive_t *drive)
{
- /* Just guess at capacity for now. */
- ide_capacity[DEV_HWIF][dev->select.b.drive] = 0x1fffff;
+ struct cdrom_info *info = drive->driver_data;
+ int nslots;
+
+ kdev_t dev = MKDEV (HWIF (drive)->major,
+ drive->select.b.unit << PARTN_BITS);
+
+ set_device_ro (dev, 1);
+ blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] =
+ CD_FRAMESIZE;
+
+ drive->special.all = 0;
+ drive->ready_stat = 0;
+
+ CDROM_STATE_FLAGS (drive)->media_changed = 0;
+ CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+ CDROM_STATE_FLAGS (drive)->door_locked = 0;
+
+#if NO_DOOR_LOCKING
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+#else
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
+#endif
+
+ if (drive->id != NULL)
+ CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
+ ((drive->id->config & 0x0060) == 0x20);
+ else
+ CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
+
+ CDROM_CONFIG_FLAGS (drive)->is_changer = 0;
+ CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0;
+
+#if ! STANDARD_ATAPI
+ /* by default Sanyo 3 CD changer support is turned off and
+ ATAPI Rev 2.2+ standard support for CD changers is used */
+ CDROM_STATE_FLAGS (drive)->sanyo_slot = 0;
+
+ CDROM_CONFIG_FLAGS (drive)->nec260 = 0;
+ CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 0;
+
+ if (drive->id != NULL) {
+ if (strcmp (drive->id->model, "V003S0DS") == 0 &&
+ drive->id->fw_rev[4] == '1' &&
+ drive->id->fw_rev[6] <= '2') {
+ /* Vertos 300.
+ Some versions of this drive like to talk BCD. */
+ CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+ }
+
+ else if (strcmp (drive->id->model, "V006E0DS") == 0 &&
+ drive->id->fw_rev[4] == '1' &&
+ drive->id->fw_rev[6] <= '2') {
+ /* Vertos 600 ESD. */
+ CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
+ }
+
+ else if (strcmp (drive->id->model,
+ "NEC CD-ROM DRIVE:260") == 0 &&
+ strcmp (drive->id->fw_rev, "1.01") == 0) {
+ /* Old NEC260 (not R).
+ This drive was released before the 1.2 version
+ of the spec. */
+ CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->nec260 = 1;
+ }
+
+ else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 &&
+ strcmp (drive->id->fw_rev, "A1.1") == 0) {
+ /* Wearnes */
+ CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1;
+ }
+
+ /* Sanyo 3 CD changer uses a non-standard command
+ for CD changing. */
+ else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) ||
+ (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0)) {
+ /* uses CD in slot 0 when value is set to 3 */
+ CDROM_STATE_FLAGS (drive)->sanyo_slot = 3;
+ }
+
+ }
+#endif /* not STANDARD_ATAPI */
- ide_blksizes[DEV_HWIF][dev->select.b.drive << PARTN_BITS] = CD_FRAMESIZE;
+ info->toc = NULL;
+ info->sector_buffer = NULL;
+ info->sector_buffered = 0;
+ info->nsectors_buffered = 0;
+ info->changer_info = NULL;
- dev->special.all = 0;
+ nslots = ide_cdrom_probe_capabilities (drive);
- CDROM_FLAGS (dev)->media_changed = 0;
- CDROM_FLAGS (dev)->toc_valid = 0;
+ if (ide_cdrom_register (drive, nslots)) {
+ printk ("%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name);
+ info->devinfo.handle = NULL;
+ return 1;
+ }
+ return 0;
+}
- CDROM_FLAGS (dev)->no_playaudio12 = 0;
- CDROM_FLAGS (dev)->no_lba_toc = 0;
- CDROM_FLAGS (dev)->msf_as_bcd = 0;
- CDROM_FLAGS (dev)->drq_interrupt = ((dev->id->config & 0x0060) == 0x20);
+/* Forwarding functions to generic routines. */
- /* Accommodate some broken drives... */
- if (strcmp (dev->id->model, "CD220E") == 0) /* Creative Labs */
- CDROM_FLAGS (dev)->no_lba_toc = 1;
+int ide_cdrom_ioctl (ide_drive_t *drive,
+ struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return cdrom_fops.ioctl (inode, file, cmd, arg);
+}
- else if (strcmp (dev->id->model, "TO-ICSLYAL") == 0 || /* Acer CD525E */
- strcmp (dev->id->model, "OTI-SCYLLA") == 0)
- CDROM_FLAGS (dev)->no_lba_toc = 1;
+int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
+{
+ int rc;
- else if (strcmp (dev->id->model, "CDA26803I SE") == 0) /* Aztech */
- {
- CDROM_FLAGS (dev)->no_lba_toc = 1;
+ MOD_INC_USE_COUNT;
+ rc = cdrom_fops.open (ip, fp);
+ if (rc) {
+ drive->usage--;
+ MOD_DEC_USE_COUNT;
+ }
+ return rc;
+}
+
+void ide_cdrom_release (struct inode *inode, struct file *file,
+ ide_drive_t *drive)
+{
+ cdrom_fops.release (inode, file);
+ MOD_DEC_USE_COUNT;
+}
+
+int ide_cdrom_check_media_change (ide_drive_t *drive)
+{
+ return cdrom_fops.check_media_change
+ (MKDEV (HWIF (drive)->major,
+ (drive->select.b.unit)<<PARTN_BITS));
+}
- /* This drive _also_ does not implement PLAYAUDIO12 correctly. */
- CDROM_FLAGS (dev)->no_playaudio12 = 1;
- }
- cdrom_info[DEV_HWIF][dev->select.b.drive].toc = NULL;
- cdrom_info[DEV_HWIF][dev->select.b.drive].sector_buffer = NULL;
- cdrom_info[DEV_HWIF][dev->select.b.drive].sector_buffered = 0;
- cdrom_info[DEV_HWIF][dev->select.b.drive].nsectors_buffered = 0;
+int ide_cdrom_cleanup(ide_drive_t *drive)
+{
+ struct cdrom_info *info = drive->driver_data;
+ struct cdrom_device_info *devinfo = &info->devinfo;
+
+ if (ide_unregister_subdriver (drive))
+ return 1;
+ if (info->sector_buffer != NULL)
+ kfree (info->sector_buffer);
+ if (info->toc != NULL)
+ kfree (info->toc);
+ if (devinfo->handle == drive && unregister_cdrom (devinfo))
+ printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name);
+ kfree (info);
+ drive->driver_data = NULL;
+ return 0;
}
+int ide_cdrom_init (void);
+static ide_module_t ide_cdrom_module = {
+ IDE_DRIVER_MODULE,
+ ide_cdrom_init,
+ NULL
+};
+
+static ide_driver_t ide_cdrom_driver = {
+ ide_cdrom, /* media */
+ 0, /* busy */
+ 1, /* supports_dma */
+ ide_cdrom_cleanup, /* cleanup */
+ ide_do_rw_cdrom, /* do_request */
+ NULL, /* ??? or perhaps
+ cdrom_end_request? */
+ ide_cdrom_ioctl, /* ioctl */
+ ide_cdrom_open, /* open */
+ ide_cdrom_release, /* release */
+ ide_cdrom_check_media_change, /* media_change */
+ NULL, /* pre_reset */
+ NULL, /* capacity */
+ NULL /* special */
+};
-#undef MIN
-#undef SECTOR_SIZE
-#undef SECTOR_BITS
+#ifdef MODULE
+int init_module (void)
+{
+ return ide_cdrom_init();
+}
+void cleanup_module(void)
+{
+ ide_drive_t *drive;
+ int failed = 0;
+
+ while ((drive = ide_scan_devices (ide_cdrom, &ide_cdrom_driver, failed)) != NULL)
+ if (ide_cdrom_cleanup (drive)) {
+ printk ("%s: cleanup_module() called while still busy\n", drive->name);
+ failed++;
+ }
+ ide_unregister_module (&ide_cdrom_module);
+}
+#endif /* MODULE */
+
+int ide_cdrom_init (void)
+{
+ ide_drive_t *drive;
+ struct cdrom_info *info;
+ int failed = 0;
+
+ MOD_INC_USE_COUNT;
+ while ((drive = ide_scan_devices (ide_cdrom, NULL, failed++)) != NULL) {
+ info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
+ if (info == NULL) {
+ printk ("%s: Can't allocate a cdrom structure\n", drive->name);
+ continue;
+ }
+ if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) {
+ printk ("%s: Failed to register the driver with ide.c\n", drive->name);
+ kfree (info);
+ continue;
+ }
+ memset (info, 0, sizeof (struct cdrom_info));
+ drive->driver_data = info;
+ DRIVER(drive)->busy++;
+ if (ide_cdrom_setup (drive)) {
+ DRIVER(drive)->busy--;
+ if (ide_cdrom_cleanup (drive))
+ printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name);
+ continue;
+ }
+ DRIVER(drive)->busy--;
+ failed--;
+ }
+ ide_register_module(&ide_cdrom_module);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+/*==========================================================================*/
/*
- * TODO:
- * Read actual disk capacity.
- * Multisession support.
- * Direct reading of audio data.
- * Eject-on-dismount.
- * Lock door while there's a mounted volume.
- * Establish interfaces for an IDE port driver, and break out the cdrom
- * code into a loadable module.
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
*/
-
diff --git a/drivers/block/ide-cd.h b/drivers/block/ide-cd.h
new file mode 100644
index 000000000..4071929ab
--- /dev/null
+++ b/drivers/block/ide-cd.h
@@ -0,0 +1,553 @@
+#ifndef _IDE_CD_H
+#define _IDE_CD_H
+/*
+ * linux/drivers/block/ide_modes.h
+ *
+ * Copyright (C) 1996 Erik Andersen
+ */
+
+/* Turn this on to have the driver print out the meanings of the
+ ATAPI error codes. This will use up additional kernel-space
+ memory, though. */
+
+#ifndef VERBOSE_IDE_CD_ERRORS
+#define VERBOSE_IDE_CD_ERRORS 0
+#endif
+
+
+/* Turn this on to have the driver print out the meanings of the
+ ATAPI error codes. This will use up additional kernel-space
+ memory, though. */
+
+#ifndef VERBOSE_IDE_CD_ERRORS
+#define VERBOSE_IDE_CD_ERRORS 0
+#endif
+
+
+/* Turning this on will remove code to work around various nonstandard
+ ATAPI implementations. If you know your drive follows the standard,
+ this will give you a slightly smaller kernel. */
+
+#ifndef STANDARD_ATAPI
+#define STANDARD_ATAPI 0
+#endif
+
+
+/* Turning this on will disable the door-locking functionality.
+ This is apparently needed for supermount. */
+
+#ifndef NO_DOOR_LOCKING
+#define NO_DOOR_LOCKING 0
+#endif
+
+
+/* Size of buffer to allocate, in blocks, for audio reads. */
+
+#ifndef CDROM_NBLOCKS_BUFFER
+#define CDROM_NBLOCKS_BUFFER 8
+#endif
+
+
+/************************************************************************/
+
+#define SECTOR_SIZE 512
+#define SECTOR_BITS 9
+#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE)
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* special command codes for strategy routine. */
+#define PACKET_COMMAND 4315
+#define REQUEST_SENSE_COMMAND 4316
+#define RESET_DRIVE_COMMAND 4317
+
+/*
+ * For controlling drive spindown time.
+ */
+#define CDROMGETSPINDOWN 0x531d
+#define CDROMSETSPINDOWN 0x531e
+
+
+/* Some ATAPI command opcodes (just like SCSI).
+ (Some other cdrom-specific codes are in cdrom.h.) */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define START_STOP 0x1b
+#define ALLOW_MEDIUM_REMOVAL 0x1e
+#define READ_CAPACITY 0x25
+#define READ_10 0x28
+#define MODE_SENSE_10 0x5a
+#define MODE_SELECT_10 0x55
+#define READ_CD 0xbe
+
+#define LOAD_UNLOAD 0xa6
+#define MECHANISM_STATUS 0xbd
+
+
+/* Page codes for mode sense/set */
+
+#define PAGE_READERR 0x01
+#define PAGE_CDROM 0x0d
+#define PAGE_AUDIO 0x0e
+#define PAGE_CAPABILITIES 0x2a
+#define PAGE_ALL 0x3f
+
+
+/* ATAPI sense keys (mostly copied from scsi.h). */
+
+#define NO_SENSE 0x00
+#define RECOVERED_ERROR 0x01
+#define NOT_READY 0x02
+#define MEDIUM_ERROR 0x03
+#define HARDWARE_ERROR 0x04
+#define ILLEGAL_REQUEST 0x05
+#define UNIT_ATTENTION 0x06
+#define DATA_PROTECT 0x07
+#define ABORTED_COMMAND 0x0b
+#define MISCOMPARE 0x0e
+
+/* We want some additional flags for cd-rom drives.
+ To save space in the ide_drive_t struct, use some fields which
+ doesn't make sense for cd-roms -- `bios_cyl' and `bios_head'. */
+
+/* Configuration flags. These describe the capabilities of the drive.
+ They generally do not change after initialization, unless we learn
+ more about the drive from stuff failing. */
+struct ide_cd_config_flags {
+ __u8 drq_interrupt : 1; /* Device sends an interrupt when ready
+ for a packet command. */
+ __u8 no_doorlock : 1; /* Drive cannot lock the door. */
+ __u8 nec260 : 1; /* Drive is a pre-1.2 NEC 260 drive. */
+ __u8 playmsf_as_bcd : 1; /* PLAYMSF command takes BCD args. */
+ __u8 tocaddr_as_bcd : 1; /* TOC addresses are in BCD. */
+ __u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */
+ __u8 subchan_as_bcd : 1; /* Subchannel info is in BCD. */
+ __u8 is_changer : 1; /* Drive is a changer. */
+ __u8 supp_disc_present: 1; /* Changer can report exact contents
+ of slots. */
+ __u8 reserved : 7;
+};
+#define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_cyl))
+
+
+/* State flags. These give information about the current state of the
+ drive, and will change during normal operation. */
+struct ide_cd_state_flags {
+ __u8 media_changed : 1; /* Driver has noticed a media change. */
+ __u8 toc_valid : 1; /* Saved TOC information is current. */
+ __u8 door_locked : 1; /* We think that the drive door is locked. */
+ __u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */
+ __u8 reserved : 3;
+};
+#define CDROM_STATE_FLAGS(drive) ((struct ide_cd_state_flags *)&((drive)->bios_head))
+
+
+struct atapi_request_sense {
+ unsigned char error_code : 7;
+ unsigned char valid : 1;
+ byte reserved1;
+ unsigned char sense_key : 4;
+ unsigned char reserved2 : 1;
+ unsigned char ili : 1;
+ unsigned char reserved3 : 2;
+ byte info[4];
+ byte sense_len;
+ byte command_info[4];
+ byte asc;
+ byte ascq;
+ byte fru;
+ byte sense_key_specific[3];
+};
+
+struct packet_command {
+ char *buffer;
+ int buflen;
+ int stat;
+ struct atapi_request_sense *sense_data;
+ unsigned char c[12];
+};
+
+/* Structure of a MSF cdrom address. */
+struct atapi_msf {
+ byte reserved;
+ byte minute;
+ byte second;
+ byte frame;
+};
+
+/* Space to hold the disk TOC. */
+#define MAX_TRACKS 99
+struct atapi_toc_header {
+ unsigned short toc_length;
+ byte first_track;
+ byte last_track;
+};
+
+struct atapi_toc_entry {
+ byte reserved1;
+ unsigned control : 4;
+ unsigned adr : 4;
+ byte track;
+ byte reserved2;
+ union {
+ unsigned lba;
+ struct atapi_msf msf;
+ } addr;
+};
+
+struct atapi_toc {
+ int last_session_lba;
+ int xa_flag;
+ unsigned capacity;
+ struct atapi_toc_header hdr;
+ struct atapi_toc_entry ent[MAX_TRACKS+1];
+ /* One extra for the leadout. */
+};
+
+
+/* This structure is annoyingly close to, but not identical with,
+ the cdrom_subchnl structure from cdrom.h. */
+struct atapi_cdrom_subchnl {
+ u_char acdsc_reserved;
+ u_char acdsc_audiostatus;
+ u_short acdsc_length;
+ u_char acdsc_format;
+
+ u_char acdsc_adr: 4;
+ u_char acdsc_ctrl: 4;
+ u_char acdsc_trk;
+ u_char acdsc_ind;
+ union {
+ struct atapi_msf msf;
+ int lba;
+ } acdsc_absaddr;
+ union {
+ struct atapi_msf msf;
+ int lba;
+ } acdsc_reladdr;
+};
+
+
+typedef enum {
+ mechtype_caddy = 0,
+ mechtype_tray = 1,
+ mechtype_popup = 2,
+ mechtype_individual_changer = 4,
+ mechtype_cartridge_changer = 5
+} mechtype_t;
+
+
+struct atapi_capabilities_page {
+ unsigned page_code : 6;
+ unsigned reserved1 : 1;
+ unsigned parameters_saveable : 1;
+
+ byte page_length;
+
+ /* Drive supports read from CD-R discs (orange book, part II) */
+ unsigned cd_r_read : 1; /* reserved in 1.2 */
+ /* Drive supports read from CD-E discs (orange book, part III) */
+ unsigned cd_e_read : 1; /* reserved in 1.2 */
+ /* Drive supports reading CD-R discs with addressing method 2 */
+ unsigned method2 : 1; /* reserved in 1.2 */
+ unsigned reserved2 : 5;
+
+ /* Drive supports write to CD-R discs (orange book, part II) */
+ unsigned cd_r_write : 1; /* reserved in 1.2 */
+ /* Drive supports write to CD-E discs (orange book, part III) */
+ unsigned cd_e_write : 1; /* reserved in 1.2 */
+ unsigned reserved3 : 6;
+
+ /* Drive supports audio play operations. */
+ unsigned audio_play : 1;
+ /* Drive can deliver a composite audio/video data stream. */
+ unsigned composite : 1;
+ /* Drive supports digital output on port 1. */
+ unsigned digport1 : 1;
+ /* Drive supports digital output on port 2. */
+ unsigned digport2 : 1;
+ /* Drive can read mode 2, form 1 (XA) data. */
+ unsigned mode2_form1 : 1;
+ /* Drive can read mode 2, form 2 data. */
+ unsigned mode2_form2 : 1;
+ /* Drive can read multisession discs. */
+ unsigned multisession : 1;
+ unsigned reserved4 : 1;
+
+ /* Drive can read Red Book audio data. */
+ unsigned cdda : 1;
+ /* Drive can continue a read cdda operation from a loss of streaming.*/
+ unsigned cdda_accurate : 1;
+ /* Subchannel reads can return combined R-W information. */
+ unsigned rw_supported : 1;
+ /* R-W data will be returned deinterleaved and error corrected. */
+ unsigned rw_corr : 1;
+ /* Drive supports C2 error pointers. */
+ unsigned c2_pointers : 1;
+ /* Drive can return International Standard Recording Code info. */
+ unsigned isrc : 1;
+ /* Drive can return Media Catalog Number (UPC) info. */
+ unsigned upc : 1;
+ unsigned reserved5 : 1;
+
+ /* Drive can lock the door. */
+ unsigned lock : 1;
+ /* Present state of door lock. */
+ unsigned lock_state : 1;
+ /* State of prevent/allow jumper. */
+ unsigned prevent_jumper : 1;
+ /* Drive can eject a disc or changer cartridge. */
+ unsigned eject : 1;
+ unsigned reserved6 : 1;
+ /* Drive mechanism types. */
+ mechtype_t mechtype : 3;
+
+ /* Audio level for each channel can be controlled independently. */
+ unsigned separate_volume : 1;
+ /* Audio for each channel can be muted independently. */
+ unsigned separate_mute : 1;
+ /* Changer can report exact contents of slots. */
+ unsigned disc_present : 1; /* reserved in 1.2 */
+ /* Drive supports software slot selection. */
+ unsigned sss : 1; /* reserved in 1.2 */
+ unsigned reserved7 : 4;
+
+ /* Note: the following four fields are returned in big-endian form. */
+ /* Maximum speed (in kB/s). */
+ unsigned short maxspeed;
+ /* Number of discrete volume levels. */
+ unsigned short n_vol_levels;
+ /* Size of cache in drive, in kB. */
+ unsigned short buffer_size;
+ /* Current speed (in kB/s). */
+ unsigned short curspeed;
+
+ /* Truncate the structure here, so we don't have headaches reading
+ from older drives. */
+};
+
+
+struct atapi_mechstat_header {
+ unsigned curslot : 5;
+ unsigned changer_state : 2;
+ unsigned fault : 1;
+
+ unsigned reserved1 : 5;
+ unsigned mech_state : 3;
+
+ byte curlba[3];
+ byte nslots;
+ unsigned short slot_tablelen;
+};
+
+
+struct atapi_slot {
+ unsigned change : 1;
+ unsigned reserved1 : 6;
+ unsigned disc_present : 1;
+
+ byte reserved2[3];
+};
+
+
+struct atapi_changer_info {
+ struct atapi_mechstat_header hdr;
+ struct atapi_slot slots[0];
+};
+
+
+/* Extra per-device info for cdrom drives. */
+struct cdrom_info {
+
+ /* Buffer for table of contents. NULL if we haven't allocated
+ a TOC buffer for this device yet. */
+
+ struct atapi_toc *toc;
+
+ /* Sector buffer. If a read request wants only the first part
+ of a cdrom block, we cache the rest of the block here,
+ in the expectation that that data is going to be wanted soon.
+ SECTOR_BUFFERED is the number of the first buffered sector,
+ and NSECTORS_BUFFERED is the number of sectors in the buffer.
+ Before the buffer is allocated, we should have
+ SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
+
+ unsigned long sector_buffered;
+ unsigned long nsectors_buffered;
+ char *sector_buffer;
+
+ /* The result of the last successful request sense command
+ on this device. */
+ struct atapi_request_sense sense_data;
+
+ struct request request_sense_request;
+ struct packet_command request_sense_pc;
+ int dma;
+ /* Buffer to hold mechanism status and changer slot table. */
+ struct atapi_changer_info *changer_info;
+
+
+ /* Per-device info needed by cdrom.c generic driver. */
+ struct cdrom_device_info devinfo;
+};
+
+
+#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
+
+
+/****************************************************************************
+ * Descriptions of ATAPI error codes.
+ */
+
+#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
+
+#if VERBOSE_IDE_CD_ERRORS
+
+/* From Table 124 of the ATAPI 1.2 spec.
+ Unchanged in Table 140 of the ATAPI 2.6 draft standard. */
+
+char *sense_key_texts[16] = {
+ "No sense data",
+ "Recovered error",
+ "Not ready",
+ "Medium error",
+ "Hardware error",
+ "Illegal request",
+ "Unit attention",
+ "Data protect",
+ "(reserved)",
+ "(reserved)",
+ "(reserved)",
+ "Aborted command",
+ "(reserved)",
+ "(reserved)",
+ "Miscompare",
+ "(reserved)",
+};
+
+
+/* From Table 125 of the ATAPI 1.2 spec.,
+ with additions from Tables 141 and 142 of the ATAPI 2.6 draft standard. */
+
+struct {
+ short asc_ascq;
+ char *text;
+} sense_data_texts[] = {
+ { 0x0000, "No additional sense information" },
+
+ { 0x0011, "Audio play operation in progress" },
+ { 0x0012, "Audio play operation paused" },
+ { 0x0013, "Audio play operation successfully completed" },
+ { 0x0014, "Audio play operation stopped due to error" },
+ { 0x0015, "No current audio status to return" },
+
+ { 0x0100, "Mechanical positioning or changer error" },
+
+ { 0x0200, "No seek complete" },
+
+ { 0x0400, "Logical unit not ready - cause not reportable" },
+ { 0x0401,
+ "Logical unit not ready - in progress (sic) of becoming ready" },
+ { 0x0402, "Logical unit not ready - initializing command required" },
+ { 0x0403, "Logical unit not ready - manual intervention required" },
+
+ { 0x0501, "Media load - eject failed" },
+
+ { 0x0600, "No reference position found" },
+
+ { 0x0900, "Track following error" },
+ { 0x0901, "Tracking servo failure" },
+ { 0x0902, "Focus servo failure" },
+ { 0x0903, "Spindle servo failure" },
+
+ { 0x1100, "Unrecovered read error" },
+ { 0x1106, "CIRC unrecovered error" },
+
+ { 0x1500, "Random positioning error" },
+ { 0x1501, "Mechanical positioning or changer error" },
+ { 0x1502, "Positioning error detected by read of medium" },
+
+ { 0x1700, "Recovered data with no error correction applied" },
+ { 0x1701, "Recovered data with retries" },
+ { 0x1702, "Recovered data with positive head offset" },
+ { 0x1703, "Recovered data with negative head offset" },
+ { 0x1704, "Recovered data with retries and/or CIRC applied" },
+ { 0x1705, "Recovered data using previous sector ID" },
+
+ { 0x1800, "Recovered data with error correction applied" },
+ { 0x1801, "Recovered data with error correction and retries applied" },
+ { 0x1802, "Recovered data - the data was auto-reallocated" },
+ { 0x1803, "Recovered data with CIRC" },
+ { 0x1804, "Recovered data with L-EC" },
+ /* Following two not in 2.6. */
+ { 0x1805, "Recovered data - recommend reassignment" },
+ { 0x1806, "Recovered data - recommend rewrite" },
+
+ { 0x1a00, "Parameter list length error" },
+
+ { 0x2000, "Invalid command operation code" },
+
+ { 0x2100, "Logical block address out of range" },
+
+ { 0x2400, "Invalid field in command packet" },
+
+ { 0x2600, "Invalid field in parameter list" },
+ { 0x2601, "Parameter not supported" },
+ { 0x2602, "Parameter value invalid" },
+ /* Following code not in 2.6. */
+ { 0x2603, "Threshold parameters not supported" },
+
+ { 0x2800, "Not ready to ready transition, medium may have changed" },
+
+ { 0x2900, "Power on, reset or bus device reset occurred" },
+
+ { 0x2a00, "Parameters changed" },
+ { 0x2a01, "Mode parameters changed" },
+
+ { 0x3000, "Incompatible medium installed" },
+ { 0x3001, "Cannot read medium - unknown format" },
+ { 0x3002, "Cannot read medium - incompatible format" },
+
+ /* Following code not in 2.6. */
+ { 0x3700, "Rounded parameter" },
+
+ { 0x3900, "Saving parameters not supported" },
+
+ { 0x3a00, "Medium not present" },
+
+ { 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
+ { 0x3f01, "Microcode has been changed" },
+ /* Following two not in 2.6. */
+ { 0x3f02, "Changed operating definition" },
+ { 0x3f03, "Inquiry data has changed" },
+
+ { 0x4000, "Diagnostic failure on component (ASCQ)" },
+
+ { 0x4400, "Internal ATAPI CD-ROM drive failure" },
+
+ { 0x4e00, "Overlapped commands attempted" },
+
+ { 0x5300, "Media load or eject failed" },
+ { 0x5302, "Medium removal prevented" },
+
+ { 0x5700, "Unable to recover table of contents" },
+
+ { 0x5a00, "Operator request or state change input (unspecified)" },
+ { 0x5a01, "Operator medium removal request" },
+
+ /* Following two not in 2.6. */
+ { 0x5b00, "Threshold condition met" },
+ { 0x5c00, "Status change" },
+
+ { 0x6300, "End of user area encountered on this track" },
+
+ { 0x6400, "Illegal mode for this track" },
+
+ { 0xb900, "Play operation oborted (sic)" },
+
+ { 0xbf00, "Loss of streaming" },
+};
+#endif
+
+
+#endif /* _IDE_CD_H */
diff --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c
new file mode 100644
index 000000000..97eef358c
--- /dev/null
+++ b/drivers/block/ide-disk.c
@@ -0,0 +1,660 @@
+/*
+ * linux/drivers/block/ide-disk.c Version 1.0 Oct 6, 1996
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Maintained by Mark Lord <mlord@pobox.com>
+ * and Gadi Oxman <gadio@netvision.net.il>
+ *
+ * This is the IDE/ATA disk driver, as evolved from hd.c and ide.c.
+ *
+ * From hd.c:
+ * |
+ * | It traverses the request-list, using interrupts to jump between functions.
+ * | As nearly all functions can be called within interrupts, we may not sleep.
+ * | Special care is recommended. Have Fun!
+ * |
+ * | modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ * |
+ * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * | in the early extended-partition checks and added DM partitions.
+ * |
+ * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
+ * |
+ * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ * | and general streamlining by Mark Lord (mlord@pobox.com).
+ *
+ * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
+ *
+ * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg)
+ * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
+ * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
+ *
+ * This was a rewrite of just about everything from hd.c, though some original
+ * code is still sprinkled about. Think of it as a major evolution, with
+ * inspiration from lots of linux users, esp. hamish@zot.apana.org.au
+ *
+ * Version 1.0 move disk only code from ide.c to ide-disk.c
+ * support optional byte-swapping of all data
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include "ide.h"
+
+static void idedisk_bswap_data (void *buffer, int wcount)
+{
+ u16 *p = buffer;
+
+ while (wcount--) {
+ *p++ = *p << 8 | *p >> 8;
+ *p++ = *p << 8 | *p >> 8;
+ }
+}
+
+static inline void idedisk_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ ide_input_data(drive, buffer, wcount);
+ if (drive->bswap)
+ idedisk_bswap_data(buffer, wcount);
+}
+
+static inline void idedisk_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ ide_output_data(drive, buffer, wcount);
+ if (drive->bswap)
+ idedisk_bswap_data(buffer, wcount);
+}
+
+/*
+ * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
+ * value for this drive (from its reported identification information).
+ *
+ * Returns: 1 if lba_capacity looks sensible
+ * 0 otherwise
+ */
+static int lba_capacity_is_ok (struct hd_driveid *id)
+{
+ unsigned long lba_sects = id->lba_capacity;
+ unsigned long chs_sects = id->cyls * id->heads * id->sectors;
+ unsigned long _10_percent = chs_sects / 10;
+
+ /* perform a rough sanity check on lba_sects: within 10% is "okay" */
+ if ((lba_sects - chs_sects) < _10_percent)
+ return 1; /* lba_capacity is good */
+
+ /* some drives have the word order reversed */
+ lba_sects = (lba_sects << 16) | (lba_sects >> 16);
+ if ((lba_sects - chs_sects) < _10_percent) {
+ id->lba_capacity = lba_sects; /* fix it */
+ return 1; /* lba_capacity is (now) good */
+ }
+ return 0; /* lba_capacity value is bad */
+}
+
+/*
+ * read_intr() is the handler for disk read/multread interrupts
+ */
+static void read_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ unsigned int msect, nsect;
+ struct request *rq;
+
+ if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+ ide_error(drive, "read_intr", stat);
+ return;
+ }
+ msect = drive->mult_count;
+read_next:
+ rq = HWGROUP(drive)->rq;
+ if (msect) {
+ if ((nsect = rq->current_nr_sectors) > msect)
+ nsect = msect;
+ msect -= nsect;
+ } else
+ nsect = 1;
+ idedisk_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG
+ printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
+ drive->name, rq->sector, rq->sector+nsect-1,
+ (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
+#endif
+ rq->sector += nsect;
+ rq->buffer += nsect<<9;
+ rq->errors = 0;
+ i = (rq->nr_sectors -= nsect);
+ if ((rq->current_nr_sectors -= nsect) <= 0)
+ ide_end_request(1, HWGROUP(drive));
+ if (i > 0) {
+ if (msect)
+ goto read_next;
+ ide_set_handler (drive, &read_intr, WAIT_CMD);
+ }
+}
+
+/*
+ * write_intr() is the handler for disk write interrupts
+ */
+static void write_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = hwgroup->rq;
+
+ if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+#ifdef DEBUG
+ printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
+ drive->name, rq->sector, (unsigned long) rq->buffer,
+ rq->nr_sectors-1);
+#endif
+ if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
+ rq->sector++;
+ rq->buffer += 512;
+ rq->errors = 0;
+ i = --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ if (rq->current_nr_sectors <= 0)
+ ide_end_request(1, hwgroup);
+ if (i > 0) {
+ idedisk_output_data (drive, rq->buffer, SECTOR_WORDS);
+ ide_set_handler (drive, &write_intr, WAIT_CMD);
+ }
+ return;
+ }
+ }
+ ide_error(drive, "write_intr", stat);
+}
+
+/*
+ * ide_multwrite() transfers a block of up to mcount sectors of data
+ * to a drive as part of a disk multiple-sector write operation.
+ */
+void ide_multwrite (ide_drive_t *drive, unsigned int mcount)
+{
+ struct request *rq = &HWGROUP(drive)->wrq;
+
+ do {
+ unsigned int nsect = rq->current_nr_sectors;
+ if (nsect > mcount)
+ nsect = mcount;
+ mcount -= nsect;
+
+ idedisk_output_data(drive, rq->buffer, nsect<<7);
+#ifdef DEBUG
+ printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
+ drive->name, rq->sector, (unsigned long) rq->buffer,
+ nsect, rq->nr_sectors - nsect);
+#endif
+ if ((rq->nr_sectors -= nsect) <= 0)
+ break;
+ if ((rq->current_nr_sectors -= nsect) == 0) {
+ if ((rq->bh = rq->bh->b_reqnext) != NULL) {
+ rq->current_nr_sectors = rq->bh->b_size>>9;
+ rq->buffer = rq->bh->b_data;
+ } else {
+ panic("%s: buffer list corrupted\n", drive->name);
+ break;
+ }
+ } else {
+ rq->buffer += nsect << 9;
+ }
+ } while (mcount);
+}
+
+/*
+ * multwrite_intr() is the handler for disk multwrite interrupts
+ */
+static void multwrite_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = &hwgroup->wrq;
+
+ if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+ if (stat & DRQ_STAT) {
+ if (rq->nr_sectors) {
+ ide_multwrite(drive, drive->mult_count);
+ ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
+ return;
+ }
+ } else {
+ if (!rq->nr_sectors) { /* all done? */
+ rq = hwgroup->rq;
+ for (i = rq->nr_sectors; i > 0;){
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, hwgroup);
+ }
+ return;
+ }
+ }
+ }
+ ide_error(drive, "multwrite_intr", stat);
+}
+
+/*
+ * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
+ */
+static void set_multmode_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ if (OK_STAT(stat,READY_STAT,BAD_STAT)) {
+ drive->mult_count = drive->mult_req;
+ } else {
+ drive->mult_req = drive->mult_count = 0;
+ drive->special.b.recalibrate = 1;
+ (void) ide_dump_status(drive, "set_multmode", stat);
+ }
+}
+
+/*
+ * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
+ */
+static void set_geometry_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_error(drive, "set_geometry_intr", stat);
+}
+
+/*
+ * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
+ */
+static void recal_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_error(drive, "recal_intr", stat);
+}
+
+/*
+ * do_rw_disk() issues READ and WRITE commands to a disk,
+ * using LBA if supported, or CHS otherwise, to address sectors.
+ * It also takes care of issuing special DRIVE_CMDs.
+ */
+static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+#ifdef CONFIG_BLK_DEV_PROMISE
+ ide_hwif_t *hwif = HWIF(drive);
+ int use_promise_io = 0;
+#endif /* CONFIG_BLK_DEV_PROMISE */
+
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE(rq->nr_sectors,IDE_NSECTOR_REG);
+#ifdef CONFIG_BLK_DEV_PROMISE
+ if (IS_PROMISE_DRIVE) {
+ if (hwif->is_promise2 || rq->cmd == READ) {
+ use_promise_io = 1;
+ }
+ }
+ if (drive->select.b.lba || use_promise_io) {
+#else /* !CONFIG_BLK_DEV_PROMISE */
+ if (drive->select.b.lba) {
+#endif /* CONFIG_BLK_DEV_PROMISE */
+#ifdef DEBUG
+ printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
+ drive->name, (rq->cmd==READ)?"read":"writ",
+ block, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ OUT_BYTE(block,IDE_SECTOR_REG);
+ OUT_BYTE(block>>=8,IDE_LCYL_REG);
+ OUT_BYTE(block>>=8,IDE_HCYL_REG);
+ OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG);
+ } else {
+ unsigned int sect,head,cyl,track;
+ track = block / drive->sect;
+ sect = block % drive->sect + 1;
+ OUT_BYTE(sect,IDE_SECTOR_REG);
+ head = track % drive->head;
+ cyl = track / drive->head;
+ OUT_BYTE(cyl,IDE_LCYL_REG);
+ OUT_BYTE(cyl>>8,IDE_HCYL_REG);
+ OUT_BYTE(head|drive->select.all,IDE_SELECT_REG);
+#ifdef DEBUG
+ printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
+ drive->name, (rq->cmd==READ)?"read":"writ", cyl,
+ head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ }
+#ifdef CONFIG_BLK_DEV_PROMISE
+ if (use_promise_io) {
+ do_promise_io (drive, rq);
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_PROMISE */
+ if (rq->cmd == READ) {
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
+ return;
+#endif /* CONFIG_BLK_DEV_TRITON */
+ ide_set_handler(drive, &read_intr, WAIT_CMD);
+ OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG);
+ return;
+ }
+ if (rq->cmd == WRITE) {
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
+ return;
+#endif /* CONFIG_BLK_DEV_TRITON */
+ OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG);
+ if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+ printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name,
+ drive->mult_count ? "MULTWRITE" : "WRITE");
+ return;
+ }
+ if (!drive->unmask)
+ cli();
+ if (drive->mult_count) {
+ HWGROUP(drive)->wrq = *rq; /* scratchpad */
+ ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
+ ide_multwrite(drive, drive->mult_count);
+ } else {
+ ide_set_handler (drive, &write_intr, WAIT_CMD);
+ idedisk_output_data(drive, rq->buffer, SECTOR_WORDS);
+ }
+ return;
+ }
+ printk(KERN_ERR "%s: bad command: %d\n", drive->name, rq->cmd);
+ ide_end_request(0, HWGROUP(drive));
+}
+
+static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ MOD_INC_USE_COUNT;
+ if (drive->removable && drive->usage == 1) {
+ byte door_lock[] = {WIN_DOORLOCK,0,0,0};
+ struct request rq;
+ check_disk_change(inode->i_rdev);
+ ide_init_drive_cmd (&rq);
+ rq.buffer = door_lock;
+ /*
+ * Ignore the return code from door_lock,
+ * since the open() has already succeeded,
+ * and the door_lock is irrelevant at this point.
+ */
+ (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ }
+ return 0;
+}
+
+static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ if (drive->removable && !drive->usage) {
+ byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
+ struct request rq;
+ invalidate_buffers(inode->i_rdev);
+ ide_init_drive_cmd (&rq);
+ rq.buffer = door_unlock;
+ (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ }
+ MOD_DEC_USE_COUNT;
+}
+
+static int idedisk_media_change (ide_drive_t *drive)
+{
+ return drive->removable; /* if removable, always assume it was changed */
+}
+
+/*
+ * current_capacity() returns the capacity (in sectors) of a drive
+ * according to its current geometry/LBA settings.
+ */
+static unsigned long idedisk_capacity (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ unsigned long capacity = drive->cyl * drive->head * drive->sect;
+
+ drive->select.b.lba = 0;
+ /* Determine capacity, and use LBA if the drive properly supports it */
+ if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
+ if (id->lba_capacity >= capacity) {
+ capacity = id->lba_capacity;
+ drive->select.b.lba = 1;
+ }
+ }
+ return (capacity - drive->sect0);
+}
+
+static void idedisk_special (ide_drive_t *drive)
+{
+ special_t *s = &drive->special;
+
+ if (s->b.set_geometry) {
+ s->b.set_geometry = 0;
+ OUT_BYTE(drive->sect,IDE_SECTOR_REG);
+ OUT_BYTE(drive->cyl,IDE_LCYL_REG);
+ OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
+ OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
+ if (!IS_PROMISE_DRIVE)
+ ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
+ } else if (s->b.recalibrate) {
+ s->b.recalibrate = 0;
+ if (!IS_PROMISE_DRIVE)
+ ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
+ } else if (s->b.set_multmode) {
+ s->b.set_multmode = 0;
+ if (drive->id && drive->mult_req > drive->id->max_multsect)
+ drive->mult_req = drive->id->max_multsect;
+ if (!IS_PROMISE_DRIVE)
+ ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
+ } else if (s->all) {
+ int special = s->all;
+ s->all = 0;
+ printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special);
+ }
+}
+
+static void idedisk_pre_reset (ide_drive_t *drive)
+{
+ drive->special.all = 0;
+ drive->special.b.set_geometry = 1;
+ drive->special.b.recalibrate = 1;
+ if (OK_TO_RESET_CONTROLLER)
+ drive->mult_count = 0;
+ if (!drive->keep_settings)
+ drive->mult_req = 0;
+ if (drive->mult_req != drive->mult_count)
+ drive->special.b.set_multmode = 1;
+}
+
+int idedisk_init (void);
+static ide_module_t idedisk_module = {
+ IDE_DRIVER_MODULE,
+ idedisk_init,
+ NULL
+};
+
+/*
+ * IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idedisk_driver = {
+ ide_disk, /* media */
+ 0, /* busy */
+ 1, /* supports_dma */
+ NULL, /* cleanup */
+ do_rw_disk, /* do_request */
+ NULL, /* end_request */
+ NULL, /* ioctl */
+ idedisk_open, /* open */
+ idedisk_release, /* release */
+ idedisk_media_change, /* media_change */
+ idedisk_pre_reset, /* pre_reset */
+ idedisk_capacity, /* capacity */
+ idedisk_special /* special */
+};
+
+static int idedisk_cleanup (ide_drive_t *drive)
+{
+ return ide_unregister_subdriver(drive);
+}
+
+static int idedisk_identify_device (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+
+ if (id == NULL)
+ return 0;
+
+ /* SunDisk drives: force one unit */
+ if (id->model[0] == 'S' && id->model[1] == 'u' && (drive->select.all & (1<<4)))
+ return 1;
+
+ return 0;
+}
+
+static void idedisk_setup (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ unsigned long capacity, check;
+
+ if (id == NULL)
+ return;
+
+ /* check for removable disks (eg. SYQUEST), ignore 'WD' drives */
+ if (id->config & (1<<7)) { /* removable disk ? */
+ if (id->model[0] != 'W' || id->model[1] != 'D')
+ drive->removable = 1;
+ }
+
+ /* SunDisk drives: treat as non-removable */
+ if (id->model[0] == 'S' && id->model[1] == 'u')
+ drive->removable = 0;
+
+ /* Extract geometry if we did not already have one for the drive */
+ if (!drive->cyl || !drive->head || !drive->sect) {
+ drive->cyl = drive->bios_cyl = id->cyls;
+ drive->head = drive->bios_head = id->heads;
+ drive->sect = drive->bios_sect = id->sectors;
+ }
+ /* Handle logical geometry translation by the drive */
+ if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
+ && (id->cur_heads <= 16) && id->cur_sectors)
+ {
+ /*
+ * Extract the physical drive geometry for our use.
+ * Note that we purposely do *not* update the bios info.
+ * This way, programs that use it (like fdisk) will
+ * still have the same logical view as the BIOS does,
+ * which keeps the partition table from being screwed.
+ *
+ * An exception to this is the cylinder count,
+ * which we reexamine later on to correct for 1024 limitations.
+ */
+ drive->cyl = id->cur_cyls;
+ drive->head = id->cur_heads;
+ drive->sect = id->cur_sectors;
+
+ /* check for word-swapped "capacity" field in id information */
+ capacity = drive->cyl * drive->head * drive->sect;
+ check = (id->cur_capacity0 << 16) | id->cur_capacity1;
+ if (check == capacity) { /* was it swapped? */
+ /* yes, bring it into little-endian order: */
+ id->cur_capacity0 = (capacity >> 0) & 0xffff;
+ id->cur_capacity1 = (capacity >> 16) & 0xffff;
+ }
+ }
+ /* Use physical geometry if what we have still makes no sense */
+ if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) {
+ drive->cyl = id->cyls;
+ drive->head = id->heads;
+ drive->sect = id->sectors;
+ }
+ /* Correct the number of cyls if the bios value is too small */
+ if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
+ if (drive->cyl > drive->bios_cyl)
+ drive->bios_cyl = drive->cyl;
+ }
+
+ (void) idedisk_capacity (drive); /* initialize LBA selection */
+
+ printk (KERN_INFO "%s: %.40s, %ldMB w/%dkB Cache, %sCHS=%d/%d/%d%s\n",
+ drive->name, id->model, idedisk_capacity(drive)/2048L, id->buf_size/2,
+ drive->select.b.lba ? "LBA, " : "",
+ drive->bios_cyl, drive->bios_head, drive->bios_sect,
+ drive->using_dma ? ", DMA" : "");
+
+ drive->mult_count = 0;
+ if (id->max_multsect) {
+ drive->mult_req = INITIAL_MULT_COUNT;
+ if (drive->mult_req > id->max_multsect)
+ drive->mult_req = id->max_multsect;
+ if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
+ drive->special.b.set_multmode = 1;
+ }
+}
+
+int idedisk_init (void)
+{
+ ide_drive_t *drive;
+ int failed = 0;
+
+ MOD_INC_USE_COUNT;
+ while ((drive = ide_scan_devices (ide_disk, NULL, failed++)) != NULL) {
+ if (idedisk_identify_device (drive))
+ continue;
+ if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) {
+ printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name);
+ continue;
+ }
+ idedisk_setup(drive);
+ if ((!drive->head || drive->head > 16) && !drive->select.b.lba) {
+ printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head);
+ (void) idedisk_cleanup(drive);
+ continue;
+ }
+ failed--;
+ }
+ ide_register_module(&idedisk_module);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return idedisk_init();
+}
+
+void cleanup_module (void)
+{
+ ide_drive_t *drive;
+ int failed = 0;
+
+ while ((drive = ide_scan_devices (ide_disk, &idedisk_driver, failed)) != NULL)
+ if (idedisk_cleanup (drive)) {
+ printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name);
+ failed++;
+ }
+ ide_unregister_module(&idedisk_module);
+}
+#endif /* MODULE */
diff --git a/drivers/block/ide-floppy.c b/drivers/block/ide-floppy.c
new file mode 100644
index 000000000..8de2b1a27
--- /dev/null
+++ b/drivers/block/ide-floppy.c
@@ -0,0 +1,1432 @@
+/*
+ * linux/drivers/block/ide-floppy.c Version 0.2 - ALPHA Oct 31, 1996
+ *
+ * Copyright (C) 1996 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * IDE ATAPI floppy driver.
+ *
+ * The driver currently doesn't have any fancy features, just the bare
+ * minimum read/write support.
+ *
+ * Many thanks to Lode Leroy <Lode.Leroy@www.ibase.be>, who tested so many
+ * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive.
+ *
+ * Ver 0.1 Oct 17 96 Initial test version, mostly based on ide-tape.c.
+ * Ver 0.2 Oct 31 96 Minor changes.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/bitops.h>
+
+/*
+ * Main Linux ide driver include file
+ */
+#include "ide.h"
+
+/*
+ * The following are used to debug the driver.
+ */
+#define IDEFLOPPY_DEBUG_LOG 0
+#define IDEFLOPPY_DEBUG_INFO 0
+#define IDEFLOPPY_DEBUG_BUGS 1
+
+/*
+ * After each failed packet command we issue a request sense command
+ * and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times.
+ */
+#define IDEFLOPPY_MAX_PC_RETRIES 3
+
+/*
+ * With each packet command, we allocate a buffer of
+ * IDEFLOPPY_PC_BUFFER_SIZE bytes.
+ */
+#define IDEFLOPPY_PC_BUFFER_SIZE 256
+
+/*
+ * In various places in the driver, we need to allocate storage
+ * for packet commands and requests, which will remain valid while
+ * we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDEFLOPPY_PC_STACK (10 + IDEFLOPPY_MAX_PC_RETRIES)
+
+/*
+ * Our view of a packet command.
+ */
+typedef struct idefloppy_packet_command_s {
+ u8 c[12]; /* Actual packet bytes */
+ int retries; /* On each retry, we increment retries */
+ int error; /* Error code */
+ int request_transfer; /* Bytes to transfer */
+ int actually_transferred; /* Bytes actually transferred */
+ int buffer_size; /* Size of our data buffer */
+ char *b_data; /* Pointer which runs on the buffers */
+ int b_count; /* Missing/Available data on the current buffer */
+ struct request *rq; /* The corresponding request */
+ byte *buffer; /* Data buffer */
+ byte *current_position; /* Pointer into the above buffer */
+ void (*callback) (ide_drive_t *); /* Called when this packet command is completed */
+ byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE]; /* Temporary buffer */
+ unsigned int flags; /* Status/Action bit flags */
+} idefloppy_pc_t;
+
+/*
+ * Packet command flag bits.
+ */
+#define PC_ABORT 0 /* Set when an error is considered normal - We won't retry */
+#define PC_DMA_RECOMMENDED 2 /* 1 when we prefer to use DMA if possible */
+#define PC_DMA_IN_PROGRESS 3 /* 1 while DMA in progress */
+#define PC_DMA_ERROR 4 /* 1 when encountered problem during DMA */
+#define PC_WRITING 5 /* Data direction */
+
+/*
+ * Removable Block Access Capabilities Page
+ */
+typedef struct {
+ unsigned page_code :6; /* Page code - Should be 0x1b */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned ps :1; /* Should be 0 */
+ u8 page_length; /* Page Length - Should be 0xa */
+ unsigned reserved2 :6;
+ unsigned srfp :1; /* Supports reporting progress of format */
+ unsigned sflp :1; /* System floppy type device */
+ unsigned tlun :3; /* Total logical units supported by the device */
+ unsigned reserved3 :3;
+ unsigned sml :1; /* Single / Multiple lun supported */
+ unsigned ncd :1; /* Non cd optical device */
+ u8 reserved[8];
+} idefloppy_capabilities_page_t;
+
+/*
+ * Flexible disk page.
+ */
+typedef struct {
+ unsigned page_code :6; /* Page code - Should be 0x5 */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned ps :1; /* The device is capable of saving the page */
+ u8 page_length; /* Page Length - Should be 0x1e */
+ u16 transfer_rate; /* In kilobits per second */
+ u8 heads, sectors; /* Number of heads, Number of sectors per track */
+ u16 sector_size; /* Byes per sector */
+ u16 cyls; /* Number of cylinders */
+ u8 reserved10[10];
+ u8 motor_delay; /* Motor off delay */
+ u8 reserved21[7];
+ u16 rpm; /* Rotations per minute */
+ u8 reserved30[2];
+} idefloppy_flexible_disk_page_t;
+
+/*
+ * Format capacity
+ */
+typedef struct {
+ u8 reserved[3];
+ u8 length; /* Length of the following descriptors in bytes */
+} idefloppy_capacity_header_t;
+
+typedef struct {
+ u32 blocks; /* Number of blocks */
+ unsigned dc :2; /* Descriptor Code */
+ unsigned reserved :6;
+ u8 length_msb; /* Block Length (MSB)*/
+ u16 length; /* Block Length */
+} idefloppy_capacity_descriptor_t;
+
+#define CAPACITY_INVALID 0x00
+#define CAPACITY_UNFORMATTED 0x01
+#define CAPACITY_CURRENT 0x02
+#define CAPACITY_NO_CARTRIDGE 0x03
+
+/*
+ * Most of our global data which we need to save even as we leave the
+ * driver due to an interrupt or a timer event is stored in a variable
+ * of type idefloppy_floppy_t, defined below.
+ */
+typedef struct {
+ ide_drive_t *drive;
+
+ idefloppy_pc_t *pc; /* Current packet command */
+ idefloppy_pc_t *failed_pc; /* Last failed packet command */
+ idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];/* Packet command stack */
+ int pc_stack_index; /* Next free packet command storage space */
+ struct request rq_stack[IDEFLOPPY_PC_STACK];
+ int rq_stack_index; /* We implement a circular array */
+
+ /*
+ * Last error information
+ */
+ byte sense_key, asc, ascq;
+
+ /*
+ * Device information
+ */
+ int blocks, block_size, bs_factor; /* Current format */
+ idefloppy_capacity_descriptor_t capacity; /* Last format capacity */
+ idefloppy_flexible_disk_page_t flexible_disk_page; /* Copy of the flexible disk page */
+
+ unsigned int flags; /* Status/Action flags */
+} idefloppy_floppy_t;
+
+/*
+ * Floppy flag bits values.
+ */
+#define IDEFLOPPY_DRQ_INTERRUPT 0 /* DRQ interrupt device */
+#define IDEFLOPPY_MEDIA_CHANGED 1 /* Media may have changed */
+#define IDEFLOPPY_USE_READ12 2 /* Use READ12/WRITE12 or READ10/WRITE10 */
+
+/*
+ * ATAPI floppy drive packet commands
+ */
+#define IDEFLOPPY_FORMAT_UNIT_CMD 0x04
+#define IDEFLOPPY_INQUIRY_CMD 0x12
+#define IDEFLOPPY_MODE_SELECT_CMD 0x55
+#define IDEFLOPPY_MODE_SENSE_CMD 0x5a
+#define IDEFLOPPY_READ10_CMD 0x28
+#define IDEFLOPPY_READ12_CMD 0xa8
+#define IDEFLOPPY_READ_CAPACITY_CMD 0x23
+#define IDEFLOPPY_REQUEST_SENSE_CMD 0x03
+#define IDEFLOPPY_PREVENT_REMOVAL_CMD 0x1e
+#define IDEFLOPPY_SEEK_CMD 0x2b
+#define IDEFLOPPY_START_STOP_CMD 0x1b
+#define IDEFLOPPY_TEST_UNIT_READY_CMD 0x00
+#define IDEFLOPPY_VERIFY_CMD 0x2f
+#define IDEFLOPPY_WRITE10_CMD 0x2a
+#define IDEFLOPPY_WRITE12_CMD 0xaa
+#define IDEFLOPPY_WRITE_VERIFY_CMD 0x2e
+
+/*
+ * Defines for the mode sense command
+ */
+#define MODE_SENSE_CURRENT 0x00
+#define MODE_SENSE_CHANGEABLE 0x01
+#define MODE_SENSE_DEFAULT 0x02
+#define MODE_SENSE_SAVED 0x03
+
+/*
+ * Special requests for our block device strategy routine.
+ */
+#define IDEFLOPPY_FIRST_RQ 90
+
+/*
+ * IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define IDEFLOPPY_PC_RQ 90
+
+#define IDEFLOPPY_LAST_RQ 90
+
+/*
+ * A macro which can be used to check if a given request command
+ * originated in the driver or in the buffer cache layer.
+ */
+#define IDEFLOPPY_RQ_CMD(cmd) ((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ))
+
+/*
+ * Error codes which are returned in rq->errors to the higher part
+ * of the driver.
+ */
+#define IDEFLOPPY_ERROR_GENERAL 101
+
+/*
+ * The ATAPI Status Register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned check :1; /* Error occurred */
+ unsigned idx :1; /* Reserved */
+ unsigned corr :1; /* Correctable error occurred */
+ unsigned drq :1; /* Data is request by the device */
+ unsigned dsc :1; /* Media access command finished */
+ unsigned reserved5 :1; /* Reserved */
+ unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */
+ unsigned bsy :1; /* The device has access to the command block */
+ } b;
+} idefloppy_status_reg_t;
+
+/*
+ * The ATAPI error register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned ili :1; /* Illegal Length Indication */
+ unsigned eom :1; /* End Of Media Detected */
+ unsigned abrt :1; /* Aborted command - As defined by ATA */
+ unsigned mcr :1; /* Media Change Requested - As defined by ATA */
+ unsigned sense_key :4; /* Sense key of the last failed packet command */
+ } b;
+} idefloppy_error_reg_t;
+
+/*
+ * ATAPI Feature Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned dma :1; /* Using DMA or PIO */
+ unsigned reserved321 :3; /* Reserved */
+ unsigned reserved654 :3; /* Reserved (Tag Type) */
+ unsigned reserved7 :1; /* Reserved */
+ } b;
+} idefloppy_feature_reg_t;
+
+/*
+ * ATAPI Byte Count Register.
+ */
+typedef union {
+ unsigned all :16;
+ struct {
+ unsigned low :8; /* LSB */
+ unsigned high :8; /* MSB */
+ } b;
+} idefloppy_bcount_reg_t;
+
+/*
+ * ATAPI Interrupt Reason Register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned cod :1; /* Information transferred is command (1) or data (0) */
+ unsigned io :1; /* The device requests us to read (1) or write (0) */
+ unsigned reserved :6; /* Reserved */
+ } b;
+} idefloppy_ireason_reg_t;
+
+/*
+ * ATAPI floppy Drive Select Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned sam_lun :3; /* Logical unit number */
+ unsigned reserved3 :1; /* Reserved */
+ unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */
+ unsigned one5 :1; /* Should be set to 1 */
+ unsigned reserved6 :1; /* Reserved */
+ unsigned one7 :1; /* Should be set to 1 */
+ } b;
+} idefloppy_drivesel_reg_t;
+
+/*
+ * ATAPI Device Control Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned zero0 :1; /* Should be set to zero */
+ unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */
+ unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+ unsigned one3 :1; /* Should be set to 1 */
+ unsigned reserved4567 :4; /* Reserved */
+ } b;
+} idefloppy_control_reg_t;
+
+/*
+ * The following is used to format the general configuration word of
+ * the ATAPI IDENTIFY DEVICE command.
+ */
+struct idefloppy_id_gcw {
+ unsigned packet_size :2; /* Packet Size */
+ unsigned reserved234 :3; /* Reserved */
+ unsigned drq_type :2; /* Command packet DRQ type */
+ unsigned removable :1; /* Removable media */
+ unsigned device_type :5; /* Device type */
+ unsigned reserved13 :1; /* Reserved */
+ unsigned protocol :2; /* Protocol type */
+};
+
+/*
+ * INQUIRY packet command - Data Format
+ */
+typedef struct {
+ unsigned device_type :5; /* Peripheral Device Type */
+ unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */
+ unsigned reserved1_6t0 :7; /* Reserved */
+ unsigned rmb :1; /* Removable Medium Bit */
+ unsigned ansi_version :3; /* ANSI Version */
+ unsigned ecma_version :3; /* ECMA Version */
+ unsigned iso_version :2; /* ISO Version */
+ unsigned response_format :4; /* Response Data Format */
+ unsigned reserved3_45 :2; /* Reserved */
+ unsigned reserved3_6 :1; /* TrmIOP - Reserved */
+ unsigned reserved3_7 :1; /* AENC - Reserved */
+ u8 additional_length; /* Additional Length (total_length-4) */
+ u8 rsv5, rsv6, rsv7; /* Reserved */
+ u8 vendor_id[8]; /* Vendor Identification */
+ u8 product_id[16]; /* Product Identification */
+ u8 revision_level[4]; /* Revision Level */
+ u8 vendor_specific[20]; /* Vendor Specific - Optional */
+ u8 reserved56t95[40]; /* Reserved - Optional */
+ /* Additional information may be returned */
+} idefloppy_inquiry_result_t;
+
+/*
+ * REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+ unsigned error_code :7; /* Current error (0x70) */
+ unsigned valid :1; /* The information field conforms to SFF-8070i */
+ u8 reserved1 :8; /* Reserved */
+ unsigned sense_key :4; /* Sense Key */
+ unsigned reserved2_4 :1; /* Reserved */
+ unsigned ili :1; /* Incorrect Length Indicator */
+ unsigned reserved2_67 :2;
+ u32 information __attribute__ ((packed));
+ u8 asl; /* Additional sense length (n-7) */
+ u32 command_specific; /* Additional command specific information */
+ u8 asc; /* Additional Sense Code */
+ u8 ascq; /* Additional Sense Code Qualifier */
+ u8 replaceable_unit_code; /* Field Replaceable Unit Code */
+ u8 reserved[3];
+ u8 pad[2]; /* Padding to 20 bytes */
+} idefloppy_request_sense_result_t;
+
+/*
+ * Pages of the SELECT SENSE / MODE SENSE packet commands.
+ */
+#define IDEFLOPPY_CAPABILITIES_PAGE 0x1b
+#define IDEFLOPPY_FLEXIBLE_DISK_PAGE 0x05
+
+/*
+ * Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+ u16 mode_data_length; /* Length of the following data transfer */
+ u8 medium_type; /* Medium Type */
+ unsigned reserved3 :7;
+ unsigned wp :1; /* Write protect */
+ u8 reserved[4];
+} idefloppy_mode_parameter_header_t;
+
+#define IDEFLOPPY_MIN(a,b) ((a)<(b) ? (a):(b))
+#define IDEFLOPPY_MAX(a,b) ((a)>(b) ? (a):(b))
+
+/*
+ * Too bad. The drive wants to send us data which we are not ready to accept.
+ * Just throw it away.
+ */
+static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ IN_BYTE (IDE_DATA_REG);
+}
+
+#if IDEFLOPPY_DEBUG_BUGS
+static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ OUT_BYTE (0, IDE_DATA_REG);
+}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+/*
+ * idefloppy_end_request is used to finish servicing a request.
+ *
+ * For read/write requests, we will call ide_end_request to pass to the
+ * next buffer.
+ */
+static void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+ ide_drive_t *drive = hwgroup->drive;
+ struct request *rq = hwgroup->rq;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Reached idefloppy_end_request\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
+ ide_end_request (uptodate, hwgroup);
+ return;
+ }
+ switch (uptodate) {
+ case 0: rq->errors = IDEFLOPPY_ERROR_GENERAL; break;
+ case 1: rq->errors = 0; break;
+ default: rq->errors = uptodate;
+ }
+ ide_end_drive_cmd (drive, 0, 0);
+}
+
+static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+ struct request *rq = pc->rq;
+ struct buffer_head *bh = rq->bh;
+ int count;
+
+ while (bcount) {
+#if IDEFLOPPY_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "%s: bh == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount);
+ idefloppy_discard_data (drive, bcount);
+ return;
+ }
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+ count = IDEFLOPPY_MIN (bh->b_size - pc->b_count, bcount);
+ atapi_input_bytes (drive, bh->b_data + pc->b_count, count);
+ bcount -= count; pc->b_count += count;
+ if (pc->b_count == bh->b_size) {
+ rq->sector += rq->current_nr_sectors;
+ rq->nr_sectors -= rq->current_nr_sectors;
+ idefloppy_end_request (1, HWGROUP(drive));
+ if ((bh = rq->bh) != NULL)
+ pc->b_count = 0;
+ }
+ }
+}
+
+static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+ struct request *rq = pc->rq;
+ struct buffer_head *bh = rq->bh;
+ int count;
+
+ while (bcount) {
+#if IDEFLOPPY_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
+ idefloppy_write_zeros (drive, bcount);
+ return;
+ }
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+ count = IDEFLOPPY_MIN (pc->b_count, bcount);
+ atapi_output_bytes (drive, pc->b_data, count);
+ bcount -= count; pc->b_data += count; pc->b_count -= count;
+ if (!pc->b_count) {
+ rq->sector += rq->current_nr_sectors;
+ rq->nr_sectors -= rq->current_nr_sectors;
+ idefloppy_end_request (1, HWGROUP(drive));
+ if ((bh = rq->bh) != NULL) {
+ pc->b_data = bh->b_data;
+ pc->b_count = bh->b_size;
+ }
+ }
+ }
+}
+
+#ifdef CONFIG_BLK_DEV_TRITON
+static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+ struct request *rq = pc->rq;
+ struct buffer_head *bh = rq->bh;
+
+ while ((bh = rq->bh) != NULL)
+ idefloppy_end_request (1, HWGROUP(drive));
+}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+/*
+ * idefloppy_queue_pc_head generates a new packet command request in front
+ * of the request queue, before the current request, so that it will be
+ * processed immediately, on the next pass through the driver.
+ */
+static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq)
+{
+ ide_init_drive_cmd (rq);
+ rq->buffer = (char *) pc;
+ rq->cmd = IDEFLOPPY_PC_RQ;
+ (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ if (floppy->pc_stack_index==IDEFLOPPY_PC_STACK)
+ floppy->pc_stack_index=0;
+ return (&floppy->pc_stack[floppy->pc_stack_index++]);
+}
+
+static struct request *idefloppy_next_rq_storage (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ if (floppy->rq_stack_index==IDEFLOPPY_PC_STACK)
+ floppy->rq_stack_index=0;
+ return (&floppy->rq_stack[floppy->rq_stack_index++]);
+}
+
+/*
+ * idefloppy_analyze_error is called on each failed packet command retry
+ * to analyze the request sense.
+ */
+static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq;
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+}
+
+static void idefloppy_request_sense_callback (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: Reached idefloppy_request_sense_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ if (!floppy->pc->error) {
+ idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer);
+ idefloppy_end_request (1,HWGROUP (drive));
+ } else {
+ printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
+ idefloppy_end_request (0,HWGROUP (drive));
+ }
+}
+
+/*
+ * General packet command callback function.
+ */
+static void idefloppy_pc_callback (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ idefloppy_end_request (floppy->pc->error ? 0:1, HWGROUP(drive));
+}
+
+/*
+ * idefloppy_init_pc initializes a packet command.
+ */
+static void idefloppy_init_pc (idefloppy_pc_t *pc)
+{
+ memset (pc->c, 0, 12);
+ pc->retries = 0;
+ pc->flags = 0;
+ pc->request_transfer = 0;
+ pc->buffer = pc->pc_buffer;
+ pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE;
+ pc->b_data = NULL;
+ pc->callback = &idefloppy_pc_callback;
+}
+
+static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc)
+{
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD;
+ pc->c[4] = 255;
+ pc->request_transfer = 18;
+ pc->callback = &idefloppy_request_sense_callback;
+}
+
+/*
+ * idefloppy_retry_pc is called when an error was detected during the
+ * last packet command. We queue a request sense packet command in
+ * the head of the request list.
+ */
+static void idefloppy_retry_pc (ide_drive_t *drive)
+{
+ idefloppy_pc_t *pc;
+ struct request *rq;
+ idefloppy_error_reg_t error;
+
+ error.all = IN_BYTE (IDE_ERROR_REG);
+ pc = idefloppy_next_pc_storage (drive);
+ rq = idefloppy_next_rq_storage (drive);
+ idefloppy_create_request_sense_cmd (pc);
+ idefloppy_queue_pc_head (drive, pc, rq);
+}
+
+/*
+ * idefloppy_pc_intr is the usual interrupt handler which will be called
+ * during a packet command.
+ */
+static void idefloppy_pc_intr (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_status_reg_t status;
+ idefloppy_bcount_reg_t bcount;
+ idefloppy_ireason_reg_t ireason;
+ idefloppy_pc_t *pc=floppy->pc;
+ struct request *rq = pc->rq;
+ unsigned int temp;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+ if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) {
+ set_bit (PC_DMA_ERROR, &pc->flags);
+ } else {
+ pc->actually_transferred=pc->request_transfer;
+ idefloppy_update_buffers (drive, pc);
+ }
+ (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive)); /* End DMA */
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: DMA finished\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ }
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+ status.all = GET_STAT(); /* Clear the interrupt */
+
+ if (!status.b.drq) { /* No more interrupts */
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+
+ ide_sti();
+
+ if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) { /* Error detected */
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: %s: I/O error, ",drive->name);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ rq->errors++;
+ if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+ printk (KERN_ERR "ide-floppy: I/O error in request sense command\n");
+ ide_do_reset (drive);
+ return;
+ }
+ idefloppy_retry_pc (drive); /* Retry operation */
+ return;
+ }
+ pc->error = 0;
+ if (floppy->failed_pc == pc)
+ floppy->failed_pc=NULL;
+ pc->callback(drive); /* Command finished - Call the callback function */
+ return;
+ }
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+ printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
+ printk (KERN_ERR "ide-floppy: DMA disabled, reverting to PIO\n");
+ drive->using_dma=0;
+ ide_do_reset (drive);
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_TRITON */
+ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */
+ bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */
+ ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+ if (ireason.b.cod) {
+ printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
+ ide_do_reset (drive);
+ return;
+ }
+ if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */
+ printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read");
+ printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write");
+ ide_do_reset (drive);
+ return;
+ }
+ if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */
+ temp = pc->actually_transferred + bcount.all;
+ if ( temp > pc->request_transfer) {
+ if (temp > pc->buffer_size) {
+ printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n");
+ idefloppy_discard_data (drive,bcount.all);
+ ide_set_handler (drive,&idefloppy_pc_intr,WAIT_CMD);
+ return;
+ }
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+ }
+ }
+ if (test_bit (PC_WRITING, &pc->flags)) {
+ if (pc->buffer != NULL)
+ atapi_output_bytes (drive,pc->current_position,bcount.all); /* Write the current buffer */
+ else
+ idefloppy_output_buffers (drive, pc, bcount.all);
+ } else {
+ if (pc->buffer != NULL)
+ atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */
+ else
+ idefloppy_input_buffers (drive, pc, bcount.all);
+ }
+ pc->actually_transferred+=bcount.all; /* Update the current position */
+ pc->current_position+=bcount.all;
+
+ ide_set_handler (drive,&idefloppy_pc_intr,WAIT_CMD); /* And set the interrupt handler again */
+}
+
+static void idefloppy_transfer_pc (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_ireason_reg_t ireason;
+
+ if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+ printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
+ return;
+ }
+ ireason.all=IN_BYTE (IDE_IREASON_REG);
+ if (!ireason.b.cod || ireason.b.io) {
+ printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n");
+ ide_do_reset (drive);
+ return;
+ }
+ ide_set_handler (drive, &idefloppy_pc_intr, WAIT_CMD); /* Set the interrupt routine */
+ atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */
+}
+
+/*
+ * Issue a packet command
+ */
+static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_bcount_reg_t bcount;
+ int dma_ok = 0;
+
+#if IDEFLOPPY_DEBUG_BUGS
+ if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+ printk (KERN_ERR "ide-floppy: possible ide-floppy.c bug - Two request sense in serial were issued\n");
+ }
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+ if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD)
+ floppy->failed_pc=pc;
+ floppy->pc=pc; /* Set the current packet command */
+
+ if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
+ /*
+ * We will "abort" retrying a packet command in case
+ * a legitimate error code was received.
+ */
+ if (!test_bit (PC_ABORT, &pc->flags)) {
+ printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+ drive->name, pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+ pc->error = IDEFLOPPY_ERROR_GENERAL; /* Giving up */
+ }
+ floppy->failed_pc=NULL;
+ pc->callback(drive);
+ return;
+ }
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Retry number - %d\n",pc->retries);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ pc->retries++;
+ pc->actually_transferred=0; /* We haven't transferred any data yet */
+ pc->current_position=pc->buffer;
+ bcount.all=pc->request_transfer; /* Request to transfer the entire buffer at once */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (clear_bit (PC_DMA_ERROR, &pc->flags)) {
+ printk (KERN_WARNING "ide-floppy: DMA disabled, reverting to PIO\n");
+ drive->using_dma=0;
+ }
+ if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
+ dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+ OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */
+ OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+ OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+ OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (dma_ok) { /* Begin DMA, if necessary */
+ set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+ (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+ }
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+ if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) {
+ ide_set_handler (drive, &idefloppy_transfer_pc, WAIT_CMD);
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */
+ } else {
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+ idefloppy_transfer_pc (drive);
+ }
+}
+
+static void idefloppy_rw_callback (ide_drive_t *drive)
+{
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ return;
+}
+
+static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent)
+{
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "ide-floppy: creating prevent removal command, prevent = %d\n", prevent);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_PREVENT_REMOVAL_CMD;
+ pc->c[4] = prevent;
+}
+
+static void idefloppy_create_read_capacity_cmd (idefloppy_pc_t *pc)
+{
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_READ_CAPACITY_CMD;
+ pc->c[7] = 255;
+ pc->c[8] = 255;
+}
+
+/*
+ * A mode sense command is used to "sense" floppy parameters.
+ */
+static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, byte page_code, byte type)
+{
+ unsigned short length = sizeof (idefloppy_mode_parameter_header_t);
+
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_MODE_SENSE_CMD;
+ pc->c[1] = 0;
+ pc->c[2] = page_code + (type << 6);
+
+ switch (page_code) {
+ case IDEFLOPPY_CAPABILITIES_PAGE:
+ length += 12;
+ break;
+ case IDEFLOPPY_FLEXIBLE_DISK_PAGE:
+ length += 32;
+ break;
+ default:
+ printk (KERN_ERR "ide-floppy: unsupported page code in create_mode_sense_cmd\n");
+ }
+ put_unaligned (htons (length), (unsigned short *) &pc->c[7]);
+ pc->request_transfer = length;
+}
+
+static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start)
+{
+ idefloppy_init_pc (pc);
+ pc->c[0] = IDEFLOPPY_START_STOP_CMD;
+ pc->c[4] = start;
+}
+
+static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq)
+{
+ int block = rq->sector / floppy->bs_factor;
+ int blocks = rq->nr_sectors / floppy->bs_factor;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk ("create_rw1%d_cmd: block == %d, blocks == %d\n",
+ 2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ idefloppy_init_pc (pc);
+ if (test_bit (IDEFLOPPY_USE_READ12, &floppy->flags)) {
+ pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD;
+ put_unaligned (htonl (blocks), (unsigned int *) &pc->c[6]);
+ } else {
+ pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ10_CMD : IDEFLOPPY_WRITE10_CMD;
+ put_unaligned (htons (blocks), (unsigned short *) &pc->c[7]);
+ }
+ put_unaligned (htonl (block), (unsigned int *) &pc->c[2]);
+ pc->callback = &idefloppy_rw_callback;
+ pc->rq = rq;
+ pc->b_data = rq->buffer;
+ pc->b_count = rq->cmd == READ ? 0 : rq->bh->b_size;
+ if (rq->cmd == WRITE)
+ set_bit (PC_WRITING, &pc->flags);
+ pc->buffer = NULL;
+ pc->request_transfer = pc->buffer_size = blocks * floppy->block_size;
+ set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+}
+
+/*
+ * idefloppy_do_request is our request handling function.
+ */
+static void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_pc_t *pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+ printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ if (rq->errors >= ERROR_MAX) {
+ printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+ drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+ idefloppy_end_request (0, HWGROUP(drive));
+ return;
+ }
+ switch (rq->cmd) {
+ case READ:
+ case WRITE:
+ if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) {
+ printk ("%s: unsupported r/w request size\n", drive->name);
+ idefloppy_end_request (0, HWGROUP(drive));
+ return;
+ }
+ pc = idefloppy_next_pc_storage (drive);
+ idefloppy_create_rw_cmd (floppy, pc, rq);
+ break;
+ case IDEFLOPPY_PC_RQ:
+ pc = (idefloppy_pc_t *) rq->buffer;
+ break;
+ default:
+ printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd);
+ idefloppy_end_request (0,HWGROUP (drive));
+ return;
+ }
+ pc->rq = rq;
+ idefloppy_issue_pc (drive, pc);
+}
+
+/*
+ * idefloppy_queue_pc_tail adds a special packet command request to the
+ * tail of the request queue, and waits for it to be serviced.
+ */
+static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc)
+{
+ struct request rq;
+
+ ide_init_drive_cmd (&rq);
+ rq.buffer = (char *) pc;
+ rq.cmd = IDEFLOPPY_PC_RQ;
+ return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ * Look at the flexible disk page parameters. We will ignore the CHS
+ * capacity parameters and use the LBA parameters instead.
+ */
+static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_pc_t pc;
+ idefloppy_mode_parameter_header_t *header;
+ idefloppy_flexible_disk_page_t *page;
+ int capacity;
+
+ idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT);
+ if (idefloppy_queue_pc_tail (drive,&pc)) {
+ printk (KERN_ERR "ide-floppy: Can't get flexible disk page parameters\n");
+ return 1;
+ }
+ header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+ page = (idefloppy_flexible_disk_page_t *) (header + 1);
+
+ page->transfer_rate = ntohs (page->transfer_rate);
+ page->sector_size = ntohs (page->sector_size);
+ page->cyls = ntohs (page->cyls);
+ page->rpm = ntohs (page->rpm);
+ capacity = page->cyls * page->heads * page->sectors * page->sector_size;
+ if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t))) {
+ printk (KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, %d sector size, %d rpm\n",
+ drive->name, capacity / 1024, page->cyls, page->heads, page->sectors,
+ page->transfer_rate / 8, page->sector_size, page->rpm);
+ floppy->flexible_disk_page = *page;
+ if (capacity != floppy->blocks * floppy->block_size)
+ printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
+ drive->name, capacity, floppy->blocks * floppy->block_size);
+ }
+ return 0;
+}
+
+/*
+ * Determine if a media is present in the floppy drive, and if so,
+ * its LBA capacity.
+ */
+static int idefloppy_get_capacity (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_pc_t pc;
+ idefloppy_capacity_header_t *header;
+ idefloppy_capacity_descriptor_t *descriptor;
+ int i, descriptors, rc = 1, blocks, length;
+
+ idefloppy_create_read_capacity_cmd (&pc);
+ if (idefloppy_queue_pc_tail (drive, &pc)) {
+ printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n");
+ return 1;
+ }
+ header = (idefloppy_capacity_header_t *) pc.buffer;
+ descriptors = header->length / sizeof (idefloppy_capacity_descriptor_t);
+ descriptor = (idefloppy_capacity_descriptor_t *) (header + 1);
+ for (i = 0; i < descriptors; i++, descriptor++) {
+ blocks = descriptor->blocks = ntohl (descriptor->blocks);
+ length = descriptor->length = ntohs (descriptor->length);
+ if (!i && descriptor->dc == CAPACITY_CURRENT) {
+ if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t))) {
+ printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size\n", drive->name, blocks * length / 1024, blocks, length);
+ floppy->capacity = *descriptor;
+ }
+ if (!length || length % 512)
+ printk (KERN_ERR "%s: %d bytes block size not supported\n", drive->name, length);
+ else {
+ floppy->blocks = blocks;
+ floppy->block_size = length;
+ if ((floppy->bs_factor = length / 512) != 1)
+ printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name);
+ drive->part[0].nr_sects = blocks * floppy->bs_factor;
+ if (length > BLOCK_SIZE)
+ blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] = length;
+ rc = 0;
+ }
+ }
+#if IDEFLOPPY_DEBUG_INFO
+ if (!i) printk (KERN_INFO "Descriptor 0 Code: %d\n", descriptor->dc);
+ printk (KERN_INFO "Descriptor %d: %dkB, %d blocks, %d sector size\n", i, blocks * length / 1024, blocks, length);
+#endif /* IDEFLOPPY_DEBUG_INFO */
+ }
+ (void) idefloppy_get_flexible_disk_page (drive);
+ return rc;
+}
+
+/*
+ * Our special ide-floppy ioctl's.
+ *
+ * Currently there aren't any ioctl's.
+ */
+static int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return -EIO;
+}
+
+/*
+ * Our open/release functions
+ */
+static int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ idefloppy_pc_t pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Reached idefloppy_open\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ MOD_INC_USE_COUNT;
+ if (drive->usage == 1) {
+ idefloppy_create_start_stop_cmd (&pc, 1);
+ (void) idefloppy_queue_pc_tail (drive, &pc);
+ if (idefloppy_get_capacity (drive)) {
+ drive->usage--;
+ MOD_DEC_USE_COUNT;
+ return -EIO;
+ }
+ set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+ idefloppy_create_prevent_cmd (&pc, 1);
+ (void) idefloppy_queue_pc_tail (drive, &pc);
+ check_disk_change(inode->i_rdev);
+ }
+ return 0;
+}
+
+static void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ idefloppy_pc_t pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+ printk (KERN_INFO "Reached idefloppy_release\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+ if (!drive->usage) {
+ invalidate_buffers (inode->i_rdev);
+ idefloppy_create_prevent_cmd (&pc, 0);
+ (void) idefloppy_queue_pc_tail (drive, &pc);
+ }
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Check media change. Use a simple algorithm for now.
+ */
+static int idefloppy_media_change (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ return clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+}
+
+/*
+ * Return the current floppy capacity to ide.c.
+ */
+static unsigned long idefloppy_capacity (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+ unsigned long capacity = floppy->blocks * floppy->bs_factor;
+
+ return capacity ? capacity : 0x7fffffff;
+}
+
+/*
+ * idefloppy_identify_device checks if we can support a drive,
+ * based on the ATAPI IDENTIFY command results.
+ */
+static int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+{
+ struct idefloppy_id_gcw gcw;
+#if IDEFLOPPY_DEBUG_INFO
+ unsigned short mask,i;
+ char buffer[80];
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+ *((unsigned short *) &gcw) = id->config;
+
+#if IDEFLOPPY_DEBUG_INFO
+ printk (KERN_INFO "Dumping ATAPI Identify Device floppy parameters\n");
+ switch (gcw.protocol) {
+ case 0: case 1: sprintf (buffer, "ATA");break;
+ case 2: sprintf (buffer, "ATAPI");break;
+ case 3: sprintf (buffer, "Reserved (Unknown to ide-floppy)");break;
+ }
+ printk (KERN_INFO "Protocol Type: %s\n", buffer);
+ switch (gcw.device_type) {
+ case 0: sprintf (buffer, "Direct-access Device");break;
+ case 1: sprintf (buffer, "Streaming Tape Device");break;
+ case 2: case 3: case 4: sprintf (buffer, "Reserved");break;
+ case 5: sprintf (buffer, "CD-ROM Device");break;
+ case 6: sprintf (buffer, "Reserved");
+ case 7: sprintf (buffer, "Optical memory Device");break;
+ case 0x1f: sprintf (buffer, "Unknown or no Device type");break;
+ default: sprintf (buffer, "Reserved");
+ }
+ printk (KERN_INFO "Device Type: %x - %s\n", gcw.device_type, buffer);
+ printk (KERN_INFO "Removable: %s\n",gcw.removable ? "Yes":"No");
+ switch (gcw.drq_type) {
+ case 0: sprintf (buffer, "Microprocessor DRQ");break;
+ case 1: sprintf (buffer, "Interrupt DRQ");break;
+ case 2: sprintf (buffer, "Accelerated DRQ");break;
+ case 3: sprintf (buffer, "Reserved");break;
+ }
+ printk (KERN_INFO "Command Packet DRQ Type: %s\n", buffer);
+ switch (gcw.packet_size) {
+ case 0: sprintf (buffer, "12 bytes");break;
+ case 1: sprintf (buffer, "16 bytes");break;
+ default: sprintf (buffer, "Reserved");break;
+ }
+ printk (KERN_INFO "Command Packet Size: %s\n", buffer);
+ printk (KERN_INFO "Model: %s\n",id->model);
+ printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev);
+ printk (KERN_INFO "Serial Number: %s\n",id->serial_no);
+ printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512);
+ printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+ printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+ printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+ printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+ printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
+ printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO);
+ printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA);
+ printk (KERN_INFO "Single Word DMA supported modes:\n");
+ for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+ if (id->dma_1word & mask)
+ printk (KERN_INFO " Mode %d%s\n", i, (id->dma_1word & (mask << 8)) ? " (active)" : "");
+ }
+ printk (KERN_INFO "Multi Word DMA supported modes:\n");
+ for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+ if (id->dma_mword & mask)
+ printk (KERN_INFO " Mode %d%s\n", i, (id->dma_mword & (mask << 8)) ? " (active)" : "");
+ }
+ if (id->field_valid & 0x0002) {
+ printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+ if (id->eide_dma_min == 0)
+ sprintf (buffer, "Not supported");
+ else
+ sprintf (buffer, "%d ns",id->eide_dma_min);
+ printk (KERN_INFO "Minimum Multi-word DMA cycle per word: %s\n", buffer);
+ if (id->eide_dma_time == 0)
+ sprintf (buffer, "Not supported");
+ else
+ sprintf (buffer, "%d ns",id->eide_dma_time);
+ printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: %s\n", buffer);
+ if (id->eide_pio == 0)
+ sprintf (buffer, "Not supported");
+ else
+ sprintf (buffer, "%d ns",id->eide_pio);
+ printk (KERN_INFO "Minimum PIO cycle without IORDY: %s\n", buffer);
+ if (id->eide_pio_iordy == 0)
+ sprintf (buffer, "Not supported");
+ else
+ sprintf (buffer, "%d ns",id->eide_pio_iordy);
+ printk (KERN_INFO "Minimum PIO cycle with IORDY: %s\n", buffer);
+ } else
+ printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+ if (gcw.protocol != 2)
+ printk (KERN_ERR "ide-floppy: Protocol is not ATAPI\n");
+ else if (gcw.device_type != 0)
+ printk (KERN_ERR "ide-floppy: Device type is not set to floppy\n");
+ else if (!gcw.removable)
+ printk (KERN_ERR "ide-floppy: The removable flag is not set\n");
+ else if (gcw.drq_type == 3) {
+ printk (KERN_ERR "ide-floppy: Sorry, DRQ type %d not supported\n", gcw.drq_type);
+ } else if (gcw.packet_size != 0) {
+ printk (KERN_ERR "ide-floppy: Packet size is not 12 bytes long\n");
+ } else
+ return 1;
+ return 0;
+}
+
+/*
+ * idefloppy_get_capabilities asks the floppy about its various
+ * parameters.
+ */
+static void idefloppy_get_capabilities (ide_drive_t *drive)
+{
+ idefloppy_pc_t pc;
+ idefloppy_mode_parameter_header_t *header;
+ idefloppy_capabilities_page_t *capabilities;
+
+ idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, MODE_SENSE_CURRENT);
+ if (idefloppy_queue_pc_tail (drive,&pc)) {
+ printk (KERN_ERR "ide-floppy: Can't get drive capabilities\n");
+ return;
+ }
+ header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+ capabilities = (idefloppy_capabilities_page_t *) (header + 1);
+
+ if (!capabilities->sflp)
+ printk (KERN_INFO "%s: Warning - system floppy device bit is not set\n", drive->name);
+
+#if IDEFLOPPY_DEBUG_INFO
+ printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n");
+ printk (KERN_INFO "Mode Parameter Header:\n");
+ printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length);
+ printk (KERN_INFO "Medium Type - %d\n",header->medium_type);
+ printk (KERN_INFO "WP - %d\n",header->wp);
+
+ printk (KERN_INFO "Capabilities Page:\n");
+ printk (KERN_INFO "Page code - %d\n",capabilities->page_code);
+ printk (KERN_INFO "Page length - %d\n",capabilities->page_length);
+ printk (KERN_INFO "PS - %d\n",capabilities->ps);
+ printk (KERN_INFO "System Floppy Type device - %s\n",capabilities->sflp ? "Yes":"No");
+ printk (KERN_INFO "Supports Reporting progress of Format - %s\n",capabilities->srfp ? "Yes":"No");
+ printk (KERN_INFO "Non CD Optical device - %s\n",capabilities->ncd ? "Yes":"No");
+ printk (KERN_INFO "Multiple LUN support - %s\n",capabilities->sml ? "Yes":"No");
+ printk (KERN_INFO "Total LUN supported - %s\n",capabilities->tlun ? "Yes":"No");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+}
+
+/*
+ * Driver initialization.
+ */
+static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy)
+{
+ struct idefloppy_id_gcw gcw;
+
+ *((unsigned short *) &gcw) = drive->id->config;
+ drive->driver_data = floppy;
+ drive->ready_stat = 0;
+ memset (floppy, 0, sizeof (idefloppy_floppy_t));
+ floppy->drive = drive;
+ floppy->pc = floppy->pc_stack;
+ if (gcw.drq_type == 1)
+ set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
+
+ idefloppy_get_capabilities (drive);
+ (void) idefloppy_get_capacity (drive);
+}
+
+static int idefloppy_cleanup (ide_drive_t *drive)
+{
+ idefloppy_floppy_t *floppy = drive->driver_data;
+
+ if (ide_unregister_subdriver (drive))
+ return 1;
+ drive->driver_data = NULL;
+ kfree (floppy);
+ return 0;
+}
+
+int idefloppy_init (void);
+static ide_module_t idefloppy_module = {
+ IDE_DRIVER_MODULE,
+ idefloppy_init,
+ NULL
+};
+
+/*
+ * IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idefloppy_driver = {
+ ide_floppy, /* media */
+ 0, /* busy */
+ 1, /* supports_dma */
+ idefloppy_cleanup, /* cleanup */
+ idefloppy_do_request, /* do_request */
+ idefloppy_end_request, /* end_request */
+ idefloppy_ioctl, /* ioctl */
+ idefloppy_open, /* open */
+ idefloppy_release, /* release */
+ idefloppy_media_change, /* media_change */
+ NULL, /* pre_reset */
+ idefloppy_capacity, /* capacity */
+ NULL /* special */
+};
+
+/*
+ * idefloppy_init will register the driver for each floppy.
+ */
+int idefloppy_init (void)
+{
+ ide_drive_t *drive;
+ idefloppy_floppy_t *floppy;
+ int failed = 0;
+
+ MOD_INC_USE_COUNT;
+ while ((drive = ide_scan_devices (ide_floppy, NULL, failed++)) != NULL) {
+ if (!idefloppy_identify_device (drive, drive->id)) {
+ printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name);
+ continue;
+ }
+ if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) {
+ printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name);
+ continue;
+ }
+ if (ide_register_subdriver (drive, &idefloppy_driver, IDE_SUBDRIVER_VERSION)) {
+ printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name);
+ kfree (floppy);
+ continue;
+ }
+ idefloppy_setup (drive, floppy);
+ failed--;
+ }
+ ide_register_module(&idefloppy_module);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return idefloppy_init ();
+}
+
+void cleanup_module (void)
+{
+ ide_drive_t *drive;
+ int failed = 0;
+
+ while ((drive = ide_scan_devices (ide_floppy, &idefloppy_driver, failed)) != NULL)
+ if (idefloppy_cleanup (drive)) {
+ printk ("%s: cleanup_module() called while still busy\n", drive->name);
+ failed++;
+ }
+ ide_unregister_module(&idefloppy_module);
+}
+#endif /* MODULE */
diff --git a/drivers/block/ide-probe.c b/drivers/block/ide-probe.c
new file mode 100644
index 000000000..7ddbede36
--- /dev/null
+++ b/drivers/block/ide-probe.c
@@ -0,0 +1,724 @@
+/*
+ * linux/drivers/block/ide-probe.c Version 1.0 Oct 31, 1996
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Maintained by Mark Lord <mlord@pobox.com>
+ * and Gadi Oxman <gadio@netvision.net.il>
+ *
+ * This is the IDE probe module, as evolved from hd.c and ide.c.
+ *
+ * From hd.c:
+ * |
+ * | It traverses the request-list, using interrupts to jump between functions.
+ * | As nearly all functions can be called within interrupts, we may not sleep.
+ * | Special care is recommended. Have Fun!
+ * |
+ * | modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ * |
+ * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * | in the early extended-partition checks and added DM partitions.
+ * |
+ * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
+ * |
+ * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ * | and general streamlining by Mark Lord (mlord@pobox.com).
+ *
+ * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
+ *
+ * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg)
+ * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
+ * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
+ *
+ * This was a rewrite of just about everything from hd.c, though some original
+ * code is still sprinkled about. Think of it as a major evolution, with
+ * inspiration from lots of linux users, esp. hamish@zot.apana.org.au
+ *
+ * Version 1.0 move drive probing code from ide.c to ide-probe.c
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include "ide.h"
+
+static inline void do_identify (ide_drive_t *drive, byte cmd)
+{
+ int bswap = 1;
+ struct hd_driveid *id;
+
+ id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_KERNEL);
+ ide_input_data(drive, id, SECTOR_WORDS); /* read 512 bytes of id info */
+ sti();
+ ide_fix_driveid(id);
+
+#if defined (CONFIG_SCSI_EATA_DMA) || defined (CONFIG_SCSI_EATA_PIO) || defined (CONFIG_SCSI_EATA)
+ /*
+ * EATA SCSI controllers do a hardware ATA emulation:
+ * Ignore them if there is a driver for them available.
+ */
+ if ((id->model[0] == 'P' && id->model[1] == 'M')
+ || (id->model[0] == 'S' && id->model[1] == 'K')) {
+ printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
+ drive->present = 0;
+ return;
+ }
+#endif /* CONFIG_SCSI_EATA_DMA || CONFIG_SCSI_EATA_PIO */
+
+ /*
+ * WIN_IDENTIFY returns little-endian info,
+ * WIN_PIDENTIFY *usually* returns little-endian info.
+ */
+ if (cmd == WIN_PIDENTIFY) {
+ if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
+ || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
+ || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
+ bswap ^= 1; /* Vertos drives may still be weird */
+ }
+ ide_fixstring (id->model, sizeof(id->model), bswap);
+ ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap);
+ ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
+
+ drive->present = 1;
+ printk("%s: %s, ", drive->name, id->model);
+
+ /*
+ * Check for an ATAPI device
+ */
+ if (cmd == WIN_PIDENTIFY) {
+ byte type = (id->config >> 8) & 0x1f;
+ printk("ATAPI ");
+#ifdef CONFIG_BLK_DEV_PROMISE
+ if (HWIF(drive)->is_promise2) {
+ printk(" -- not supported on 2nd Promise port\n");
+ drive->present = 0;
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_PROMISE */
+ switch (type) {
+ case ide_floppy:
+ if (strstr (id->model, "oppy") || strstr (id->model, "poyp")) {
+ printk ("FLOPPY");
+ break;
+ }
+ printk ("cdrom or floppy?, assuming ");
+ type = ide_cdrom; /* Early cdrom models used zero */
+ case ide_cdrom:
+ printk ("CDROM");
+ drive->removable = 1;
+ break;
+ case ide_tape:
+ printk ("TAPE");
+ break;
+ default:
+ printk("UNKNOWN (type %d)", type);
+ break;
+ }
+ printk (" drive\n");
+ drive->media = type;
+ return;
+ }
+
+ drive->media = ide_disk;
+ printk("ATA DISK drive\n");
+ return;
+}
+
+/*
+ * Delay for *at least* 50ms. As we don't know how much time is left
+ * until the next tick occurs, we wait an extra tick to be safe.
+ * This is used only during the probing/polling for drives at boot time.
+ */
+static void delay_50ms (void)
+{
+ unsigned long timer = jiffies + ((HZ + 19)/20) + 1;
+ while (timer > jiffies);
+}
+
+/*
+ * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
+ * and waits for a response. It also monitors irqs while this is
+ * happening, in hope of automatically determining which one is
+ * being used by the interface.
+ *
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ */
+static int try_to_identify (ide_drive_t *drive, byte cmd)
+{
+ int rc;
+ ide_ioreg_t hd_status;
+ unsigned long timeout;
+ int irqs = 0;
+
+ if (!HWIF(drive)->irq) { /* already got an IRQ? */
+ probe_irq_off(probe_irq_on()); /* clear dangling irqs */
+ irqs = probe_irq_on(); /* start monitoring irqs */
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */
+ }
+
+ delay_50ms(); /* take a deep breath */
+ if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) {
+ printk("%s: probing with STATUS instead of ALTSTATUS\n", drive->name);
+ hd_status = IDE_STATUS_REG; /* ancient Seagate drives */
+ } else
+ hd_status = IDE_ALTSTATUS_REG; /* use non-intrusive polling */
+
+#if CONFIG_BLK_DEV_PROMISE
+ if (IS_PROMISE_DRIVE) {
+ if (promise_cmd(drive,PROMISE_IDENTIFY)) {
+ if (irqs)
+ (void) probe_irq_off(irqs);
+ return 1;
+ }
+ } else
+#endif /* CONFIG_BLK_DEV_PROMISE */
+ OUT_BYTE(cmd,IDE_COMMAND_REG); /* ask drive for ID */
+ timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
+ timeout += jiffies;
+ do {
+ if (jiffies > timeout) {
+ if (irqs)
+ (void) probe_irq_off(irqs);
+ return 1; /* drive timed-out */
+ }
+ delay_50ms(); /* give drive a breather */
+ } while (IN_BYTE(hd_status) & BUSY_STAT);
+
+ delay_50ms(); /* wait for IRQ and DRQ_STAT */
+ if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
+ unsigned long flags;
+ save_flags(flags);
+ cli(); /* some systems need this */
+ do_identify(drive, cmd); /* drive returned ID */
+ rc = 0; /* drive responded with ID */
+ (void) GET_STAT(); /* clear drive IRQ */
+ restore_flags(flags);
+ } else
+ rc = 2; /* drive refused ID */
+ if (!HWIF(drive)->irq) {
+ irqs = probe_irq_off(irqs); /* get our irq number */
+ if (irqs > 0) {
+ HWIF(drive)->irq = irqs; /* save it for later */
+ irqs = probe_irq_on();
+ OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */
+ udelay(5);
+ (void) probe_irq_off(irqs);
+ (void) probe_irq_off(probe_irq_on()); /* clear self-inflicted irq */
+ (void) GET_STAT(); /* clear drive IRQ */
+
+ } else { /* Mmmm.. multiple IRQs.. don't know which was ours */
+ printk("%s: IRQ probe failed (%d)\n", drive->name, irqs);
+#ifdef CONFIG_BLK_DEV_CMD640
+#ifdef CMD640_DUMP_REGS
+ if (HWIF(drive)->chipset == ide_cmd640) {
+ printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
+ CMD640_DUMP_REGS;
+ }
+#endif /* CMD640_DUMP_REGS */
+#endif /* CONFIG_BLK_DEV_CMD640 */
+ }
+ }
+ return rc;
+}
+
+/*
+ * do_probe() has the difficult job of finding a drive if it exists,
+ * without getting hung up if it doesn't exist, without trampling on
+ * ethernet cards, and without leaving any IRQs dangling to haunt us later.
+ *
+ * If a drive is "known" to exist (from CMOS or kernel parameters),
+ * but does not respond right away, the probe will "hang in there"
+ * for the maximum wait time (about 30 seconds), otherwise it will
+ * exit much more quickly.
+ *
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ * 3 bad status from device (possible for ATAPI drives)
+ * 4 probe was not attempted because failure was obvious
+ */
+static int do_probe (ide_drive_t *drive, byte cmd)
+{
+ int rc;
+ ide_hwif_t *hwif = HWIF(drive);
+ if (drive->present) { /* avoid waiting for inappropriate probes */
+ if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
+ return 4;
+ }
+#ifdef DEBUG
+ printk("probing for %s: present=%d, media=%d, probetype=%s\n",
+ drive->name, drive->present, drive->media,
+ (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
+#endif
+ SELECT_DRIVE(hwif,drive);
+ delay_50ms();
+ if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
+ OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
+ delay_50ms(); /* allow BUSY_STAT to assert & clear */
+ return 3; /* no i/f present: avoid killing ethernet cards */
+ }
+
+ if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
+ || drive->present || cmd == WIN_PIDENTIFY)
+ {
+ if ((rc = try_to_identify(drive,cmd))) /* send cmd and wait */
+ rc = try_to_identify(drive,cmd); /* failed: try again */
+ if (rc == 1)
+ printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
+ (void) GET_STAT(); /* ensure drive irq is clear */
+ } else {
+ rc = 3; /* not present or maybe ATAPI */
+ }
+ if (drive->select.b.unit != 0) {
+ OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
+ delay_50ms();
+ (void) GET_STAT(); /* ensure drive irq is clear */
+ }
+ return rc;
+}
+
+/*
+ * probe_for_drive() tests for existence of a given drive using do_probe().
+ *
+ * Returns: 0 no device was found
+ * 1 device was found (note: drive->present might still be 0)
+ */
+static inline byte probe_for_drive (ide_drive_t *drive)
+{
+ if (drive->noprobe) /* skip probing? */
+ return drive->present;
+ if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
+ (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
+ }
+ if (!drive->present)
+ return 0; /* drive not found */
+ if (drive->id == NULL) { /* identification failed? */
+ if (drive->media == ide_disk) {
+ printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
+ drive->name, drive->cyl, drive->head, drive->sect);
+ } else if (drive->media == ide_cdrom) {
+ printk("%s: ATAPI cdrom (?)\n", drive->name);
+ } else {
+ drive->present = 0; /* nuke it */
+ }
+ }
+ return 1; /* drive was found */
+}
+
+/*
+ * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
+ * controller that is BIOS compatible with ST-506, and thus showing up in our
+ * BIOS table, but not register compatible, and therefore not present in CMOS.
+ *
+ * Furthermore, we will assume that our ST-506 drives <if any> are the primary
+ * drives in the system -- the ones reflected as drive 1 or 2. The first
+ * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
+ * nibble. This will be either a 4 bit drive type or 0xf indicating use byte
+ * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
+ * means we have an AT controller hard disk for that drive.
+ *
+ * Of course, there is no guarantee that either drive is actually on the
+ * "primary" IDE interface, but we don't bother trying to sort that out here.
+ * If a drive is not actually on the primary interface, then these parameters
+ * will be ignored. This results in the user having to supply the logical
+ * drive geometry as a boot parameter for each drive not on the primary i/f.
+ *
+ * The only "perfect" way to handle this would be to modify the setup.[cS] code
+ * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
+ * for us during initialization. I have the necessary docs -- any takers? -ml
+ */
+static void probe_cmos_for_drives (ide_hwif_t *hwif)
+{
+#ifdef __i386__
+ extern struct drive_info_struct drive_info;
+ byte cmos_disks, *BIOS = (byte *) &drive_info;
+ int unit;
+
+#ifdef CONFIG_BLK_DEV_PROMISE
+ if (hwif->is_promise2)
+ return;
+#endif /* CONFIG_BLK_DEV_PROMISE */
+ outb_p(0x12,0x70); /* specify CMOS address 0x12 */
+ cmos_disks = inb_p(0x71); /* read the data from 0x12 */
+ /* Extract drive geometry from CMOS+BIOS if not already setup */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present && !drive->nobios) {
+ drive->cyl = drive->bios_cyl = *(unsigned short *)BIOS;
+ drive->head = drive->bios_head = *(BIOS+2);
+ drive->sect = drive->bios_sect = *(BIOS+14);
+ drive->ctl = *(BIOS+8);
+ drive->present = 1;
+ }
+ BIOS += 16;
+ }
+#endif
+}
+
+/*
+ * This routine only knows how to look for drive units 0 and 1
+ * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
+ */
+static void probe_hwif (ide_hwif_t *hwif)
+{
+ unsigned int unit;
+ unsigned long flags;
+
+ if (hwif->noprobe)
+ return;
+ if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA)
+ probe_cmos_for_drives (hwif);
+#if CONFIG_BLK_DEV_PROMISE
+ if (!hwif->is_promise2 &&
+ (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1))) {
+#else
+ if (ide_check_region(hwif->io_ports[IDE_DATA_OFFSET],8) || ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET],1)) {
+#endif /* CONFIG_BLK_DEV_PROMISE */
+ int msgout = 0;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present) {
+ drive->present = 0;
+ printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
+ msgout = 1;
+ }
+ }
+ if (!msgout)
+ printk("%s: ports already in use, skipping probe\n", hwif->name);
+ return;
+ }
+
+ save_flags(flags);
+ sti(); /* needed for jiffies and irq probing */
+ /*
+ * Second drive should only exist if first drive was found,
+ * but a lot of cdrom drives are configured as single slaves.
+ */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ (void) probe_for_drive (drive);
+ if (drive->present && !hwif->present) {
+ hwif->present = 1;
+ ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name);
+ ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name);
+ }
+ }
+ if (hwif->reset) {
+ unsigned long timeout = jiffies + WAIT_WORSTCASE;
+ byte stat;
+
+ printk("%s: reset\n", hwif->name);
+ OUT_BYTE(12, hwif->io_ports[IDE_CONTROL_OFFSET]);
+ udelay(10);
+ OUT_BYTE(8, hwif->io_ports[IDE_CONTROL_OFFSET]);
+ do {
+ delay_50ms();
+ stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+ } while ((stat & BUSY_STAT) && jiffies < timeout);
+ }
+ restore_flags(flags);
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present) {
+ ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+ if (tuneproc != NULL && drive->autotune == 1)
+ tuneproc(drive, 255); /* auto-tune PIO mode */
+ }
+ }
+}
+
+#if MAX_HWIFS > 1
+/*
+ * save_match() is used to simplify logic in init_irq() below.
+ *
+ * A loophole here is that we may not know about a particular
+ * hwif's irq until after that hwif is actually probed/initialized..
+ * This could be a problem for the case where an hwif is on a
+ * dual interface that requires serialization (eg. cmd640) and another
+ * hwif using one of the same irqs is initialized beforehand.
+ *
+ * This routine detects and reports such situations, but does not fix them.
+ */
+static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
+{
+ ide_hwif_t *m = *match;
+
+ if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
+ if (!new->hwgroup)
+ return;
+ printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name);
+ }
+ if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
+ *match = new;
+}
+#endif /* MAX_HWIFS > 1 */
+
+/*
+ * This routine sets up the irq for an ide interface, and creates a new
+ * hwgroup for the irq/hwif if none was previously assigned.
+ *
+ * Much of the code is for correctly detecting/handling irq sharing
+ * and irq serialization situations. This is somewhat complex because
+ * it handles static as well as dynamic (PCMCIA) IDE interfaces.
+ *
+ * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
+ * interrupts completely disabled. This can be bad for interrupt latency,
+ * but anything else has led to problems on some machines. We re-enable
+ * interrupts as much as we can safely do in most places.
+ */
+static int init_irq (ide_hwif_t *hwif)
+{
+ unsigned long flags;
+#if MAX_HWIFS > 1
+ unsigned int index;
+#endif /* MAX_HWIFS > 1 */
+ ide_hwgroup_t *hwgroup;
+ ide_hwif_t *match = NULL;
+
+ save_flags(flags);
+ cli();
+
+ hwif->hwgroup = NULL;
+#if MAX_HWIFS > 1
+ /*
+ * Group up with any other hwifs that share our irq(s).
+ */
+ for (index = 0; index < MAX_HWIFS; index++) {
+ ide_hwif_t *h = &ide_hwifs[index];
+ if (h->hwgroup) { /* scan only initialized hwif's */
+ if (hwif->irq == h->irq) {
+ hwif->sharing_irq = h->sharing_irq = 1;
+ save_match(hwif, h, &match);
+ }
+ if (hwif->serialized) {
+ ide_hwif_t *mate = &ide_hwifs[hwif->index^1];
+ if (index == mate->index || h->irq == mate->irq)
+ save_match(hwif, h, &match);
+ }
+ if (h->serialized) {
+ ide_hwif_t *mate = &ide_hwifs[h->index^1];
+ if (hwif->irq == mate->irq)
+ save_match(hwif, h, &match);
+ }
+ }
+ }
+#endif /* MAX_HWIFS > 1 */
+ /*
+ * If we are still without a hwgroup, then form a new one
+ */
+ if (match) {
+ hwgroup = match->hwgroup;
+ } else {
+ hwgroup = kmalloc(sizeof(ide_hwgroup_t), GFP_KERNEL);
+ hwgroup->hwif = hwgroup->next_hwif = hwif->next = hwif;
+ hwgroup->rq = NULL;
+ hwgroup->handler = NULL;
+ if (hwif->drives[0].present)
+ hwgroup->drive = &hwif->drives[0];
+ else
+ hwgroup->drive = &hwif->drives[1];
+ hwgroup->poll_timeout = 0;
+ init_timer(&hwgroup->timer);
+ hwgroup->timer.function = &ide_timer_expiry;
+ hwgroup->timer.data = (unsigned long) hwgroup;
+ }
+
+ /*
+ * Allocate the irq, if not already obtained for another hwif
+ */
+ if (!match || match->irq != hwif->irq) {
+ if (ide_request_irq(hwif->irq, &ide_intr, SA_INTERRUPT, hwif->name, hwgroup)) {
+ if (!match)
+ kfree(hwgroup);
+ restore_flags(flags);
+ return 1;
+ }
+ }
+
+ /*
+ * Everything is okay, so link us into the hwgroup
+ */
+ hwif->hwgroup = hwgroup;
+ hwif->next = hwgroup->hwif->next;
+ hwgroup->hwif->next = hwif;
+
+ restore_flags(flags); /* safe now that hwif->hwgroup is set up */
+
+#ifndef __mc68000__
+ printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
+ hwif->io_ports[IDE_DATA_OFFSET], hwif->io_ports[IDE_DATA_OFFSET]+7, hwif->io_ports[IDE_CONTROL_OFFSET], hwif->irq);
+#else
+ printk("%s at %p on irq 0x%08x", hwif->name, hwif->io_ports[IDE_DATA_OFFSET], hwif->irq);
+#endif /* __mc68000__ */
+ if (match)
+ printk(" (%sed with %s)", hwif->sharing_irq ? "shar" : "serializ", match->name);
+ printk("\n");
+ return 0;
+}
+
+/*
+ * init_gendisk() (as opposed to ide_geninit) is called for each major device,
+ * after probing for drives, to allocate partition tables and other data
+ * structures needed for the routines in genhd.c. ide_geninit() gets called
+ * somewhat later, during the partition check.
+ */
+static void init_gendisk (ide_hwif_t *hwif)
+{
+ struct gendisk *gd, **gdp;
+ unsigned int unit, units, minors;
+ int *bs;
+
+ /* figure out maximum drive number on the interface */
+ for (units = MAX_DRIVES; units > 0; --units) {
+ if (hwif->drives[units-1].present)
+ break;
+ }
+ minors = units * (1<<PARTN_BITS);
+ gd = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
+ gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
+ gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
+ bs = kmalloc (minors*sizeof(int), GFP_KERNEL);
+
+ memset(gd->part, 0, minors * sizeof(struct hd_struct));
+
+ /* cdroms and msdos f/s are examples of non-1024 blocksizes */
+ blksize_size[hwif->major] = bs;
+ for (unit = 0; unit < minors; ++unit)
+ *bs++ = BLOCK_SIZE;
+
+ for (unit = 0; unit < units; ++unit)
+ hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
+
+ gd->major = hwif->major; /* our major device number */
+ gd->major_name = IDE_MAJOR_NAME; /* treated special in genhd.c */
+ gd->minor_shift = PARTN_BITS; /* num bits for partitions */
+ gd->max_p = 1<<PARTN_BITS; /* 1 + max partitions / drive */
+ gd->max_nr = units; /* max num real drives */
+ gd->nr_real = units; /* current num real drives */
+ gd->init = &ide_geninit; /* initialization function */
+ gd->real_devices= hwif; /* ptr to internal data */
+ gd->next = NULL; /* linked list of major devs */
+
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
+ hwif->gd = *gdp = gd; /* link onto tail of list */
+}
+
+static int hwif_init (int h)
+{
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ void (*rfn)(void);
+
+ if (!hwif->present)
+ return 0;
+ if (!hwif->irq) {
+ if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]))) {
+ printk("%s: DISABLED, NO IRQ\n", hwif->name);
+ return (hwif->present = 0);
+ }
+ }
+#ifdef CONFIG_BLK_DEV_HD
+ if (hwif->irq == HD_IRQ && hwif->io_ports[IDE_DATA_OFFSET] != HD_DATA) {
+ printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
+ return (hwif->present = 0);
+ }
+#endif /* CONFIG_BLK_DEV_HD */
+
+ hwif->present = 0; /* we set it back to 1 if all is ok below */
+ switch (hwif->major) {
+ case IDE0_MAJOR: rfn = &do_ide0_request; break;
+#if MAX_HWIFS > 1
+ case IDE1_MAJOR: rfn = &do_ide1_request; break;
+#endif
+#if MAX_HWIFS > 2
+ case IDE2_MAJOR: rfn = &do_ide2_request; break;
+#endif
+#if MAX_HWIFS > 3
+ case IDE3_MAJOR: rfn = &do_ide3_request; break;
+#endif
+ default:
+ printk("%s: request_fn NOT DEFINED\n", hwif->name);
+ return (hwif->present = 0);
+ }
+ if (register_blkdev (hwif->major, hwif->name, ide_fops)) {
+ printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
+ } else if (init_irq (hwif)) {
+ printk("%s: UNABLE TO GET IRQ %d\n", hwif->name, hwif->irq);
+ (void) unregister_blkdev (hwif->major, hwif->name);
+ } else {
+ init_gendisk(hwif);
+ blk_dev[hwif->major].request_fn = rfn;
+ read_ahead[hwif->major] = 8; /* (4kB) */
+ hwif->present = 1; /* success */
+ }
+ return hwif->present;
+}
+
+int ideprobe_init (void);
+static ide_module_t ideprobe_module = {
+ IDE_PROBE_MODULE,
+ ideprobe_init,
+ NULL
+};
+
+int ideprobe_init (void)
+{
+ unsigned int index;
+ int probe[MAX_HWIFS];
+
+ MOD_INC_USE_COUNT;
+ memset(probe, 0, MAX_HWIFS * sizeof(int));
+ for (index = 0; index < MAX_HWIFS; ++index)
+ probe[index] = !ide_hwifs[index].present;
+
+ /*
+ * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
+ */
+ for (index = 0; index < MAX_HWIFS; ++index)
+ if (probe[index]) probe_hwif (&ide_hwifs[index]);
+ for (index = 0; index < MAX_HWIFS; ++index)
+ if (probe[index]) hwif_init (index);
+ ide_register_module(&ideprobe_module);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ unsigned int index;
+
+ for (index = 0; index < MAX_HWIFS; ++index)
+ ide_unregister(index);
+ return ideprobe_init();
+}
+
+void cleanup_module (void)
+{
+ ide_unregister_module(&ideprobe_module);
+}
+#endif /* MODULE */
diff --git a/drivers/block/ide-tape.c b/drivers/block/ide-tape.c
new file mode 100644
index 000000000..244072d91
--- /dev/null
+++ b/drivers/block/ide-tape.c
@@ -0,0 +1,3786 @@
+/*
+ * linux/drivers/block/ide-tape.c Version 1.10 - BETA Nov 5, 1996
+ *
+ * Copyright (C) 1995, 1996 Gadi Oxman <gadio@netvision.net.il>
+ *
+ * This driver was constructed as a student project in the software laboratory
+ * of the faculty of electrical engineering in the Technion - Israel's
+ * Institute Of Technology, with the guide of Avner Lottem and Dr. Ilana David.
+ *
+ * It is hereby placed under the terms of the GNU general public license.
+ * (See linux/COPYING).
+ */
+
+/*
+ * IDE ATAPI streaming tape driver.
+ *
+ * This driver is a part of the Linux ide driver and works in co-operation
+ * with linux/drivers/block/ide.c.
+ *
+ * The driver, in co-operation with ide.c, basically traverses the
+ * request-list for the block device interface. The character device
+ * interface, on the other hand, creates new requests, adds them
+ * to the request-list of the block device, and waits for their completion.
+ *
+ * Pipelined operation mode is now supported on both reads and writes.
+ *
+ * The block device major and minor numbers are determined from the
+ * tape's relative position in the ide interfaces, as explained in ide.c.
+ *
+ * The character device interface consists of the following devices:
+ *
+ * ht0 major 37, minor 0 first IDE tape, rewind on close.
+ * ht1 major 37, minor 1 second IDE tape, rewind on close.
+ * ...
+ * nht0 major 37, minor 128 first IDE tape, no rewind on close.
+ * nht1 major 37, minor 129 second IDE tape, no rewind on close.
+ * ...
+ *
+ * Run linux/scripts/MAKEDEV.ide to create the above entries.
+ *
+ * The general magnetic tape commands compatible interface, as defined by
+ * include/linux/mtio.h, is accessible through the character device.
+ *
+ * General ide driver configuration options, such as the interrupt-unmask
+ * flag, can be configured by issuing an ioctl to the block device interface,
+ * as any other ide device.
+ *
+ * Our own ide-tape ioctl's can can be issued to either the block device or
+ * the character device interface.
+ *
+ * Maximal throughput with minimal bus load will usually be achieved in the
+ * following scenario:
+ *
+ * 1. ide-tape is operating in the pipelined operation mode.
+ * 2. No buffering is performed by the user backup program.
+ *
+ * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive.
+ *
+ * Ver 0.1 Nov 1 95 Pre-working code :-)
+ * Ver 0.2 Nov 23 95 A short backup (few megabytes) and restore procedure
+ * was successful ! (Using tar cvf ... on the block
+ * device interface).
+ * A longer backup resulted in major swapping, bad
+ * overall Linux performance and eventually failed as
+ * we received non serial read-ahead requests from the
+ * buffer cache.
+ * Ver 0.3 Nov 28 95 Long backups are now possible, thanks to the
+ * character device interface. Linux's responsiveness
+ * and performance doesn't seem to be much affected
+ * from the background backup procedure.
+ * Some general mtio.h magnetic tape operations are
+ * now supported by our character device. As a result,
+ * popular tape utilities are starting to work with
+ * ide tapes :-)
+ * The following configurations were tested:
+ * 1. An IDE ATAPI TAPE shares the same interface
+ * and irq with an IDE ATAPI CDROM.
+ * 2. An IDE ATAPI TAPE shares the same interface
+ * and irq with a normal IDE disk.
+ * Both configurations seemed to work just fine !
+ * However, to be on the safe side, it is meanwhile
+ * recommended to give the IDE TAPE its own interface
+ * and irq.
+ * The one thing which needs to be done here is to
+ * add a "request postpone" feature to ide.c,
+ * so that we won't have to wait for the tape to finish
+ * performing a long media access (DSC) request (such
+ * as a rewind) before we can access the other device
+ * on the same interface. This effect doesn't disturb
+ * normal operation most of the time because read/write
+ * requests are relatively fast, and once we are
+ * performing one tape r/w request, a lot of requests
+ * from the other device can be queued and ide.c will
+ * service all of them after this single tape request.
+ * Ver 1.0 Dec 11 95 Integrated into Linux 1.3.46 development tree.
+ * On each read / write request, we now ask the drive
+ * if we can transfer a constant number of bytes
+ * (a parameter of the drive) only to its buffers,
+ * without causing actual media access. If we can't,
+ * we just wait until we can by polling the DSC bit.
+ * This ensures that while we are not transferring
+ * more bytes than the constant referred to above, the
+ * interrupt latency will not become too high and
+ * we won't cause an interrupt timeout, as happened
+ * occasionally in the previous version.
+ * While polling for DSC, the current request is
+ * postponed and ide.c is free to handle requests from
+ * the other device. This is handled transparently to
+ * ide.c. The hwgroup locking method which was used
+ * in the previous version was removed.
+ * Use of new general features which are provided by
+ * ide.c for use with atapi devices.
+ * (Programming done by Mark Lord)
+ * Few potential bug fixes (Again, suggested by Mark)
+ * Single character device data transfers are now
+ * not limited in size, as they were before.
+ * We are asking the tape about its recommended
+ * transfer unit and send a larger data transfer
+ * as several transfers of the above size.
+ * For best results, use an integral number of this
+ * basic unit (which is shown during driver
+ * initialization). I will soon add an ioctl to get
+ * this important parameter.
+ * Our data transfer buffer is allocated on startup,
+ * rather than before each data transfer. This should
+ * ensure that we will indeed have a data buffer.
+ * Ver 1.1 Dec 14 95 Fixed random problems which occurred when the tape
+ * shared an interface with another device.
+ * (poll_for_dsc was a complete mess).
+ * Removed some old (non-active) code which had
+ * to do with supporting buffer cache originated
+ * requests.
+ * The block device interface can now be opened, so
+ * that general ide driver features like the unmask
+ * interrupts flag can be selected with an ioctl.
+ * This is the only use of the block device interface.
+ * New fast pipelined operation mode (currently only on
+ * writes). When using the pipelined mode, the
+ * throughput can potentially reach the maximum
+ * tape supported throughput, regardless of the
+ * user backup program. On my tape drive, it sometimes
+ * boosted performance by a factor of 2. Pipelined
+ * mode is enabled by default, but since it has a few
+ * downfalls as well, you may want to disable it.
+ * A short explanation of the pipelined operation mode
+ * is available below.
+ * Ver 1.2 Jan 1 96 Eliminated pipelined mode race condition.
+ * Added pipeline read mode. As a result, restores
+ * are now as fast as backups.
+ * Optimized shared interface behavior. The new behavior
+ * typically results in better IDE bus efficiency and
+ * higher tape throughput.
+ * Pre-calculation of the expected read/write request
+ * service time, based on the tape's parameters. In
+ * the pipelined operation mode, this allows us to
+ * adjust our polling frequency to a much lower value,
+ * and thus to dramatically reduce our load on Linux,
+ * without any decrease in performance.
+ * Implemented additional mtio.h operations.
+ * The recommended user block size is returned by
+ * the MTIOCGET ioctl.
+ * Additional minor changes.
+ * Ver 1.3 Feb 9 96 Fixed pipelined read mode bug which prevented the
+ * use of some block sizes during a restore procedure.
+ * The character device interface will now present a
+ * continuous view of the media - any mix of block sizes
+ * during a backup/restore procedure is supported. The
+ * driver will buffer the requests internally and
+ * convert them to the tape's recommended transfer
+ * unit, making performance almost independent of the
+ * chosen user block size.
+ * Some improvements in error recovery.
+ * By cooperating with triton.c, bus mastering DMA can
+ * now sometimes be used with IDE tape drives as well.
+ * Bus mastering DMA has the potential to dramatically
+ * reduce the CPU's overhead when accessing the device,
+ * and can be enabled by using hdparm -d1 on the tape's
+ * block device interface. For more info, read the
+ * comments in triton.c.
+ * Ver 1.4 Mar 13 96 Fixed serialize support.
+ * Ver 1.5 Apr 12 96 Fixed shared interface operation, broken in 1.3.85.
+ * Fixed pipelined read mode inefficiency.
+ * Fixed nasty null dereferencing bug.
+ * Ver 1.6 Aug 16 96 Fixed FPU usage in the driver.
+ * Fixed end of media bug.
+ * Ver 1.7 Sep 10 96 Minor changes for the CONNER CTT8000-A model.
+ * Ver 1.8 Sep 26 96 Attempt to find a better balance between good
+ * interactive response and high system throughput.
+ * Ver 1.9 Nov 5 96 Automatically cross encountered filemarks rather
+ * than requiring an explicit FSF command.
+ * Abort pending requests at end of media.
+ * MTTELL was sometimes returning incorrect results.
+ * Return the real block size in the MTIOCGET ioctl.
+ * Some error recovery bug fixes.
+ * Ver 1.10 Nov 5 96 Major reorganization.
+ * Reduced CPU overhead a bit by eliminating internal
+ * bounce buffers.
+ * Added module support.
+ * Added multiple tape drives support.
+ * Added partition support.
+ * Rewrote DSC handling.
+ * Some portability fixes.
+ * Removed ide-tape.h.
+ * Additional minor changes.
+ *
+ * Here are some words from the first releases of hd.c, which are quoted
+ * in ide.c and apply here as well:
+ *
+ * | Special care is recommended. Have Fun!
+ *
+ */
+
+/*
+ * An overview of the pipelined operation mode.
+ *
+ * In the pipelined write mode, we will usually just add requests to our
+ * pipeline and return immediately, before we even start to service them. The
+ * user program will then have enough time to prepare the next request while
+ * we are still busy servicing previous requests. In the pipelined read mode,
+ * the situation is similar - we add read-ahead requests into the pipeline,
+ * before the user even requested them.
+ *
+ * The pipeline can be viewed as a "safety net" which will be activated when
+ * the system load is high and prevents the user backup program from keeping up
+ * with the current tape speed. At this point, the pipeline will get
+ * shorter and shorter but the tape will still be streaming at the same speed.
+ * Assuming we have enough pipeline stages, the system load will hopefully
+ * decrease before the pipeline is completely empty, and the backup program
+ * will be able to "catch up" and refill the pipeline again.
+ *
+ * When using the pipelined mode, it would be best to disable any type of
+ * buffering done by the user program, as ide-tape already provides all the
+ * benefits in the kernel, where it can be done in a more efficient way.
+ * As we will usually not block the user program on a request, the most
+ * efficient user code will then be a simple read-write-read-... cycle.
+ * Any additional logic will usually just slow down the backup process.
+ *
+ * Using the pipelined mode, I get a constant over 400 KBps throughput,
+ * which seems to be the maximum throughput supported by my tape.
+ *
+ * However, there are some downfalls:
+ *
+ * 1. We use memory (for data buffers) in proportional to the number
+ * of pipeline stages (each stage is about 26 KB with my tape).
+ * 2. In the pipelined write mode, we cheat and postpone error codes
+ * to the user task. In read mode, the actual tape position
+ * will be a bit further than the last requested block.
+ *
+ * Concerning (1):
+ *
+ * 1. We allocate stages dynamically only when we need them. When
+ * we don't need them, we don't consume additional memory. In
+ * case we can't allocate stages, we just manage without them
+ * (at the expense of decreased throughput) so when Linux is
+ * tight in memory, we will not pose additional difficulties.
+ *
+ * 2. The maximum number of stages (which is, in fact, the maximum
+ * amount of memory) which we allocate is limited by the compile
+ * time parameter IDETAPE_MAX_PIPELINE_STAGES.
+ *
+ * 3. The maximum number of stages is a controlled parameter - We
+ * don't start from the user defined maximum number of stages
+ * but from the lower IDETAPE_MIN_PIPELINE_STAGES (again, we
+ * will not even allocate this amount of stages if the user
+ * program can't handle the speed). We then implement a feedback
+ * loop which checks if the pipeline is empty, and if it is, we
+ * increase the maximum number of stages as necessary until we
+ * reach the optimum value which just manages to keep the tape
+ * busy with with minimum allocated memory or until we reach
+ * IDETAPE_MAX_PIPELINE_STAGES.
+ *
+ * Concerning (2):
+ *
+ * In pipelined write mode, ide-tape can not return accurate error codes
+ * to the user program since we usually just add the request to the
+ * pipeline without waiting for it to be serviced. In case an error
+ * occurs, I will report it on the next user request.
+ *
+ * In the pipelined read mode, subsequent read requests or forward
+ * filemark spacing will perform correctly, as we preserve all blocks
+ * and filemarks which we encountered during our excess read-ahead.
+ *
+ * For accurate tape positioning and error reporting, disabling
+ * pipelined mode might be the best option.
+ *
+ * You can enable/disable/tune the pipelined operation mode by adjusting
+ * the compile time parameters below.
+ */
+
+/*
+ * Possible improvements.
+ *
+ * 1. Support for the ATAPI overlap protocol.
+ *
+ * In order to maximize bus throughput, we currently use the DSC
+ * overlap method which enables ide.c to service requests from the
+ * other device while the tape is busy executing a command. The
+ * DSC overlap method involves polling the tape's status register
+ * for the DSC bit, and servicing the other device while the tape
+ * isn't ready.
+ *
+ * In the current QIC development standard (December 1995),
+ * it is recommended that new tape drives will *in addition*
+ * implement the ATAPI overlap protocol, which is used for the
+ * same purpose - efficient use of the IDE bus, but is interrupt
+ * driven and thus has much less CPU overhead.
+ *
+ * ATAPI overlap is likely to be supported in most new ATAPI
+ * devices, including new ATAPI cdroms, and thus provides us
+ * a method by which we can achieve higher throughput when
+ * sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/bitops.h>
+
+/*
+ * Main Linux ide driver include file
+ */
+#include "ide.h"
+
+/*
+ * For general magnetic tape device compatibility.
+ */
+#include <linux/mtio.h>
+
+/**************************** Tunable parameters *****************************/
+
+/*
+ * Pipelined mode parameters.
+ *
+ * We try to use the minimum number of stages which is enough to
+ * keep the tape constantly streaming. To accomplish that, we implement
+ * a feedback loop around the maximum number of stages:
+ *
+ * We start from MIN maximum stages (we will not even use MIN stages
+ * if we don't need them), increment it by RATE*(MAX-MIN)
+ * whenever we sense that the pipeline is empty, until we reach
+ * the optimum value or until we reach MAX.
+ *
+ * Setting the following parameter to 0 will disable the pipelined mode.
+ */
+#define IDETAPE_MIN_PIPELINE_STAGES 100
+#define IDETAPE_MAX_PIPELINE_STAGES 200
+#define IDETAPE_INCREASE_STAGES_RATE 20
+
+/*
+ * Assuming the tape shares an interface with another device, the default
+ * behavior is to service our pending pipeline requests as soon as
+ * possible, but to gracefully postpone them in favor of the other device
+ * when the tape is busy. This has the potential to maximize our
+ * throughput and in the same time, to make efficient use of the IDE bus.
+ *
+ * Note that when we transfer data to / from the tape, we co-operate with
+ * the relatively fast tape buffers and the tape will perform the
+ * actual media access in the background, without blocking the IDE
+ * bus. This means that as long as the maximum IDE bus throughput is much
+ * higher than the sum of our maximum throughput and the maximum
+ * throughput of the other device, we should probably leave the default
+ * behavior.
+ *
+ * However, if it is still desired to give the other device a share even
+ * in our own (small) bus bandwidth, you can set IDETAPE_LOW_TAPE_PRIORITY
+ * to 1. This will let the other device finish *all* its pending requests
+ * before we even check if we can service our next pending request.
+ */
+#define IDETAPE_LOW_TAPE_PRIORITY 0
+
+/*
+ * The following are used to debug the driver:
+ *
+ * Setting IDETAPE_DEBUG_LOG to 1 will log driver flow control.
+ * Setting IDETAPE_DEBUG_BUGS to 1 will enable self-sanity checks in
+ * some places.
+ *
+ * Setting them to 0 will restore normal operation mode:
+ *
+ * 1. Disable logging normal successful operations.
+ * 2. Disable self-sanity checks.
+ * 3. Errors will still be logged, of course.
+ *
+ * All the #if DEBUG code will be removed some day, when the driver
+ * is verified to be stable enough. This will make it much more
+ * esthetic.
+ */
+#define IDETAPE_DEBUG_LOG 0
+#define IDETAPE_DEBUG_BUGS 1
+
+/*
+ * After each failed packet command we issue a request sense command
+ * and retry the packet command IDETAPE_MAX_PC_RETRIES times.
+ *
+ * Setting IDETAPE_MAX_PC_RETRIES to 0 will disable retries.
+ */
+#define IDETAPE_MAX_PC_RETRIES 3
+
+/*
+ * With each packet command, we allocate a buffer of
+ * IDETAPE_PC_BUFFER_SIZE bytes. This is used for several packet
+ * commands (Not for READ/WRITE commands).
+ */
+#define IDETAPE_PC_BUFFER_SIZE 256
+
+/*
+ * In various places in the driver, we need to allocate storage
+ * for packet commands and requests, which will remain valid while
+ * we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDETAPE_PC_STACK (10 + IDETAPE_MAX_PC_RETRIES)
+
+/*
+ * DSC polling parameters.
+ *
+ * Polling for DSC (a single bit in the status register) is a very
+ * important function in ide-tape. There are two cases in which we
+ * poll for DSC:
+ *
+ * 1. Before a read/write packet command, to ensure that we
+ * can transfer data from/to the tape's data buffers, without
+ * causing an actual media access. In case the tape is not
+ * ready yet, we take out our request from the device
+ * request queue, so that ide.c will service requests from
+ * the other device on the same interface meanwhile.
+ *
+ * 2. After the successful initialization of a "media access
+ * packet command", which is a command which can take a long
+ * time to complete (it can be several seconds or even an hour).
+ *
+ * Again, we postpone our request in the middle to free the bus
+ * for the other device. The polling frequency here should be
+ * lower than the read/write frequency since those media access
+ * commands are slow. We start from a "fast" frequency -
+ * IDETAPE_DSC_MA_FAST (one second), and if we don't receive DSC
+ * after IDETAPE_DSC_MA_THRESHOLD (5 minutes), we switch it to a
+ * lower frequency - IDETAPE_DSC_MA_SLOW (1 minute).
+ *
+ * We also set a timeout for the timer, in case something goes wrong.
+ * The timeout should be longer then the maximum execution time of a
+ * tape operation.
+ */
+
+/*
+ * The following parameter is used to select the point in the internal
+ * tape fifo in which we will start to refill the buffer. Decreasing
+ * the following parameter will improve the system's latency and
+ * interactive response, while using a high value might improve sytem
+ * throughput.
+ */
+#define IDETAPE_FIFO_THRESHOLD 2
+
+/*
+ * DSC timings.
+ */
+#define IDETAPE_DSC_RW_MIN 5*HZ/100 /* 50 msec */
+#define IDETAPE_DSC_RW_MAX 40*HZ/100 /* 400 msec */
+#define IDETAPE_DSC_RW_TIMEOUT 2*60*HZ /* 2 minutes */
+#define IDETAPE_DSC_MA_FAST 2*HZ /* 2 seconds */
+#define IDETAPE_DSC_MA_THRESHOLD 5*60*HZ /* 5 minutes */
+#define IDETAPE_DSC_MA_SLOW 30*HZ /* 30 seconds */
+#define IDETAPE_DSC_MA_TIMEOUT 2*60*60*HZ /* 2 hours */
+
+/*************************** End of tunable parameters ***********************/
+
+typedef enum {
+ idetape_direction_none,
+ idetape_direction_read,
+ idetape_direction_write
+} idetape_chrdev_direction_t;
+
+/*
+ * Our view of a packet command.
+ */
+typedef struct idetape_packet_command_s {
+ u8 c[12]; /* Actual packet bytes */
+ int retries; /* On each retry, we increment retries */
+ int error; /* Error code */
+ int request_transfer; /* Bytes to transfer */
+ int actually_transferred; /* Bytes actually transferred */
+ int buffer_size; /* Size of our data buffer */
+ struct buffer_head *bh;
+ char *b_data;
+ int b_count;
+ byte *buffer; /* Data buffer */
+ byte *current_position; /* Pointer into the above buffer */
+ void (*callback) (ide_drive_t *); /* Called when this packet command is completed */
+ byte pc_buffer[IDETAPE_PC_BUFFER_SIZE]; /* Temporary buffer */
+ unsigned int flags; /* Status/Action bit flags */
+} idetape_pc_t;
+
+/*
+ * Packet command flag bits.
+ */
+#define PC_ABORT 0 /* Set when an error is considered normal - We won't retry */
+#define PC_WAIT_FOR_DSC 1 /* 1 When polling for DSC on a media access command */
+#define PC_DMA_RECOMMENDED 2 /* 1 when we prefer to use DMA if possible */
+#define PC_DMA_IN_PROGRESS 3 /* 1 while DMA in progress */
+#define PC_DMA_ERROR 4 /* 1 when encountered problem during DMA */
+#define PC_WRITING 5 /* Data direction */
+
+/*
+ * Capabilities and Mechanical Status Page
+ */
+typedef struct {
+ unsigned page_code :6; /* Page code - Should be 0x2a */
+ unsigned reserved1_67 :2;
+ u8 page_length; /* Page Length - Should be 0x12 */
+ u8 reserved2, reserved3;
+ unsigned ro :1; /* Read Only Mode */
+ unsigned reserved4_1234 :4;
+ unsigned sprev :1; /* Supports SPACE in the reverse direction */
+ unsigned reserved4_67 :2;
+ unsigned reserved5_012 :3;
+ unsigned efmt :1; /* Supports ERASE command initiated formatting */
+ unsigned reserved5_4 :1;
+ unsigned qfa :1; /* Supports the QFA two partition formats */
+ unsigned reserved5_67 :2;
+ unsigned lock :1; /* Supports locking the volume */
+ unsigned locked :1; /* The volume is locked */
+ unsigned prevent :1; /* The device defaults in the prevent state after power up */
+ unsigned eject :1; /* The device can eject the volume */
+ unsigned reserved6_45 :2; /* Reserved */
+ unsigned ecc :1; /* Supports error correction */
+ unsigned cmprs :1; /* Supports data compression */
+ unsigned reserved7_0 :1;
+ unsigned blk512 :1; /* Supports 512 bytes block size */
+ unsigned blk1024 :1; /* Supports 1024 bytes block size */
+ unsigned reserved7_3_6 :4;
+ unsigned slowb :1; /* The device restricts the byte count for PIO */
+ /* transfers for slow buffer memory ??? */
+ u16 max_speed; /* Maximum speed supported in KBps */
+ u8 reserved10, reserved11;
+ u16 ctl; /* Continuous Transfer Limit in blocks */
+ u16 speed; /* Current Speed, in KBps */
+ u16 buffer_size; /* Buffer Size, in 512 bytes */
+ u8 reserved18, reserved19;
+} idetape_capabilities_page_t;
+
+/*
+ * A pipeline stage.
+ */
+typedef struct idetape_stage_s {
+ struct request rq; /* The corresponding request */
+ struct buffer_head *bh; /* The data buffers */
+ struct idetape_stage_s *next; /* Pointer to the next stage */
+} idetape_stage_t;
+
+/*
+ * Most of our global data which we need to save even as we leave the
+ * driver due to an interrupt or a timer event is stored in a variable
+ * of type idetape_tape_t, defined below.
+ */
+typedef struct {
+ ide_drive_t *drive;
+
+ /*
+ * Since a typical character device operation requires more
+ * than one packet command, we provide here enough memory
+ * for the maximum of interconnected packet commands.
+ * The packet commands are stored in the circular array pc_stack.
+ * pc_stack_index points to the last used entry, and warps around
+ * to the start when we get to the last array entry.
+ *
+ * pc points to the current processed packet command.
+ *
+ * failed_pc points to the last failed packet command, or contains
+ * NULL if we do not need to retry any packet command. This is
+ * required since an additional packet command is needed before the
+ * retry, to get detailed information on what went wrong.
+ */
+ idetape_pc_t *pc; /* Current packet command */
+ idetape_pc_t *failed_pc; /* Last failed packet command */
+ idetape_pc_t pc_stack[IDETAPE_PC_STACK];/* Packet command stack */
+ int pc_stack_index; /* Next free packet command storage space */
+ struct request rq_stack[IDETAPE_PC_STACK];
+ int rq_stack_index; /* We implement a circular array */
+
+ /*
+ * DSC polling variables.
+ *
+ * While polling for DSC we use postponed_rq to postpone the
+ * current request so that ide.c will be able to service
+ * pending requests on the other device. Note that at most
+ * we will have only one DSC (usually data transfer) request
+ * in the device request queue. Additional requests can be
+ * queued in our internal pipeline, but they will be visible
+ * to ide.c only one at a time.
+ */
+ struct request *postponed_rq;
+ unsigned long dsc_polling_start; /* The time in which we started polling for DSC */
+ struct timer_list dsc_timer; /* Timer used to poll for dsc */
+ unsigned long best_dsc_rw_frequency; /* Read/Write dsc polling frequency */
+ unsigned long dsc_polling_frequency; /* The current polling frequency */
+ unsigned long dsc_timeout; /* Maximum waiting time */
+
+ /*
+ * Position information
+ */
+ byte partition;
+ unsigned int block_address; /* Current block */
+
+ /*
+ * Last error information
+ */
+ byte sense_key, asc, ascq;
+
+ /*
+ * Character device operation
+ */
+ unsigned int minor;
+ char name[4]; /* device name */
+ idetape_chrdev_direction_t chrdev_direction; /* Current character device data transfer direction */
+
+ /*
+ * Device information
+ */
+ unsigned short tape_block_size; /* Usually 512 or 1024 bytes */
+ int user_bs_factor;
+ idetape_capabilities_page_t capabilities; /* Copy of the tape's Capabilities and Mechanical Page */
+
+ /*
+ * Active data transfer request parameters.
+ *
+ * At most, there is only one ide-tape originated data transfer
+ * request in the device request queue. This allows ide.c to
+ * easily service requests from the other device when we
+ * postpone our active request. In the pipelined operation
+ * mode, we use our internal pipeline structure to hold
+ * more data requests.
+ *
+ * The data buffer size is chosen based on the tape's
+ * recommendation.
+ */
+ struct request *active_data_request; /* Pointer to the request which is waiting in the device request queue */
+ int stage_size; /* Data buffer size (chosen based on the tape's recommendation */
+ idetape_stage_t *merge_stage;
+ int merge_stage_size;
+ struct buffer_head *bh;
+ char *b_data;
+ int b_count;
+
+ /*
+ * Pipeline parameters.
+ *
+ * To accomplish non-pipelined mode, we simply set the following
+ * variables to zero (or NULL, where appropriate).
+ */
+ int nr_stages; /* Number of currently used stages */
+ int nr_pending_stages; /* Number of pending stages */
+ int max_stages; /* We will not allocate more than this number of stages */
+ idetape_stage_t *first_stage; /* The first stage which will be removed from the pipeline */
+ idetape_stage_t *active_stage; /* The currently active stage */
+ idetape_stage_t *next_stage; /* Will be serviced after the currently active request */
+ idetape_stage_t *last_stage; /* New requests will be added to the pipeline here */
+ idetape_stage_t *cache_stage; /* Optional free stage which we can use */
+ int pages_per_stage;
+ int excess_bh_size; /* Wasted space in each stage */
+
+ unsigned int flags; /* Status/Action flags */
+} idetape_tape_t;
+
+/*
+ * Tape flag bits values.
+ */
+#define IDETAPE_IGNORE_DSC 0
+#define IDETAPE_ADDRESS_VALID 1 /* 0 When the tape position is unknown */
+#define IDETAPE_BUSY 2 /* Device already opened */
+#define IDETAPE_PIPELINE_ERROR 3 /* Error detected in a pipeline stage */
+#define IDETAPE_DETECT_BS 4 /* Attempt to auto-detect the current user block size */
+#define IDETAPE_FILEMARK 5 /* Currently on a filemark */
+
+/*
+ * Supported ATAPI tape drives packet commands
+ */
+#define IDETAPE_TEST_UNIT_READY_CMD 0x00
+#define IDETAPE_REWIND_CMD 0x01
+#define IDETAPE_REQUEST_SENSE_CMD 0x03
+#define IDETAPE_READ_CMD 0x08
+#define IDETAPE_WRITE_CMD 0x0a
+#define IDETAPE_WRITE_FILEMARK_CMD 0x10
+#define IDETAPE_SPACE_CMD 0x11
+#define IDETAPE_INQUIRY_CMD 0x12
+#define IDETAPE_ERASE_CMD 0x19
+#define IDETAPE_MODE_SENSE_CMD 0x1a
+#define IDETAPE_LOAD_UNLOAD_CMD 0x1b
+#define IDETAPE_LOCATE_CMD 0x2b
+#define IDETAPE_READ_POSITION_CMD 0x34
+
+/*
+ * Some defines for the SPACE command
+ */
+#define IDETAPE_SPACE_OVER_FILEMARK 1
+#define IDETAPE_SPACE_TO_EOD 3
+
+/*
+ * Some defines for the LOAD UNLOAD command
+ */
+#define IDETAPE_LU_LOAD_MASK 1
+#define IDETAPE_LU_RETENSION_MASK 2
+#define IDETAPE_LU_EOT_MASK 4
+
+/*
+ * Special requests for our block device strategy routine.
+ *
+ * In order to service a character device command, we add special
+ * requests to the tail of our block device request queue and wait
+ * for their completion.
+ *
+ */
+#define IDETAPE_FIRST_RQ 90
+
+/*
+ * IDETAPE_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define IDETAPE_PC_RQ 90
+
+/*
+ * IDETAPE_READ_RQ and IDETAPE_WRITE_RQ are used by our
+ * character device interface to request read/write operations from
+ * our block device interface.
+ */
+#define IDETAPE_READ_RQ 92
+#define IDETAPE_WRITE_RQ 93
+#define IDETAPE_ABORTED_WRITE_RQ 94
+
+#define IDETAPE_LAST_RQ 94
+
+/*
+ * A macro which can be used to check if a we support a given
+ * request command.
+ */
+#define IDETAPE_RQ_CMD(cmd) ((cmd >= IDETAPE_FIRST_RQ) && (cmd <= IDETAPE_LAST_RQ))
+
+/*
+ * We are now able to postpone an idetape request in the stage
+ * where it is polling for DSC and service requests from the other
+ * ide device meanwhile.
+ */
+#define IDETAPE_RQ_POSTPONED 0x1234
+
+/*
+ * Error codes which are returned in rq->errors to the higher part
+ * of the driver.
+ */
+#define IDETAPE_ERROR_GENERAL 101
+#define IDETAPE_ERROR_FILEMARK 102
+#define IDETAPE_ERROR_EOD 103
+
+/*
+ * The ATAPI Status Register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned check :1; /* Error occurred */
+ unsigned idx :1; /* Reserved */
+ unsigned corr :1; /* Correctable error occurred */
+ unsigned drq :1; /* Data is request by the device */
+ unsigned dsc :1; /* Buffer availability / Media access command finished */
+ unsigned reserved5 :1; /* Reserved */
+ unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */
+ unsigned bsy :1; /* The device has access to the command block */
+ } b;
+} idetape_status_reg_t;
+
+/*
+ * The ATAPI error register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned ili :1; /* Illegal Length Indication */
+ unsigned eom :1; /* End Of Media Detected */
+ unsigned abrt :1; /* Aborted command - As defined by ATA */
+ unsigned mcr :1; /* Media Change Requested - As defined by ATA */
+ unsigned sense_key :4; /* Sense key of the last failed packet command */
+ } b;
+} idetape_error_reg_t;
+
+/*
+ * ATAPI Feature Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned dma :1; /* Using DMA of PIO */
+ unsigned reserved321 :3; /* Reserved */
+ unsigned reserved654 :3; /* Reserved (Tag Type) */
+ unsigned reserved7 :1; /* Reserved */
+ } b;
+} idetape_feature_reg_t;
+
+/*
+ * ATAPI Byte Count Register.
+ */
+typedef union {
+ unsigned all :16;
+ struct {
+ unsigned low :8; /* LSB */
+ unsigned high :8; /* MSB */
+ } b;
+} idetape_bcount_reg_t;
+
+/*
+ * ATAPI Interrupt Reason Register.
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned cod :1; /* Information transferred is command (1) or data (0) */
+ unsigned io :1; /* The device requests us to read (1) or write (0) */
+ unsigned reserved :6; /* Reserved */
+ } b;
+} idetape_ireason_reg_t;
+
+/*
+ * ATAPI Drive Select Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned sam_lun :4; /* Should be zero with ATAPI (not used) */
+ unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */
+ unsigned one5 :1; /* Should be set to 1 */
+ unsigned reserved6 :1; /* Reserved */
+ unsigned one7 :1; /* Should be set to 1 */
+ } b;
+} idetape_drivesel_reg_t;
+
+/*
+ * ATAPI Device Control Register
+ */
+typedef union {
+ unsigned all :8;
+ struct {
+ unsigned zero0 :1; /* Should be set to zero */
+ unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */
+ unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+ unsigned one3 :1; /* Should be set to 1 */
+ unsigned reserved4567 :4; /* Reserved */
+ } b;
+} idetape_control_reg_t;
+
+/*
+ * idetape_chrdev_t provides the link between out character device
+ * interface and our block device interface and the corresponding
+ * ide_drive_t structure.
+ */
+typedef struct {
+ ide_drive_t *drive;
+} idetape_chrdev_t;
+
+/*
+ * The following is used to format the general configuration word of
+ * the ATAPI IDENTIFY DEVICE command.
+ */
+struct idetape_id_gcw {
+ unsigned packet_size :2; /* Packet Size */
+ unsigned reserved234 :3; /* Reserved */
+ unsigned drq_type :2; /* Command packet DRQ type */
+ unsigned removable :1; /* Removable media */
+ unsigned device_type :5; /* Device type */
+ unsigned reserved13 :1; /* Reserved */
+ unsigned protocol :2; /* Protocol type */
+};
+
+/*
+ * INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C)
+ */
+typedef struct {
+ unsigned device_type :5; /* Peripheral Device Type */
+ unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */
+ unsigned reserved1_6t0 :7; /* Reserved */
+ unsigned rmb :1; /* Removable Medium Bit */
+ unsigned ansi_version :3; /* ANSI Version */
+ unsigned ecma_version :3; /* ECMA Version */
+ unsigned iso_version :2; /* ISO Version */
+ unsigned response_format :4; /* Response Data Format */
+ unsigned reserved3_45 :2; /* Reserved */
+ unsigned reserved3_6 :1; /* TrmIOP - Reserved */
+ unsigned reserved3_7 :1; /* AENC - Reserved */
+ u8 additional_length; /* Additional Length (total_length-4) */
+ u8 rsv5, rsv6, rsv7; /* Reserved */
+ u8 vendor_id[8]; /* Vendor Identification */
+ u8 product_id[16]; /* Product Identification */
+ u8 revision_level[4]; /* Revision Level */
+ u8 vendor_specific[20]; /* Vendor Specific - Optional */
+ u8 reserved56t95[40]; /* Reserved - Optional */
+ /* Additional information may be returned */
+} idetape_inquiry_result_t;
+
+/*
+ * READ POSITION packet command - Data Format (From Table 6-57)
+ */
+typedef struct {
+ unsigned reserved0_10 :2; /* Reserved */
+ unsigned bpu :1; /* Block Position Unknown */
+ unsigned reserved0_543 :3; /* Reserved */
+ unsigned eop :1; /* End Of Partition */
+ unsigned bop :1; /* Beginning Of Partition */
+ u8 partition; /* Partition Number */
+ u8 reserved2, reserved3; /* Reserved */
+ u32 first_block; /* First Block Location */
+ u32 last_block; /* Last Block Location (Optional) */
+ u8 reserved12; /* Reserved */
+ u8 blocks_in_buffer[3]; /* Blocks In Buffer - (Optional) */
+ u32 bytes_in_buffer; /* Bytes In Buffer (Optional) */
+} idetape_read_position_result_t;
+
+/*
+ * REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+ unsigned error_code :7; /* Current of deferred errors */
+ unsigned valid :1; /* The information field conforms to QIC-157C */
+ u8 reserved1 :8; /* Segment Number - Reserved */
+ unsigned sense_key :4; /* Sense Key */
+ unsigned reserved2_4 :1; /* Reserved */
+ unsigned ili :1; /* Incorrect Length Indicator */
+ unsigned eom :1; /* End Of Medium */
+ unsigned filemark :1; /* Filemark */
+ u32 information __attribute__ ((packed));
+ u8 asl; /* Additional sense length (n-7) */
+ u32 command_specific; /* Additional command specific information */
+ u8 asc; /* Additional Sense Code */
+ u8 ascq; /* Additional Sense Code Qualifier */
+ u8 replaceable_unit_code; /* Field Replaceable Unit Code */
+ unsigned sk_specific1 :7; /* Sense Key Specific */
+ unsigned sksv :1; /* Sense Key Specific information is valid */
+ u8 sk_specific2; /* Sense Key Specific */
+ u8 sk_specific3; /* Sense Key Specific */
+ u8 pad[2]; /* Padding to 20 bytes */
+} idetape_request_sense_result_t;
+
+/*
+ * Follows structures which are related to the SELECT SENSE / MODE SENSE
+ * packet commands. Those packet commands are still not supported
+ * by ide-tape.
+ */
+#define IDETAPE_CAPABILITIES_PAGE 0x2a
+
+/*
+ * Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+ u8 mode_data_length; /* Length of the following data transfer */
+ u8 medium_type; /* Medium Type */
+ u8 dsp; /* Device Specific Parameter */
+ u8 bdl; /* Block Descriptor Length */
+} idetape_mode_parameter_header_t;
+
+/*
+ * Mode Parameter Block Descriptor the MODE SENSE packet command
+ *
+ * Support for block descriptors is optional.
+ */
+typedef struct {
+ u8 density_code; /* Medium density code */
+ u8 blocks[3]; /* Number of blocks */
+ u8 reserved4; /* Reserved */
+ u8 length[3]; /* Block Length */
+} idetape_parameter_block_descriptor_t;
+
+/*
+ * The Data Compression Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+ unsigned page_code :6; /* Page Code - Should be 0xf */
+ unsigned reserved0 :1; /* Reserved */
+ unsigned ps :1;
+ u8 page_length; /* Page Length - Should be 14 */
+ unsigned reserved2 :6; /* Reserved */
+ unsigned dcc :1; /* Data Compression Capable */
+ unsigned dce :1; /* Data Compression Enable */
+ unsigned reserved3 :5; /* Reserved */
+ unsigned red :2; /* Report Exception on Decompression */
+ unsigned dde :1; /* Data Decompression Enable */
+ u32 ca; /* Compression Algorithm */
+ u32 da; /* Decompression Algorithm */
+ u8 reserved[4]; /* Reserved */
+} idetape_data_compression_page_t;
+
+/*
+ * The Medium Partition Page, as returned by the MODE SENSE packet command.
+ */
+typedef struct {
+ unsigned page_code :6; /* Page Code - Should be 0x11 */
+ unsigned reserved1_6 :1; /* Reserved */
+ unsigned ps :1;
+ u8 page_length; /* Page Length - Should be 6 */
+ u8 map; /* Maximum Additional Partitions - Should be 0 */
+ u8 apd; /* Additional Partitions Defined - Should be 0 */
+ unsigned reserved4_012 :3; /* Reserved */
+ unsigned psum :2; /* Should be 0 */
+ unsigned idp :1; /* Should be 0 */
+ unsigned sdp :1; /* Should be 0 */
+ unsigned fdp :1; /* Fixed Data Partitions */
+ u8 mfr; /* Medium Format Recognition */
+ u8 reserved[2]; /* Reserved */
+} idetape_medium_partition_page_t;
+
+#define IDETAPE_MIN(a,b) ((a)<(b) ? (a):(b))
+#define IDETAPE_MAX(a,b) ((a)>(b) ? (a):(b))
+
+/*
+ * Run time configurable parameters.
+ */
+typedef struct {
+ int dsc_rw_frequency;
+ int dsc_media_access_frequency;
+ int nr_stages;
+} idetape_config_t;
+
+/*
+ * The variables below are used for the character device interface.
+ * Additional state variables are defined in our ide_drive_t structure.
+ */
+static idetape_chrdev_t idetape_chrdevs[MAX_HWIFS * MAX_DRIVES];
+static int idetape_chrdev_present = 0;
+
+/*
+ * Too bad. The drive wants to send us data which we are not ready to accept.
+ * Just throw it away.
+ */
+static void idetape_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+ while (bcount--)
+ IN_BYTE (IDE_DATA_REG);
+}
+
+static void idetape_input_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount)
+{
+ struct buffer_head *bh = pc->bh;
+ int count;
+
+ while (bcount) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_input_buffers\n");
+ idetape_discard_data (drive, bcount);
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDETAPE_MIN (bh->b_size - bh->b_count, bcount);
+ atapi_input_bytes (drive, bh->b_data + bh->b_count, count);
+ bcount -= count; bh->b_count += count;
+ if (bh->b_count == bh->b_size) {
+ bh = bh->b_reqnext;
+ if (bh)
+ bh->b_count = 0;
+ }
+ }
+ pc->bh = bh;
+}
+
+static void idetape_output_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount)
+{
+ struct buffer_head *bh = pc->bh;
+ int count;
+
+ while (bcount) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_output_buffers\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDETAPE_MIN (pc->b_count, bcount);
+ atapi_output_bytes (drive, pc->b_data, count);
+ bcount -= count; pc->b_data += count; pc->b_count -= count;
+ if (!pc->b_count) {
+ pc->bh = bh = bh->b_reqnext;
+ if (bh) {
+ pc->b_data = bh->b_data;
+ pc->b_count = bh->b_count;
+ }
+ }
+ }
+}
+
+#ifdef CONFIG_BLK_DEV_TRITON
+static void idetape_update_buffers (idetape_pc_t *pc)
+{
+ struct buffer_head *bh = pc->bh;
+ int count, bcount = pc->actually_transferred;
+
+ if (test_bit (PC_WRITING, &pc->flags))
+ return;
+ while (bcount) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_update_buffers\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDETAPE_MIN (bh->b_size, bcount);
+ bh->b_count = count;
+ if (bh->b_count == bh->b_size)
+ bh = bh->b_reqnext;
+ bcount -= count;
+ }
+ pc->bh = bh;
+}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+/*
+ * idetape_poll_for_dsc gets invoked by a timer (which was set
+ * by idetape_postpone_request) to reinsert our postponed request
+ * into the request queue.
+ *
+ * Note that the procedure done here is different than the method
+ * we are using in idetape_queue_pc_head - There we are putting
+ * request(s) before our currently called request.
+ *
+ * Here, on the other hand, HWGROUP(drive)->rq is not our request
+ * but rather a request to another device. Therefore, we will let
+ * it finish and only then service our postponed request --> We don't
+ * touch HWGROUP(drive)->rq.
+ */
+static void idetape_poll_for_dsc (unsigned long data)
+{
+ ide_drive_t *drive=(ide_drive_t *) data;
+ idetape_tape_t *tape = drive->driver_data;
+
+ del_timer (&tape->dsc_timer);
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: Putting back postponed request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+ if (tape->postponed_rq == NULL) {
+ printk (KERN_ERR "tape->postponed_rq is NULL in idetape_poll_for_dsc\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ (void) ide_do_drive_cmd (drive, tape->postponed_rq, ide_next);
+}
+
+/*
+ * idetape_postpone_request postpones the current request so that
+ * ide.c will be able to service requests from another device on
+ * the same hwgroup while we are polling for DSC.
+ */
+static void idetape_postpone_request (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ struct request *rq;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_postpone_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+ if (tape->postponed_rq != NULL)
+ printk (KERN_ERR "ide-tape.c bug - postponed_rq not NULL in idetape_postpone_request\n");
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ /*
+ * Set the timer parameters.
+ */
+ tape->dsc_timer.expires=jiffies + tape->dsc_polling_frequency;
+ tape->dsc_timer.data=(unsigned long) drive;
+ tape->dsc_timer.function = &idetape_poll_for_dsc;
+ init_timer (&tape->dsc_timer);
+
+ /*
+ * Remove current request from the request queue:
+ */
+ tape->postponed_rq = rq = HWGROUP(drive)->rq;
+ rq->rq_status = IDETAPE_RQ_POSTPONED;
+ blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
+ HWGROUP(drive)->rq = NULL;
+
+ add_timer(&tape->dsc_timer); /* Activate the polling timer */
+}
+
+/*
+ * idetape_queue_pc_head generates a new packet command request in front
+ * of the request queue, before the current request, so that it will be
+ * processed immediately, on the next pass through the driver.
+ *
+ * idetape_queue_pc_head is called from the request handling part of
+ * the driver (the "bottom" part). Safe storage for the request should
+ * be allocated with idetape_next_pc_storage and idetape_next_rq_storage
+ * before calling idetape_queue_pc_head.
+ *
+ * Memory for those requests is pre-allocated at initialization time, and
+ * is limited to IDETAPE_PC_STACK requests. We assume that we have enough
+ * space for the maximum possible number of inter-dependent packet commands.
+ *
+ * The higher level of the driver - The ioctl handler and the character
+ * device handling functions should queue request to the lower level part
+ * and wait for their completion using idetape_queue_pc_tail or
+ * idetape_queue_rw_tail.
+ */
+static void idetape_queue_pc_head (ide_drive_t *drive,idetape_pc_t *pc,struct request *rq)
+{
+ unsigned int major = HWIF(drive)->major;
+ struct blk_dev_struct *bdev = &blk_dev[major];
+
+ bdev->current_request=HWGROUP (drive)->rq; /* Since we may have taken it out */
+
+ ide_init_drive_cmd (rq);
+ rq->buffer = (char *) pc;
+ rq->cmd = IDETAPE_PC_RQ;
+ (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+/*
+ * idetape_next_pc_storage returns a pointer to a place in which we can
+ * safely store a packet command, even though we intend to leave the
+ * driver. A storage space for a maximum of IDETAPE_PC_STACK packet
+ * commands is allocated at initialization time.
+ */
+static idetape_pc_t *idetape_next_pc_storage (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: pc_stack_index=%d\n",tape->pc_stack_index);
+#endif /* IDETAPE_DEBUG_LOG */
+ if (tape->pc_stack_index==IDETAPE_PC_STACK)
+ tape->pc_stack_index=0;
+ return (&tape->pc_stack[tape->pc_stack_index++]);
+}
+
+/*
+ * idetape_next_rq_storage is used along with idetape_next_pc_storage.
+ * Since we queue packet commands in the request queue, we need to
+ * allocate a request, along with the allocation of a packet command.
+ */
+
+/**************************************************************
+ * *
+ * This should get fixed to use kmalloc(GFP_ATOMIC, ..) *
+ * followed later on by kfree(). -ml *
+ * *
+ **************************************************************/
+
+static struct request *idetape_next_rq_storage (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: rq_stack_index=%d\n",tape->rq_stack_index);
+#endif /* IDETAPE_DEBUG_LOG */
+ if (tape->rq_stack_index==IDETAPE_PC_STACK)
+ tape->rq_stack_index=0;
+ return (&tape->rq_stack[tape->rq_stack_index++]);
+}
+
+/*
+ * Pipeline related functions
+ */
+
+static inline int idetape_pipeline_active (idetape_tape_t *tape)
+{
+ return tape->active_data_request != NULL;
+}
+
+/*
+ * idetape_kfree_stage calls kfree to completely free a stage, along with
+ * its related buffers.
+ */
+static void __idetape_kfree_stage (idetape_stage_t *stage)
+{
+ struct buffer_head *prev_bh, *bh = stage->bh;
+ int size;
+
+ while (bh != NULL) {
+ if (bh->b_data != NULL) {
+ size = (int) bh->b_size;
+ while (size > 0) {
+ free_page ((unsigned long) bh->b_data);
+ size -= PAGE_SIZE;
+ bh->b_data += PAGE_SIZE;
+ }
+ }
+ prev_bh = bh;
+ bh = bh->b_reqnext;
+ kfree (prev_bh);
+ }
+ kfree (stage);
+}
+
+static void idetape_kfree_stage (idetape_tape_t *tape, idetape_stage_t *stage)
+{
+ if (tape->cache_stage == NULL)
+ tape->cache_stage = stage;
+ else
+ __idetape_kfree_stage (stage);
+}
+
+/*
+ * idetape_kmalloc_stage uses __get_free_page to allocate a pipeline
+ * stage, along with all the necessary small buffers which together make
+ * a buffer of size tape->stage_size (or a bit more). We attempt to
+ * combine sequential pages as much as possible.
+ *
+ * Returns a pointer to the new allocated stage, or NULL if we
+ * can't (or don't want to) allocate a stage.
+ *
+ * Pipeline stages are optional and are used to increase performance.
+ * If we can't allocate them, we'll manage without them.
+ */
+static idetape_stage_t *__idetape_kmalloc_stage (idetape_tape_t *tape)
+{
+ idetape_stage_t *stage;
+ struct buffer_head *prev_bh, *bh;
+ int pages = tape->pages_per_stage;
+ char *b_data;
+
+ if ((stage = (idetape_stage_t *) kmalloc (sizeof (idetape_stage_t),GFP_KERNEL)) == NULL)
+ return NULL;
+ stage->next = NULL;
+
+ bh = stage->bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL);
+ if (bh == NULL)
+ goto abort;
+ bh->b_reqnext = NULL;
+ if ((bh->b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL)
+ goto abort;
+ bh->b_size = PAGE_SIZE;
+ set_bit (BH_Lock, &bh->b_state);
+
+ while (--pages) {
+ if ((b_data = (char *) __get_free_page (GFP_KERNEL)) == NULL)
+ goto abort;
+ if (bh->b_data == b_data + PAGE_SIZE && virt_to_bus (bh->b_data) == virt_to_bus (b_data) + PAGE_SIZE) {
+ bh->b_size += PAGE_SIZE;
+ bh->b_data -= PAGE_SIZE;
+ continue;
+ }
+ if (b_data == bh->b_data + bh->b_size && virt_to_bus (b_data) == virt_to_bus (bh->b_data) + bh->b_size) {
+ bh->b_size += PAGE_SIZE;
+ continue;
+ }
+ prev_bh = bh;
+ if ((bh = (struct buffer_head *) kmalloc (sizeof (struct buffer_head), GFP_KERNEL)) == NULL) {
+ free_page ((unsigned long) b_data);
+ goto abort;
+ }
+ bh->b_reqnext = NULL;
+ bh->b_data = b_data;
+ bh->b_size = PAGE_SIZE;
+ set_bit (BH_Lock, &bh->b_state);
+ prev_bh->b_reqnext = bh;
+ }
+ bh->b_size -= tape->excess_bh_size;
+ return stage;
+abort:
+ __idetape_kfree_stage (stage);
+ return NULL;
+}
+
+static idetape_stage_t *idetape_kmalloc_stage (idetape_tape_t *tape)
+{
+ idetape_stage_t *cache_stage = tape->cache_stage;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_kmalloc_stage\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->nr_stages >= tape->max_stages)
+ return NULL;
+ if (cache_stage != NULL) {
+ tape->cache_stage = NULL;
+ return cache_stage;
+ }
+ return __idetape_kmalloc_stage (tape);
+}
+
+static void idetape_copy_stage_from_user (idetape_tape_t *tape, idetape_stage_t *stage, const char *buf, int n)
+{
+ struct buffer_head *bh = tape->bh;
+ int count;
+
+ while (n) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_from_user\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDETAPE_MIN (bh->b_size - bh->b_count, n);
+ copy_from_user (bh->b_data + bh->b_count, buf, count);
+ n -= count; bh->b_count += count; buf += count;
+ if (bh->b_count == bh->b_size) {
+ bh = bh->b_reqnext;
+ if (bh)
+ bh->b_count = 0;
+ }
+ }
+ tape->bh = bh;
+}
+
+static void idetape_copy_stage_to_user (idetape_tape_t *tape, char *buf, idetape_stage_t *stage, int n)
+{
+ struct buffer_head *bh = tape->bh;
+ int count;
+
+ while (n) {
+#if IDETAPE_DEBUG_BUGS
+ if (bh == NULL) {
+ printk (KERN_ERR "ide-tape: bh == NULL in idetape_copy_stage_to_user\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ count = IDETAPE_MIN (tape->b_count, n);
+ copy_to_user (buf, tape->b_data, count);
+ n -= count; tape->b_data += count; tape->b_count -= count; buf += count;
+ if (!tape->b_count) {
+ tape->bh = bh = bh->b_reqnext;
+ if (bh) {
+ tape->b_data = bh->b_data;
+ tape->b_count = bh->b_count;
+ }
+ }
+ }
+}
+
+static void idetape_init_merge_stage (idetape_tape_t *tape)
+{
+ struct buffer_head *bh = tape->merge_stage->bh;
+
+ tape->bh = bh;
+ if (tape->chrdev_direction == idetape_direction_write)
+ bh->b_count = 0;
+ else {
+ tape->b_data = bh->b_data;
+ tape->b_count = bh->b_count;
+ }
+}
+
+static void idetape_switch_buffers (idetape_tape_t *tape, idetape_stage_t *stage)
+{
+ struct buffer_head *tmp;
+
+ tmp = stage->bh;
+ stage->bh = tape->merge_stage->bh;
+ tape->merge_stage->bh = tmp;
+ idetape_init_merge_stage (tape);
+}
+
+/*
+ * idetape_increase_max_pipeline_stages is a part of the feedback
+ * loop which tries to find the optimum number of stages. In the
+ * feedback loop, we are starting from a minimum maximum number of
+ * stages, and if we sense that the pipeline is empty, we try to
+ * increase it, until we reach the user compile time memory limit.
+ */
+static void idetape_increase_max_pipeline_stages (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_increase_max_pipeline_stages\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ tape->max_stages = IDETAPE_MIN (tape->max_stages + IDETAPE_INCREASE_STAGES_RATE, IDETAPE_MAX_PIPELINE_STAGES);
+}
+
+/*
+ * idetape_add_stage_tail adds a new stage at the end of the pipeline.
+ */
+static void idetape_add_stage_tail (ide_drive_t *drive,idetape_stage_t *stage)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_add_stage_tail\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ save_flags (flags);
+ cli ();
+ stage->next=NULL;
+ if (tape->last_stage != NULL)
+ tape->last_stage->next=stage;
+ else
+ tape->first_stage=tape->next_stage=stage;
+ tape->last_stage=stage;
+ if (tape->next_stage == NULL)
+ tape->next_stage=tape->last_stage;
+ tape->nr_stages++;
+ tape->nr_pending_stages++;
+ restore_flags (flags);
+}
+
+/*
+ * idetape_remove_stage_head removes tape->first_stage from the pipeline.
+ * The caller should avoid race conditions.
+ */
+static void idetape_remove_stage_head (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_remove_stage_head\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+ if (tape->first_stage == NULL) {
+ printk (KERN_ERR "ide-tape: bug: tape->first_stage is NULL\n");
+ return;
+ }
+ if (tape->active_stage == tape->first_stage) {
+ printk (KERN_ERR "ide-tape: bug: Trying to free our active pipeline stage\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ stage=tape->first_stage;
+ tape->first_stage=stage->next;
+ idetape_kfree_stage (tape, stage);
+ tape->nr_stages--;
+ if (tape->first_stage == NULL) {
+ tape->last_stage=NULL;
+#if IDETAPE_DEBUG_BUGS
+ if (tape->next_stage != NULL)
+ printk (KERN_ERR "ide-tape: bug: tape->next_stage != NULL\n");
+ if (tape->nr_stages)
+ printk (KERN_ERR "ide-tape: bug: nr_stages should be 0 now\n");
+#endif /* IDETAPE_DEBUG_BUGS */
+ }
+}
+
+/*
+ * idetape_active_next_stage will declare the next stage as "active".
+ */
+static void idetape_active_next_stage (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage=tape->next_stage;
+ struct request *rq = &stage->rq;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_active_next_stage\n");
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+ if (stage == NULL) {
+ printk (KERN_ERR "ide-tape: bug: Trying to activate a non existing stage\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ rq->buffer = NULL;
+ rq->bh = stage->bh;
+ tape->active_data_request=rq;
+ tape->active_stage=stage;
+ tape->next_stage=stage->next;
+}
+
+/*
+ * idetape_insert_pipeline_into_queue is used to start servicing the
+ * pipeline stages, starting from tape->next_stage.
+ */
+static void idetape_insert_pipeline_into_queue (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ if (tape->next_stage == NULL)
+ return;
+ if (!idetape_pipeline_active (tape)) {
+ idetape_active_next_stage (drive);
+ (void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end);
+ }
+}
+
+static void idetape_abort_pipeline (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage = tape->next_stage;
+
+ while (stage) {
+ stage->rq.cmd = IDETAPE_ABORTED_WRITE_RQ;
+ stage = stage->next;
+ }
+}
+
+/*
+ * idetape_end_request is used to finish servicing a request, and to
+ * insert a pending pipeline request into the main device queue.
+ */
+static void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+ ide_drive_t *drive = hwgroup->drive;
+ struct request *rq = hwgroup->rq;
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned int major = HWIF(drive)->major;
+ struct blk_dev_struct *bdev = &blk_dev[major];
+ int error;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_end_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ bdev->current_request=rq; /* Since we may have taken it out */
+
+ switch (uptodate) {
+ case 0: error = IDETAPE_ERROR_GENERAL; break;
+ case 1: error = 0; break;
+ default: error = uptodate;
+ }
+ rq->errors = error;
+ if (error)
+ tape->failed_pc = NULL;
+
+ if (tape->active_data_request == rq) { /* The request was a pipelined data transfer request */
+ tape->active_stage = NULL;
+ tape->active_data_request = NULL;
+ tape->nr_pending_stages--;
+ if (rq->cmd == IDETAPE_WRITE_RQ) {
+ if (error) {
+ set_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+ if (error == IDETAPE_ERROR_EOD)
+ idetape_abort_pipeline (drive);
+ }
+ idetape_remove_stage_head (drive);
+ }
+ if (tape->next_stage != NULL) {
+ idetape_active_next_stage (drive);
+
+ /*
+ * Insert the next request into the request queue.
+ * The choice of using ide_next or ide_end is now left to the user.
+ */
+#if IDETAPE_LOW_TAPE_PRIORITY
+ (void) ide_do_drive_cmd (drive, tape->active_data_request, ide_end);
+#else
+ (void) ide_do_drive_cmd (drive, tape->active_data_request, ide_next);
+#endif /* IDETAPE_LOW_TAPE_PRIORITY */
+ } else if (!error)
+ idetape_increase_max_pipeline_stages (drive);
+ }
+ ide_end_drive_cmd (drive, 0, 0);
+}
+
+/*
+ * idetape_analyze_error is called on each failed packet command retry
+ * to analyze the request sense. We currently do not utilize this
+ * information.
+ */
+static void idetape_analyze_error (ide_drive_t *drive,idetape_request_sense_result_t *result)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc = tape->failed_pc;
+
+ tape->sense_key = result->sense_key; tape->asc = result->asc; tape->ascq = result->ascq;
+#if IDETAPE_DEBUG_LOG
+ /*
+ * Without debugging, we only log an error if we decided to
+ * give up retrying.
+ */
+ printk (KERN_INFO "ide-tape: pc = %x, sense key = %x, asc = %x, ascq = %x\n",pc->c[0],result->sense_key,result->asc,result->ascq);
+#endif /* IDETAPE_DEBUG_LOG */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+
+ /*
+ * Correct pc->actually_transferred by asking the tape.
+ */
+ if (test_bit (PC_DMA_ERROR, &pc->flags)) {
+ pc->actually_transferred = pc->request_transfer - tape->tape_block_size * ntohl (get_unaligned (&result->information));
+ idetape_update_buffers (pc);
+ }
+#endif /* CONFIG_BLK_DEV_TRITON */
+ if (pc->c[0] == IDETAPE_READ_CMD && result->filemark) {
+ pc->error = IDETAPE_ERROR_FILEMARK;
+ set_bit (PC_ABORT, &pc->flags);
+ }
+ if (pc->c[0] == IDETAPE_WRITE_CMD) {
+ if (result->eom || (result->sense_key == 0xd && result->asc == 0x0 && result->ascq == 0x2)) {
+ pc->error = IDETAPE_ERROR_EOD;
+ set_bit (PC_ABORT, &pc->flags);
+ }
+ }
+ if (pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) {
+ if (result->sense_key == 8) {
+ pc->error = IDETAPE_ERROR_EOD;
+ set_bit (PC_ABORT, &pc->flags);
+ }
+ if (!test_bit (PC_ABORT, &pc->flags) && pc->actually_transferred)
+ pc->retries = IDETAPE_MAX_PC_RETRIES + 1;
+ }
+}
+
+static void idetape_request_sense_callback (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: Reached idetape_request_sense_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ if (!tape->pc->error) {
+ idetape_analyze_error (drive,(idetape_request_sense_result_t *) tape->pc->buffer);
+ idetape_end_request (1,HWGROUP (drive));
+ } else {
+ printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
+ idetape_end_request (0,HWGROUP (drive));
+ }
+}
+
+/*
+ * idetape_init_pc initializes a packet command.
+ */
+static void idetape_init_pc (idetape_pc_t *pc)
+{
+ memset (pc->c, 0, 12);
+ pc->retries = 0;
+ pc->flags = 0;
+ pc->request_transfer = 0;
+ pc->buffer = pc->pc_buffer;
+ pc->buffer_size = IDETAPE_PC_BUFFER_SIZE;
+ pc->bh = NULL;
+ pc->b_data = NULL;
+}
+
+static void idetape_create_request_sense_cmd (idetape_pc_t *pc)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_REQUEST_SENSE_CMD;
+ pc->c[4] = 255;
+ pc->request_transfer = 18;
+ pc->callback = &idetape_request_sense_callback;
+}
+
+/*
+ * idetape_retry_pc is called when an error was detected during the
+ * last packet command. We queue a request sense packet command in
+ * the head of the request list.
+ */
+static void idetape_retry_pc (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc;
+ struct request *rq;
+ idetape_error_reg_t error;
+
+ error.all = IN_BYTE (IDE_ERROR_REG);
+ pc = idetape_next_pc_storage (drive);
+ rq = idetape_next_rq_storage (drive);
+ idetape_create_request_sense_cmd (pc);
+ set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
+ idetape_queue_pc_head (drive, pc, rq);
+}
+
+/*
+ * idetape_pc_intr is the usual interrupt handler which will be called
+ * during a packet command. We will transfer some of the data (as
+ * requested by the drive) and will re-point interrupt handler to us.
+ * When data transfer is finished, we will act according to the
+ * algorithm described before idetape_issue_packet_command.
+ *
+ */
+static void idetape_pc_intr (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_status_reg_t status;
+ idetape_bcount_reg_t bcount;
+ idetape_ireason_reg_t ireason;
+ idetape_pc_t *pc=tape->pc;
+ unsigned int temp;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: Reached idetape_pc_intr interrupt handler\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+ if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) {
+ set_bit (PC_DMA_ERROR, &pc->flags);
+ /*
+ * We will currently correct the following in
+ * idetape_analyze_error.
+ */
+ pc->actually_transferred=HWIF(drive)->dmaproc(ide_dma_transferred, drive);
+ } else {
+ pc->actually_transferred=pc->request_transfer;
+ idetape_update_buffers (pc);
+ }
+ (void) (HWIF(drive)->dmaproc(ide_dma_abort, drive)); /* End DMA */
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: DMA finished\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ }
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+ status.all = GET_STAT(); /* Clear the interrupt */
+
+ if (!status.b.drq) { /* No more interrupts */
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDETAPE_DEBUG_LOG */
+ clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+
+ ide_sti();
+
+ if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) { /* Error detected */
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: %s: I/O error, ",tape->name);
+#endif /* IDETAPE_DEBUG_LOG */
+ if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+ printk (KERN_ERR "ide-tape: I/O error in request sense command\n");
+ ide_do_reset (drive);
+ return;
+ }
+ idetape_retry_pc (drive); /* Retry operation */
+ return;
+ }
+ pc->error = 0;
+ if (test_bit (PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) { /* Media access command */
+ tape->dsc_polling_start = jiffies;
+ tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST;
+ tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT;
+ idetape_postpone_request (drive); /* Allow ide.c to handle other requests */
+ return;
+ }
+ if (tape->failed_pc == pc)
+ tape->failed_pc=NULL;
+ pc->callback(drive); /* Command finished - Call the callback function */
+ return;
+ }
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+ printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n");
+ printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n");
+ drive->using_dma=0;
+ ide_do_reset (drive);
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_TRITON */
+ bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */
+ bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */
+ ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+ if (ireason.b.cod) {
+ printk (KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n");
+ ide_do_reset (drive);
+ return;
+ }
+ if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */
+ printk (KERN_ERR "ide-tape: We wanted to %s, ", ireason.b.io ? "Write":"Read");
+ printk (KERN_ERR "but the tape wants us to %s !\n",ireason.b.io ? "Read":"Write");
+ ide_do_reset (drive);
+ return;
+ }
+ if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */
+ temp = pc->actually_transferred + bcount.all;
+ if ( temp > pc->request_transfer) {
+ if (temp > pc->buffer_size) {
+ printk (KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n");
+ idetape_discard_data (drive,bcount.all);
+ ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD);
+ return;
+ }
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_NOTICE "ide-tape: The tape wants to send us more data than expected - allowing transfer\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ }
+ }
+ if (test_bit (PC_WRITING, &pc->flags)) {
+ if (pc->bh != NULL)
+ idetape_output_buffers (drive, pc, bcount.all);
+ else
+ atapi_output_bytes (drive,pc->current_position,bcount.all); /* Write the current buffer */
+ } else {
+ if (pc->bh != NULL)
+ idetape_input_buffers (drive, pc, bcount.all);
+ else
+ atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */
+ }
+ pc->actually_transferred+=bcount.all; /* Update the current position */
+ pc->current_position+=bcount.all;
+
+ ide_set_handler (drive,&idetape_pc_intr,WAIT_CMD); /* And set the interrupt handler again */
+}
+
+/*
+ * Packet Command Interface
+ *
+ * The current Packet Command is available in tape->pc, and will not
+ * change until we finish handling it. Each packet command is associated
+ * with a callback function that will be called when the command is
+ * finished.
+ *
+ * The handling will be done in three stages:
+ *
+ * 1. idetape_issue_packet_command will send the packet command to the
+ * drive, and will set the interrupt handler to idetape_pc_intr.
+ *
+ * 2. On each interrupt, idetape_pc_intr will be called. This step
+ * will be repeated until the device signals us that no more
+ * interrupts will be issued.
+ *
+ * 3. ATAPI Tape media access commands have immediate status with a
+ * delayed process. In case of a successful initiation of a
+ * media access packet command, the DSC bit will be set when the
+ * actual execution of the command is finished.
+ * Since the tape drive will not issue an interrupt, we have to
+ * poll for this event. In this case, we define the request as
+ * "low priority request" by setting rq_status to
+ * IDETAPE_RQ_POSTPONED, set a timer to poll for DSC and exit
+ * the driver.
+ *
+ * ide.c will then give higher priority to requests which
+ * originate from the other device, until will change rq_status
+ * to RQ_ACTIVE.
+ *
+ * 4. When the packet command is finished, it will be checked for errors.
+ *
+ * 5. In case an error was found, we queue a request sense packet command
+ * in front of the request queue and retry the operation up to
+ * IDETAPE_MAX_PC_RETRIES times.
+ *
+ * 6. In case no error was found, or we decided to give up and not
+ * to retry again, the callback function will be called and then
+ * we will handle the next request.
+ *
+ */
+static void idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_bcount_reg_t bcount;
+ idetape_ireason_reg_t ireason;
+ int dma_ok=0;
+
+#if IDETAPE_DEBUG_BUGS
+ if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+ printk (KERN_ERR "ide-tape: possible ide-tape.c bug - Two request sense in serial were issued\n");
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD)
+ tape->failed_pc=pc;
+ tape->pc=pc; /* Set the current packet command */
+
+ if (pc->retries > IDETAPE_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
+ /*
+ * We will "abort" retrying a packet command in case
+ * a legitimate error code was received (crossing a
+ * filemark, or DMA error in the end of media, for
+ * example).
+ */
+ if (!test_bit (PC_ABORT, &pc->flags)) {
+ printk (KERN_ERR "ide-tape: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+ tape->name, pc->c[0], tape->sense_key, tape->asc, tape->ascq);
+ pc->error = IDETAPE_ERROR_GENERAL; /* Giving up */
+ }
+ tape->failed_pc=NULL;
+ pc->callback(drive);
+ return;
+ }
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Retry number - %d\n",pc->retries);
+#endif /* IDETAPE_DEBUG_LOG */
+
+ pc->retries++;
+ pc->actually_transferred=0; /* We haven't transferred any data yet */
+ pc->current_position=pc->buffer;
+ bcount.all=pc->request_transfer; /* Request to transfer the entire buffer at once */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (clear_bit (PC_DMA_ERROR, &pc->flags)) {
+ printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n");
+ drive->using_dma=0;
+ }
+ if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
+ dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+ OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */
+ OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+ OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+ OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+ ide_set_handler (drive, &idetape_pc_intr, WAIT_CMD); /* Set the interrupt routine */
+ OUT_BYTE (WIN_PACKETCMD,IDE_COMMAND_REG); /* Issue the packet command */
+
+ if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { /* Wait for DRQ to be ready - Assuming Accelerated DRQ */
+ /*
+ * We currently only support tape drives which report
+ * accelerated DRQ assertion. For this case, specs
+ * allow up to 50us. We really shouldn't get here.
+ *
+ * ??? Still needs to think what to do if we reach
+ * here anyway.
+ */
+ printk (KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n");
+ return;
+ }
+ ireason.all=IN_BYTE (IDE_IREASON_REG);
+ if (!ireason.b.cod || ireason.b.io) {
+ printk (KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing a packet command\n");
+ ide_do_reset (drive);
+ return;
+ }
+ atapi_output_bytes (drive,pc->c,12); /* Send the actual packet */
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (dma_ok) { /* Begin DMA, if necessary */
+ set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+ (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+ }
+#endif /* CONFIG_BLK_DEV_TRITON */
+}
+
+static void idetape_media_access_finished (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc = tape->pc;
+ idetape_status_reg_t status;
+
+ status.all = GET_STAT();
+ if (status.b.dsc) {
+ if (status.b.check) { /* Error detected */
+ printk (KERN_ERR "ide-tape: %s: I/O error, ",tape->name);
+ idetape_retry_pc (drive); /* Retry operation */
+ return;
+ }
+ pc->error = 0;
+ if (tape->failed_pc == pc)
+ tape->failed_pc = NULL;
+ } else {
+ pc->error = IDETAPE_ERROR_GENERAL;
+ tape->failed_pc = NULL;
+ }
+ pc->callback (drive);
+}
+
+/*
+ * General packet command callback function.
+ */
+static void idetape_pc_callback (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: Reached idetape_pc_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ idetape_end_request (tape->pc->error ? 0:1, HWGROUP(drive));
+}
+
+static void idetape_rw_callback (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ struct request *rq = HWGROUP(drive)->rq;
+ int blocks = tape->pc->actually_transferred / tape->tape_block_size;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: Reached idetape_rw_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ tape->block_address += blocks;
+ rq->current_nr_sectors -= blocks;
+
+ if (!tape->pc->error)
+ idetape_end_request (1, HWGROUP (drive));
+ else
+ idetape_end_request (tape->pc->error, HWGROUP (drive));
+}
+
+static void idetape_create_locate_cmd (idetape_pc_t *pc, unsigned int block, byte partition)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_LOCATE_CMD;
+ pc->c[1] = 2;
+ put_unaligned (htonl (block), (unsigned int *) &pc->c[3]);
+ pc->c[8] = partition;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_rewind_cmd (idetape_pc_t *pc)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_REWIND_CMD;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+/*
+ * A mode sense command is used to "sense" tape parameters.
+ */
+static void idetape_create_mode_sense_cmd (idetape_pc_t *pc, byte page_code)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_MODE_SENSE_CMD;
+ pc->c[1] = 8; /* DBD = 1 - Don't return block descriptors for now */
+ pc->c[2] = page_code;
+ pc->c[3] = 255; /* Don't limit the returned information */
+ pc->c[4] = 255; /* (We will just discard data in that case) */
+ if (page_code == IDETAPE_CAPABILITIES_PAGE)
+ pc->request_transfer = 24;
+#if IDETAPE_DEBUG_BUGS
+ else
+ printk (KERN_ERR "ide-tape: unsupported page code in create_mode_sense_cmd\n");
+#endif /* IDETAPE_DEBUG_BUGS */
+ pc->callback = &idetape_pc_callback;
+}
+
+/*
+ * idetape_create_write_filemark_cmd will:
+ *
+ * 1. Write a filemark if write_filemark=1.
+ * 2. Flush the device buffers without writing a filemark
+ * if write_filemark=0.
+ *
+ */
+static void idetape_create_write_filemark_cmd (idetape_pc_t *pc,int write_filemark)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_WRITE_FILEMARK_CMD;
+ pc->c[4] = write_filemark;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_load_unload_cmd (idetape_pc_t *pc,int cmd)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_LOAD_UNLOAD_CMD;
+ pc->c[4] = cmd;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_erase_cmd (idetape_pc_t *pc)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_ERASE_CMD;
+ pc->c[1] = 1;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_read_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_READ_CMD;
+ put_unaligned (htonl (length), (unsigned int *) &pc->c[1]);
+ pc->c[1] = 1;
+ pc->callback = &idetape_rw_callback;
+ pc->bh = bh;
+ bh->b_count = 0;
+ pc->buffer = NULL;
+ pc->request_transfer = pc->buffer_size = length * tape->tape_block_size;
+ if (pc->request_transfer == tape->stage_size)
+ set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+}
+
+static void idetape_create_space_cmd (idetape_pc_t *pc,int count,byte cmd)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_SPACE_CMD;
+ put_unaligned (htonl (count), (unsigned int *) &pc->c[1]);
+ pc->c[1] = cmd;
+ set_bit (PC_WAIT_FOR_DSC, &pc->flags);
+ pc->callback = &idetape_pc_callback;
+}
+
+static void idetape_create_write_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct buffer_head *bh)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_WRITE_CMD;
+ put_unaligned (htonl (length), (unsigned int *) &pc->c[1]);
+ pc->c[1] = 1;
+ pc->callback = &idetape_rw_callback;
+ set_bit (PC_WRITING, &pc->flags);
+ pc->bh = bh;
+ pc->b_data = bh->b_data;
+ pc->b_count = bh->b_count;
+ pc->buffer = NULL;
+ pc->request_transfer = pc->buffer_size = length * tape->tape_block_size;
+ if (pc->request_transfer == tape->stage_size)
+ set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+}
+
+static void idetape_read_position_callback (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_read_position_result_t *result;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: Reached idetape_read_position_callback\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (!tape->pc->error) {
+ result = (idetape_read_position_result_t *) tape->pc->buffer;
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "BOP - %s\n",result->bop ? "Yes":"No");
+ printk (KERN_INFO "EOP - %s\n",result->eop ? "Yes":"No");
+#endif /* IDETAPE_DEBUG_LOG */
+ if (result->bpu) {
+ printk (KERN_INFO "ide-tape: Block location is unknown to the tape\n");
+ clear_bit (IDETAPE_ADDRESS_VALID, &tape->flags);
+ idetape_end_request (0,HWGROUP (drive));
+ } else {
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Block Location - %lu\n", ntohl (result->first_block));
+#endif /* IDETAPE_DEBUG_LOG */
+ tape->partition = result->partition;
+ tape->block_address = ntohl (result->first_block);
+ set_bit (IDETAPE_ADDRESS_VALID, &tape->flags);
+ idetape_end_request (1,HWGROUP (drive));
+ }
+ } else
+ idetape_end_request (0,HWGROUP (drive));
+}
+
+static void idetape_create_read_position_cmd (idetape_pc_t *pc)
+{
+ idetape_init_pc (pc);
+ pc->c[0] = IDETAPE_READ_POSITION_CMD;
+ pc->request_transfer = 20;
+ pc->callback = &idetape_read_position_callback;
+}
+
+/*
+ * idetape_do_request is our request handling function.
+ */
+static void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t *pc;
+ struct blk_dev_struct *bdev = &blk_dev[HWIF(drive)->major];
+ struct request *postponed_rq = tape->postponed_rq;
+ idetape_status_reg_t status;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+ printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (!IDETAPE_RQ_CMD (rq->cmd)) {
+ /*
+ * We do not support buffer cache originated requests.
+ */
+ printk (KERN_NOTICE "ide-tape: %s: Unsupported command in request queue\n", drive->name);
+ ide_end_request (0,HWGROUP (drive)); /* Let the common code handle it */
+ return;
+ }
+
+ /*
+ * This is an important point. We will try to remove our request
+ * from the block device request queue while we service the
+ * request. Note that the request must be returned to
+ * bdev->current_request before the next call to
+ * ide_end_drive_cmd or ide_do_drive_cmd to conform with the
+ * normal behavior of the IDE driver, which leaves the active
+ * request in bdev->current_request during I/O.
+ *
+ * This will eliminate fragmentation of disk/cdrom requests
+ * around a tape request, now that we are using ide_next to
+ * insert pending pipeline requests, since we have only one
+ * ide-tape.c data request in the device request queue, and
+ * thus once removed, ll_rw_blk.c will only see requests from
+ * the other device.
+ *
+ * The potential fragmentation inefficiency was pointed to me
+ * by Mark Lord.
+ *
+ * Uhuh.. the following "fix" is actually not entirely correct.
+ * Some day we should probably move to a per device request
+ * queue, rather than per interface.
+ */
+ if (rq->next != NULL && rq->rq_dev != rq->next->rq_dev)
+ bdev->current_request=rq->next;
+
+ /*
+ * Retry a failed packet command
+ */
+ if (tape->failed_pc != NULL && tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) {
+ idetape_issue_packet_command (drive, tape->failed_pc);
+ return;
+ }
+#if IDETAPE_DEBUG_BUGS
+ if (postponed_rq != NULL)
+ if (postponed_rq->rq_status != RQ_ACTIVE || rq != postponed_rq) {
+ printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
+ idetape_end_request (0,HWGROUP (drive));
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ tape->postponed_rq = NULL;
+
+ /*
+ * If the tape is still busy, postpone our request and service
+ * the other device meanwhile.
+ */
+ status.all = GET_STAT();
+ if (!clear_bit (IDETAPE_IGNORE_DSC, &tape->flags) && !status.b.dsc) {
+ if (postponed_rq == NULL) {
+ tape->dsc_polling_start = jiffies;
+ tape->dsc_polling_frequency = tape->best_dsc_rw_frequency;
+ tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT;
+ } else if ((signed long) (jiffies - tape->dsc_timeout) > 0) {
+ printk (KERN_ERR "ide-tape: %s: DSC timeout\n", tape->name);
+ if (rq->cmd == IDETAPE_PC_RQ)
+ idetape_media_access_finished (drive);
+ else
+ ide_do_reset (drive);
+ return;
+ } else if (jiffies - tape->dsc_polling_start > IDETAPE_DSC_MA_THRESHOLD)
+ tape->dsc_polling_frequency = IDETAPE_DSC_MA_SLOW;
+ idetape_postpone_request (drive);
+ return;
+ }
+ switch (rq->cmd) {
+ case IDETAPE_READ_RQ:
+ pc=idetape_next_pc_storage (drive);
+ idetape_create_read_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
+ break;
+ case IDETAPE_WRITE_RQ:
+ pc=idetape_next_pc_storage (drive);
+ idetape_create_write_cmd (tape, pc, rq->current_nr_sectors, rq->bh);
+ break;
+ case IDETAPE_ABORTED_WRITE_RQ:
+ rq->cmd = IDETAPE_WRITE_RQ;
+ rq->errors = IDETAPE_ERROR_EOD;
+ idetape_end_request (1, HWGROUP(drive));
+ return;
+ case IDETAPE_PC_RQ:
+ if (postponed_rq != NULL) {
+ idetape_media_access_finished (drive);
+ return;
+ }
+ pc=(idetape_pc_t *) rq->buffer;
+ break;
+ default:
+ printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n");
+ idetape_end_request (0,HWGROUP (drive));
+ return;
+ }
+ idetape_issue_packet_command (drive, pc);
+}
+
+/*
+ * idetape_queue_pc_tail is based on the following functions:
+ *
+ * ide_do_drive_cmd from ide.c
+ * cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c
+ *
+ * We add a special packet command request to the tail of the request queue,
+ * and wait for it to be serviced.
+ *
+ * This is not to be called from within the request handling part
+ * of the driver ! We allocate here data in the stack, and it is valid
+ * until the request is finished. This is not the case for the bottom
+ * part of the driver, where we are always leaving the functions to wait
+ * for an interrupt or a timer event.
+ *
+ * From the bottom part of the driver, we should allocate safe memory
+ * using idetape_next_pc_storage and idetape_next_rq_storage, and add
+ * the request to the request list without waiting for it to be serviced !
+ * In that case, we usually use idetape_queue_pc_head.
+ */
+static int idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc)
+{
+ struct request rq;
+
+ ide_init_drive_cmd (&rq);
+ rq.buffer = (char *) pc;
+ rq.cmd = IDETAPE_PC_RQ;
+ return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ * idetape_wait_for_request installs a semaphore in a pending request
+ * and sleeps until it is serviced.
+ *
+ * The caller should ensure that the request will not be serviced
+ * before we install the semaphore (usually by disabling interrupts).
+ */
+static void idetape_wait_for_request (struct request *rq)
+{
+ struct semaphore sem = MUTEX_LOCKED;
+
+#if IDETAPE_DEBUG_BUGS
+ if (rq == NULL || !IDETAPE_RQ_CMD (rq->cmd)) {
+ printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ rq->sem = &sem;
+ down (&sem);
+}
+
+/*
+ * idetape_queue_rw_tail generates a read/write request for the block
+ * device interface and wait for it to be serviced.
+ */
+static int idetape_queue_rw_tail (ide_drive_t *drive, int cmd, int blocks, struct buffer_head *bh)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ struct request rq;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "idetape_queue_rw_tail: cmd=%d\n",cmd);
+#endif /* IDETAPE_DEBUG_LOG */
+#if IDETAPE_DEBUG_BUGS
+ if (idetape_pipeline_active (tape)) {
+ printk (KERN_ERR "ide-tape: bug: the pipeline is active in idetape_queue_rw_tail\n");
+ return (0);
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+
+ ide_init_drive_cmd (&rq);
+ rq.bh = bh;
+ rq.cmd = cmd;
+ rq.sector = tape->block_address;
+ rq.nr_sectors = rq.current_nr_sectors = blocks;
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
+
+ idetape_init_merge_stage (tape);
+ if (rq.errors == IDETAPE_ERROR_GENERAL)
+ return -EIO;
+ return (tape->tape_block_size * (blocks-rq.current_nr_sectors));
+}
+
+/*
+ * idetape_add_chrdev_read_request is called from idetape_chrdev_read
+ * to service a character device read request and add read-ahead
+ * requests to our pipeline.
+ */
+static int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *new_stage;
+ unsigned long flags;
+ struct request rq,*rq_ptr;
+ int bytes_read;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_add_chrdev_read_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ ide_init_drive_cmd (&rq);
+ rq.cmd = IDETAPE_READ_RQ;
+ rq.sector = tape->block_address;
+ rq.nr_sectors = rq.current_nr_sectors = blocks;
+
+ if (idetape_pipeline_active (tape) || tape->nr_stages <= tape->max_stages / 4) {
+ new_stage=idetape_kmalloc_stage (tape);
+ while (new_stage != NULL) {
+ new_stage->rq=rq;
+ idetape_add_stage_tail (drive,new_stage);
+ new_stage=idetape_kmalloc_stage (tape);
+ }
+ if (!idetape_pipeline_active (tape))
+ idetape_insert_pipeline_into_queue (drive);
+ }
+ if (tape->first_stage == NULL) {
+ /*
+ * Linux is short on memory. Revert to non-pipelined
+ * operation mode for this request.
+ */
+ return (idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, blocks, tape->merge_stage->bh));
+ }
+ save_flags (flags);
+ cli ();
+ if (tape->active_stage == tape->first_stage)
+ idetape_wait_for_request (tape->active_data_request);
+ restore_flags (flags);
+
+ rq_ptr = &tape->first_stage->rq;
+ bytes_read = tape->tape_block_size * (rq_ptr->nr_sectors - rq_ptr->current_nr_sectors);
+ rq_ptr->nr_sectors = rq_ptr->current_nr_sectors = 0;
+
+ idetape_switch_buffers (tape, tape->first_stage);
+
+ if (rq_ptr->errors != IDETAPE_ERROR_FILEMARK) {
+ clear_bit (IDETAPE_FILEMARK, &tape->flags);
+ idetape_remove_stage_head (drive);
+ } else
+ set_bit (IDETAPE_FILEMARK, &tape->flags);
+#if IDETAPE_DEBUG_BUGS
+ if (bytes_read > blocks*tape->tape_block_size) {
+ printk (KERN_ERR "ide-tape: bug: trying to return more bytes than requested\n");
+ bytes_read=blocks*tape->tape_block_size;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ return (bytes_read);
+}
+
+/*
+ * idetape_add_chrdev_write_request tries to add a character device
+ * originated write request to our pipeline. In case we don't succeed,
+ * we revert to non-pipelined operation mode for this request.
+ *
+ * 1. Try to allocate a new pipeline stage.
+ * 2. If we can't, wait for more and more requests to be serviced
+ * and try again each time.
+ * 3. If we still can't allocate a stage, fallback to
+ * non-pipelined operation mode for this request.
+ */
+static int idetape_add_chrdev_write_request (ide_drive_t *drive, int blocks)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *new_stage;
+ unsigned long flags;
+ struct request *rq;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_add_chrdev_write_request\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ /*
+ * Attempt to allocate a new stage.
+ * Pay special attention to possible race conditions.
+ */
+ while ((new_stage = idetape_kmalloc_stage (tape)) == NULL) {
+ save_flags (flags);
+ cli ();
+ if (idetape_pipeline_active (tape)) {
+ idetape_wait_for_request (tape->active_data_request);
+ restore_flags (flags);
+ } else {
+ restore_flags (flags);
+ idetape_insert_pipeline_into_queue (drive);
+ if (idetape_pipeline_active (tape))
+ continue;
+ /*
+ * Linux is short on memory. Fallback to
+ * non-pipelined operation mode for this request.
+ */
+ return idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bh);
+ }
+ }
+ rq = &new_stage->rq;
+ ide_init_drive_cmd (rq);
+ rq->cmd = IDETAPE_WRITE_RQ;
+ rq->sector = tape->block_address; /* Doesn't actually matter - We always assume sequential access */
+ rq->nr_sectors = rq->current_nr_sectors = blocks;
+
+ idetape_switch_buffers (tape, new_stage);
+ idetape_add_stage_tail (drive,new_stage);
+
+ /*
+ * Check if we are currently servicing requests in the bottom
+ * part of the driver.
+ *
+ * If not, wait for the pipeline to be full enough (75%) before
+ * starting to service requests, so that we will be able to
+ * keep up with the higher speeds of the tape.
+ */
+ if (!idetape_pipeline_active (tape) && tape->nr_stages >= (3 * tape->max_stages) / 4)
+ idetape_insert_pipeline_into_queue (drive);
+
+ if (clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags)) /* Return a deferred error */
+ return -EIO;
+ return blocks;
+}
+
+static void idetape_discard_read_pipeline (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+
+#if IDETAPE_DEBUG_BUGS
+ if (tape->chrdev_direction != idetape_direction_read) {
+ printk (KERN_ERR "ide-tape: bug: Trying to discard read pipeline, but we are not reading.\n");
+ return;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ tape->merge_stage_size = 0;
+ if (tape->merge_stage != NULL) {
+ __idetape_kfree_stage (tape->merge_stage);
+ tape->merge_stage = NULL;
+ }
+ tape->chrdev_direction = idetape_direction_none;
+
+ if (tape->first_stage == NULL)
+ return;
+
+ save_flags (flags);
+ cli ();
+ tape->next_stage = NULL;
+ if (idetape_pipeline_active (tape))
+ idetape_wait_for_request (tape->active_data_request);
+ restore_flags (flags);
+
+ while (tape->first_stage != NULL)
+ idetape_remove_stage_head (drive);
+ tape->nr_pending_stages = 0;
+ tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+}
+
+/*
+ * idetape_wait_for_pipeline will wait until all pending pipeline
+ * requests are serviced. Typically called on device close.
+ */
+static void idetape_wait_for_pipeline (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned long flags;
+
+ if (!idetape_pipeline_active (tape))
+ idetape_insert_pipeline_into_queue (drive);
+
+ save_flags (flags);
+ cli ();
+ if (!idetape_pipeline_active (tape))
+ goto abort;
+#if IDETAPE_DEBUG_BUGS
+ if (tape->last_stage == NULL)
+ printk ("ide-tape: tape->last_stage == NULL\n");
+ else
+#endif /* IDETAPE_DEBUG_BUGS */
+ idetape_wait_for_request (&tape->last_stage->rq);
+abort:
+ restore_flags (flags);
+}
+
+static void idetape_pad_zeros (ide_drive_t *drive, int bcount)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ struct buffer_head *bh;
+ int count, blocks;
+
+ while (bcount) {
+ bh = tape->merge_stage->bh;
+ count = IDETAPE_MIN (tape->stage_size, bcount);
+ bcount -= count;
+ blocks = count / tape->tape_block_size;
+ while (count) {
+ bh->b_count = IDETAPE_MIN (count, bh->b_size);
+ memset (bh->b_data, 0, bh->b_count);
+ count -= bh->b_count;
+ bh = bh->b_reqnext;
+ }
+ idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bh);
+ }
+}
+
+static void idetape_empty_write_pipeline (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int blocks, i;
+
+#if IDETAPE_DEBUG_BUGS
+ if (tape->chrdev_direction != idetape_direction_write) {
+ printk (KERN_ERR "ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n");
+ return;
+ }
+ if (tape->merge_stage_size > tape->stage_size) {
+ printk (KERN_ERR "ide-tape: bug: merge_buffer too big\n");
+ tape->merge_stage_size = tape->stage_size;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ if (tape->merge_stage_size) {
+ blocks=tape->merge_stage_size/tape->tape_block_size;
+ if (tape->merge_stage_size % tape->tape_block_size) {
+ blocks++;
+ i = tape->tape_block_size - tape->merge_stage_size % tape->tape_block_size;
+ memset (tape->merge_stage->bh->b_data + tape->merge_stage->bh->b_count, 0, i);
+ tape->merge_stage->bh->b_count += i;
+ }
+ (void) idetape_add_chrdev_write_request (drive, blocks);
+ tape->merge_stage_size = 0;
+ }
+ idetape_wait_for_pipeline (drive);
+ if (tape->merge_stage != NULL) {
+ __idetape_kfree_stage (tape->merge_stage);
+ tape->merge_stage = NULL;
+ }
+ clear_bit (IDETAPE_PIPELINE_ERROR, &tape->flags);
+ tape->chrdev_direction=idetape_direction_none;
+
+ /*
+ * On the next backup, perform the feedback loop again.
+ * (I don't want to keep sense information between backups,
+ * as some systems are constantly on, and the system load
+ * can be totally different on the next backup).
+ */
+ tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+#if IDETAPE_DEBUG_BUGS
+ if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->nr_stages != 0) {
+ printk (KERN_ERR "ide-tape: ide-tape pipeline bug\n");
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+}
+
+static int idetape_pipeline_size (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_stage_t *stage;
+ struct request *rq;
+ int size = 0;
+
+ idetape_wait_for_pipeline (drive);
+ stage = tape->first_stage;
+ while (stage != NULL) {
+ rq = &stage->rq;
+ size += tape->tape_block_size * (rq->nr_sectors-rq->current_nr_sectors);
+ if (rq->errors == IDETAPE_ERROR_FILEMARK)
+ size += tape->tape_block_size;
+ stage = stage->next;
+ }
+ size += tape->merge_stage_size;
+ return size;
+}
+
+/*
+ * idetape_position_tape positions the tape to the requested block
+ * using the LOCATE packet command. A READ POSITION command is then
+ * issued to check where we are positioned.
+ *
+ * Like all higher level operations, we queue the commands at the tail
+ * of the request queue and wait for their completion.
+ *
+ */
+static int idetape_position_tape (ide_drive_t *drive, unsigned int block, byte partition)
+{
+ int retval;
+ idetape_pc_t pc;
+
+ idetape_create_locate_cmd (&pc, block, partition);
+ retval=idetape_queue_pc_tail (drive,&pc);
+ if (retval) return (retval);
+
+ idetape_create_read_position_cmd (&pc);
+ return (idetape_queue_pc_tail (drive,&pc));
+}
+
+/*
+ * Rewinds the tape to the Beginning Of the current Partition (BOP).
+ *
+ * We currently support only one partition.
+ */
+static int idetape_rewind_tape (ide_drive_t *drive)
+{
+ int retval;
+ idetape_pc_t pc;
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_rewind_tape\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ idetape_create_rewind_cmd (&pc);
+ retval=idetape_queue_pc_tail (drive,&pc);
+ if (retval) return (retval);
+
+ idetape_create_read_position_cmd (&pc);
+ return (idetape_queue_pc_tail (drive,&pc));
+}
+
+static int idetape_flush_tape_buffers (ide_drive_t *drive)
+{
+ idetape_pc_t pc;
+
+ idetape_create_write_filemark_cmd (&pc,0);
+ return (idetape_queue_pc_tail (drive,&pc));
+}
+
+/*
+ * Our special ide-tape ioctl's.
+ *
+ * Currently there aren't any ioctl's.
+ * mtio.h compatible commands should be issued to the character device
+ * interface.
+ */
+static int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_config_t config;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "ide-tape: Reached idetape_blkdev_ioctl\n");
+#endif /* IDETAPE_DEBUG_LOG */
+ switch (cmd) {
+ case 0x0340:
+ if (copy_from_user ((char *) &config, (char *) arg, sizeof (idetape_config_t)))
+ return -EFAULT;
+ tape->best_dsc_rw_frequency = config.dsc_rw_frequency;
+ tape->max_stages = config.nr_stages;
+ break;
+ case 0x0350:
+ config.dsc_rw_frequency = (int) tape->best_dsc_rw_frequency;
+ config.nr_stages = tape->max_stages;
+ if (copy_to_user ((char *) arg, (char *) &config, sizeof (idetape_config_t)))
+ return -EFAULT;
+ break;
+ default:
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * The block device interface should not be used for data transfers.
+ * However, we still allow opening it so that we can issue general
+ * ide driver configuration ioctl's, such as the interrupt unmask feature.
+ */
+static int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * idetape_pre_reset is called before an ATAPI/ATA software reset.
+ */
+static void idetape_pre_reset (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ if (tape != NULL)
+ set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
+}
+
+/*
+ * Character device interface functions
+ */
+static ide_drive_t *get_drive_ptr (kdev_t i_rdev)
+{
+ unsigned int i = MINOR(i_rdev) & ~0x80;
+
+ if (i >= MAX_HWIFS * MAX_DRIVES)
+ return NULL;
+ return (idetape_chrdevs[i].drive);
+}
+
+/*
+ * idetape_space_over_filemarks is now a bit more complicated than just
+ * passing the command to the tape since we may have crossed some
+ * filemarks during our pipelined read-ahead mode.
+ *
+ * As a minor side effect, the pipeline enables us to support MTFSFM when
+ * the filemark is in our internal pipeline even if the tape doesn't
+ * support spacing over filemarks in the reverse direction.
+ */
+static int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_count)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ unsigned long flags;
+ int retval,count=0;
+
+ if (tape->chrdev_direction == idetape_direction_read) {
+
+ /*
+ * We have a read-ahead buffer. Scan it for crossed
+ * filemarks.
+ */
+ tape->merge_stage_size = 0;
+ clear_bit (IDETAPE_FILEMARK, &tape->flags);
+ while (tape->first_stage != NULL) {
+ /*
+ * Wait until the first read-ahead request
+ * is serviced.
+ */
+ save_flags (flags);
+ cli ();
+ if (tape->active_stage == tape->first_stage)
+ idetape_wait_for_request (tape->active_data_request);
+ restore_flags (flags);
+
+ if (tape->first_stage->rq.errors == IDETAPE_ERROR_FILEMARK)
+ count++;
+ if (count == mt_count) {
+ switch (mt_op) {
+ case MTFSF:
+ idetape_remove_stage_head (drive);
+ case MTFSFM:
+ return (0);
+ default:
+ break;
+ }
+ }
+ idetape_remove_stage_head (drive);
+ }
+ idetape_discard_read_pipeline (drive);
+ }
+
+ /*
+ * The filemark was not found in our internal pipeline.
+ * Now we can issue the space command.
+ */
+ switch (mt_op) {
+ case MTFSF:
+ idetape_create_space_cmd (&pc,mt_count-count,IDETAPE_SPACE_OVER_FILEMARK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTFSFM:
+ if (!tape->capabilities.sprev)
+ return (-EIO);
+ retval = idetape_space_over_filemarks (drive, MTFSF, mt_count-count);
+ if (retval) return (retval);
+ return (idetape_space_over_filemarks (drive, MTBSF, 1));
+ case MTBSF:
+ if (!tape->capabilities.sprev)
+ return (-EIO);
+ idetape_create_space_cmd (&pc,-(mt_count+count),IDETAPE_SPACE_OVER_FILEMARK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTBSFM:
+ if (!tape->capabilities.sprev)
+ return (-EIO);
+ retval = idetape_space_over_filemarks (drive, MTBSF, mt_count+count);
+ if (retval) return (retval);
+ return (idetape_space_over_filemarks (drive, MTFSF, 1));
+ default:
+ printk (KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op);
+ return (-EIO);
+ }
+}
+
+
+/*
+ * Our character device read / write functions.
+ *
+ * The tape is optimized to maximize throughput when it is transferring
+ * an integral number of the "continuous transfer limit", which is
+ * a parameter of the specific tape (26 KB on my particular tape).
+ *
+ * As of version 1.3 of the driver, the character device provides an
+ * abstract continuous view of the media - any mix of block sizes (even 1
+ * byte) on the same backup/restore procedure is supported. The driver
+ * will internally convert the requests to the recommended transfer unit,
+ * so that an unmatch between the user's block size to the recommended
+ * size will only result in a (slightly) increased driver overhead, but
+ * will no longer hit performance.
+ */
+static long idetape_chrdev_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
+{
+ ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+ idetape_tape_t *tape = drive->driver_data;
+ int bytes_read,temp,actually_read=0, original_count = count;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_chrdev_read\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->chrdev_direction != idetape_direction_read) { /* Initialize read operation */
+ if (tape->chrdev_direction == idetape_direction_write) {
+ idetape_empty_write_pipeline (drive);
+ idetape_flush_tape_buffers (drive);
+ }
+#if IDETAPE_DEBUG_BUGS
+ if (tape->merge_stage || tape->merge_stage_size) {
+ printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n");
+ tape->merge_stage_size = 0;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ if ((tape->merge_stage = __idetape_kmalloc_stage (tape)) == NULL)
+ return -ENOMEM;
+ tape->chrdev_direction = idetape_direction_read;
+
+ /*
+ * Issue a read 0 command to ensure that DSC handshake
+ * is switched from completion mode to buffer available
+ * mode.
+ */
+ bytes_read = idetape_queue_rw_tail (drive, IDETAPE_READ_RQ, 0, tape->merge_stage->bh);
+ if (bytes_read < 0) {
+ kfree (tape->merge_stage);
+ tape->merge_stage = NULL;
+ tape->chrdev_direction = idetape_direction_none;
+ return bytes_read;
+ }
+ if (test_bit (IDETAPE_DETECT_BS, &tape->flags))
+ if (count > tape->tape_block_size && (count % tape->tape_block_size) == 0)
+ tape->user_bs_factor = count / tape->tape_block_size;
+ }
+ if (count==0)
+ return (0);
+ if (tape->merge_stage_size) {
+ actually_read=IDETAPE_MIN (tape->merge_stage_size,count);
+ idetape_copy_stage_to_user (tape, buf, tape->merge_stage, actually_read);
+ buf += actually_read; tape->merge_stage_size -= actually_read; count-=actually_read;
+ }
+ while (count >= tape->stage_size) {
+ bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl);
+ if (bytes_read <= 0)
+ goto finish;
+ idetape_copy_stage_to_user (tape, buf, tape->merge_stage, bytes_read);
+ buf += bytes_read; count -= bytes_read; actually_read += bytes_read;
+ }
+ if (count) {
+ bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl);
+ if (bytes_read <= 0)
+ goto finish;
+ temp=IDETAPE_MIN (count,bytes_read);
+ idetape_copy_stage_to_user (tape, buf, tape->merge_stage, temp);
+ actually_read+=temp;
+ tape->merge_stage_size=bytes_read-temp;
+ }
+finish:
+ if (actually_read < original_count && test_bit (IDETAPE_FILEMARK, &tape->flags))
+ idetape_space_over_filemarks (drive, MTFSF, 1);
+ return (actually_read);
+}
+
+static long idetape_chrdev_write (struct inode *inode, struct file *file, const char *buf, unsigned long count)
+{
+ ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+ idetape_tape_t *tape = drive->driver_data;
+ int retval,actually_written=0;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_chrdev_write\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->chrdev_direction != idetape_direction_write) { /* Initialize write operation */
+ if (tape->chrdev_direction == idetape_direction_read)
+ idetape_discard_read_pipeline (drive);
+#if IDETAPE_DEBUG_BUGS
+ if (tape->merge_stage || tape->merge_stage_size) {
+ printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n");
+ tape->merge_stage_size = 0;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ if ((tape->merge_stage = __idetape_kmalloc_stage (tape)) == NULL)
+ return -ENOMEM;
+ tape->chrdev_direction = idetape_direction_write;
+ idetape_init_merge_stage (tape);
+
+ /*
+ * Issue a write 0 command to ensure that DSC handshake
+ * is switched from completion mode to buffer available
+ * mode.
+ */
+ retval = idetape_queue_rw_tail (drive, IDETAPE_WRITE_RQ, 0, tape->merge_stage->bh);
+ if (retval < 0) {
+ kfree (tape->merge_stage);
+ tape->merge_stage = NULL;
+ tape->chrdev_direction = idetape_direction_none;
+ return retval;
+ }
+ if (test_bit (IDETAPE_DETECT_BS, &tape->flags))
+ if (count > tape->tape_block_size && (count % tape->tape_block_size) == 0)
+ tape->user_bs_factor = count / tape->tape_block_size;
+ }
+ if (count==0)
+ return (0);
+ if (tape->merge_stage_size) {
+#if IDETAPE_DEBUG_BUGS
+ if (tape->merge_stage_size >= tape->stage_size) {
+ printk (KERN_ERR "ide-tape: bug: merge buffer too big\n");
+ tape->merge_stage_size=0;
+ }
+#endif /* IDETAPE_DEBUG_BUGS */
+ actually_written=IDETAPE_MIN (tape->stage_size-tape->merge_stage_size,count);
+ idetape_copy_stage_from_user (tape, tape->merge_stage, buf, actually_written);
+ buf+=actually_written;tape->merge_stage_size+=actually_written;count-=actually_written;
+
+ if (tape->merge_stage_size == tape->stage_size) {
+ tape->merge_stage_size = 0;
+ retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl);
+ if (retval <= 0)
+ return (retval);
+ }
+ }
+ while (count >= tape->stage_size) {
+ idetape_copy_stage_from_user (tape, tape->merge_stage, buf, tape->stage_size);
+ buf+=tape->stage_size;count-=tape->stage_size;
+ retval=idetape_add_chrdev_write_request (drive, tape->capabilities.ctl);
+ actually_written+=tape->stage_size;
+ if (retval <= 0)
+ return (retval);
+ }
+ if (count) {
+ actually_written+=count;
+ idetape_copy_stage_from_user (tape, tape->merge_stage, buf, count);
+ tape->merge_stage_size+=count;
+ }
+ return (actually_written);
+}
+
+/*
+ * idetape_mtioctop is called from idetape_chrdev_ioctl when
+ * the general mtio MTIOCTOP ioctl is requested.
+ *
+ * We currently support the following mtio.h operations:
+ *
+ * MTFSF - Space over mt_count filemarks in the positive direction.
+ * The tape is positioned after the last spaced filemark.
+ *
+ * MTFSFM - Same as MTFSF, but the tape is positioned before the
+ * last filemark.
+ *
+ * MTBSF - Steps background over mt_count filemarks, tape is
+ * positioned before the last filemark.
+ *
+ * MTBSFM - Like MTBSF, only tape is positioned after the last filemark.
+ *
+ * Note:
+ *
+ * MTBSF and MTBSFM are not supported when the tape doesn't
+ * supports spacing over filemarks in the reverse direction.
+ * In this case, MTFSFM is also usually not supported (it is
+ * supported in the rare case in which we crossed the filemark
+ * during our read-ahead pipelined operation mode).
+ *
+ * MTWEOF - Writes mt_count filemarks. Tape is positioned after
+ * the last written filemark.
+ *
+ * MTREW - Rewinds tape.
+ *
+ * MTLOAD - Loads the tape.
+ *
+ * MTOFFL - Puts the tape drive "Offline": Rewinds the tape and
+ * MTUNLOAD prevents further access until the media is replaced.
+ *
+ * MTNOP - Flushes tape buffers.
+ *
+ * MTRETEN - Retension media. This typically consists of one end
+ * to end pass on the media.
+ *
+ * MTEOM - Moves to the end of recorded data.
+ *
+ * MTERASE - Erases tape.
+ *
+ * MTSETBLK - Sets the user block size to mt_count bytes. If
+ * mt_count is 0, we will attempt to autodetect
+ * the block size.
+ *
+ * MTSEEK - Positions the tape in a specific block number, where
+ * each block is assumed to contain which user_block_size
+ * bytes.
+ *
+ * MTSETPART - Switches to another tape partition.
+ *
+ * The following commands are currently not supported:
+ *
+ * MTFSR, MTBSR, MTFSS, MTBSS, MTWSM, MTSETDENSITY,
+ * MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD.
+ */
+static int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ int i,retval;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Handling MTIOCTOP ioctl: mt_op=%d, mt_count=%d\n",mt_op,mt_count);
+#endif /* IDETAPE_DEBUG_LOG */
+ /*
+ * Commands which need our pipelined read-ahead stages.
+ */
+ switch (mt_op) {
+ case MTFSF:
+ case MTFSFM:
+ case MTBSF:
+ case MTBSFM:
+ if (!mt_count)
+ return (0);
+ return (idetape_space_over_filemarks (drive,mt_op,mt_count));
+ default:
+ break;
+ }
+
+ /*
+ * Empty the pipeline.
+ */
+ if (tape->chrdev_direction == idetape_direction_read)
+ idetape_discard_read_pipeline (drive);
+
+ switch (mt_op) {
+ case MTWEOF:
+ for (i=0;i<mt_count;i++) {
+ idetape_create_write_filemark_cmd (&pc,1);
+ retval=idetape_queue_pc_tail (drive,&pc);
+ if (retval) return (retval);
+ }
+ return (0);
+ case MTREW:
+ return (idetape_rewind_tape (drive));
+ case MTLOAD:
+ idetape_create_load_unload_cmd (&pc, IDETAPE_LU_LOAD_MASK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTUNLOAD:
+ case MTOFFL:
+ idetape_create_load_unload_cmd (&pc,!IDETAPE_LU_LOAD_MASK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTNOP:
+ return (idetape_flush_tape_buffers (drive));
+ case MTRETEN:
+ idetape_create_load_unload_cmd (&pc,IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTEOM:
+ idetape_create_space_cmd (&pc,0,IDETAPE_SPACE_TO_EOD);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTERASE:
+ (void) idetape_rewind_tape (drive);
+ idetape_create_erase_cmd (&pc);
+ return (idetape_queue_pc_tail (drive,&pc));
+ case MTSETBLK:
+ if (mt_count) {
+ if (mt_count < tape->tape_block_size || mt_count % tape->tape_block_size)
+ return -EIO;
+ tape->user_bs_factor = mt_count / tape->tape_block_size;
+ clear_bit (IDETAPE_DETECT_BS, &tape->flags);
+ } else
+ set_bit (IDETAPE_DETECT_BS, &tape->flags);
+ return 0;
+ case MTSEEK:
+ return (idetape_position_tape (drive, mt_count * tape->user_bs_factor, tape->partition));
+ case MTSETPART:
+ return (idetape_position_tape (drive, 0, mt_count));
+ default:
+ printk (KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op);
+ return (-EIO);
+ }
+}
+
+/*
+ * Our character device ioctls.
+ *
+ * General mtio.h magnetic io commands are supported here, and not in
+ * the corresponding block interface.
+ *
+ * The following ioctls are supported:
+ *
+ * MTIOCTOP - Refer to idetape_mtioctop for detailed description.
+ *
+ * MTIOCGET - The mt_dsreg field in the returned mtget structure
+ * will be set to (user block size in bytes <<
+ * MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK.
+ *
+ * The mt_blkno is set to the current user block number.
+ * The other mtget fields are not supported.
+ *
+ * MTIOCPOS - The current tape "block position" is returned. We
+ * assume that each block contains user_block_size
+ * bytes.
+ *
+ * Our own ide-tape ioctls are supported on both interfaces.
+ */
+static int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ struct mtop mtop;
+ struct mtget mtget;
+ struct mtpos mtpos;
+ int retval, block_offset = 0;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_chrdev_ioctl, cmd=%u\n",cmd);
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->chrdev_direction == idetape_direction_write) {
+ idetape_empty_write_pipeline (drive);
+ idetape_flush_tape_buffers (drive);
+ }
+ if (cmd == MTIOCGET || cmd == MTIOCPOS) {
+ block_offset = idetape_pipeline_size (drive) / (tape->tape_block_size * tape->user_bs_factor);
+ idetape_create_read_position_cmd (&pc);
+ retval=idetape_queue_pc_tail (drive,&pc);
+ if (retval) return (retval);
+ }
+ switch (cmd) {
+ case MTIOCTOP:
+ if (copy_from_user ((char *) &mtop, (char *) arg, sizeof (struct mtop)))
+ return -EFAULT;
+ return (idetape_mtioctop (drive,mtop.mt_op,mtop.mt_count));
+ case MTIOCGET:
+ memset (&mtget, 0, sizeof (struct mtget));
+ mtget.mt_blkno = tape->block_address / tape->user_bs_factor - block_offset;
+ mtget.mt_dsreg = ((tape->tape_block_size * tape->user_bs_factor) << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK;
+ if (copy_to_user ((char *) arg,(char *) &mtget, sizeof (struct mtget)))
+ return -EFAULT;
+ return 0;
+ case MTIOCPOS:
+ mtpos.mt_blkno = tape->block_address / tape->user_bs_factor - block_offset;
+ if (copy_to_user ((char *) arg,(char *) &mtpos, sizeof (struct mtpos)))
+ return -EFAULT;
+ return 0;
+ default:
+ if (tape->chrdev_direction == idetape_direction_read)
+ idetape_discard_read_pipeline (drive);
+ return (idetape_blkdev_ioctl (drive,inode,file,cmd,arg));
+ }
+}
+
+/*
+ * Our character device open function.
+ */
+static int idetape_chrdev_open (struct inode *inode, struct file *filp)
+{
+ ide_drive_t *drive;
+ idetape_tape_t *tape;
+ idetape_pc_t pc;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_chrdev_open\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if ((drive = get_drive_ptr (inode->i_rdev)) == NULL)
+ return -ENXIO;
+ tape = drive->driver_data;
+
+ if (set_bit (IDETAPE_BUSY, &tape->flags))
+ return -EBUSY;
+ MOD_INC_USE_COUNT;
+ idetape_create_read_position_cmd (&pc);
+ (void) idetape_queue_pc_tail (drive,&pc);
+ if (!test_bit (IDETAPE_ADDRESS_VALID, &tape->flags))
+ (void) idetape_rewind_tape (drive);
+ MOD_DEC_USE_COUNT;
+
+ if (tape->chrdev_direction == idetape_direction_none)
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Our character device release function.
+ */
+static void idetape_chrdev_release (struct inode *inode, struct file *filp)
+{
+ ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
+ idetape_tape_t *tape = drive->driver_data;
+ unsigned int minor=MINOR (inode->i_rdev);
+ idetape_pc_t pc;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Reached idetape_chrdev_release\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ if (tape->chrdev_direction == idetape_direction_write) {
+ idetape_empty_write_pipeline (drive);
+ tape->merge_stage = __idetape_kmalloc_stage (tape);
+ if (tape->merge_stage != NULL) {
+ idetape_pad_zeros (drive, tape->tape_block_size * (tape->user_bs_factor - 1));
+ __idetape_kfree_stage (tape->merge_stage);
+ tape->merge_stage = NULL;
+ }
+ idetape_create_write_filemark_cmd (&pc,1); /* Write a filemark */
+ if (idetape_queue_pc_tail (drive,&pc))
+ printk (KERN_ERR "ide-tape: Couldn't write a filemark\n");
+ }
+ if (tape->chrdev_direction == idetape_direction_read) {
+ if (minor < 128)
+ idetape_discard_read_pipeline (drive);
+ else
+ idetape_wait_for_pipeline (drive);
+ }
+ if (tape->cache_stage != NULL) {
+ __idetape_kfree_stage (tape->cache_stage);
+ tape->cache_stage = NULL;
+ }
+ if (minor < 128)
+ (void) idetape_rewind_tape (drive);
+
+ clear_bit (IDETAPE_BUSY, &tape->flags);
+ if (tape->chrdev_direction == idetape_direction_none)
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * idetape_identify_device is called to check the contents of the
+ * ATAPI IDENTIFY command results. We return:
+ *
+ * 1 If the tape can be supported by us, based on the information
+ * we have so far.
+ *
+ * 0 If this tape driver is not currently supported by us.
+ */
+static int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+{
+ struct idetape_id_gcw gcw;
+#if IDETAPE_DEBUG_LOG
+ unsigned short mask,i;
+#endif /* IDETAPE_DEBUG_LOG */
+
+ *((unsigned short *) &gcw) = id->config;
+
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Dumping ATAPI Identify Device tape parameters\n");
+ printk (KERN_INFO "Protocol Type: ");
+ switch (gcw.protocol) {
+ case 0: case 1: printk (KERN_INFO "ATA\n");break;
+ case 2: printk (KERN_INFO "ATAPI\n");break;
+ case 3: printk (KERN_INFO "Reserved (Unknown to ide-tape)\n");break;
+ }
+ printk (KERN_INFO "Device Type: %x - ",gcw.device_type);
+ switch (gcw.device_type) {
+ case 0: printk (KERN_INFO "Direct-access Device\n");break;
+ case 1: printk (KERN_INFO "Streaming Tape Device\n");break;
+ case 2: case 3: case 4: printk (KERN_INFO "Reserved\n");break;
+ case 5: printk (KERN_INFO "CD-ROM Device\n");break;
+ case 6: printk (KERN_INFO "Reserved\n");
+ case 7: printk (KERN_INFO "Optical memory Device\n");break;
+ case 0x1f: printk (KERN_INFO "Unknown or no Device type\n");break;
+ default: printk (KERN_INFO "Reserved\n");
+ }
+ printk (KERN_INFO "Removable: %s",gcw.removable ? "Yes\n":"No\n");
+ printk (KERN_INFO "Command Packet DRQ Type: ");
+ switch (gcw.drq_type) {
+ case 0: printk (KERN_INFO "Microprocessor DRQ\n");break;
+ case 1: printk (KERN_INFO "Interrupt DRQ\n");break;
+ case 2: printk (KERN_INFO "Accelerated DRQ\n");break;
+ case 3: printk (KERN_INFO "Reserved\n");break;
+ }
+ printk (KERN_INFO "Command Packet Size: ");
+ switch (gcw.packet_size) {
+ case 0: printk (KERN_INFO "12 bytes\n");break;
+ case 1: printk (KERN_INFO "16 bytes\n");break;
+ default: printk (KERN_INFO "Reserved\n");break;
+ }
+ printk (KERN_INFO "Model: %s\n",id->model);
+ printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev);
+ printk (KERN_INFO "Serial Number: %s\n",id->serial_no);
+ printk (KERN_INFO "Write buffer size: %d bytes\n",id->buf_size*512);
+ printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+ printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+ printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+ printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+ printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
+ printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO);
+ printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA);
+ printk (KERN_INFO "Single Word DMA supported modes: ");
+ for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+ if (id->dma_1word & mask)
+ printk (KERN_INFO "%d ",i);
+ if (id->dma_1word & (mask << 8))
+ printk (KERN_INFO "(active) ");
+ }
+ printk (KERN_INFO "\n");
+ printk (KERN_INFO "Multi Word DMA supported modes: ");
+ for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+ if (id->dma_mword & mask)
+ printk (KERN_INFO "%d ",i);
+ if (id->dma_mword & (mask << 8))
+ printk (KERN_INFO "(active) ");
+ }
+ printk (KERN_INFO "\n");
+ if (id->field_valid & 0x0002) {
+ printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+ printk (KERN_INFO "Minimum Multi-word DMA cycle per word: ");
+ if (id->eide_dma_min == 0)
+ printk (KERN_INFO "Not supported\n");
+ else
+ printk (KERN_INFO "%d ns\n",id->eide_dma_min);
+
+ printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: ");
+ if (id->eide_dma_time == 0)
+ printk (KERN_INFO "Not supported\n");
+ else
+ printk (KERN_INFO "%d ns\n",id->eide_dma_time);
+
+ printk (KERN_INFO "Minimum PIO cycle without IORDY: ");
+ if (id->eide_pio == 0)
+ printk (KERN_INFO "Not supported\n");
+ else
+ printk (KERN_INFO "%d ns\n",id->eide_pio);
+
+ printk (KERN_INFO "Minimum PIO cycle with IORDY: ");
+ if (id->eide_pio_iordy == 0)
+ printk (KERN_INFO "Not supported\n");
+ else
+ printk (KERN_INFO "%d ns\n",id->eide_pio_iordy);
+
+ } else
+ printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n");
+#endif /* IDETAPE_DEBUG_LOG */
+
+ /* Check that we can support this device */
+
+ if (gcw.protocol !=2 )
+ printk (KERN_ERR "ide-tape: Protocol is not ATAPI\n");
+ else if (gcw.device_type != 1)
+ printk (KERN_ERR "ide-tape: Device type is not set to tape\n");
+ else if (!gcw.removable)
+ printk (KERN_ERR "ide-tape: The removable flag is not set\n");
+ else if (gcw.drq_type != 2) {
+ printk (KERN_ERR "ide-tape: Sorry, DRQ types other than Accelerated DRQ\n");
+ printk (KERN_ERR "ide-tape: are still not supported by the driver\n");
+ } else if (gcw.packet_size != 0) {
+ printk (KERN_ERR "ide-tape: Packet size is not 12 bytes long\n");
+ if (gcw.packet_size == 1)
+ printk (KERN_ERR "ide-tape: Sorry, padding to 16 bytes is still not supported\n");
+ } else
+ return 1;
+ return 0;
+}
+
+/*
+ * idetape_get_mode_sense_results asks the tape about its various
+ * parameters. In particular, we will adjust our data transfer buffer
+ * size to the recommended value as returned by the tape.
+ */
+static void idetape_get_mode_sense_results (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ idetape_pc_t pc;
+ idetape_mode_parameter_header_t *header;
+ idetape_capabilities_page_t *capabilities;
+
+ idetape_create_mode_sense_cmd (&pc,IDETAPE_CAPABILITIES_PAGE);
+ if (idetape_queue_pc_tail (drive,&pc)) {
+ printk (KERN_ERR "ide-tape: Can't get tape parameters - assuming some default values\n");
+ tape->tape_block_size = 512; tape->capabilities.ctl = 52;
+ tape->capabilities.speed = 450; tape->capabilities.buffer_size = 6 * 52;
+ return;
+ }
+ header = (idetape_mode_parameter_header_t *) pc.buffer;
+ capabilities = (idetape_capabilities_page_t *) (header + 1);
+
+ capabilities->max_speed = ntohs (capabilities->max_speed);
+ capabilities->ctl = ntohs (capabilities->ctl);
+ capabilities->speed = ntohs (capabilities->speed);
+ capabilities->buffer_size = ntohs (capabilities->buffer_size);
+
+ tape->capabilities = *capabilities; /* Save us a copy */
+ tape->tape_block_size = capabilities->blk512 ? 512:1024;
+#if IDETAPE_DEBUG_LOG
+ printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n");
+ printk (KERN_INFO "Mode Parameter Header:\n");
+ printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length);
+ printk (KERN_INFO "Medium Type - %d\n",header->medium_type);
+ printk (KERN_INFO "Device Specific Parameter - %d\n",header->dsp);
+ printk (KERN_INFO "Block Descriptor Length - %d\n",header->bdl);
+
+ printk (KERN_INFO "Capabilities and Mechanical Status Page:\n");
+ printk (KERN_INFO "Page code - %d\n",capabilities->page_code);
+ printk (KERN_INFO "Page length - %d\n",capabilities->page_length);
+ printk (KERN_INFO "Read only - %s\n",capabilities->ro ? "Yes":"No");
+ printk (KERN_INFO "Supports reverse space - %s\n",capabilities->sprev ? "Yes":"No");
+ printk (KERN_INFO "Supports erase initiated formatting - %s\n",capabilities->efmt ? "Yes":"No");
+ printk (KERN_INFO "Supports QFA two Partition format - %s\n",capabilities->qfa ? "Yes":"No");
+ printk (KERN_INFO "Supports locking the medium - %s\n",capabilities->lock ? "Yes":"No");
+ printk (KERN_INFO "The volume is currently locked - %s\n",capabilities->locked ? "Yes":"No");
+ printk (KERN_INFO "The device defaults in the prevent state - %s\n",capabilities->prevent ? "Yes":"No");
+ printk (KERN_INFO "Supports ejecting the medium - %s\n",capabilities->eject ? "Yes":"No");
+ printk (KERN_INFO "Supports error correction - %s\n",capabilities->ecc ? "Yes":"No");
+ printk (KERN_INFO "Supports data compression - %s\n",capabilities->cmprs ? "Yes":"No");
+ printk (KERN_INFO "Supports 512 bytes block size - %s\n",capabilities->blk512 ? "Yes":"No");
+ printk (KERN_INFO "Supports 1024 bytes block size - %s\n",capabilities->blk1024 ? "Yes":"No");
+ printk (KERN_INFO "Restricted byte count for PIO transfers - %s\n",capabilities->slowb ? "Yes":"No");
+ printk (KERN_INFO "Maximum supported speed in KBps - %d\n",capabilities->max_speed);
+ printk (KERN_INFO "Continuous transfer limits in blocks - %d\n",capabilities->ctl);
+ printk (KERN_INFO "Current speed in KBps - %d\n",capabilities->speed);
+ printk (KERN_INFO "Buffer size - %d\n",capabilities->buffer_size*512);
+#endif /* IDETAPE_DEBUG_LOG */
+}
+
+/*
+ * ide_setup is called to:
+ *
+ * 1. Initialize our various state variables.
+ * 2. Ask the tape for its capabilities.
+ * 3. Allocate a buffer which will be used for data
+ * transfer. The buffer size is chosen based on
+ * the recommendation which we received in step (2).
+ *
+ * Note that at this point ide.c already assigned us an irq, so that
+ * we can queue requests here and wait for their completion.
+ */
+static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned long t1, tmid, tn, t;
+
+ drive->driver_data = tape;
+ drive->ready_stat = 0; /* An ATAPI device ignores DRDY */
+ memset (tape, 0, sizeof (idetape_tape_t));
+ tape->drive = drive;
+ tape->minor = minor;
+ tape->name[0] = 'h'; tape->name[1] = 't'; tape->name[2] = '0' + minor;
+ tape->chrdev_direction = idetape_direction_none;
+ tape->pc = tape->pc_stack;
+ tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+
+ idetape_get_mode_sense_results (drive);
+
+ tape->user_bs_factor = 1;
+ tape->stage_size = tape->capabilities.ctl * tape->tape_block_size;
+ while (tape->stage_size > 0xffff) {
+ printk (KERN_NOTICE "ide-tape: decreasing stage size\n");
+ tape->capabilities.ctl /= 2;
+ tape->stage_size = tape->capabilities.ctl * tape->tape_block_size;
+ }
+ tape->pages_per_stage = tape->stage_size / PAGE_SIZE;
+ if (tape->stage_size % PAGE_SIZE) {
+ tape->pages_per_stage++;
+ tape->excess_bh_size = PAGE_SIZE - tape->stage_size % PAGE_SIZE;
+ }
+
+ /*
+ * Select the "best" DSC read/write polling frequency.
+ * The following algorithm attempts to find a balance between
+ * good latency and good system throughput. It will be nice to
+ * have all this configurable in run time at some point.
+ */
+ t1 = (tape->stage_size * HZ) / (tape->capabilities.speed * 1000);
+ tmid = (tape->capabilities.buffer_size * 32 * HZ) / (tape->capabilities.speed * 125);
+ tn = (IDETAPE_FIFO_THRESHOLD * tape->stage_size * HZ) / (tape->capabilities.speed * 1000);
+
+ if (tape->max_stages) {
+ if (drive->using_dma)
+ t = tmid;
+ else {
+ if (hwif->drives[drive->select.b.unit ^ 1].present || hwif->next != hwif)
+ t = (tn + tmid) / 2;
+ else
+ t = tn;
+ }
+ } else
+ t = t1;
+ t = IDETAPE_MIN (t, tmid);
+
+ /*
+ * Ensure that the number we got makes sense.
+ */
+ tape->best_dsc_rw_frequency = IDETAPE_MAX (IDETAPE_MIN (t, IDETAPE_DSC_RW_MAX), IDETAPE_DSC_RW_MIN);
+ if (tape->best_dsc_rw_frequency != t) {
+ printk (KERN_NOTICE "ide-tape: Although the recommended polling period is %lu jiffies\n", t);
+ printk (KERN_NOTICE "ide-tape: we will use %lu jiffies\n", tape->best_dsc_rw_frequency);
+ }
+ printk (KERN_INFO "ide-tape: %s <-> %s, %dKBps, %d*%dkB buffer, %dkB pipeline, %lums tDSC%s\n",
+ drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size,
+ tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024,
+ tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":"");
+}
+
+static int idetape_cleanup (ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+ int minor = tape->minor;
+ unsigned long flags;
+
+ save_flags (flags);
+ cli ();
+ if (test_bit (IDETAPE_BUSY, &tape->flags) || tape->first_stage != NULL || tape->merge_stage_size || drive->usage) {
+ restore_flags(flags);
+ return 1;
+ }
+ idetape_chrdevs[minor].drive = NULL;
+ restore_flags (flags);
+ DRIVER(drive)->busy = 0;
+ (void) ide_unregister_subdriver (drive);
+ drive->driver_data = NULL;
+ kfree (tape);
+ for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++)
+ if (idetape_chrdevs[minor].drive != NULL)
+ return 0;
+ unregister_chrdev (IDETAPE_MAJOR, "ht");
+ idetape_chrdev_present = 0;
+ return 0;
+}
+
+int idetape_init (void);
+
+static ide_module_t idetape_module = {
+ IDE_DRIVER_MODULE,
+ idetape_init,
+ NULL
+};
+
+/*
+ * IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idetape_driver = {
+ ide_tape, /* media */
+ 1, /* busy */
+ 1, /* supports_dma */
+ idetape_cleanup, /* cleanup */
+ idetape_do_request, /* do_request */
+ idetape_end_request, /* end_request */
+ idetape_blkdev_ioctl, /* ioctl */
+ idetape_blkdev_open, /* open */
+ idetape_blkdev_release, /* release */
+ NULL, /* media_change */
+ idetape_pre_reset, /* pre_reset */
+ NULL, /* capacity */
+ NULL /* special */
+};
+
+/*
+ * Our character device supporting functions, passed to register_chrdev.
+ */
+static struct file_operations idetape_fops = {
+ NULL, /* lseek - default */
+ idetape_chrdev_read, /* read */
+ idetape_chrdev_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ idetape_chrdev_ioctl, /* ioctl */
+ NULL, /* mmap */
+ idetape_chrdev_open, /* open */
+ idetape_chrdev_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL /* revalidate */
+};
+
+/*
+ * idetape_init will register the driver for each tape.
+ */
+int idetape_init (void)
+{
+ ide_drive_t *drive;
+ idetape_tape_t *tape;
+ int minor, failed = 0, supported = 0;
+
+ MOD_INC_USE_COUNT;
+ if (!idetape_chrdev_present)
+ for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ )
+ idetape_chrdevs[minor].drive = NULL;
+
+ if ((drive = ide_scan_devices (ide_tape, NULL, failed++)) == NULL) {
+ ide_register_module (&idetape_module);
+ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+ if (!idetape_chrdev_present && register_chrdev (IDETAPE_MAJOR, "ht", &idetape_fops)) {
+ printk (KERN_ERR "ide-tape: Failed to register character device interface\n");
+ MOD_DEC_USE_COUNT;
+ return -EBUSY;
+ }
+ do {
+ if (!idetape_identify_device (drive, drive->id)) {
+ printk (KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name);
+ continue;
+ }
+ tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL);
+ if (tape == NULL) {
+ printk (KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name);
+ continue;
+ }
+ if (ide_register_subdriver (drive, &idetape_driver, IDE_SUBDRIVER_VERSION)) {
+ printk (KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name);
+ kfree (tape);
+ continue;
+ }
+ for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++);
+ idetape_setup (drive, tape, minor);
+ idetape_chrdevs[minor].drive = drive;
+ supported++; failed--;
+ } while ((drive = ide_scan_devices (ide_tape, NULL, failed++)) != NULL);
+ if (!idetape_chrdev_present && !supported) {
+ unregister_chrdev (IDETAPE_MAJOR, "ht");
+ } else
+ idetape_chrdev_present = 1;
+ ide_register_module (&idetape_module);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return idetape_init ();
+}
+
+void cleanup_module (void)
+{
+ ide_drive_t *drive;
+ int minor;
+
+ for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++) {
+ drive = idetape_chrdevs[minor].drive;
+ if (drive != NULL && idetape_cleanup (drive))
+ printk (KERN_ERR "ide-tape: %s: cleanup_module() called while still busy\n", drive->name);
+ }
+ ide_unregister_module(&idetape_module);
+}
+#endif /* MODULE */
diff --git a/drivers/block/ide.c b/drivers/block/ide.c
index a72ea3b0a..d5193c2ee 100644
--- a/drivers/block/ide.c
+++ b/drivers/block/ide.c
@@ -1,16 +1,38 @@
/*
- * linux/drivers/block/ide.c Version 3.16 May 30, 1995
+ * linux/drivers/block/ide.c Version 5.60 Nov 5, 1996
*
- * Copyright (C) 1994, 1995 Linus Torvalds & authors (see below)
+ * Copyright (C) 1994-1996 Linus Torvalds & authors (see below)
*/
+#define _IDE_C /* needed by <linux/blk.h> */
/*
- * This is the dual IDE interface driver, as evolved from hd.c.
- * It supports up to two IDE interfaces, on one or two IRQs (usually 14 & 15).
+ * Maintained by Mark Lord <mlord@pobox.com>
+ * and Gadi Oxman <gadio@netvision.net.il>
+ *
+ * This is the multiple IDE interface driver, as evolved from hd.c.
+ * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
* There can be up to two drives per interface, as per the ATA-2 spec.
*
- * Primary i/f: ide0: major=3; (hda) minor=0, (hdb) minor=64
- * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0, (hdd or hd1b) minor=64
+ * Primary: ide0, port 0x1f0; major=3; hda is minor=0; hdb is minor=64
+ * Secondary: ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
+ * Tertiary: ide2, port 0x???; major=33; hde is minor=0; hdf is minor=64
+ * Quaternary: ide3, port 0x???; major=34; hdg is minor=0; hdh is minor=64
+ *
+ * It is easy to extend ide.c to handle more than four interfaces:
+ *
+ * Change the MAX_HWIFS constant in ide.h.
+ *
+ * Define some new major numbers (in major.h), and insert them into
+ * the ide_hwif_to_major table in ide.c.
+ *
+ * Fill in the extra values for the new interfaces into the two tables
+ * inside ide.c: default_io_base[] and default_irqs[].
+ *
+ * Create the new request handlers by cloning "do_ide3_request()"
+ * for each new interface, and add them to the switch statement
+ * in the ide_init() function in ide.c.
+ *
+ * Recompile, create the new /dev/ entries, and it will probably work.
*
* From hd.c:
* |
@@ -26,61 +48,23 @@
* | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
* |
* | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- * | and general streamlining by Mark Lord (mlord@bnr.ca).
+ * | and general streamlining by Mark Lord (mlord@pobox.com).
*
* October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
*
- * Mark Lord (mlord@bnr.ca) (IDE Perf.Pkg)
+ * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg)
* Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
- * Petri Mattila (ptjmatti@kruuna.helsinki.fi) (EIDE stuff)
* Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
*
* This was a rewrite of just about everything from hd.c, though some original
- * code is still sprinkled about. Think of it as a major evolution, with
+ * code is still sprinkled about. Think of it as a major evolution, with
* inspiration from lots of linux users, esp. hamish@zot.apana.org.au
*
* Version 1.0 ALPHA initial code, primary i/f working okay
- * Version 1.1 ALPHA fixes for dual i/f
- * Version 1.2 ALPHA first serious attempt at sharing irqs
* Version 1.3 BETA dual i/f on shared irq tested & working!
* Version 1.4 BETA added auto probing for irq(s)
* Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms,
- * fixed hd.c coexistence bug, other minor stuff
- * Version 1.6 BETA fix link error when cd-rom not configured
- * Version 2.0 BETA lots of minor fixes; remove annoying messages; ...
- * Version 2.2 BETA fixed reset_drives; major overhaul of autoprobing
- * Version 2.3 BETA set DEFAULT_UNMASK_INTR to 0 again; cosmetic changes
- * Version 2.4 BETA added debounce on reading of drive status reg,
- * added config flags to remove unwanted features
- * Version 2.5 BETA fixed problem with leftover phantom IRQ after probe,
- * allow "set_geometry" even when in LBA (as per spec(?)),
- * assorted miscellaneous tweaks.
- * Version 2.6 BETA more config flag stuff, another probing tweak,
- * (not released) multmode now defaults to status quo from boot time,
- * moved >16heads check to init time, rearranged reset code
- * added HDIO_DRIVE_CMD, removed standby/xfermode stuff
- * hopefully fixed ATAPI probing code, added hdx=cdrom
- * Version 2.7 BETA fixed invocation of cdrom_setup()
- * Version 2.8 BETA fixed compile error for DISK_RECOVERY_TIME>0
- * fixed incorrect drive selection in DO_DRIVE_CMD (Bug!)
- * Version 2.9 BETA more work on ATAPI CDROM recognition
- * (not released) changed init order so partition checks go in sequence
- * Version 3.0 BETA included ide-cd.c update from Steve with Mitsumi fixes
- * attempt to fix byte-swap problem with Mitsumi id_info
- * ensure drives on second i/f get initialized on boot
- * preliminary compile-time support for 32bit IDE i/f chips
- * added check_region() and snarf_region() to probes
- * Version 3.1 BETA ensure drives on *both* i/f get initialized on boot
- * fix byte-swap problem with Mitsumi id_info
- * changed ide_timermask into ide_timerbit
- * get rid of unexpected interrupts after probing
- * don't wait for READY_STAT on cdrom drives
- * Version 3.2 BETA Ooops.. mistakenly left VLB_32BIT_IDE on by default
- * new ide-cd.c from Scott
- * Version 3.3 BETA fix compiling with PROBE_FOR_IRQS==0
- * (sent to Linus) tweak in do_probe() to fix Delman's DRDY problem
- * Version 3.4 BETA removed "444" debug message
- * (sent to Linus)
+ * ...
* Version 3.5 correct the bios_cyl field if it's too small
* (linux 1.1.76) (to help fdisk with brain-dead BIOSs)
* Version 3.6 cosmetic corrections to comments and stuff
@@ -97,7 +81,7 @@
* cdrom probe fixes, inspired by jprang@uni-duisburg.de
* Version 3.9 don't use LBA if lba_capacity looks funny
* correct the drive capacity calculations
- * fix probing for old Seagates without HD_ALTSTATUS
+ * fix probing for old Seagates without IDE_ALTSTATUS_REG
* fix byte-ordering for some NEC cdrom drives
* Version 3.10 disable multiple mode by default; was causing trouble
* Version 3.11 fix mis-identification of old WD disks as cdroms
@@ -109,9 +93,9 @@
* ignore INDEX bit when checking the ALTSTATUS reg
* Version 3.15 add SINGLE_THREADED flag for use with dual-CMD i/f
* ignore WRERR_STAT for non-write operations
- * added VLB_SYNC support for DC-2000A & others,
+ * added vlb_sync support for DC-2000A & others,
* (incl. some Promise chips), courtesy of Frank Gockel
- * Version 3.16 convert VLB_32BIT and VLB_SYNC into runtime flags
+ * Version 3.16 convert vlb_32bit and vlb_sync into runtime flags
* add ioctls to get/set VLB flags (HDIO_[SG]ET_CHIPSET)
* rename SINGLE_THREADED to SUPPORT_SERIALIZE,
* add boot flag to "serialize" operation for CMD i/f
@@ -120,300 +104,242 @@
* add boot flag to enable "dtc2278" probe
* add probe to avoid EATA (SCSI) interfaces,
* courtesy of neuffer@goofy.zdv.uni-mainz.de.
+ * Version 4.00 tidy up verify_area() calls - heiko@colossus.escape.de
+ * add flag to ignore WRERR_STAT for some drives
+ * courtesy of David.H.West@um.cc.umich.edu
+ * assembly syntax tweak to vlb_sync
+ * removable drive support from scuba@cs.tu-berlin.de
+ * add transparent support for DiskManager-6.0x "Dynamic
+ * Disk Overlay" (DDO), most of this is in genhd.c
+ * eliminate "multiple mode turned off" message at boot
+ * Version 4.10 fix bug in ioctl for "hdparm -c3"
+ * fix DM6:DDO support -- now works with LILO, fdisk, ...
+ * don't treat some naughty WD drives as removable
+ * Version 4.11 updated DM6 support using info provided by OnTrack
+ * Version 5.00 major overhaul, multmode setting fixed, vlb_sync fixed
+ * added support for 3rd/4th/alternative IDE ports
+ * created ide.h; ide-cd.c now compiles separate from ide.c
+ * hopefully fixed infinite "unexpected_intr" from cdroms
+ * zillions of other changes and restructuring
+ * somehow reduced overall memory usage by several kB
+ * probably slowed things down slightly, but worth it
+ * Version 5.01 AT LAST!! Finally understood why "unexpected_intr"
+ * was happening at various times/places: whenever the
+ * ide-interface's ctl_port was used to "mask" the irq,
+ * it also would trigger an edge in the process of masking
+ * which would result in a self-inflicted interrupt!!
+ * (such a stupid way to build a hardware interrupt mask).
+ * This is now fixed (after a year of head-scratching).
+ * Version 5.02 got rid of need for {enable,disable}_irq_list()
+ * Version 5.03 tune-ups, comments, remove "busy wait" from drive resets
+ * removed PROBE_FOR_IRQS option -- no longer needed
+ * OOOPS! fixed "bad access" bug for 2nd drive on an i/f
+ * Version 5.04 changed "ira %d" to "irq %d" in DEBUG message
+ * added more comments, cleaned up unexpected_intr()
+ * OOOPS! fixed null pointer problem in ide reset code
+ * added autodetect for Triton chipset -- no effect yet
+ * Version 5.05 OOOPS! fixed bug in revalidate_disk()
+ * OOOPS! fixed bug in ide_do_request()
+ * added ATAPI reset sequence for cdroms
+ * Version 5.10 added Bus-Mastered DMA support for Triton Chipset
+ * some (mostly) cosmetic changes
+ * Version 5.11 added ht6560b support by malafoss@snakemail.hut.fi
+ * reworked PCI scanning code
+ * added automatic RZ1000 detection/support
+ * added automatic PCI CMD640 detection/support
+ * added option for VLB CMD640 support
+ * tweaked probe to find cdrom on hdb with disks on hda,hdc
+ * Version 5.12 some performance tuning
+ * added message to alert user to bad /dev/hd[cd] entries
+ * OOOPS! fixed bug in atapi reset
+ * driver now forces "serialize" again for all cmd640 chips
+ * noticed REALLY_SLOW_IO had no effect, moved it to ide.c
+ * made do_drive_cmd() into public ide_do_drive_cmd()
+ * Version 5.13 fixed typo ('B'), thanks to houston@boyd.geog.mcgill.ca
+ * fixed ht6560b support
+ * Version 5.13b (sss) fix problem in calling ide_cdrom_setup()
+ * don't bother invalidating nonexistent partitions
+ * Version 5.14 fixes to cmd640 support.. maybe it works now(?)
+ * added & tested full EZ-DRIVE support -- don't use LILO!
+ * don't enable 2nd CMD640 PCI port during init - conflict
+ * Version 5.15 bug fix in init_cmd640_vlb()
+ * bug fix in interrupt sharing code
+ * Version 5.16 ugh.. fix "serialize" support, broken in 5.15
+ * remove "Huh?" from cmd640 code
+ * added qd6580 interface speed select from Colten Edwards
+ * Version 5.17 kludge around bug in BIOS32 on Intel triton motherboards
+ * Version 5.18 new CMD640 code, moved to cmd640.c, #include'd for now
+ * new UMC8672 code, moved to umc8672.c, #include'd for now
+ * disallow turning on DMA when h/w not capable of DMA
+ * Version 5.19 fix potential infinite timeout on resets
+ * extend reset poll into a general purpose polling scheme
+ * add atapi tape drive support from Gadi Oxman
+ * simplify exit from _intr routines -- no IDE_DO_REQUEST
+ * Version 5.20 leave current rq on blkdev request list during I/O
+ * generalized ide_do_drive_cmd() for tape/cdrom driver use
+ * Version 5.21 fix nasty cdrom/tape bug (ide_preempt was messed up)
+ * Version 5.22 fix ide_xlate_1024() to work with/without drive->id
+ * Version 5.23 miscellaneous touch-ups
+ * Version 5.24 fix #if's for SUPPORT_CMD640
+ * Version 5.25 more touch-ups, fix cdrom resets, ...
+ * cmd640.c now configs/compiles separate from ide.c
+ * Version 5.26 keep_settings now maintains the using_dma flag
+ * fix [EZD] remap message to only output at boot time
+ * fix "bad /dev/ entry" message to say hdc, not hdc0
+ * fix ide_xlate_1024() to respect user specified CHS
+ * use CHS from partn table if it looks translated
+ * re-merged flags chipset,vlb_32bit,vlb_sync into io_32bit
+ * keep track of interface chipset type, when known
+ * add generic PIO mode "tuneproc" mechanism
+ * fix cmd640_vlb option
+ * fix ht6560b support (was completely broken)
+ * umc8672.c now configures/compiles separate from ide.c
+ * move dtc2278 support to dtc2278.c
+ * move ht6560b support to ht6560b.c
+ * move qd6580 support to qd6580.c
+ * add ali14xx support in ali14xx.c
+ * Version 5.27 add [no]autotune parameters to help cmd640
+ * move rz1000 support to rz1000.c
+ * Version 5.28 #include "ide_modes.h"
+ * fix disallow_unmask: now per-interface "no_unmask" bit
+ * force io_32bit to be the same on drive pairs of dtc2278
+ * improved IDE tape error handling, and tape DMA support
+ * bugfix in ide_do_drive_cmd() for cdroms + serialize
+ * Version 5.29 fixed non-IDE check for too many physical heads
+ * don't use LBA if capacity is smaller than CHS
+ * Version 5.30 remove real_devices kludge, formerly used by genhd.c
+ * Version 5.32 change "KB" to "kB"
+ * fix serialize (was broken in kernel 1.3.72)
+ * add support for "hdparm -I"
+ * use common code for disk/tape/cdrom IDE_DRIVE_CMDs
+ * add support for Promise DC4030VL caching card
+ * improved serialize support
+ * put partition check back into alphabetical order
+ * add config option for PCMCIA baggage
+ * try to make PCMCIA support safer to use
+ * improve security on ioctls(): all are suser() only
+ * Version 5.33 improve handling of HDIO_DRIVE_CMDs that read data
+ * Version 5.34 fix irq-sharing problem from 5.33
+ * fix cdrom ioctl problem from 5.33
+ * Version 5.35 cosmetic changes
+ * fix cli() problem in try_to_identify()
+ * Version 5.36 fixes to optional PCMCIA support
+ * Version 5.37 don't use DMA when "noautotune" is specified
+ * Version 5.37a (go) fix shared irq probing (was broken in kernel 1.3.72)
+ * call unplug_device() from ide_do_drive_cmd()
+ * Version 5.38 add "hdx=none" option, courtesy of Joel Maslak
+ * mask drive irq after use, if sharing with another hwif
+ * add code to help debug weird cmd640 problems
+ * Version 5.39 fix horrible error in earlier irq sharing "fix"
+ * Version 5.40 fix serialization -- was broken in 5.39
+ * help sharing by masking device irq after probing
+ * Version 5.41 more fixes to irq sharing/serialize detection
+ * disable io_32bit by default on drive reset
+ * Version 5.42 simplify irq-masking after probe
+ * fix NULL pointer deref in save_match()
+ * Version 5.43 Ugh.. unexpected_intr is back: try to exterminate it
+ * Version 5.44 Fix for "irq probe failed" on cmd640
+ * change path on message regarding MAKEDEV.ide
+ * add a throttle to the unexpected_intr() messages
+ * Version 5.45 fix ugly parameter parsing bugs (thanks Derek)
+ * include Gadi's magic fix for cmd640 unexpected_intr
+ * include mc68000 patches from Geert Uytterhoeven
+ * add Gadi's fix for PCMCIA cdroms
+ * Version 5.46 remove the mc68000 #ifdefs for 2.0.x
+ * Version 5.47 fix set_tune race condition
+ * fix bug in earlier PCMCIA cdrom update
+ * Version 5.48 if def'd, invoke CMD640_DUMP_REGS when irq probe fails
+ * lengthen the do_reset1() pulse, for laptops
+ * add idebus=xx parameter for cmd640 and ali chipsets
+ * no_unmask flag now per-drive instead of per-hwif
+ * fix tune_req so that it gets done immediately
+ * fix missing restore_flags() in ide_ioctl
+ * prevent use of io_32bit on cmd640 with no prefetch
+ * Version 5.49 fix minor quirks in probing routines
+ * Version 5.50 allow values as small as 20 for idebus=
+ * Version 5.51 force non io_32bit in drive_cmd_intr()
+ * change delay_10ms() to delay_50ms() to fix problems
+ * Version 5.52 fix incorrect invalidation of removable devices
+ * add "hdx=slow" command line option
+ * Version 5.60 start to modularize the driver; the disk and ATAPI
+ * drivers can be compiled as loadable modules.
+ * move IDE probe code to ide-probe.c
+ * move IDE disk code to ide-disk.c
+ * add support for generic IDE device subdrivers
+ * add m68k code from Geert Uytterhoeven
+ * probe all interfaces by default
+ * add ioctl to (re)probe an interface
*
- * To do:
- * - improved CMD support: tech info is supposedly "in the mail"
- * - special 32-bit controller-type detection & support
- * - figure out how to support oddball "intelligent" caching cards
- * - reverse-engineer 3/4 drive support on fancy "Promise" cards
- */
+ * Some additional driver compile-time options are in ide.h
+ *
+ * To do, in likely order of completion:
+ * - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
+*/
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/hdreg.h>
-#include <linux/genhd.h>
-#include <linux/malloc.h>
+#include <linux/module.h>
+#include <linux/types.h>
#include <linux/string.h>
+#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/major.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
+#include <linux/major.h>
#include <linux/blkdev.h>
-#include <asm/bitops.h>
-#include <asm/irq.h>
-#include <asm/segment.h>
-#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
-/*****************************************************************************
- * IDE driver configuration options (play with these as desired):
- */
-#define REALLY_SLOW_IO /* most systems can safely undef this */
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
#include <asm/io.h>
+#include <asm/bitops.h>
-#undef REALLY_FAST_IO /* define if ide ports are perfect */
-#define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */
-#ifndef SUPPORT_VLB_32BIT /* 1 to support 32bit I/O on VLB */
-#define SUPPORT_VLB_32BIT 1 /* 0 to reduce kernel size */
-#endif
-#ifndef SUPPORT_VLB_SYNC /* 1 to support weird 32-bit chips */
-#define SUPPORT_VLB_SYNC 1 /* 0 to reduce kernel size */
-#endif
-#ifndef DISK_RECOVERY_TIME /* off=0; on=access_delay_time */
-#define DISK_RECOVERY_TIME 0 /* for hardware that needs it */
-#endif
-#ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */
-#define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */
-#endif
-#ifndef SUPPORT_TWO_INTERFACES /* 1 to support one/two interfaces */
-#define SUPPORT_TWO_INTERFACES 1 /* 0 for a smaller, faster kernel */
-#endif
-#ifndef OPTIMIZE_IRQS /* 1 for slightly faster code */
-#define OPTIMIZE_IRQS 1 /* 0 to reduce kernel size */
-#endif
-#ifndef SUPPORT_SERIALIZE /* 1 to support CMD dual interfaces */
-#define SUPPORT_SERIALIZE 1 /* 0 to reduce kernel size */
-#endif
-#ifndef SUPPORT_SHARING_IRQ /* 1 to allow two IDE i/f on one IRQ */
-#define SUPPORT_SHARING_IRQ 1 /* 0 to reduce kernel size */
-#endif
-#ifndef SUPPORT_DTC2278 /* 1 to support DTC2278 chipset */
-#define SUPPORT_DTC2278 1 /* 0 to reduce kernel size */
-#endif
-#ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */
-#define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */
-#endif
-#define PROBE_FOR_IRQS 1 /* 0 to force use of defaults below */
-#define DEFAULT_IDE0_IRQ 14 /* in case irq-probe fails */
-#define DEFAULT_IDE1_IRQ 15 /* in case irq-probe fails */
-
-/* IDE_DRIVE_CMD is used to implement many features of the hdparm utility */
-#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/
-
-/*
- * "No user-serviceable parts" beyond this point :)
- ******************************************************************************
- */
-
-/*
- * Need to change these elsewhere in the kernel (someday)
- */
-#ifndef IDE0_TIMER
-#define IDE0_TIMER HD_TIMER
-#define IDE1_TIMER HD_TIMER2
-#endif
-
-/*
- * Ensure that various configuration flags have compatible settings
- */
-#ifdef REALLY_SLOW_IO
-#undef REALLY_FAST_IO
-#endif
-#ifdef CONFIG_BLK_DEV_HD
-#undef SUPPORT_TWO_INTERFACES
-#define SUPPORT_TWO_INTERFACES 0
-#endif /* CONFIG_BLK_DEV_HD */
-
-#if SUPPORT_TWO_INTERFACES
-#define HWIF hwif
-#define DEV_HWIF (dev->hwif)
-#if SUPPORT_SERIALIZE
-#undef SUPPORT_SHARING_IRQ
-#define SUPPORT_SHARING_IRQ 1
-#endif
-#else
-#undef SUPPORT_SERIALIZE
-#define SUPPORT_SERIALIZE 0
-#undef OPTIMIZE_IRQS
-#define OPTIMIZE_IRQS 0
-#undef SUPPORT_SHARING_IRQ
-#define SUPPORT_SHARING_IRQ 0
-#ifdef CONFIG_BLK_DEV_HD
-#define HWIF 1
-#else
-#define HWIF 0
-#endif /* CONFIG_BLK_DEV_HD */
-#define DEV_HWIF HWIF
-#endif /* SUPPORT_TWO_INTERFACES */
-
-/*
- * Definitions for accessing IDE controller registers
- */
-typedef unsigned char byte; /* used everywhere */
-#define IDE_PORT(p,hwif) ((p)^((hwif)<<7)) /* IDE0: p^0x00 , IDE1: p^0x80 */
+#ifdef CONFIG_PCI
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#endif /* CONFIG_PCI */
-#ifdef REALLY_FAST_IO
-#define OUT_BYTE(b,p) outb((b),IDE_PORT(p,DEV_HWIF))
-#define IN_BYTE(p,hwif) (byte)inb(IDE_PORT(p,hwif))
-#else
-#define OUT_BYTE(b,p) outb_p((b),IDE_PORT(p,DEV_HWIF))
-#define IN_BYTE(p,hwif) (byte)inb_p(IDE_PORT(p,hwif))
-#endif /* REALLY_FAST_IO */
+#include "ide.h"
+#include "ide_modes.h"
-#if SUPPORT_VLB_32BIT
-#if SUPPORT_VLB_SYNC
-#define VLB_SYNC __asm__ __volatile__ ("pusha\n movl $0x01f2,%edx\n inb (%dx),%al\n inb (%dx),%al\n inb (%dx),%al\n popa\n")
-#endif /* SUPPORT_VLB_SYNC */
-#endif /* SUPPORT_VLB_32BIT */
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif /* CONFIG_KERNELD */
-#if SUPPORT_DTC2278
-static uint probe_dtc2278 = 0;
-#endif
+static const byte ide_hwif_to_major[] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
-#define GET_ERR(hwif) IN_BYTE(HD_ERROR,hwif)
-#define GET_STAT(hwif) IN_BYTE(HD_STATUS,hwif)
-#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
-#define BAD_R_STAT (BUSY_STAT | ERR_STAT)
-#define BAD_W_STAT (BUSY_STAT | ERR_STAT | WRERR_STAT)
-#define BAD_STAT (BAD_R_STAT | DRQ_STAT)
-#define DRIVE_READY (READY_STAT | SEEK_STAT)
-#define DATA_READY (DRIVE_READY | DRQ_STAT)
+static int idebus_parameter; /* holds the "idebus=" parameter */
+static int system_bus_speed; /* holds what we think is VESA/PCI bus speed */
+static int initializing; /* set while initializing built-in drivers */
/*
- * Some more useful definitions
+ * ide_lock is used by the Atari code to obtain access to the IDE interrupt,
+ * which is shared between several drivers.
*/
-#define BIOS_SECTORS(dev) (dev->bios_head*dev->bios_sect*dev->bios_cyl)
-#define HD_NAME "hd" /* the same for both i/f; see also genhd.c */
-#define PARTN_BITS 6 /* number of minor dev bits for partitions */
-#define PARTN_MASK ((1<<PARTN_BITS)-1) /* a useful bit mask */
-#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
-#define SECTOR_WORDS (512 / 4) /* number of 32bit words per sector */
+static int ide_lock = 0;
/*
- * Timeouts for various operations:
+ * ide_modules keeps track of the available IDE chipset/probe/driver modules.
*/
-#define WAIT_DRQ 5 /* 50msec - spec allows up to 20ms */
-#define WAIT_READY 3 /* 30msec - should be instantaneous */
-#define WAIT_PIDENTIFY 100 /* 1sec - should be less than 3ms (?) */
-#define WAIT_WORSTCASE 3000 /* 30sec - worst case when spinning up */
-#define WAIT_CMD 1000 /* 10sec - maximum wait for an IRQ to happen */
+static ide_module_t *ide_modules = NULL;
/*
- * Now for the data we need to maintain per-device: ide_dev_t
- *
- * For fast indexing, sizeof(ide_dev_t) = 32 = power_of_2;
- * Everything is carefully aligned on appropriate boundaries,
- * and several fields are placed for optimal (gcc) access.
+ * This is declared extern in ide.h, for access by other IDE modules:
*/
-typedef enum {disk, cdrom} dev_type;
-
-typedef union {
- unsigned all : 8; /* all of the bits together */
- struct {
- unsigned set_geometry : 1; /* respecify drive geometry */
- unsigned recalibrate : 1; /* seek to cyl 0 */
- unsigned set_multmode : 1; /* set multmode count */
- unsigned reserved : 5; /* unused */
- } b;
- } special_t;
-
-typedef union {
- unsigned all : 8; /* all of the bits together */
- struct {
- unsigned head : 4; /* always zeros here */
- unsigned drive : 1; /* drive number */
- unsigned bit5 : 1; /* always 1 */
- unsigned lba : 1; /* LBA instead of CHS */
- unsigned bit7 : 1; /* always 1 */
- } b;
- } select_t;
-
-typedef struct {
- byte hwif; /* first field gets very fast access */
- byte unmask; /* pretty quick access to this also */
- dev_type type : 1; /* disk or cdrom (or tape, floppy..) */
- unsigned present : 1; /* drive is physically present */
- unsigned dont_probe : 1; /* from: hdx=noprobe */
- unsigned keep_settings : 1; /* restore settings after drive reset */
- unsigned busy : 1; /* mutex for ide_open, revalidate_.. */
- unsigned vlb_32bit : 1; /* use 32bit in/out for data */
- unsigned vlb_sync : 1; /* needed for some 32bit chip sets */
- unsigned reserved0 : 1; /* unused */
- special_t special; /* special action flags */
- select_t select; /* basic drive/head select reg value */
- byte mult_count, chipset, reserved2;
- byte usage, mult_req, wpcom, ctl;
- byte head, sect, bios_head, bios_sect;
- unsigned short cyl, bios_cyl;
- const char *name;
- struct hd_driveid *id;
- struct wait_queue *wqueue;
- } ide_dev_t;
-
-/*
- * Stuff prefixed by "ide_" is indexed by the IDE interface number: 0 or 1
- */
-static const byte ide_major [2] = {IDE0_MAJOR,IDE1_MAJOR};
-static byte ide_irq [2] = {DEFAULT_IDE0_IRQ,DEFAULT_IDE1_IRQ};
-static struct hd_struct ide_hd [2][MAX_DRIVES<<PARTN_BITS] = {{{0,0},},};
-static int ide_sizes [2][MAX_DRIVES<<PARTN_BITS] = {{0,},};
-static int ide_blksizes [2][MAX_DRIVES<<PARTN_BITS] = {{0,},};
-static unsigned long ide_capacity [2][MAX_DRIVES] = {{0,},};
-static ide_dev_t ide_dev [2][MAX_DRIVES] = {{{0,},},};
-static ide_dev_t *ide_cur_dev [2] = {NULL,NULL};
-static void (*ide_handler[2])(ide_dev_t *) = {NULL,NULL};
-static struct request *ide_cur_rq [2] = {NULL,NULL}; /* current request */
-static struct request ide_write_rq [2]; /* copy of *ide_cur_rq for WRITEs */
-static const int ide_timer [2] = {IDE0_TIMER,IDE1_TIMER};
-static const int ide_timerbit[2] = {(1<<IDE0_TIMER),(1<<IDE1_TIMER)};
-static const char *ide_name [2] = {"ide0", "ide1"};
-static const char *ide_devname [2][MAX_DRIVES] = /* for printk()'s */
- {{HD_NAME "a", HD_NAME "b"}, {HD_NAME "c", HD_NAME "d"}};
-static const char *unsupported = " not supported by this kernel\n";
-
-static byte single_threaded = 0;
-#if SUPPORT_SHARING_IRQ
-static byte sharing_single_irq = 0; /* for two i/f on one IRQ */
-static volatile byte current_hwif = 0; /* for single_threaded==1 */
-#endif /* SUPPORT_SHARING_IRQ */
-
-/*
- * This structure is used to register our block device(s) with the kernel:
- */
-static void ide0_geninit(void), ide1_geninit(void);
-static struct gendisk ide_gendisk [2] =
- {{
- IDE0_MAJOR, /* major number */
- HD_NAME, /* same as below; see genhd.c before changing */
- PARTN_BITS, /* minor_shift (to extract minor number) */
- 1 << PARTN_BITS,/* max_p (number of partitions per real) */
- MAX_DRIVES, /* maximum number of real drives */
- ide0_geninit, /* init function */
- ide_hd[0], /* hd_struct */
- ide_sizes[0], /* block sizes */
- 0, /* nr_real (number of drives present) */
- ide_dev[0], /* ptr to internal data structure */
- NULL /* next */
- },{
- IDE1_MAJOR, /* major number */
- HD_NAME, /* same as above; see genhd.c before changing */
- PARTN_BITS, /* minor_shift (to extract minor number) */
- 1 << PARTN_BITS,/* max_p (number of partitions per real) */
- MAX_DRIVES, /* maximum number of real drives */
- ide1_geninit, /* init function */
- ide_hd[1], /* hd_struct */
- ide_sizes[1], /* block sizes */
- 0, /* nr_real (number of drives present) */
- ide_dev[1], /* ptr to internal data structure */
- NULL /* next */
- }};
-
-/*
- * One final include file, which references some of the data/defns from above
- */
-#define IDE_DRIVER /* "parameter" for blk.h */
-#include "blk.h"
+ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */
+#if (DISK_RECOVERY_TIME > 0)
/*
- * For really screwy hardware (hey, at least it *can* be used with Linux!
+ * For really screwy hardware (hey, at least it *can* be used with Linux)
+ * we can enforce a minimum delay time between successive operations.
*/
-#if (DISK_RECOVERY_TIME > 0)
-static unsigned long ide_lastreq[] = {0,0}; /* completion time of last I/O */
-#define SET_DISK_RECOVERY_TIMER ide_lastreq[DEV_HWIF] = read_timer();
static unsigned long read_timer(void)
{
unsigned long t, flags;
@@ -428,140 +354,343 @@ static unsigned long read_timer(void)
restore_flags(flags);
return (t - i);
}
+
+static void set_recovery_timer (ide_hwif_t *hwif)
+{
+ hwif->last_time = read_timer();
+}
+#define SET_RECOVERY_TIMER(drive) set_recovery_timer (drive)
+
#else
-#define SET_DISK_RECOVERY_TIMER /* nothing */
+
+#define SET_RECOVERY_TIMER(drive)
+
#endif /* DISK_RECOVERY_TIME */
/*
- * The heart of the driver, referenced from lots of other routines:
+ * Do not even *think* about calling this!
*/
-static void do_request (byte hwif);
-#define DO_REQUEST {SET_DISK_RECOVERY_TIMER do_request(DEV_HWIF);}
+static void init_hwif_data (unsigned int index)
+{
+ byte *p;
+ unsigned int unit;
+ ide_hwif_t *hwif = &ide_hwifs[index];
+
+ /* bulk initialize hwif & drive info with zeros */
+ p = ((byte *) hwif) + sizeof(ide_hwif_t);
+ do {
+ *--p = 0;
+ } while (p > (byte *) hwif);
+
+ /* fill in any non-zero initial values */
+ hwif->index = index;
+ ide_init_hwif_ports(hwif->io_ports, ide_default_io_base(index), &hwif->irq);
+ hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
+#ifdef CONFIG_BLK_DEV_HD
+ if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA)
+ hwif->noprobe = 1; /* may be overridden by ide_setup() */
+#endif /* CONFIG_BLK_DEV_HD */
+ hwif->major = ide_hwif_to_major[index];
+ hwif->name[0] = 'i';
+ hwif->name[1] = 'd';
+ hwif->name[2] = 'e';
+ hwif->name[3] = '0' + index;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+
+ drive->media = ide_disk;
+ drive->select.all = (unit<<4)|0xa0;
+ drive->hwif = hwif;
+ drive->ctl = 0x08;
+ drive->ready_stat = READY_STAT;
+ drive->bad_wstat = BAD_W_STAT;
+ drive->special.b.recalibrate = 1;
+ drive->special.b.set_geometry = 1;
+ drive->name[0] = 'h';
+ drive->name[1] = 'd';
+ drive->name[2] = 'a' + (index * MAX_DRIVES) + unit;
+ }
+}
/*
- * This is a macro rather than an inline to permit better gcc code.
- * Caller MUST do sti() before invoking WAIT_STAT() (for jiffies to work).
+ * init_ide_data() sets reasonable default values into all fields
+ * of all instances of the hwifs and drives, but only on the first call.
+ * Subsequent calls have no effect (they don't wipe out anything).
*
- * This routine should get fixed to not hog the cpu during extra long waits..
- * That could be done by busy-waiting for the first jiffy or two, and then
- * setting a timer to wake up at half second intervals thereafter,
- * until WAIT_WORSTCASE is achieved, before timing out.
+ * This routine is normally called at driver initialization time,
+ * but may also be called MUCH earlier during kernel "command-line"
+ * parameter processing. As such, we cannot depend on any other parts
+ * of the kernel (such as memory allocation) to be functioning yet.
+ *
+ * This is too bad, as otherwise we could dynamically allocate the
+ * ide_drive_t structs as needed, rather than always consuming memory
+ * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
+ */
+#define MAGIC_COOKIE 0x12345678
+static void init_ide_data (void)
+{
+ unsigned int index;
+ static unsigned long magic_cookie = MAGIC_COOKIE;
+
+ if (magic_cookie != MAGIC_COOKIE)
+ return; /* already initialized */
+ magic_cookie = 0;
+
+ for (index = 0; index < MAX_HWIFS; ++index)
+ init_hwif_data(index);
+
+ idebus_parameter = 0;
+ system_bus_speed = 0;
+}
+
+/*
+ * ide_system_bus_speed() returns what we think is the system VESA/PCI
+ * bus speed (in MHz). This is used for calculating interface PIO timings.
+ * The default is 40 for known PCI systems, 50 otherwise.
+ * The "idebus=xx" parameter can be used to override this value.
+ * The actual value to be used is computed/displayed the first time through.
+ */
+int ide_system_bus_speed (void)
+{
+ if (!system_bus_speed) {
+ if (idebus_parameter)
+ system_bus_speed = idebus_parameter; /* user supplied value */
+#ifdef CONFIG_PCI
+ else if (pcibios_present())
+ system_bus_speed = 40; /* safe default value for PCI */
+#endif /* CONFIG_PCI */
+ else
+ system_bus_speed = 50; /* safe default value for VESA and PCI */
+ printk("ide: Assuming %dMHz system bus speed for PIO modes%s\n", system_bus_speed,
+ idebus_parameter ? "" : "; override with idebus=xx");
+ }
+ return system_bus_speed;
+}
+
+#if SUPPORT_VLB_SYNC
+/*
+ * Some localbus EIDE interfaces require a special access sequence
+ * when using 32-bit I/O instructions to transfer data. We call this
+ * the "vlb_sync" sequence, which consists of three successive reads
+ * of the sector count register location, with interrupts disabled
+ * to ensure that the reads all happen together.
*/
-#define WAIT_STAT(dev,good,bad,timeout,msg,label) \
-{ \
- byte stat; \
- udelay(1); /* spec allows drive 400ns to assert "BUSY" */ \
- if (GET_STAT(DEV_HWIF) & BUSY_STAT) { \
- unsigned long timer = jiffies + timeout; \
- do { \
- if ((GET_STAT(DEV_HWIF) & BUSY_STAT) == 0) \
- break; \
- } while (timer > jiffies); \
- } \
- udelay(1); /* spec allows 400ns for status to stabilize */ \
- if (!OK_STAT(stat=GET_STAT(DEV_HWIF), good, bad)) { \
- ide_error(dev, msg " error", stat); \
- goto label; \
- } \
+static inline void do_vlb_sync (ide_ioreg_t port) {
+ (void) inb (port);
+ (void) inb (port);
+ (void) inb (port);
}
+#endif /* SUPPORT_VLB_SYNC */
/*
- * This is used for all data transfers *from* the IDE interface
+ * This is used for most PIO data transfers *from* the IDE interface
*/
-void input_ide_data (ide_dev_t *dev, void *buffer, uint wcount)
+void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
-#if SUPPORT_VLB_32BIT
- if (dev->vlb_32bit) {
+ byte io_32bit = drive->io_32bit;
+
+ if (io_32bit) {
#if SUPPORT_VLB_SYNC
- if (dev->vlb_sync) {
+ if (io_32bit & 2) {
cli();
- VLB_SYNC;
- insl(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount);
- if (dev->unmask)
+ do_vlb_sync(IDE_NSECTOR_REG);
+ insl(IDE_DATA_REG, buffer, wcount);
+ if (drive->unmask)
sti();
} else
#endif /* SUPPORT_VLB_SYNC */
- insl(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount);
- } else
-#endif /* SUPPORT_VLB_32BIT */
- insw(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount<<1);
+ insl(IDE_DATA_REG, buffer, wcount);
+ } else {
+#if SUPPORT_SLOW_DATA_PORTS
+ if (drive->slow) {
+ unsigned short *ptr = (unsigned short *) buffer;
+ while (wcount--) {
+ *ptr++ = inw_p(IDE_DATA_REG);
+ *ptr++ = inw_p(IDE_DATA_REG);
+ }
+ } else
+#endif /* SUPPORT_SLOW_DATA_PORTS */
+ insw(IDE_DATA_REG, buffer, wcount<<1);
+ }
}
/*
- * This is used for all data transfers *to* the IDE interface
+ * This is used for most PIO data transfers *to* the IDE interface
*/
-void output_ide_data (ide_dev_t *dev, void *buffer, uint wcount)
+void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
{
-#if SUPPORT_VLB_32BIT
- if (dev->vlb_32bit) {
+ byte io_32bit = drive->io_32bit;
+
+ if (io_32bit) {
#if SUPPORT_VLB_SYNC
- if (dev->vlb_sync) {
+ if (io_32bit & 2) {
cli();
- VLB_SYNC;
- outsl(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount);
- if (dev->unmask)
+ do_vlb_sync(IDE_NSECTOR_REG);
+ outsl(IDE_DATA_REG, buffer, wcount);
+ if (drive->unmask)
sti();
} else
- outsl(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount);
#endif /* SUPPORT_VLB_SYNC */
- } else
-#endif /* SUPPORT_VLB_32BIT */
- outsw(IDE_PORT(HD_DATA,DEV_HWIF), buffer, wcount<<1);
+ outsl(IDE_DATA_REG, buffer, wcount);
+ } else {
+#if SUPPORT_SLOW_DATA_PORTS
+ if (drive->slow) {
+ unsigned short *ptr = (unsigned short *) buffer;
+ while (wcount--) {
+ outw_p(*ptr++, IDE_DATA_REG);
+ outw_p(*ptr++, IDE_DATA_REG);
+ }
+ } else
+#endif /* SUPPORT_SLOW_DATA_PORTS */
+ outsw(IDE_DATA_REG, buffer, wcount<<1);
+ }
}
/*
- * This should get invoked on every exit path from the driver.
+ * The following routines are mainly used by the ATAPI drivers.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
*/
-static inline void start_ide_timer (byte hwif)
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
{
- if (ide_handler[HWIF] != NULL) { /* waiting for an irq? */
- timer_table[ide_timer[HWIF]].expires = jiffies + WAIT_CMD;
- timer_active |= ide_timerbit[HWIF];
+ ++bytecount;
+#ifdef CONFIG_ATARI
+ if (MACH_IS_ATARI) {
+ /* Atari has a byte-swapped IDE interface */
+ insw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
+ return;
}
+#endif /* CONFIG_ATARI */
+ ide_input_data (drive, buffer, bytecount / 4);
+ if ((bytecount & 0x03) >= 2)
+ insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
}
-static void do_ide_reset (ide_dev_t *dev)
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
{
- byte tmp;
- unsigned long timer, flags;
+ ++bytecount;
+#ifdef CONFIG_ATARI
+ if (MACH_IS_ATARI) {
+ /* Atari has a byte-swapped IDE interface */
+ outsw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
+ return;
+ }
+#endif /* CONFIG_ATARI */
+ ide_output_data (drive, buffer, bytecount / 4);
+ if ((bytecount & 0x03) >= 2)
+ outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
- save_flags(flags);
- sti();
- for (tmp = 0; tmp < MAX_DRIVES; tmp++) {
- ide_dev_t *rdev = &ide_dev[DEV_HWIF][tmp];
- rdev->special.b.set_geometry = 1;
- rdev->special.b.recalibrate = 1;
- rdev->special.b.set_multmode = 0;
- if (OK_TO_RESET_CONTROLLER)
- rdev->mult_count = 0;
- if (!rdev->keep_settings) {
- rdev->mult_req = 0;
- rdev->unmask = 0;
- }
- if (rdev->mult_req != rdev->mult_count)
- rdev->special.b.set_multmode = 1;
+/*
+ * This should get invoked any time we exit the driver to
+ * wait for an interrupt response from a drive. handler() points
+ * at the appropriate code to handle the next interrupt, and a
+ * timer is started to prevent us from waiting forever in case
+ * something goes wrong (see the ide_timer_expiry() handler later on).
+ */
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+#ifdef DEBUG
+ if (hwgroup->handler != NULL) {
+ printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
+ drive->name, hwgroup->handler, handler);
}
+#endif
+ hwgroup->handler = handler;
+ hwgroup->timer.expires = jiffies + timeout;
+ add_timer(&(hwgroup->timer));
+}
-#if OK_TO_RESET_CONTROLLER
- cli();
- OUT_BYTE(dev->ctl|6,HD_CMD); /* set nIEN, set SRST */
- udelay(10); /* more than enough time */
- OUT_BYTE(dev->ctl|2,HD_CMD); /* clear SRST */
- udelay(10); /* more than enough time */
- sti(); /* needed for jiffies */
- for (timer = jiffies + WAIT_WORSTCASE; timer > jiffies;) {
- if ((GET_STAT(DEV_HWIF) & BUSY_STAT) == 0)
- break;
+/*
+ * current_capacity() returns the capacity (in sectors) of a drive
+ * according to its current geometry/LBA settings.
+ */
+static unsigned long current_capacity (ide_drive_t *drive)
+{
+ if (!drive->present)
+ return 0;
+ if (drive->driver != NULL)
+ return DRIVER(drive)->capacity(drive);
+ return 0;
+}
+
+/*
+ * ide_geninit() is called exactly *once* for each major, from genhd.c,
+ * at the beginning of the initial partition check for the drives.
+ */
+void ide_geninit (struct gendisk *gd)
+{
+ unsigned int unit;
+ ide_hwif_t *hwif = gd->real_devices;
+
+ for (unit = 0; unit < gd->nr_real; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+
+ drive->part[0].nr_sects = current_capacity(drive);
+ if (!drive->present || drive->media != ide_disk || drive->driver == NULL)
+ drive->part[0].start_sect = -1; /* skip partition check */
}
- printk("%s: do_ide_reset: ", ide_name[DEV_HWIF]);
- /* ATAPI devices usually do *not* assert READY after a reset */
- if (!OK_STAT(tmp=GET_STAT(DEV_HWIF), 0, BUSY_STAT)) {
- printk("timed-out, status=0x%02x\n", tmp);
+}
+
+static void do_reset1 (ide_drive_t *, int); /* needed below */
+
+/*
+ * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an atapi drive reset operation. If the drive has not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static void atapi_reset_pollfunc (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ byte stat;
+
+ OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+ udelay (10);
+
+ if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
+ printk("%s: ATAPI reset complete\n", drive->name);
+ } else {
+ if (jiffies < hwgroup->poll_timeout) {
+ ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
+ return; /* continue polling */
+ }
+ hwgroup->poll_timeout = 0; /* end of polling */
+ printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
+ do_reset1 (drive, 1); /* do it the old fashioned way */
+ return;
+ }
+ hwgroup->poll_timeout = 0; /* done polling */
+}
+
+/*
+ * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an ide reset operation. If the drives have not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static void reset_pollfunc (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ ide_hwif_t *hwif = HWIF(drive);
+ byte tmp;
+
+ if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
+ if (jiffies < hwgroup->poll_timeout) {
+ ide_set_handler (drive, &reset_pollfunc, HZ/20);
+ return; /* continue polling */
+ }
+ printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
} else {
- if ((tmp = GET_ERR(DEV_HWIF)) == 1)
+ printk("%s: reset: ", hwif->name);
+ if ((tmp = GET_ERR()) == 1)
printk("success\n");
else {
- printk("%s: ", ide_devname[DEV_HWIF][0]);
+#if FANCY_STATUS_DUMPS
+ printk("master: ");
switch (tmp & 0x7f) {
case 1: printk("passed");
break;
@@ -576,57 +705,148 @@ static void do_ide_reset (ide_dev_t *dev)
default:printk("error (0x%02x?)", tmp);
}
if (tmp & 0x80)
- printk("; %s: error", ide_devname[DEV_HWIF][1]);
+ printk("; slave: failed");
printk("\n");
+#else
+ printk("failed\n");
+#endif /* FANCY_STATUS_DUMPS */
+ }
+ }
+ hwgroup->poll_timeout = 0; /* done polling */
+}
+
+static void pre_reset (ide_drive_t *drive)
+{
+ if (!drive->keep_settings) {
+ drive->unmask = 0;
+ drive->io_32bit = 0;
+ if (drive->using_dma) {
+ drive->using_dma = 0;
+ printk("%s: disabled DMA\n", drive->name);
}
}
+ if (drive->driver != NULL)
+ DRIVER(drive)->pre_reset(drive);
+}
+
+/*
+ * do_reset1() attempts to recover a confused drive by resetting it.
+ * Unfortunately, resetting a disk drive actually resets all devices on
+ * the same interface, so it can really be thought of as resetting the
+ * interface rather than resetting the drive.
+ *
+ * ATAPI devices have their own reset mechanism which allows them to be
+ * individually reset without clobbering other devices on the same interface.
+ *
+ * Unfortunately, the IDE interface does not generate an interrupt to let
+ * us know when the reset operation has finished, so we must poll for this.
+ * Equally poor, though, is the fact that this may a very long time to complete,
+ * (up to 30 seconds worstcase). So, instead of busy-waiting here for it,
+ * we set a timer to poll at 50ms intervals.
+ */
+static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
+{
+ unsigned int unit;
+ unsigned long flags;
+ ide_hwif_t *hwif = HWIF(drive);
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+ save_flags(flags);
+ cli(); /* Why ? */
+
+ /* For an ATAPI device, first try an ATAPI SRST. */
+ if (drive->media != ide_disk && !do_not_try_atapi) {
+ pre_reset(drive);
+ OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+ udelay (20);
+ OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
+ restore_flags (flags);
+ return;
+ }
+
+ /*
+ * First, reset any device state data we were maintaining
+ * for any of the drives on this interface.
+ */
+ for (unit = 0; unit < MAX_DRIVES; ++unit)
+ pre_reset(&hwif->drives[unit]);
+
+#if OK_TO_RESET_CONTROLLER
+ /*
+ * Note that we also set nIEN while resetting the device,
+ * to mask unwanted interrupts from the interface during the reset.
+ * However, due to the design of PC hardware, this will cause an
+ * immediate interrupt due to the edge transition it produces.
+ * This single interrupt gives us a "fast poll" for drives that
+ * recover from reset very quickly, saving us the first 50ms wait time.
+ */
+ OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */
+ udelay(10); /* more than enough time */
+ OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */
+ udelay(10); /* more than enough time */
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &reset_pollfunc, HZ/20);
#endif /* OK_TO_RESET_CONTROLLER */
- restore_flags(flags);
+
+ restore_flags (flags);
+}
+
+/*
+ * ide_do_reset() is the entry point to the drive/interface reset code.
+ */
+void ide_do_reset (ide_drive_t *drive)
+{
+ do_reset1 (drive, 0);
}
/*
- * Clean up after success/failure of an explicit (ioctl) drive cmd
+ * Clean up after success/failure of an explicit drive cmd
*/
-static void end_drive_cmd (ide_dev_t *dev, byte stat, byte err)
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
{
unsigned long flags;
- struct request *rq = ide_cur_rq[DEV_HWIF];
- byte *args = (byte *) rq->buffer;
+ struct request *rq = HWGROUP(drive)->rq;
- rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
- if (args) {
- args[0] = stat;
- args[1] = err;
- args[2] = IN_BYTE(HD_NSECTOR,DEV_HWIF);
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ byte *args = (byte *) rq->buffer;
+ rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
+ if (args) {
+ args[0] = stat;
+ args[1] = err;
+ args[2] = IN_BYTE(IDE_NSECTOR_REG);
+ }
}
save_flags(flags);
cli();
- up(rq->sem);
- ide_cur_rq[DEV_HWIF] = NULL;
+ blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
+ HWGROUP(drive)->rq = NULL;
+ rq->rq_status = RQ_INACTIVE;
+ if (rq->sem != NULL)
+ up(rq->sem);
restore_flags(flags);
}
/*
* Error reporting, in human readable form (luxurious, but a memory hog).
*/
-static byte dump_status (byte hwif, const char *msg, byte stat)
+byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
{
unsigned long flags;
byte err = 0;
- ide_dev_t *dev = ide_cur_dev[HWIF];
- const char *name = dev ? dev->name : ide_name[HWIF];
save_flags (flags);
- sti();
- printk("%s: %s: status=0x%02x", name, msg, stat);
+ ide_sti();
+ printk("%s: %s: status=0x%02x", drive->name, msg, stat);
#if FANCY_STATUS_DUMPS
- if (dev && dev->type == disk) {
+ if (drive->media == ide_disk) {
printk(" { ");
if (stat & BUSY_STAT)
printk("Busy ");
else {
if (stat & READY_STAT) printk("DriveReady ");
- if (stat & WRERR_STAT) printk("WriteFault ");
+ if (stat & WRERR_STAT) printk("DeviceFault ");
if (stat & SEEK_STAT) printk("SeekComplete ");
if (stat & DRQ_STAT) printk("DataRequest ");
if (stat & ECC_STAT) printk("CorrectedError ");
@@ -638,10 +858,10 @@ static byte dump_status (byte hwif, const char *msg, byte stat)
#endif /* FANCY_STATUS_DUMPS */
printk("\n");
if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
- err = GET_ERR(HWIF);
- printk("%s: %s: error=0x%02x", name, msg, err);
+ err = GET_ERR();
+ printk("%s: %s: error=0x%02x", drive->name, msg, err);
#if FANCY_STATUS_DUMPS
- if (dev && dev->type == disk) {
+ if (drive->media == ide_disk) {
printk(" { ");
if (err & BBD_ERR) printk("BadSector ");
if (err & ECC_ERR) printk("UncorrectableError ");
@@ -651,22 +871,22 @@ static byte dump_status (byte hwif, const char *msg, byte stat)
if (err & MARK_ERR) printk("AddrMarkNotFound ");
printk("}");
if (err & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
- byte cur = IN_BYTE(HD_CURRENT,HWIF);
+ byte cur = IN_BYTE(IDE_SELECT_REG);
if (cur & 0x40) { /* using LBA? */
printk(", LBAsect=%ld", (unsigned long)
((cur&0xf)<<24)
- |(IN_BYTE(HD_HCYL,HWIF)<<16)
- |(IN_BYTE(HD_LCYL,HWIF)<<8)
- | IN_BYTE(HD_SECTOR,HWIF));
+ |(IN_BYTE(IDE_HCYL_REG)<<16)
+ |(IN_BYTE(IDE_LCYL_REG)<<8)
+ | IN_BYTE(IDE_SECTOR_REG));
} else {
printk(", CHS=%d/%d/%d",
- (IN_BYTE(HD_HCYL,HWIF)<<8) +
- IN_BYTE(HD_LCYL,HWIF),
+ (IN_BYTE(IDE_HCYL_REG)<<8) +
+ IN_BYTE(IDE_LCYL_REG),
cur & 0xf,
- IN_BYTE(HD_SECTOR,HWIF));
+ IN_BYTE(IDE_SECTOR_REG));
}
- if (ide_cur_rq[HWIF])
- printk(", sector=%ld", ide_cur_rq[HWIF]->sector);
+ if (HWGROUP(drive)->rq)
+ printk(", sector=%ld", HWGROUP(drive)->rq->sector);
}
}
#endif /* FANCY_STATUS_DUMPS */
@@ -677,436 +897,268 @@ static byte dump_status (byte hwif, const char *msg, byte stat)
}
/*
+ * try_to_flush_leftover_data() is invoked in response to a drive
+ * unexpectedly having its DRQ_STAT bit set. As an alternative to
+ * resetting the drive, this routine tries to clear the condition
+ * by read a sector's worth of data from the drive. Of course,
+ * this may not help if the drive is *waiting* for data from *us*.
+ */
+static void try_to_flush_leftover_data (ide_drive_t *drive)
+{
+ int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
+
+ while (i > 0) {
+ unsigned long buffer[16];
+ unsigned int wcount = (i > 16) ? 16 : i;
+ i -= wcount;
+ ide_input_data (drive, buffer, wcount);
+ }
+}
+
+/*
* ide_error() takes action based on the error returned by the controller.
*/
-#define ERROR_MAX 8 /* Max read/write errors per sector */
-#define ERROR_RESET 3 /* Reset controller every 4th retry */
-#define ERROR_RECAL 1 /* Recalibrate every 2nd retry */
-static void ide_error (ide_dev_t *dev, const char *msg, byte stat)
+void ide_error (ide_drive_t *drive, const char *msg, byte stat)
{
struct request *rq;
byte err;
- err = dump_status(DEV_HWIF, msg, stat);
- if ((rq = ide_cur_rq[DEV_HWIF]) == NULL || dev == NULL)
+ err = ide_dump_status(drive, msg, stat);
+ if ((rq = HWGROUP(drive)->rq) == NULL || drive == NULL)
return;
-#ifdef IDE_DRIVE_CMD
- if (rq->cmd == IDE_DRIVE_CMD) { /* never retry an explicit DRIVE_CMD */
- end_drive_cmd(dev, stat, err);
+ /* retry only "normal" I/O: */
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ rq->errors = 1;
+ ide_end_drive_cmd(drive, stat, err);
return;
}
-#endif /* IDE_DRIVE_CMD */
if (stat & BUSY_STAT) { /* other bits are useless when BUSY */
rq->errors |= ERROR_RESET;
} else {
- if (dev->type == disk && (stat & ERR_STAT)) {
- /* err has different meaning on cdrom */
- if (err & BBD_ERR) /* retries won't help this! */
+ if (drive->media == ide_disk && (stat & ERR_STAT)) {
+ /* err has different meaning on cdrom and tape */
+ if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */
rq->errors = ERROR_MAX;
else if (err & TRK0_ERR) /* help it find track zero */
rq->errors |= ERROR_RECAL;
}
- if ((stat & DRQ_STAT) && rq->cmd == READ) {
- int i = dev->mult_count ? dev->mult_count<<8 : 1<<8;
- while (i-- > 0) /* try to flush data */
- (void) IN_BYTE(HD_DATA, dev->hwif);
- }
+ if ((stat & DRQ_STAT) && rq->cmd != WRITE)
+ try_to_flush_leftover_data(drive);
}
- if (GET_STAT(dev->hwif) & (BUSY_STAT|DRQ_STAT))
+ if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
rq->errors |= ERROR_RESET; /* Mmmm.. timing problem */
- if (rq->errors >= ERROR_MAX)
- end_request(0, DEV_HWIF);
- else {
- if ((rq->errors & ERROR_RESET) == ERROR_RESET)
- do_ide_reset(dev);
- else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
- dev->special.b.recalibrate = 1;
+ if (rq->errors >= ERROR_MAX) {
+ if (drive->driver != NULL)
+ DRIVER(drive)->end_request(0, HWGROUP(drive));
+ ide_end_request(0, HWGROUP(drive));
+ } else {
+ if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
+ ++rq->errors;
+ ide_do_reset(drive);
+ return;
+ } else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+ drive->special.b.recalibrate = 1;
++rq->errors;
}
}
-static void read_intr (ide_dev_t *dev)
+/*
+ * Issue a simple drive command
+ * The drive must be selected beforehand.
+ */
+void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
{
- byte stat;
- int i;
- unsigned int msect, nsect;
- struct request *rq;
-
- if (!OK_STAT(stat=GET_STAT(DEV_HWIF),DATA_READY,BAD_R_STAT)) {
- sti();
- ide_error(dev, "read_intr", stat);
- DO_REQUEST;
- return;
- }
- msect = dev->mult_count;
-read_next:
- rq = ide_cur_rq[DEV_HWIF];
- if (msect) {
- if ((nsect = rq->current_nr_sectors) > msect)
- nsect = msect;
- msect -= nsect;
- } else
- nsect = 1;
- input_ide_data(dev, rq->buffer, nsect * SECTOR_WORDS);
-#ifdef DEBUG
- printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
- dev->name, rq->sector, rq->sector+nsect-1,
- (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
-#endif
- rq->sector += nsect;
- rq->buffer += nsect<<9;
- rq->errors = 0;
- i = (rq->nr_sectors -= nsect);
- if ((rq->current_nr_sectors -= nsect) <= 0)
- end_request(1, DEV_HWIF);
- if (i > 0) {
- if (msect)
- goto read_next;
- ide_handler[DEV_HWIF] = &read_intr;
- return;
- }
- /* (void) GET_STAT(DEV_HWIF); */ /* hd.c did this */
- DO_REQUEST;
+ ide_set_handler (drive, handler, WAIT_CMD);
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE(nsect,IDE_NSECTOR_REG);
+ OUT_BYTE(cmd,IDE_COMMAND_REG);
}
-static void write_intr (ide_dev_t *dev)
+/*
+ * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
+ */
+static void drive_cmd_intr (ide_drive_t *drive)
{
- byte stat;
- int i;
- struct request *rq = ide_cur_rq[DEV_HWIF];
-
- if (OK_STAT(stat=GET_STAT(DEV_HWIF),DRIVE_READY,BAD_W_STAT)) {
-#ifdef DEBUG
- printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
- dev->name, rq->sector, (unsigned long) rq->buffer,
- rq->nr_sectors-1);
-#endif
- if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
- rq->sector++;
- rq->buffer += 512;
- rq->errors = 0;
- i = --rq->nr_sectors;
- --rq->current_nr_sectors;
- if (rq->current_nr_sectors <= 0)
- end_request(1, DEV_HWIF);
- if (i > 0) {
- ide_handler[DEV_HWIF] = &write_intr;
- output_ide_data(dev, rq->buffer, SECTOR_WORDS);
- return;
- }
- DO_REQUEST;
- return;
- }
- }
- sti();
- ide_error(dev, "write_intr", stat);
- DO_REQUEST;
+ struct request *rq = HWGROUP(drive)->rq;
+ byte *args = (byte *) rq->buffer;
+ byte stat = GET_STAT();
+
+ ide_sti();
+ if ((stat & DRQ_STAT) && args && args[3]) {
+ byte io_32bit = drive->io_32bit;
+ drive->io_32bit = 0;
+ ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
+ drive->io_32bit = io_32bit;
+ stat = GET_STAT();
+ }
+ if (OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_end_drive_cmd (drive, stat, GET_ERR());
+ else
+ ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
}
-static void multwrite (ide_dev_t *dev)
+/*
+ * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
+ * commands to a drive. It used to do much more, but has been scaled back.
+ */
+static inline void do_special (ide_drive_t *drive)
{
- struct request *rq = &ide_write_rq[DEV_HWIF];
- unsigned int mcount = dev->mult_count;
-
- do {
- unsigned int nsect = rq->current_nr_sectors;
- if (nsect > mcount)
- nsect = mcount;
- mcount -= nsect;
+ special_t *s = &drive->special;
- output_ide_data(dev, rq->buffer, nsect<<7);
#ifdef DEBUG
- printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
- dev->name, rq->sector, (unsigned long) rq->buffer,
- nsect, rq->nr_sectors - nsect);
+ printk("%s: do_special: 0x%02x\n", drive->name, s->all);
#endif
- if ((rq->nr_sectors -= nsect) <= 0)
- break;
- if ((rq->current_nr_sectors -= nsect) == 0) {
- if ((rq->bh = rq->bh->b_reqnext) != NULL) {
- rq->current_nr_sectors = rq->bh->b_size>>9;
- rq->buffer = rq->bh->b_data;
- } else {
- panic("%s: buffer list corrupted\n", dev->name);
- break;
- }
- } else {
- rq->buffer += nsect << 9;
- }
- } while (mcount);
-}
-
-static void multwrite_intr (ide_dev_t *dev)
-{
- byte stat;
- int i;
- struct request *rq = &ide_write_rq[DEV_HWIF];
-
- if (OK_STAT(stat=GET_STAT(DEV_HWIF),DRIVE_READY,BAD_W_STAT)) {
- if (stat & DRQ_STAT) {
- if (rq->nr_sectors) {
- if (dev->mult_count)
- multwrite(dev);
- ide_handler[DEV_HWIF] = &multwrite_intr;
- return;
- }
- } else {
- if (!rq->nr_sectors) { /* all done? */
- rq = ide_cur_rq[DEV_HWIF];
- for (i = rq->nr_sectors; i > 0;){
- i -= rq->current_nr_sectors;
- end_request(1, DEV_HWIF);
- }
- DO_REQUEST;
- return;
- }
- }
+ if (s->b.set_tune) {
+ ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+ s->b.set_tune = 0;
+ if (tuneproc != NULL)
+ tuneproc(drive, drive->tune_req);
+ } else if (drive->driver != NULL) {
+ DRIVER(drive)->special(drive);
+ } else if (s->all) {
+ printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
+ s->all = 0;
}
- sti();
- ide_error(dev, "multwrite_intr", stat);
- DO_REQUEST;
}
/*
- * Issue a simple drive command
- * The drive must be selected beforehand.
+ * This routine busy-waits for the drive status to be not "busy".
+ * It then checks the status for all of the "good" bits and none
+ * of the "bad" bits, and if all is okay it returns 0. All other
+ * cases return 1 after invoking ide_error() -- caller should just return.
+ *
+ * This routine should get fixed to not hog the cpu during extra long waits..
+ * That could be done by busy-waiting for the first jiffy or two, and then
+ * setting a timer to wake up at half second intervals thereafter,
+ * until timeout is achieved, before timing out.
*/
-static inline void ide_cmd(ide_dev_t *dev, byte cmd, byte nsect,
- void (*handler)(ide_dev_t *dev))
-{
- OUT_BYTE(dev->ctl,HD_CMD);
- OUT_BYTE(nsect,HD_NSECTOR);
- OUT_BYTE(cmd,HD_COMMAND);
- ide_handler[DEV_HWIF] = handler;
-}
-
-static void set_multmode_intr (ide_dev_t *dev)
+int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
{
- byte stat = GET_STAT(DEV_HWIF);
+ byte stat;
+ unsigned long flags;
- sti();
- if (!OK_STAT(stat,READY_STAT,BAD_STAT)) {
- dev->mult_req = dev->mult_count = 0;
- dev->special.b.recalibrate = 1;
- (void) dump_status(DEV_HWIF, "set_multmode", stat);
- } else {
- if ((dev->mult_count = dev->mult_req))
- printk (" %s: enabled %d-sector multiple mode\n",
- dev->name, dev->mult_count);
- else
- printk (" %s: multiple mode turned off\n", dev->name);
+test:
+ udelay(1); /* spec allows drive 400ns to change "BUSY" */
+ if (OK_STAT((stat = GET_STAT()), good, bad))
+ return 0; /* fast exit for most frequent case */
+ if (!(stat & BUSY_STAT)) {
+ ide_error(drive, "status error", stat);
+ return 1;
}
- DO_REQUEST;
-}
-
-static void set_geometry_intr (ide_dev_t *dev)
-{
- byte stat = GET_STAT(DEV_HWIF);
-
- sti();
- if (!OK_STAT(stat,READY_STAT,BAD_STAT))
- ide_error(dev, "set_geometry_intr", stat);
- DO_REQUEST;
-}
-
-static void recal_intr (ide_dev_t *dev)
-{
- byte stat = GET_STAT(DEV_HWIF);
-
- sti();
- if (!OK_STAT(stat,READY_STAT,BAD_STAT))
- ide_error(dev, "recal_intr", stat);
- DO_REQUEST;
-}
-
-static void drive_cmd_intr (ide_dev_t *dev)
-{
- byte stat = GET_STAT(DEV_HWIF);
-
- sti();
- if (!OK_STAT(stat,READY_STAT,BAD_STAT))
- ide_error(dev, "drive_cmd", stat); /* calls end_drive_cmd() */
- else
- end_drive_cmd (dev, stat, GET_ERR(DEV_HWIF));
- DO_REQUEST;
-}
-
-static void timer_expiry (byte hwif)
-{
- unsigned long flags;
save_flags(flags);
- cli();
+ ide_sti();
+ timeout += jiffies;
+ do {
+ if (!((stat = GET_STAT()) & BUSY_STAT)) {
+ restore_flags(flags);
+ goto test;
+ }
+ } while (jiffies <= timeout);
- if (ide_handler[HWIF] == NULL || (timer_active & ide_timerbit[HWIF])) {
- /* The drive must have responded just as the timer expired */
- sti();
- printk("%s: marginal timeout\n", ide_name[HWIF]);
- } else {
- ide_handler[HWIF] = NULL;
- disable_irq(ide_irq[HWIF]);
-#if SUPPORT_SERIALIZE
- if (single_threaded && ide_irq[HWIF] != ide_irq[HWIF^1])
- disable_irq(ide_irq[HWIF^1]);
-#endif /* SUPPORT_SERIALIZE */
- sti();
- ide_error(ide_cur_dev[HWIF], "timeout", GET_STAT(HWIF));
- do_request(HWIF);
-#if SUPPORT_SHARING_IRQ
- if (single_threaded) /* this line is indeed necessary */
- hwif = current_hwif;
-#endif /* SUPPORT_SHARING_IRQ */
- cli();
- start_ide_timer(HWIF);
- enable_irq(ide_irq[HWIF]);
-#if SUPPORT_SERIALIZE
- if (single_threaded && ide_irq[HWIF] != ide_irq[HWIF^1])
- enable_irq(ide_irq[HWIF^1]);
-#endif /* SUPPORT_SERIALIZE */
- }
restore_flags(flags);
+ ide_error(drive, "status timeout", GET_STAT());
+ return 1;
}
-static void ide0_timer_expiry (void) /* invoked from sched.c */
-{
- timer_expiry (0);
-}
-
-static void ide1_timer_expiry (void) /* invoked from sched.c */
-{
- timer_expiry (1);
-}
-
-static int do_special (ide_dev_t *dev)
+/*
+ * execute_drive_cmd() issues a special drive command,
+ * usually initiated by ioctl() from the external hdparm program.
+ */
+static void execute_drive_cmd (ide_drive_t *drive, struct request *rq)
{
- special_t *s = &dev->special;
+ byte *args = rq->buffer;
+ if (args) {
#ifdef DEBUG
- printk("%s: do_special: 0x%02x\n", dev->name, s->all);
+ printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
+ drive->name, args[0], args[1], args[2], args[3]);
#endif
- if (s->b.set_geometry) {
- s->b.set_geometry = 0;
- if (dev->type == disk) {
- OUT_BYTE(dev->sect,HD_SECTOR);
- OUT_BYTE(dev->cyl,HD_LCYL);
- OUT_BYTE(dev->cyl>>8,HD_HCYL);
- OUT_BYTE(((dev->head-1)|dev->select.all)&0xBF,HD_CURRENT);
- ide_cmd(dev, WIN_SPECIFY, dev->sect, &set_geometry_intr);
- }
- } else if (s->b.recalibrate) {
- s->b.recalibrate = 0;
- if (dev->type == disk)
- ide_cmd(dev,WIN_RESTORE,dev->sect,&recal_intr);
- } else if (s->b.set_multmode) {
- if (dev->type == disk) {
- if (dev->id && dev->mult_req > dev->id->max_multsect)
- dev->mult_req = dev->id->max_multsect;
- ide_cmd(dev,WIN_SETMULT,dev->mult_req,&set_multmode_intr);
- } else {
- dev->mult_req = 0;
- printk("%s: multmode not supported by this device\n", dev->name);
- }
- s->b.set_multmode = 0;
+ OUT_BYTE(args[2],IDE_FEATURE_REG);
+ ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
+ return;
} else {
- if (s->all) {
- printk("%s: bad special flag: 0x%02x\n", dev->name, s->all);
- s->all = 0;
- }
+ /*
+ * NULL is actually a valid way of waiting for
+ * all current requests to be flushed from the queue.
+ */
+#ifdef DEBUG
+ printk("%s: DRIVE_CMD (null)\n", drive->name);
+#endif
+ ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+ return;
}
- return (ide_handler[DEV_HWIF] == NULL) ? 1 : 0;
}
-#ifdef CONFIG_BLK_DEV_IDECD
-static byte wait_stat (ide_dev_t *dev, byte good, byte bad, unsigned long timeout)
+/*
+ * do_request() initiates handling of a new I/O request
+ */
+static inline void do_request (ide_hwif_t *hwif, struct request *rq)
{
- unsigned long flags;
-
- save_flags(flags);
- sti();
- WAIT_STAT(dev, good, bad, timeout, "status", error);
- restore_flags(flags);
- return 0;
-error:
- restore_flags(flags);
- return 1;
-}
-
-#include "ide-cd.c"
-#endif /* CONFIG_BLK_DEV_IDECD */
+ unsigned int minor, unit;
+ unsigned long block, blockend;
+ ide_drive_t *drive = NULL;
-static inline int do_rw_disk (ide_dev_t *dev, struct request *rq, unsigned long block)
-{
- OUT_BYTE(dev->ctl,HD_CMD);
- OUT_BYTE(rq->nr_sectors,HD_NSECTOR);
- if (dev->select.b.lba) {
+ ide_sti();
#ifdef DEBUG
- printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
- dev->name, (rq->cmd==READ)?"read":"writ",
- block, rq->nr_sectors, (unsigned long) rq->buffer);
+ printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
#endif
- OUT_BYTE(block,HD_SECTOR);
- OUT_BYTE(block>>=8,HD_LCYL);
- OUT_BYTE(block>>=8,HD_HCYL);
- OUT_BYTE(((block>>8)&0x0f)|dev->select.all,HD_CURRENT);
- } else {
- unsigned int sect,head,cyl,track;
- track = block / dev->sect;
- sect = block % dev->sect + 1;
- OUT_BYTE(sect,HD_SECTOR);
- head = track % dev->head;
- cyl = track / dev->head;
- OUT_BYTE(cyl,HD_LCYL);
- OUT_BYTE(cyl>>8,HD_HCYL);
- OUT_BYTE(head|dev->select.all,HD_CURRENT);
+ minor = MINOR(rq->rq_dev);
+ unit = minor >> PARTN_BITS;
+ if (MAJOR(rq->rq_dev) != hwif->major || unit >= MAX_DRIVES) {
+ printk("%s: bad device number: %s\n",
+ hwif->name, kdevname(rq->rq_dev));
+ goto kill_rq;
+ }
+ drive = &hwif->drives[unit];
#ifdef DEBUG
- printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
- dev->name, (rq->cmd==READ)?"read":"writ", cyl,
- head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
-#endif
+ if (rq->bh && !buffer_locked(rq->bh)) {
+ printk("%s: block not locked\n", drive->name);
+ goto kill_rq;
}
- if (rq->cmd == READ) {
- OUT_BYTE(dev->mult_count ? WIN_MULTREAD : WIN_READ, HD_COMMAND);
- ide_handler[DEV_HWIF] = &read_intr;
- return 0;
+#endif
+ block = rq->sector;
+ blockend = block + rq->nr_sectors;
+ if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {
+ printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name,
+ (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);
+ goto kill_rq;
+ }
+ block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
+#if FAKE_FDISK_FOR_EZDRIVE
+ if (block == 0 && drive->remap_0_to_1)
+ block = 1; /* redirect MBR access to EZ-Drive partn table */
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ ((ide_hwgroup_t *)hwif->hwgroup)->drive = drive;
+#if (DISK_RECOVERY_TIME > 0)
+ while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
+#endif
+
+ SELECT_DRIVE(hwif,drive);
+ if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
+ printk("%s: drive not ready for command\n", drive->name);
+ return;
}
- if (rq->cmd == WRITE) {
- OUT_BYTE(dev->wpcom,HD_PRECOMP); /* for ancient drives */
- OUT_BYTE(dev->mult_count ? WIN_MULTWRITE : WIN_WRITE, HD_COMMAND);
- WAIT_STAT(dev, DATA_READY, BAD_W_STAT, WAIT_DRQ, "DRQ", error);
- if (!dev->unmask)
- cli();
- if (dev->mult_count) {
- ide_write_rq[DEV_HWIF] = *rq; /* scratchpad */
- multwrite(dev);
- ide_handler[DEV_HWIF] = &multwrite_intr;
- } else {
- output_ide_data(dev, rq->buffer, SECTOR_WORDS);
- ide_handler[DEV_HWIF] = &write_intr;
+
+ if (!drive->special.all) {
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ execute_drive_cmd(drive, rq);
+ return;
}
- return 0;
- }
-#ifdef IDE_DRIVE_CMD
- if (rq->cmd == IDE_DRIVE_CMD) {
- byte *args = rq->buffer;
- if (args) {
- OUT_BYTE(args[2],HD_FEATURE);
- ide_cmd(dev, args[0], args[1], &drive_cmd_intr);
- printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x\n",
- dev->name, args[0], args[1], args[2]);
- return 0;
- } else {
-#ifdef DEBUG
- printk("%s: DRIVE_CMD (null)\n", dev->name);
-#endif
- end_drive_cmd(dev,GET_STAT(DEV_HWIF),GET_ERR(DEV_HWIF));
- return 1;
+ if (drive->driver != NULL) {
+ DRIVER(drive)->do_request(drive, rq, block);
+ return;
}
- }
-#endif /* IDE_DRIVE_CMD */
- printk("%s: bad command: %d\n", dev->name, rq->cmd);
- end_request(0, DEV_HWIF);
-error:
- return 1;
+ printk("%s: media type %d not supported\n", drive->name, drive->media);
+ goto kill_rq;
+ }
+ do_special(drive);
+ return;
+kill_rq:
+ if (drive != NULL && drive->driver != NULL)
+ DRIVER(drive)->end_request(0, HWGROUP(drive));
+ else
+ ide_end_request(0, hwif->hwgroup);
}
/*
@@ -1114,137 +1166,129 @@ error:
* (a) the device-interrupt is always masked before entry, and
* (b) the timeout-interrupt is always disabled before entry.
*
+ * If we enter here from, say irq14, and then start a new request for irq15,
+ * (possible with "serialize" option) then we cannot ensure that we exit
+ * before the irq15 hits us. So, we must be careful not to let this bother us.
+ *
* Interrupts are still masked (by default) whenever we are exchanging
* data/cmds with a drive, because some drives seem to have very poor
* tolerance for latency during I/O. For devices which don't suffer from
- * this problem (most don't), the ide_dev[][].unmask flag can be set to permit
- * other interrupts during data/cmd transfers by using the "hdparm" utility.
+ * this problem (most don't), the unmask flag can be set using the "hdparm"
+ * utility, to permit other interrupts during data/cmd transfers.
*/
-static void do_request (byte hwif)
+void ide_do_request (ide_hwgroup_t *hwgroup)
{
- unsigned int minor, drive;
- unsigned long block, blockend;
- struct request *rq;
- ide_dev_t *dev;
-repeat:
- sti();
-#if SUPPORT_SHARING_IRQ
- current_hwif = hwif; /* used *only* when single_threaded==1 */
-#endif /* SUPPORT_SHARING_IRQ */
- if ((rq = ide_cur_rq[HWIF]) == NULL) {
- rq = blk_dev[ide_major[HWIF]].current_request;
- if ((rq == NULL) || (rq->dev < 0)) {
-#if SUPPORT_SHARING_IRQ
- if (single_threaded) {
- if (sharing_single_irq && (dev = ide_cur_dev[hwif])) /* disable irq */
- OUT_BYTE(dev->ctl|2,HD_CMD);
- rq = blk_dev[ide_major[hwif^=1]].current_request;
- if ((rq != NULL) && (rq->dev >= 0))
- goto repeat;
- }
-#endif /* SUPPORT_SHARING_IRQ */
- return;
- }
- blk_dev[ide_major[HWIF]].current_request = rq->next;
- ide_cur_rq[HWIF] = rq;
- }
-#ifdef DEBUG
- printk("%s: do_request: current=0x%08lx\n",ide_name[HWIF],(unsigned long)rq);
-#endif
- minor = MINOR(rq->dev);
- drive = minor >> PARTN_BITS;
- ide_cur_dev[HWIF] = dev = &ide_dev[HWIF][drive];
- if ((MAJOR(rq->dev) != ide_major[HWIF]) || (drive >= MAX_DRIVES)) {
- printk("%s: bad device number: 0x%04x\n", ide_name[HWIF], rq->dev);
- end_request(0, HWIF);
- goto repeat;
- }
- if (rq->bh && !rq->bh->b_lock) {
- printk("%s: block not locked\n", ide_name[HWIF]);
- end_request(0, HWIF);
- goto repeat;
- }
- block = rq->sector;
- blockend = block + rq->nr_sectors;
- if ((blockend < block) || (blockend > ide_hd[HWIF][minor].nr_sects)) {
- printk("%s: bad access: block=%ld, count=%ld\n",
- dev->name, block, rq->nr_sectors);
- end_request(0, HWIF);
- goto repeat;
+ cli(); /* paranoia */
+ if (hwgroup->handler != NULL) {
+ printk("%s: EEeekk!! handler not NULL in ide_do_request()\n", hwgroup->hwif->name);
+ return;
}
- block += ide_hd[HWIF][minor].start_sect;
-#if (DISK_RECOVERY_TIME > 0)
- while ((read_timer() - ide_lastreq[HWIF]) < DISK_RECOVERY_TIME);
-#endif
- OUT_BYTE(dev->select.all,HD_CURRENT);
-#ifdef CONFIG_BLK_DEV_IDECD
- WAIT_STAT(dev, (dev->type == cdrom) ? 0 : READY_STAT,
- BUSY_STAT|DRQ_STAT, WAIT_READY, "DRDY", repeat);
-#else
- WAIT_STAT(dev, READY_STAT, BUSY_STAT|DRQ_STAT, WAIT_READY, "DRDY", repeat);
-#endif /* CONFIG_BLK_DEV_IDECD */
- if (!dev->special.all) {
-#ifdef CONFIG_BLK_DEV_IDECD
- if (dev->type == disk) {
-#endif /* CONFIG_BLK_DEV_IDECD */
- if (do_rw_disk(dev, rq, block))
- goto repeat;
-#ifdef CONFIG_BLK_DEV_IDECD
- } else {
- if (do_rw_cdrom(dev, block))
- goto repeat;
+ do {
+ ide_hwif_t *hwif = hwgroup->hwif;
+ struct request *rq;
+ if ((rq = hwgroup->rq) == NULL) {
+ if (hwif->sharing_irq && hwgroup->drive) /* set nIEN */
+ OUT_BYTE(hwgroup->drive->ctl|2,hwif->io_ports[IDE_CONTROL_OFFSET]);
+ /*
+ * hwgroup->next_hwif is different from hwgroup->hwif
+ * only when a request is inserted using "ide_next".
+ * This saves wear and tear on IDE tapes.
+ */
+ hwif = hwgroup->next_hwif;
+ do {
+ rq = blk_dev[hwif->major].current_request;
+ if (rq != NULL && rq->rq_status != RQ_INACTIVE)
+ goto got_rq;
+ } while ((hwif = hwif->next) != hwgroup->next_hwif);
+ ide_release_lock(&ide_lock);
+ return; /* no work left for this hwgroup */
}
-#endif /* CONFIG_BLK_DEV_IDECD */
- } else {
- if (do_special(dev))
- goto repeat;
- }
+ got_rq:
+ do_request(hwgroup->hwif = hwgroup->next_hwif = hwif, hwgroup->rq = rq);
+ cli();
+ } while (hwgroup->handler == NULL);
}
/*
- * This is a macro rather than an inline function to
- * prevent gcc from over-optimizing accesses to current_hwif,
- * which may have a different value on exit from do_request().
+ * do_hwgroup_request() invokes ide_do_request() after first masking
+ * all possible interrupts for the current hwgroup. This prevents race
+ * conditions in the event that an unexpected interrupt occurs while
+ * we are in the driver.
+ *
+ * Note that when an interrupt is used to reenter the driver, the first level
+ * handler will already have masked the irq that triggered, but any other ones
+ * for the hwgroup will still be unmasked. The driver tries to be careful
+ * about such things.
*/
-#define DO_IDE_REQUEST(hwif) \
-{ \
- if (ide_handler[hwif] == NULL) { \
- disable_irq(ide_irq[hwif]); \
- if (single_threaded && ide_irq[hwif] != ide_irq[hwif^1]) \
- disable_irq(ide_irq[hwif^1]); \
- do_request(hwif); \
- cli(); \
- start_ide_timer(hwif); \
- enable_irq(ide_irq[hwif]); \
- if (single_threaded && ide_irq[hwif] != ide_irq[hwif^1]) \
- enable_irq(ide_irq[hwif^1]); \
- } \
+static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
+{
+ if (hwgroup->handler == NULL) {
+ ide_hwif_t *hgif = hwgroup->hwif;
+ ide_hwif_t *hwif = hgif;
+
+ ide_get_lock(&ide_lock, ide_intr, hwgroup);
+ do {
+ disable_irq(hwif->irq);
+ } while ((hwif = hwif->next) != hgif);
+ ide_do_request (hwgroup);
+ do {
+ enable_irq(hwif->irq);
+ } while ((hwif = hwif->next) != hgif);
+ }
}
-#if SUPPORT_TWO_INTERFACES
-static void do_ide0_request (void) /* invoked with cli() */
+void do_ide0_request (void) /* invoked with cli() */
{
- DO_IDE_REQUEST(0);
+ do_hwgroup_request (ide_hwifs[0].hwgroup);
}
-static void do_ide1_request (void) /* invoked with cli() */
+#if MAX_HWIFS > 1
+void do_ide1_request (void) /* invoked with cli() */
{
- DO_IDE_REQUEST(1);
+ do_hwgroup_request (ide_hwifs[1].hwgroup);
}
-#else
-#define do_ide1_request do_ide0_request
-static void do_ide0_request (void) /* invoked with cli() */
+#endif /* MAX_HWIFS > 1 */
+
+#if MAX_HWIFS > 2
+void do_ide2_request (void) /* invoked with cli() */
{
- DO_IDE_REQUEST(HWIF);
+ do_hwgroup_request (ide_hwifs[2].hwgroup);
}
-#endif /* SUPPORT_TWO_INTERFACES */
+#endif /* MAX_HWIFS > 2 */
-#if SUPPORT_SHARING_IRQ
-static void do_shared_request (void) /* invoked with cli() */
+#if MAX_HWIFS > 3
+void do_ide3_request (void) /* invoked with cli() */
{
- DO_IDE_REQUEST(current_hwif);
+ do_hwgroup_request (ide_hwifs[3].hwgroup);
+}
+#endif /* MAX_HWIFS > 3 */
+
+void ide_timer_expiry (unsigned long data)
+{
+ ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
+ ide_drive_t *drive = hwgroup->drive;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (hwgroup->poll_timeout != 0) { /* polling in progress? */
+ ide_handler_t *handler = hwgroup->handler;
+ hwgroup->handler = NULL;
+ handler(drive);
+ } else if (hwgroup->handler == NULL) { /* not waiting for anything? */
+ ide_sti(); /* drive must have responded just as the timer expired */
+ printk("%s: marginal timeout\n", drive->name);
+ } else {
+ hwgroup->handler = NULL; /* abort the operation */
+ if (hwgroup->hwif->dmaproc)
+ (void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
+ ide_error(drive, "irq timeout", GET_STAT());
+ }
+ if (hwgroup->handler == NULL)
+ do_hwgroup_request (hwgroup);
+ restore_flags(flags);
}
-#endif /* SUPPORT_SHARING_IRQ */
/*
* There's nothing really useful we can do with an unexpected interrupt,
@@ -1255,151 +1299,198 @@ static void do_shared_request (void) /* invoked with cli() */
* On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
* drive enters "idle", "standby", or "sleep" mode, so if the status looks
* "good", we just ignore the interrupt completely.
+ *
+ * This routine assumes cli() is in effect when called.
+ *
+ * If an unexpected interrupt happens on irq15 while we are handling irq14
+ * and if the two interfaces are "serialized" (CMD640), then it looks like
+ * we could screw up by interfering with a new request being set up for irq15.
+ *
+ * In reality, this is a non-issue. The new command is not sent unless the
+ * drive is ready to accept one, in which case we know the drive is not
+ * trying to interrupt us. And ide_set_handler() is always invoked before
+ * completing the issuance of any new drive command, so we will not be
+ * accidently invoked as a result of any valid command completion interrupt.
+ *
*/
-static void unexpected_intr (byte hwif)
+static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
{
byte stat;
+ unsigned int unit;
+ ide_hwif_t *hwif = hwgroup->hwif;
- if (!OK_STAT(stat=GET_STAT(HWIF), DRIVE_READY, BAD_STAT))
- (void) dump_status(HWIF, "unexpected_intr", stat);
- outb_p(2,IDE_PORT(HD_CMD,hwif)); /* disable device irq */
-#if SUPPORT_SHARING_IRQ
- if (single_threaded && ide_irq[hwif] == ide_irq[hwif^1]) {
- if (!OK_STAT(stat=GET_STAT(hwif^1), DRIVE_READY, BAD_STAT))
- (void) dump_status(hwif^1, "unexpected_intr", stat);
- outb_p(2,IDE_PORT(HD_CMD,hwif^1)); /* disable device irq */
- }
-#endif /* SUPPORT_SHARING_IRQ */
+ /*
+ * handle the unexpected interrupt
+ */
+ do {
+ if (hwif->irq == irq) {
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (!drive->present)
+ continue;
+ SELECT_DRIVE(hwif,drive);
+ udelay(100); /* Ugly, but wait_stat() may not be safe here */
+ if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT)) {
+ /* Try to not flood the console with msgs */
+ static unsigned long last_msgtime = 0;
+ if ((last_msgtime + (HZ/2)) < jiffies) {
+ last_msgtime = jiffies;
+ (void) ide_dump_status(drive, "unexpected_intr", stat);
+ }
+ }
+ if ((stat & DRQ_STAT))
+ try_to_flush_leftover_data(drive);
+ }
+ }
+ } while ((hwif = hwif->next) != hwgroup->hwif);
+ SELECT_DRIVE(hwif,hwgroup->drive); /* Ugh.. probably interrupts current I/O */
+ udelay(100); /* Ugly, but wait_stat() may not be safe here */
}
/*
- * This is a macro rather than an inline function to
- * prevent gcc from over-optimizing accesses to current_hwif,
- * which may have a different value on exit from handler().
+ * entry point for all interrupts, caller does cli() for us
*/
-#define IDE_INTR(hwif) \
-{ \
- ide_dev_t *dev; \
- void (*handler)(ide_dev_t *); \
- \
- timer_active &= ~ide_timerbit[hwif]; \
- if ((handler = ide_handler[hwif]) != NULL) { \
- ide_handler[hwif] = NULL; \
- dev = ide_cur_dev[hwif]; \
- if (dev->unmask) \
- sti(); \
- handler(dev); \
- } else \
- unexpected_intr(hwif); \
- cli(); \
-}
-
-#if SUPPORT_SERIALIZE
-/* entry point for all interrupts when single_threaded==1 */
-static void ide_seq_intr (int irq, struct pt_regs *regs)
+void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
{
- byte hwif = (irq != ide_irq[0]);
- IDE_INTR(HWIF);
- start_ide_timer(current_hwif);
-}
-#endif /* SUPPORT_SERIALIZE */
-
-#if OPTIMIZE_IRQS
-
-/* entry point for all interrupts on ide0 when single_threaded==0 */
-static void ide0_intr (int irq, struct pt_regs *regs)
-{
- IDE_INTR(0);
- start_ide_timer(0);
-}
-
-/* entry point for all interrupts on ide1 when single_threaded==0 */
-static void ide1_intr (int irq, struct pt_regs *regs)
-{
- IDE_INTR(1);
- start_ide_timer(1);
-}
-
-#else /* OPTIMIZE_IRQS */
+ ide_hwgroup_t *hwgroup = dev_id;
+ ide_handler_t *handler;
-#define ide0_intr ide_intr
-#define ide1_intr ide_intr
-
-/* entry point for all interrupts when single_threaded==0 */
-static void ide_intr (int irq, struct pt_regs *regs)
-{
-#if SUPPORT_TWO_INTERFACES
- byte hwif = (irq != ide_irq[0]);
-#endif /* SUPPORT_TWO_INTERFACES */
- IDE_INTR(HWIF);
- start_ide_timer(HWIF);
-}
-
-#endif /* OPTIMIZE_IRQS */
+ if (!ide_ack_intr (hwgroup->hwif->io_ports[IDE_DATA_OFFSET],
+ hwgroup->hwif->io_ports[IDE_IRQ_OFFSET]))
+ return;
-#if SUPPORT_SHARING_IRQ
-/* entry point for all interrupts on ide0/ide1 when sharing_single_irq==1 */
-static void ide_shared_intr (int irq, struct pt_regs * regs)
-{
- IDE_INTR(current_hwif);
- start_ide_timer(current_hwif);
+ if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
+ ide_drive_t *drive = hwgroup->drive;
+ hwgroup->handler = NULL;
+ del_timer(&(hwgroup->timer));
+ if (drive->unmask)
+ ide_sti();
+ handler(drive);
+ cli(); /* this is necessary, as next rq may be different irq */
+ if (hwgroup->handler == NULL) {
+ SET_RECOVERY_TIMER(HWIF(drive));
+ ide_do_request(hwgroup);
+ }
+ } else {
+ unexpected_intr(irq, hwgroup);
+ }
+ cli();
}
-#endif /* SUPPORT_SHARING_IRQ */
-static ide_dev_t *get_info_ptr (int i_rdev)
-{
- unsigned int drive = DEVICE_NR(i_rdev);
- ide_dev_t *dev;
-
- if (drive < MAX_DRIVES) {
- switch (MAJOR(i_rdev)) {
- case IDE0_MAJOR: dev = &ide_dev[0][drive];
- if (dev->present) return dev;
- break;
- case IDE1_MAJOR: dev = &ide_dev[1][drive];
- if (dev->present) return dev;
- break;
+/*
+ * get_info_ptr() returns the (ide_drive_t *) for a given device number.
+ * It returns NULL if the given device number does not match any present drives.
+ */
+static ide_drive_t *get_info_ptr (kdev_t i_rdev)
+{
+ int major = MAJOR(i_rdev);
+ unsigned int h;
+
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if (hwif->present && major == hwif->major) {
+ unsigned unit = DEVICE_NR(i_rdev);
+ if (unit < MAX_DRIVES) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present)
+ return drive;
+ } else if (major == IDE0_MAJOR && unit < 4) {
+ printk("ide: probable bad entry for /dev/hd%c\n", 'a'+unit);
+ printk("ide: to fix it, run: /usr/src/linux/scripts/MAKEDEV.ide\n");
+ }
+ break;
}
}
return NULL;
}
-static int ide_open(struct inode * inode, struct file * filp)
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd (struct request *rq)
{
- ide_dev_t *dev;
- unsigned long flags;
-
- if ((dev = get_info_ptr(inode->i_rdev)) == NULL)
- return -ENODEV;
- save_flags(flags);
- cli();
- while (dev->busy)
- sleep_on(&dev->wqueue);
- dev->usage++;
- restore_flags(flags);
-#ifdef CONFIG_BLK_DEV_IDECD
- if (dev->type == cdrom)
- return cdrom_open (inode, filp, dev);
-#endif /* CONFIG_BLK_DEV_IDECD */
- return 0;
+ rq->buffer = NULL;
+ rq->cmd = IDE_DRIVE_CMD;
+ rq->sector = 0;
+ rq->nr_sectors = 0;
+ rq->current_nr_sectors = 0;
+ rq->sem = NULL;
+ rq->bh = NULL;
+ rq->bhtail = NULL;
+ rq->next = NULL;
}
/*
- * Releasing a block device means we sync() it, so that it can safely
- * be forgotten about...
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed. This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed. This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed. As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the
+ * request queue, and the function returns immediately without waiting
+ * for the new rq to be completed. This is again intended for careful
+ * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
+ * when operating in the pipelined operation mode).
*/
-static void ide_release(struct inode * inode, struct file * file)
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
{
- ide_dev_t *dev;
+ unsigned long flags;
+ unsigned int major = HWIF(drive)->major;
+ struct request *cur_rq;
+ struct blk_dev_struct *bdev = &blk_dev[major];
+ struct semaphore sem = MUTEX_LOCKED;
- if ((dev = get_info_ptr(inode->i_rdev)) != NULL) {
- sync_dev(inode->i_rdev);
- dev->usage--;
-#ifdef CONFIG_BLK_DEV_IDECD
- if (dev->type == cdrom)
- cdrom_release (inode, file, dev);
-#endif /* CONFIG_BLK_DEV_IDECD */
+ if (IS_PROMISE_DRIVE && rq->buffer != NULL)
+ return -ENOSYS; /* special drive cmds not supported */
+ rq->errors = 0;
+ rq->rq_status = RQ_ACTIVE;
+ rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
+ if (action == ide_wait)
+ rq->sem = &sem;
+ unplug_device(bdev);
+
+ save_flags(flags);
+ cli();
+ if (action == ide_next)
+ HWGROUP(drive)->next_hwif = HWIF(drive);
+ cur_rq = bdev->current_request;
+
+ if (cur_rq == NULL || action == ide_preempt) {
+ rq->next = cur_rq;
+ bdev->current_request = rq;
+ if (action == ide_preempt) {
+ HWGROUP(drive)->rq = NULL;
+ } else
+ if (HWGROUP(drive)->rq == NULL) { /* is this necessary (?) */
+ bdev->request_fn();
+ cli();
+ }
+ } else {
+ if (action == ide_wait || action == ide_end) {
+ while (cur_rq->next != NULL) /* find end of list */
+ cur_rq = cur_rq->next;
+ }
+ rq->next = cur_rq->next;
+ cur_rq->next = rq;
}
+ if (action == ide_wait && rq->rq_status != RQ_INACTIVE)
+ down(&sem); /* wait for it to be serviced */
+ restore_flags(flags);
+ return rq->errors ? -EIO : 0; /* return -EIO if errors */
}
/*
@@ -1410,278 +1501,492 @@ static void ide_release(struct inode * inode, struct file * file)
* usage == 1 (we need an open channel to use an ioctl :-), so this
* is our limit.
*/
-static int revalidate_disk(int i_rdev)
+int ide_revalidate_disk(kdev_t i_rdev)
{
- unsigned int i, major, start, drive = DEVICE_NR(i_rdev);
- ide_dev_t *dev;
- struct gendisk *gd;
+ ide_drive_t *drive;
+ unsigned int p, major, minor;
long flags;
- if ((dev = get_info_ptr(i_rdev)) == NULL)
+ if ((drive = get_info_ptr(i_rdev)) == NULL)
return -ENODEV;
-
+ major = MAJOR(i_rdev);
+ minor = drive->select.b.unit << PARTN_BITS;
save_flags(flags);
cli();
- if (dev->busy || (dev->usage > 1)) {
+ if (drive->busy || (drive->usage > 1)) {
restore_flags(flags);
return -EBUSY;
};
- dev->busy = 1;
+ drive->busy = 1;
+ MOD_INC_USE_COUNT;
restore_flags(flags);
- gd = &ide_gendisk[DEV_HWIF];
- major = ide_major[DEV_HWIF] << 8;
- start = drive << PARTN_BITS;
-
- for (i = 0; i < (1<<PARTN_BITS); i++) {
- unsigned int minor = start + i;
- sync_dev (major | minor);
- invalidate_inodes (major | minor);
- invalidate_buffers (major | minor);
- gd->part[minor].start_sect = 0;
- gd->part[minor].nr_sects = 0;
+ for (p = 0; p < (1<<PARTN_BITS); ++p) {
+ if (drive->part[p].nr_sects > 0) {
+ kdev_t devp = MKDEV(major, minor+p);
+ fsync_dev (devp);
+ invalidate_inodes (devp);
+ invalidate_buffers (devp);
+ }
+ drive->part[p].start_sect = 0;
+ drive->part[p].nr_sects = 0;
};
- gd->part[start].nr_sects = ide_capacity[DEV_HWIF][drive];
- resetup_one_dev(gd, drive);
+ drive->part[0].nr_sects = current_capacity(drive);
+ if (drive->media != ide_disk || drive->driver == NULL)
+ drive->part[0].start_sect = -1;
+ resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
- dev->busy = 0;
- wake_up(&dev->wqueue);
+ drive->busy = 0;
+ wake_up(&drive->wqueue);
+ MOD_DEC_USE_COUNT;
return 0;
}
-#ifdef IDE_DRIVE_CMD
+static void revalidate_drives (void)
+{
+ ide_hwif_t *hwif;
+ ide_drive_t *drive;
+ int index, unit;
+
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ drive = &ide_hwifs[index].drives[unit];
+ if (drive->revalidate) {
+ drive->revalidate = 0;
+ if (!initializing)
+ (void) ide_revalidate_disk(MKDEV(hwif->major, unit<<PARTN_BITS));
+ }
+ }
+ }
+}
+
+static void ide_init_module (int type)
+{
+ ide_module_t *module = ide_modules;
+
+ while (module) {
+ if (module->type == type)
+ (void) module->init();
+ module = module->next;
+ }
+ revalidate_drives();
+}
+
+static int ide_open(struct inode * inode, struct file * filp)
+{
+ ide_drive_t *drive;
+ int rc;
+
+ if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+ return -ENXIO;
+ MOD_INC_USE_COUNT;
+ if (drive->driver == NULL)
+ ide_init_module(IDE_DRIVER_MODULE);
+#ifdef CONFIG_KERNELD
+ if (drive->driver == NULL) {
+ if (drive->media == ide_disk)
+ (void) request_module("ide-disk");
+ if (drive->media == ide_cdrom)
+ (void) request_module("ide-cd");
+ if (drive->media == ide_tape)
+ (void) request_module("ide-tape");
+ if (drive->media == ide_floppy)
+ (void) request_module("ide-floppy");
+ }
+#endif /* CONFIG_KERNELD */
+ while (drive->busy)
+ sleep_on(&drive->wqueue);
+ drive->usage++;
+ if (drive->driver != NULL) {
+ if ((rc = DRIVER(drive)->open(inode, filp, drive)))
+ MOD_DEC_USE_COUNT;
+ return rc;
+ }
+ printk ("%s: driver not present\n", drive->name);
+ drive->usage--;
+ MOD_DEC_USE_COUNT;
+ return -ENXIO;
+}
+
/*
- * This function issues a specific IDE drive command onto the
- * tail of the request queue, and waits for it to be completed.
- * If arg is NULL, it goes through all the motions,
- * but without actually sending a command to the drive.
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
*/
-static int do_drive_cmd(int dev, char *args)
+static void ide_release(struct inode * inode, struct file * file)
{
- unsigned long flags;
- unsigned int major = MAJOR(dev);
- struct request rq, *cur_rq;
- struct blk_dev_struct *bdev;
- struct semaphore sem = MUTEX_LOCKED;
+ ide_drive_t *drive;
+
+ if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
+ fsync_dev(inode->i_rdev);
+ drive->usage--;
+ if (drive->driver != NULL)
+ DRIVER(drive)->release(inode, file, drive);
+ MOD_DEC_USE_COUNT;
+ }
+}
- /* build up a special request, and add it to the queue */
- rq.buffer = args;
- rq.cmd = IDE_DRIVE_CMD;
- rq.errors = 0;
- rq.sector = 0;
- rq.nr_sectors = 0;
- rq.current_nr_sectors = 0;
- rq.sem = &sem;
- rq.bh = NULL;
- rq.bhtail = NULL;
- rq.next = NULL;
- rq.dev = dev;
- bdev = &blk_dev[major];
+void ide_unregister (unsigned int index)
+{
+ struct gendisk *gd, **gdp;
+ ide_drive_t *drive;
+ ide_hwif_t *hwif, *g;
+ ide_hwgroup_t *hwgroup;
+ int irq_count = 0, unit;
+ unsigned long flags;
+ if (index >= MAX_HWIFS)
+ return;
save_flags(flags);
cli();
- cur_rq = bdev->current_request;
- if (cur_rq == NULL) { /* empty request list? */
- bdev->current_request = &rq; /* service ours immediately */
- bdev->request_fn();
- } else {
- while (cur_rq->next != NULL) /* find end of request list */
- cur_rq = cur_rq->next;
- cur_rq->next = &rq; /* add rq to the end */
+ hwif = &ide_hwifs[index];
+ if (!hwif->present)
+ goto abort;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ drive = &hwif->drives[unit];
+ if (!drive->present)
+ continue;
+ if (drive->busy || drive->usage)
+ goto abort;
+ if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
+ goto abort;
+ if (drive->id != NULL) {
+ kfree(drive->id);
+ drive->id = NULL;
+ }
+ drive->present = 0;
}
+ hwif->present = 0;
+ hwgroup = hwif->hwgroup;
+
+ /*
+ * free the irq if we were the only hwif using it
+ */
+ g = hwgroup->hwif;
+ do {
+ if (g->irq == hwif->irq)
+ ++irq_count;
+ g = g->next;
+ } while (g != hwgroup->hwif);
+ if (irq_count == 1)
+ free_irq(hwif->irq, hwgroup);
+
+ /*
+ * Note that we only release the standard ports,
+ * and do not even try to handle any extra ports
+ * allocated for weird IDE interface chipsets.
+ */
+ ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8);
+ ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
- down(&sem); /* wait for it to be serviced */
+ /*
+ * Remove us from the hwgroup, and free
+ * the hwgroup if we were the only member
+ */
+ while (hwgroup->hwif->next != hwif)
+ hwgroup->hwif = hwgroup->hwif->next;
+ hwgroup->hwif->next = hwif->next;
+ if (hwgroup->hwif == hwif)
+ hwgroup->hwif = hwif->next;
+ if (hwgroup->next_hwif == hwif)
+ hwgroup->next_hwif = hwif->next;
+ if (hwgroup->hwif == hwif)
+ kfree(hwgroup);
+
+ /*
+ * Remove us from the kernel's knowledge
+ */
+ unregister_blkdev(hwif->major, hwif->name);
+ kfree(blksize_size[hwif->major]);
+ blk_dev[hwif->major].request_fn = NULL;
+ blksize_size[hwif->major] = NULL;
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+ if (*gdp == hwif->gd)
+ break;
+ if (*gdp == NULL)
+ printk("gd not in disk chain!\n");
+ else {
+ gd = *gdp; *gdp = gd->next;
+ kfree(gd->sizes);
+ kfree(gd->part);
+ kfree(gd);
+ }
+ init_hwif_data (index); /* restore hwif data to pristine status */
+abort:
restore_flags(flags);
- return rq.errors ? -EIO : 0; /* return -EIO if errors */
}
-#endif /* IDE_DRIVE_CMD */
-static int write_fs_long (unsigned long useraddr, long value)
+int ide_register (int arg1, int arg2, int irq)
{
- int err;
+ int index, retry = 1;
+ ide_hwif_t *hwif;
+ ide_ioreg_t data_port = (ide_ioreg_t) arg1, ctl_port = (ide_ioreg_t) arg2;
- if (NULL == (long *)useraddr)
- return -EINVAL;
- if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long))))
- return err;
- put_fs_long((unsigned)value, (long *) useraddr);
- return 0;
+ do {
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ if (hwif->io_ports[IDE_DATA_OFFSET] == data_port)
+ goto found;
+ }
+ for (index = 0; index < MAX_HWIFS; ++index) {
+ hwif = &ide_hwifs[index];
+ if (!hwif->present) {
+ ide_init_hwif_ports(hwif->io_ports, data_port, &hwif->irq);
+ if (ctl_port)
+ hwif->io_ports[IDE_CONTROL_OFFSET] = ctl_port;
+ hwif->irq = irq;
+ goto found;
+ }
+ }
+ for (index = 0; index < MAX_HWIFS; index++)
+ ide_unregister(index);
+ } while (retry--);
+ return -1;
+found:
+ if (hwif->present)
+ ide_unregister(index);
+ if (hwif->present)
+ return -1;
+ hwif->noprobe = 0;
+ ide_init_module(IDE_PROBE_MODULE);
+ ide_init_module(IDE_DRIVER_MODULE);
+ return hwif->present ? index : -1;
}
static int ide_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct hd_geometry *loc = (struct hd_geometry *) arg;
int err;
- ide_dev_t *dev;
+ ide_drive_t *drive;
unsigned long flags;
+ struct request rq;
- if (!inode || !inode->i_rdev)
+ if (!inode || !(inode->i_rdev))
return -EINVAL;
- if ((dev = get_info_ptr(inode->i_rdev)) == NULL)
+ if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
return -ENODEV;
+ ide_init_drive_cmd (&rq);
switch (cmd) {
case HDIO_GETGEO:
- if (!loc || dev->type != disk) return -EINVAL;
- err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
- if (err) return err;
- put_fs_byte(dev->bios_head,
- (char *) &loc->heads);
- put_fs_byte(dev->bios_sect,
- (char *) &loc->sectors);
- put_fs_word(dev->bios_cyl,
- (short *) &loc->cylinders);
- put_fs_long((unsigned)ide_hd[DEV_HWIF][MINOR(inode->i_rdev)].start_sect,
- (long *) &loc->start);
+ {
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+ if (!loc || drive->media != ide_disk) return -EINVAL;
+ if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
+ if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
+ if (put_user(drive->bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
+ if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+ (unsigned long *) &loc->start)) return -EFAULT;
return 0;
-
+ }
case BLKFLSBUF:
- if(!suser()) return -EACCES;
+ if (!suser()) return -EACCES;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
case BLKRASET:
- if(!suser()) return -EACCES;
+ if (!suser()) return -EACCES;
if(arg > 0xff) return -EINVAL;
read_ahead[MAJOR(inode->i_rdev)] = arg;
return 0;
case BLKRAGET:
- return write_fs_long(arg, read_ahead[MAJOR(inode->i_rdev)]);
+ return put_user(read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
+
+ case BLKGETSIZE: /* Return device size */
+ return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
- case BLKGETSIZE: /* Return device size */
- return write_fs_long(arg, ide_hd[DEV_HWIF][MINOR(inode->i_rdev)].nr_sects);
case BLKRRPART: /* Re-read partition tables */
- return revalidate_disk(inode->i_rdev);
+ if (!suser()) return -EACCES;
+ return ide_revalidate_disk(inode->i_rdev);
+
+ case HDIO_GET_KEEPSETTINGS:
+ return put_user(drive->keep_settings, (long *) arg);
- case HDIO_GET_KEEPSETTINGS:
- return write_fs_long(arg, dev->keep_settings);
+ case HDIO_GET_UNMASKINTR:
+ return put_user(drive->unmask, (long *) arg);
- case HDIO_GET_UNMASKINTR:
- return write_fs_long(arg, dev->unmask);
+ case HDIO_GET_DMA:
+ return put_user(drive->using_dma, (long *) arg);
- case HDIO_GET_CHIPSET:
- return write_fs_long(arg, dev->chipset);
+ case HDIO_GET_32BIT:
+ return put_user(drive->io_32bit, (long *) arg);
- case HDIO_GET_MULTCOUNT:
- return write_fs_long(arg, dev->mult_count);
+ case HDIO_GET_MULTCOUNT:
+ return put_user(drive->mult_count, (long *) arg);
case HDIO_GET_IDENTITY:
- if (!arg || (MINOR(inode->i_rdev) & PARTN_MASK))
+ if (MINOR(inode->i_rdev) & PARTN_MASK)
return -EINVAL;
- if (dev->id == NULL)
+ if (drive->id == NULL)
return -ENOMSG;
- err = verify_area(VERIFY_WRITE, (char *)arg, sizeof(*dev->id));
- if (err) return err;
- memcpy_tofs((char *)arg, (char *)dev->id, sizeof(*dev->id));
+ if (copy_to_user((char *)arg, (char *)drive->id, sizeof(*drive->id)))
+ return -EFAULT;
return 0;
+ case HDIO_GET_NOWERR:
+ return put_user(drive->bad_wstat == BAD_R_STAT, (long *) arg);
+
+ case HDIO_SET_DMA:
+ if (!suser()) return -EACCES;
+ if (drive->driver != NULL && !DRIVER(drive)->supports_dma)
+ return -EPERM;
+ if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
+ return -EPERM;
case HDIO_SET_KEEPSETTINGS:
case HDIO_SET_UNMASKINTR:
+ case HDIO_SET_NOWERR:
+ if (arg > 1)
+ return -EINVAL;
+ case HDIO_SET_32BIT:
if (!suser()) return -EACCES;
- if ((arg > 1) || (MINOR(inode->i_rdev) & PARTN_MASK))
+ if ((MINOR(inode->i_rdev) & PARTN_MASK))
return -EINVAL;
save_flags(flags);
cli();
- if (cmd == HDIO_SET_KEEPSETTINGS)
- dev->keep_settings = arg;
- else
- dev->unmask = arg;
+ switch (cmd) {
+ case HDIO_SET_DMA:
+ if (!(HWIF(drive)->dmaproc)) {
+ restore_flags(flags);
+ return -EPERM;
+ }
+ drive->using_dma = arg;
+ break;
+ case HDIO_SET_KEEPSETTINGS:
+ drive->keep_settings = arg;
+ break;
+ case HDIO_SET_UNMASKINTR:
+ if (arg && drive->no_unmask) {
+ restore_flags(flags);
+ return -EPERM;
+ }
+ drive->unmask = arg;
+ break;
+ case HDIO_SET_NOWERR:
+ drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
+ break;
+ case HDIO_SET_32BIT:
+ if (arg > (1 + (SUPPORT_VLB_SYNC<<1))) {
+ restore_flags(flags);
+ return -EINVAL;
+ }
+ if (arg && drive->no_io_32bit) {
+ restore_flags(flags);
+ return -EPERM;
+ }
+ drive->io_32bit = arg;
+#ifdef CONFIG_BLK_DEV_DTC2278
+ if (HWIF(drive)->chipset == ide_dtc2278)
+ HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+ break;
+ }
restore_flags(flags);
return 0;
- case HDIO_SET_CHIPSET:
+ case HDIO_SET_MULTCOUNT:
if (!suser()) return -EACCES;
- if ((arg > 3) || (MINOR(inode->i_rdev) & PARTN_MASK))
+ if (MINOR(inode->i_rdev) & PARTN_MASK)
+ return -EINVAL;
+ if (drive->id && arg > drive->id->max_multsect)
return -EINVAL;
save_flags(flags);
cli();
- dev->chipset = arg;
- dev->vlb_sync = (arg & 2) >> 1;
- dev->vlb_32bit = (arg & 1);
+ if (drive->special.b.set_multmode) {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ drive->mult_req = arg;
+ drive->special.b.set_multmode = 1;
restore_flags(flags);
- return 0;
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
+ return (drive->mult_count == arg) ? 0 : -EIO;
- case HDIO_SET_MULTCOUNT:
+ case HDIO_DRIVE_CMD:
+ {
+ byte args[4], *argbuf = args;
+ int argsize = 4;
+ if (!suser()) return -EACCES;
+ if (NULL == (void *) arg)
+ return ide_do_drive_cmd(drive, &rq, ide_wait);
+ if (copy_from_user(args, (void *)arg, 4))
+ return -EFAULT;
+ if (args[3]) {
+ argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
+ argbuf = kmalloc(argsize, GFP_KERNEL);
+ if (argbuf == NULL)
+ return -ENOMEM;
+ memcpy(argbuf, args, 4);
+ }
+ rq.buffer = argbuf;
+ err = ide_do_drive_cmd(drive, &rq, ide_wait);
+ if (copy_to_user((void *)arg, argbuf, argsize))
+ err = -EFAULT;
+ if (argsize > 4)
+ kfree(argbuf);
+ return err;
+ }
+ case HDIO_SET_PIO_MODE:
if (!suser()) return -EACCES;
if (MINOR(inode->i_rdev) & PARTN_MASK)
return -EINVAL;
- if ((dev->id != NULL) && (arg > dev->id->max_multsect))
- return -EINVAL;
+ if (!HWIF(drive)->tuneproc)
+ return -ENOSYS;
save_flags(flags);
cli();
- if (dev->special.b.set_multmode) {
+ if (drive->special.b.set_tune) {
restore_flags(flags);
return -EBUSY;
}
- dev->mult_req = arg;
- dev->special.b.set_multmode = 1;
+ drive->tune_req = (byte) arg;
+ drive->special.b.set_tune = 1;
restore_flags(flags);
-#ifdef IDE_DRIVE_CMD
- do_drive_cmd (inode->i_rdev, NULL);
- return (dev->mult_count == arg) ? 0 : -EIO;
-#else
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
return 0;
-#endif /* IDE_DRIVE_CMD */
-#ifdef IDE_DRIVE_CMD
- case HDIO_DRIVE_CMD:
+ case HDIO_SCAN_HWIF:
{
- unsigned long args;
-
- if (NULL == (long *) arg)
- err = do_drive_cmd(inode->i_rdev,NULL);
- else {
- if (!(err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long))))
- {
- args = get_fs_long((long *)arg);
- err = do_drive_cmd(inode->i_rdev,(char *)&args);
- put_fs_long(args,(long *)arg);
- }
- }
- return err;
+ int args[3];
+ if (!suser()) return -EACCES;
+ if (copy_from_user(args, (void *)arg, 3 * sizeof(int)))
+ return -EFAULT;
+ if (ide_register(args[0], args[1], args[2]) == -1)
+ return -EIO;
+ return 0;
}
-#endif /* IDE_DRIVE_CMD */
RO_IOCTLS(inode->i_rdev, arg);
default:
-#ifdef CONFIG_BLK_DEV_IDECD
- if (dev->type == cdrom)
- return ide_cdrom_ioctl(dev, inode, file, cmd, arg);
-#endif /* CONFIG_BLK_DEV_IDECD */
+ if (drive->driver != NULL)
+ return DRIVER(drive)->ioctl(drive, inode, file, cmd, arg);
return -EPERM;
}
}
-#ifdef CONFIG_BLK_DEV_IDECD
-static int ide_check_media_change (dev_t full_dev)
+static int ide_check_media_change (kdev_t i_rdev)
{
- ide_dev_t *dev;
+ ide_drive_t *drive;
- if ((dev = get_info_ptr(full_dev)) == NULL)
+ if ((drive = get_info_ptr(i_rdev)) == NULL)
return -ENODEV;
- if (dev->type != cdrom)
- return 0;
- return cdrom_check_media_change (dev);
+ if (drive->driver != NULL)
+ return DRIVER(drive)->media_change(drive);
+ return 0;
}
-#endif /* CONFIG_BLK_DEV_IDECD */
-
-static void fixstring (byte *s, int bytecount, int byteswap)
+void ide_fixstring (byte *s, const int bytecount, const int byteswap)
{
- byte *p, *end = &s[bytecount &= ~1]; /* bytecount must be even */
+ byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
if (byteswap) {
- /* convert from big-endian to little-endian */
+ /* convert from big-endian to host byte order */
for (p = end ; p != s;) {
unsigned short *pp = (unsigned short *) (p -= 2);
- *pp = (*pp >> 8) | (*pp << 8);
+ *pp = ntohs(*pp);
}
}
- p = s;
/* strip leading blanks */
while (s != end && *s == ' ')
@@ -1698,601 +2003,669 @@ static void fixstring (byte *s, int bytecount, int byteswap)
*p++ = '\0';
}
-static int lba_capacity_is_ok (struct hd_driveid *id)
/*
- * Returns: 1 if lba_capacity looks sensible
- * 0 otherwise
+ * stridx() returns the offset of c within s,
+ * or -1 if c is '\0' or not found within s.
*/
+static int stridx (const char *s, char c)
{
- unsigned long lba_sects = id->lba_capacity;
- unsigned long chs_sects = id->cyls * id->heads * id->sectors;
- unsigned long _10_percent = chs_sects / 10;
+ char *i = strchr(s, c);
+ return (i && c) ? i - s : -1;
+}
- /* perform a rough sanity check on lba_sects: within 10% is "okay" */
- if ((lba_sects - chs_sects) < _10_percent)
- return 1; /* lba_capacity is good */
+/*
+ * match_parm() does parsing for ide_setup():
+ *
+ * 1. the first char of s must be '='.
+ * 2. if the remainder matches one of the supplied keywords,
+ * the index (1 based) of the keyword is negated and returned.
+ * 3. if the remainder is a series of no more than max_vals numbers
+ * separated by commas, the numbers are saved in vals[] and a
+ * count of how many were saved is returned. Base10 is assumed,
+ * and base16 is allowed when prefixed with "0x".
+ * 4. otherwise, zero is returned.
+ */
+static int match_parm (char *s, const char *keywords[], int vals[], int max_vals)
+{
+ static const char *decimal = "0123456789";
+ static const char *hex = "0123456789abcdef";
+ int i, n;
- /* some drives have the word order reversed */
- lba_sects = (lba_sects << 16) | (lba_sects >> 16);
- if ((lba_sects - chs_sects) < _10_percent) {
- id->lba_capacity = lba_sects; /* fix it */
- return 1; /* lba_capacity is (now) good */
+ if (*s++ == '=') {
+ /*
+ * Try matching against the supplied keywords,
+ * and return -(index+1) if we match one
+ */
+ if (keywords != NULL) {
+ for (i = 0; *keywords != NULL; ++i) {
+ if (!strcmp(s, *keywords++))
+ return -(i+1);
+ }
+ }
+ /*
+ * Look for a series of no more than "max_vals"
+ * numeric values separated by commas, in base10,
+ * or base16 when prefixed with "0x".
+ * Return a count of how many were found.
+ */
+ for (n = 0; (i = stridx(decimal, *s)) >= 0;) {
+ vals[n] = i;
+ while ((i = stridx(decimal, *++s)) >= 0)
+ vals[n] = (vals[n] * 10) + i;
+ if (*s == 'x' && !vals[n]) {
+ while ((i = stridx(hex, *++s)) >= 0)
+ vals[n] = (vals[n] * 0x10) + i;
+ }
+ if (++n == max_vals)
+ break;
+ if (*s == ',')
+ ++s;
+ }
+ if (!*s)
+ return n;
}
- return 0; /* lba_capacity value is bad */
+ return 0; /* zero = nothing matched */
}
-static unsigned long probe_mem_start; /* used by drive/irq probing routines */
-
-static void do_identify (ide_dev_t *dev, byte cmd)
+/*
+ * ide_setup() gets called VERY EARLY during initialization,
+ * to handle kernel "command line" strings beginning with "hdx="
+ * or "ide". Here is the complete set currently supported:
+ *
+ * "hdx=" is recognized for all "x" from "a" to "h", such as "hdc".
+ * "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
+ *
+ * "hdx=noprobe" : drive may be present, but do not probe for it
+ * "hdx=none" : drive is NOT present, ignore cmos and do not probe
+ * "hdx=nowerr" : ignore the WRERR_STAT bit on this drive
+ * "hdx=cdrom" : drive is present, and is a cdrom drive
+ * "hdx=cyl,head,sect" : disk drive is present, with specified geometry
+ * "hdx=autotune" : driver will attempt to tune interface speed
+ * to the fastest PIO mode supported,
+ * if possible for this drive only.
+ * Not fully supported by all chipset types,
+ * and quite likely to cause trouble with
+ * older/odd IDE drives.
+ *
+ * "idebus=xx" : inform IDE driver of VESA/PCI bus speed in MHz,
+ * where "xx" is between 20 and 66 inclusive,
+ * used when tuning chipset PIO modes.
+ * For PCI bus, 25 is correct for a P75 system,
+ * 30 is correct for P90,P120,P180 systems,
+ * and 33 is used for P100,P133,P166 systems.
+ * If in doubt, use idebus=33 for PCI.
+ * As for VLB, it is safest to not specify it.
+ *
+ * "idex=noprobe" : do not attempt to access/use this interface
+ * "idex=base" : probe for an interface at the addr specified,
+ * where "base" is usually 0x1f0 or 0x170
+ * and "ctl" is assumed to be "base"+0x206
+ * "idex=base,ctl" : specify both base and ctl
+ * "idex=base,ctl,irq" : specify base, ctl, and irq number
+ * "idex=autotune" : driver will attempt to tune interface speed
+ * to the fastest PIO mode supported,
+ * for all drives on this interface.
+ * Not fully supported by all chipset types,
+ * and quite likely to cause trouble with
+ * older/odd IDE drives.
+ * "idex=noautotune" : driver will NOT attempt to tune interface speed
+ * This is the default for most chipsets,
+ * except the cmd640.
+ * "idex=serialize" : do not overlap operations on idex and ide(x^1)
+ *
+ * The following are valid ONLY on ide0,
+ * and the defaults for the base,ctl ports must not be altered.
+ *
+ * "ide0=dtc2278" : probe/support DTC2278 interface
+ * "ide0=ht6560b" : probe/support HT6560B interface
+ * "ide0=cmd640_vlb" : *REQUIRED* for VLB cards with the CMD640 chip
+ * (not for PCI -- automatically detected)
+ * "ide0=qd6580" : probe/support qd6580 interface
+ * "ide0=ali14xx" : probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
+ * "ide0=umc8672" : probe/support umc8672 chipsets
+ */
+void ide_setup (char *s)
{
- int bswap;
- struct hd_driveid *id;
- unsigned long capacity, check;
+ int i, vals[3];
+ ide_hwif_t *hwif;
+ ide_drive_t *drive;
+ unsigned int hw, unit;
+ const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
+ const char max_hwif = '0' + (MAX_HWIFS - 1);
- id = dev->id = (struct hd_driveid *) probe_mem_start; /* kmalloc() */
- probe_mem_start += 512;
- input_ide_data(dev, id, SECTOR_WORDS); /* read 512 bytes of id info */
- sti();
+ printk("ide_setup: %s", s);
+ init_ide_data ();
/*
- * EATA SCSI controllers do a hardware ATA emulation: ignore them
+ * Look for drive options: "hdx="
*/
- if ((id->model[0] == 'P' && id->model[1] == 'M')
- || (id->model[0] == 'S' && id->model[1] == 'K')) {
- printk("%s: EATA SCSI HBA %.10s\n", dev->name, id->model);
- dev->present = 0;
- return;
+ if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
+ const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom",
+ "serialize", "autotune", "noautotune",
+ "slow", "swapdata", NULL};
+ unit = s[2] - 'a';
+ hw = unit / MAX_DRIVES;
+ unit = unit % MAX_DRIVES;
+ hwif = &ide_hwifs[hw];
+ drive = &hwif->drives[unit];
+ switch (match_parm(&s[3], hd_words, vals, 3)) {
+ case -1: /* "none" */
+ drive->nobios = 1; /* drop into "noprobe" */
+ case -2: /* "noprobe" */
+ drive->noprobe = 1;
+ goto done;
+ case -3: /* "nowerr" */
+ drive->bad_wstat = BAD_R_STAT;
+ hwif->noprobe = 0;
+ goto done;
+ case -4: /* "cdrom" */
+ drive->present = 1;
+ drive->media = ide_cdrom;
+ hwif->noprobe = 0;
+ goto done;
+ case -5: /* "serialize" */
+ printk(" -- USE \"ide%d=serialize\" INSTEAD", hw);
+ goto do_serialize;
+ case -6: /* "autotune" */
+ drive->autotune = 1;
+ goto done;
+ case -7: /* "noautotune" */
+ drive->autotune = 2;
+ goto done;
+ case -8: /* "slow" */
+ drive->slow = 1;
+ goto done;
+ case -9: /* swapdata */
+ drive->bswap = 1;
+ goto done;
+ case 3: /* cyl,head,sect */
+ drive->media = ide_disk;
+ drive->cyl = drive->bios_cyl = vals[0];
+ drive->head = drive->bios_head = vals[1];
+ drive->sect = drive->bios_sect = vals[2];
+ drive->present = 1;
+ drive->forced_geom = 1;
+ hwif->noprobe = 0;
+ goto done;
+ default:
+ goto bad_option;
+ }
}
+ if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e')
+ goto bad_option;
/*
- * WIN_IDENTIFY returns little-endian info,
- * WIN_PIDENTIFY *usually* returns little-endian info.
+ * Look for bus speed option: "idebus="
*/
- bswap = 1;
- if (cmd == WIN_PIDENTIFY) {
- if ((id->model[0] == 'N' && id->model[1] == 'E')
- || (id->model[0] == 'F' && id->model[1] == 'X'))
- bswap = 0; /* NEC and *some* Mitsumi units */
- } /* Vertos drives may still be weird */
- fixstring (id->model, sizeof(id->model), bswap);
- fixstring (id->fw_rev, sizeof(id->fw_rev), bswap);
- fixstring (id->serial_no, sizeof(id->serial_no), bswap);
-
- /*
- * Check for an ATAPI device
- */
- if (cmd == WIN_PIDENTIFY) {
-#ifdef CONFIG_BLK_DEV_IDECD
- byte type = (id->config >> 8) & 0x0f;
-#endif /* CONFIG_BLK_DEV_IDECD */
- printk("%s: %s, ATAPI,", dev->name, id->model);
-#ifdef CONFIG_BLK_DEV_IDECD
- if (type == 0 || type == 5)
- printk(" CDROM drive\n");
+ if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') {
+ if (match_parm(&s[6], NULL, vals, 1) != 1)
+ goto bad_option;
+ if (vals[0] >= 20 && vals[0] <= 66)
+ idebus_parameter = vals[0];
else
- printk(" UNKNOWN device\n");
- dev->type = cdrom; /* until we do it "correctly" above */
- dev->present = 1;
-#else
- printk(unsupported);
-#endif /* CONFIG_BLK_DEV_IDECD */
- return;
+ printk(" -- BAD BUS SPEED! Expected value from 20 to 66");
+ goto done;
}
+ /*
+ * Look for interface options: "idex="
+ */
+ if (s[3] >= '0' && s[3] <= max_hwif) {
+ /*
+ * Be VERY CAREFUL changing this: note hardcoded indexes below
+ */
+ const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune",
+ "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", "reset", NULL};
+ hw = s[3] - '0';
+ hwif = &ide_hwifs[hw];
+ i = match_parm(&s[4], ide_words, vals, 3);
- dev->type = disk;
- /* Extract geometry if we did not already have one for the drive */
- if (!dev->present) {
- dev->present = 1;
- dev->cyl = dev->bios_cyl = id->cyls;
- dev->head = dev->bios_head = id->heads;
- dev->sect = dev->bios_sect = id->sectors;
- }
- /* Handle logical geometry translation by the drive */
- if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
- && (id->cur_heads <= 16) && id->cur_sectors)
- {
/*
- * Extract the physical drive geometry for our use.
- * Note that we purposely do *not* update the bios info.
- * This way, programs that use it (like fdisk) will
- * still have the same logical view as the BIOS does,
- * which keeps the partition table from being screwed.
- *
- * An exception to this is the cylinder count,
- * which we reexamine later on to correct for 1024 limitations.
+ * Cryptic check to ensure chipset not already set for hwif:
*/
- dev->cyl = id->cur_cyls;
- dev->head = id->cur_heads;
- dev->sect = id->cur_sectors;
- capacity = dev->cyl * dev->head * dev->sect;
-
- /* check for word-swapped "capacity" field in id information */
- check = (id->cur_capacity0 << 16) | id->cur_capacity1;
- if (check == capacity) /* was it swapped? */
- *((int *)&id->cur_capacity0) = capacity; /* fix it */
- }
- /* Use physical geometry if what we have still makes no sense */
- if ((!dev->head || dev->head > 16) && id->heads && id->heads <= 16) {
- dev->cyl = id->cyls;
- dev->head = id->heads;
- dev->sect = id->sectors;
- }
- /* Correct the number of cyls if the bios value is too small */
- if (dev->sect == dev->bios_sect && dev->head == dev->bios_head) {
- if (dev->cyl > dev->bios_cyl)
- dev->bios_cyl = dev->cyl;
- }
- /* Determine capacity, and use LBA if the drive properly supports it */
- if ((id->capability & 2) && lba_capacity_is_ok(id)) {
- dev->select.b.lba = 1;
- capacity = id->lba_capacity;
- } else {
- capacity = dev->cyl * dev->head * dev->sect;
- }
+ if (i > 0 || (i <= -5 && i != -12)) {
+ if (hwif->chipset != ide_unknown)
+ goto bad_option;
+ if (i <= -5) {
+ if (ide_hwifs[1].chipset != ide_unknown)
+ goto bad_option;
+ /*
+ * Interface keywords work only for ide0:
+ */
+ if (hw != 0)
+ goto bad_hwif;
+ printk("\n");
+ }
+ }
- ide_capacity[DEV_HWIF][dev->select.b.drive] = capacity;
- printk ("%s: %.40s, %ldMB w/%dKB Cache, %sCHS=%d/%d/%d",
- dev->name, id->model, capacity/2048L, id->buf_size/2,
- dev->select.b.lba ? "LBA, " : "",
- dev->bios_cyl, dev->bios_head, dev->bios_sect);
-
- dev->mult_count = 0;
- if (id->max_multsect) {
- dev->mult_req = INITIAL_MULT_COUNT;
- if (dev->mult_req > id->max_multsect)
- dev->mult_req = id->max_multsect;
- if (dev->mult_req || ((id->multsect_valid & 1) && id->multsect))
- dev->special.b.set_multmode = 1;
- printk(", MaxMult=%d", id->max_multsect);
+ switch (i) {
+ case -12: /* "reset" */
+ hwif->reset = 1;
+ goto done;
+#ifdef CONFIG_BLK_DEV_PROMISE
+ case -11: /* "dc4030" */
+ {
+ setup_dc4030(hwif);
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_PROMISE */
+#ifdef CONFIG_BLK_DEV_ALI14XX
+ case -10: /* "ali14xx" */
+ {
+ extern void init_ali14xx (void);
+ init_ali14xx();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_ALI14XX */
+#ifdef CONFIG_BLK_DEV_UMC8672
+ case -9: /* "umc8672" */
+ {
+ extern void init_umc8672 (void);
+ init_umc8672();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_UMC8672 */
+#ifdef CONFIG_BLK_DEV_DTC2278
+ case -8: /* "dtc2278" */
+ {
+ extern void init_dtc2278 (void);
+ init_dtc2278();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+#ifdef CONFIG_BLK_DEV_CMD640
+ case -7: /* "cmd640_vlb" */
+ {
+ extern int cmd640_vlb; /* flag for cmd640.c */
+ cmd640_vlb = 1;
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_CMD640 */
+#ifdef CONFIG_BLK_DEV_HT6560B
+ case -6: /* "ht6560b" */
+ {
+ extern void init_ht6560b (void);
+ init_ht6560b();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_HT6560B */
+#if CONFIG_BLK_DEV_QD6580
+ case -5: /* "qd6580" (has secondary i/f) */
+ {
+ extern void init_qd6580 (void);
+ init_qd6580();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_QD6580 */
+ case -4: /* "noautotune" */
+ hwif->drives[0].autotune = 2;
+ hwif->drives[1].autotune = 2;
+ goto done;
+ case -3: /* "autotune" */
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ goto done;
+ case -2: /* "serialize" */
+ do_serialize:
+ ide_hwifs[hw].serialized = 1; /* serialize */
+ ide_hwifs[hw^1].serialized = 1; /* with mate */
+ goto done;
+
+ case -1: /* "noprobe" */
+ hwif->noprobe = 1;
+ goto done;
+
+ case 1: /* base */
+ vals[1] = vals[0] + 0x206; /* default ctl */
+ case 2: /* base,ctl */
+ vals[2] = 0; /* default irq = probe for it */
+ case 3: /* base,ctl,irq */
+ ide_init_hwif_ports(hwif->io_ports, (ide_ioreg_t) vals[0], &hwif->irq);
+ hwif->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) vals[1];
+ hwif->irq = vals[2];
+ hwif->noprobe = 0;
+ hwif->chipset = ide_generic;
+ goto done;
+
+ case 0: goto bad_option;
+ default:
+ printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n");
+ return;
+ }
}
+bad_option:
+ printk(" -- BAD OPTION\n");
+ return;
+bad_hwif:
+ printk("-- NOT SUPPORTED ON ide%d", hw);
+done:
printk("\n");
}
-static void delay_10ms (void)
-{
- unsigned long timer = jiffies + 2;
- while (timer > jiffies);
-}
-
-
-static int try_to_identify (ide_dev_t *dev, byte cmd)
/*
- * Returns: 0 device was identified
- * 1 device timed-out (no response to identify request)
- * 2 device aborted the command (refused to identify itself)
+ * This routine is called from the partition-table code in genhd.c
+ * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
+ *
+ * The second parameter, "xparm", determines exactly how the translation
+ * will be handled:
+ * 0 = convert to CHS with fewer than 1024 cyls
+ * using the same method as Ontrack DiskManager.
+ * 1 = same as "0", plus offset everything by 63 sectors.
+ * -1 = similar to "0", plus redirect sector 0 to sector 1.
+ * >1 = convert to a CHS geometry with "xparm" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
*/
+int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg)
{
- int hd_status, rc;
- unsigned long timeout;
-#if PROBE_FOR_IRQS
- int irqs = 0;
- static byte irq_probed[2] = {0,0};
-#endif /* PROBE_FOR_IRQS */
-
- OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */
-#if PROBE_FOR_IRQS
- if (!irq_probed[DEV_HWIF]) { /* already probed for IRQ? */
- irqs = probe_irq_on(); /* start monitoring irqs */
- OUT_BYTE(dev->ctl,HD_CMD); /* enable device irq */
+ ide_drive_t *drive;
+ static const byte head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
+ const byte *heads = head_vals;
+ unsigned long tracks;
+
+ if ((drive = get_info_ptr(i_rdev)) == NULL || drive->forced_geom)
+ return 0;
+
+ if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
+ return 0; /* we already have a translation */
+
+ printk("%s ", msg);
+
+ if (drive->id) {
+ drive->cyl = drive->id->cyls;
+ drive->head = drive->id->heads;
+ drive->sect = drive->id->sectors;
}
-#endif /* PROBE_FOR_IRQS */
- delay_10ms(); /* take a deep breath */
- if ((IN_BYTE(HD_ALTSTATUS,DEV_HWIF) ^ IN_BYTE(HD_STATUS,DEV_HWIF)) & ~INDEX_STAT) {
- hd_status = HD_STATUS; /* an ancient Seagate drive */
- printk("%s: probing with STATUS instead of ALTSTATUS\n", dev->name);
- } else
- hd_status = HD_ALTSTATUS; /* use non-intrusive polling */
- OUT_BYTE(cmd,HD_COMMAND); /* ask drive for ID */
- timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
- timeout += jiffies;
- do {
- if (jiffies > timeout) {
-#if PROBE_FOR_IRQS
- if (!irq_probed[DEV_HWIF])
- (void) probe_irq_off(irqs);
-#endif /* PROBE_FOR_IRQS */
- return 1; /* drive timed-out */
+ drive->bios_cyl = drive->cyl;
+ drive->bios_head = drive->head;
+ drive->bios_sect = drive->sect;
+ drive->special.b.set_geometry = 1;
+
+ tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
+ drive->bios_sect = 63;
+ if (xparm > 1) {
+ drive->bios_head = xparm;
+ drive->bios_cyl = tracks / drive->bios_head;
+ } else {
+ while (drive->bios_cyl >= 1024) {
+ drive->bios_head = *heads;
+ drive->bios_cyl = tracks / drive->bios_head;
+ if (0 == *++heads)
+ break;
+ }
+#if FAKE_FDISK_FOR_EZDRIVE
+ if (xparm == -1) {
+ drive->remap_0_to_1 = 1;
+ msg = "0->1";
+ } else
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ if (xparm == 1) {
+ drive->sect0 = 63;
+ drive->bios_cyl = (tracks - 1) / drive->bios_head;
+ msg = "+63";
}
- delay_10ms(); /* give drive a breather */
- } while (IN_BYTE(hd_status,DEV_HWIF) & BUSY_STAT);
- delay_10ms(); /* wait for IRQ and DRQ_STAT */
- if (OK_STAT(GET_STAT(DEV_HWIF),DRQ_STAT,BAD_R_STAT)) {
- cli(); /* some systems need this */
- do_identify(dev, cmd); /* drive returned ID */
- rc = 0; /* success */
- } else
- rc = 2; /* drive refused ID */
-#if PROBE_FOR_IRQS
- if (!irq_probed[DEV_HWIF]) {
- irqs = probe_irq_off(irqs); /* get irq number */
- if (irqs > 0) {
- irq_probed[DEV_HWIF] = 1;
- ide_irq[DEV_HWIF] = irqs;
- } else /* Mmmm.. multiple IRQs */
- printk("%s: IRQ probe failed (%d)\n", dev->name, irqs);
+ printk("[remap %s] ", msg);
}
-#endif /* PROBE_FOR_IRQS */
- return rc;
+ drive->part[0].nr_sects = current_capacity(drive);
+ printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
+ return 1;
}
+#ifdef CONFIG_PCI
+#if defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) || defined(CONFIG_BLK_DEV_OPTI621)
+
+typedef void (ide_pci_init_proc_t)(byte, byte);
+
/*
- * This routine has the difficult job of finding a drive if it exists,
- * without getting hung up if it doesn't exist, without trampling on
- * ethernet cards, and without leaving any IRQs dangling to haunt us later.
- *
- * If a drive is "known" to exist (from CMOS or kernel parameters),
- * but does not respond right away, the probe will "hang in there"
- * for the maximum wait time (about 30 seconds), otherwise it will
- * exit much more quickly.
- */
-static int do_probe (ide_dev_t *dev, byte cmd)
-/*
- * Returns: 0 device was identified
- * 1 device timed-out (no response to identify request)
- * 2 device aborted the command (refused to identify itself)
- * 3 bad status from device (possible for ATAPI drives)
- * 4 probe was not attempted
+ * ide_probe_pci() scans PCI for a specific vendor/device function,
+ * and invokes the supplied init routine for each instance detected.
*/
+static void ide_probe_pci (unsigned short vendor, unsigned short device, ide_pci_init_proc_t *init, int func_adj)
{
- int rc;
+ unsigned long flags;
+ unsigned index;
+ byte fn, bus;
-#ifdef CONFIG_BLK_DEV_IDECD
- if (dev->present) { /* avoid waiting for inappropriate probes */
- if ((dev->type == disk) ^ (cmd == WIN_IDENTIFY))
- return 4;
- }
-#endif /* CONFIG_BLK_DEV_IDECD */
-#if DEBUG
- printk("probing for %s: present=%d, type=%s, probetype=%s\n",
- dev->name, dev->present, dev->type ? "cdrom":"disk",
- (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
-#endif
- OUT_BYTE(dev->select.all,HD_CURRENT); /* select target drive */
- delay_10ms(); /* wait for BUSY_STAT */
- if (IN_BYTE(HD_CURRENT,DEV_HWIF) != dev->select.all && !dev->present) {
- OUT_BYTE(0xa0,HD_CURRENT); /* exit with drive0 selected */
- return 3; /* no i/f present: avoid killing ethernet cards */
+ save_flags(flags);
+ cli();
+ for (index = 0; !pcibios_find_device (vendor, device, index, &bus, &fn); ++index) {
+ init (bus, fn + func_adj);
}
+ restore_flags(flags);
+}
+
+#endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) || defined(CONFIG_BLK_DEV_OPTI621) */
+#endif /* CONFIG_PCI */
- if (OK_STAT(GET_STAT(DEV_HWIF),READY_STAT,BUSY_STAT)
- || dev->present || cmd == WIN_PIDENTIFY)
+/*
+ * ide_init_pci() finds/initializes "known" PCI IDE interfaces
+ *
+ * This routine should ideally be using pcibios_find_class() to find
+ * all IDE interfaces, but that function causes some systems to "go weird".
+ */
+static void probe_for_hwifs (void)
+{
+#ifdef CONFIG_PCI
+ /*
+ * Find/initialize PCI IDE interfaces
+ */
+ if (pcibios_present()) {
+#ifdef CONFIG_BLK_DEV_RZ1000
+ ide_pci_init_proc_t init_rz1000;
+ ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, &init_rz1000, 0);
+ ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, &init_rz1000, 0);
+#endif /* CONFIG_BLK_DEV_RZ1000 */
+#ifdef CONFIG_BLK_DEV_TRITON
+ /*
+ * Apparently the BIOS32 services on Intel motherboards are
+ * buggy and won't find the PCI_DEVICE_ID_INTEL_82371_1 for us.
+ * So instead, we search for PCI_DEVICE_ID_INTEL_82371_0,
+ * and then add 1.
+ */
+ ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
+ ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
+#endif /* CONFIG_BLK_DEV_TRITON */
+#ifdef CONFIG_BLK_DEV_OPTI621
+ ide_probe_pci (PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, &ide_init_opti621, 0);
+#endif /* CONFIG_BLK_DEV_OPTI621 */
+ }
+#endif /* CONFIG_PCI */
+#ifdef CONFIG_BLK_DEV_CMD640
{
- if ((rc = try_to_identify(dev, cmd))) /* send cmd and wait */
- rc = try_to_identify(dev, cmd); /* failed: try again */
- if (rc == 1)
- printk("%s: no response (status = 0x%02x)\n",
- dev->name, GET_STAT(DEV_HWIF));
- OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */
- delay_10ms();
- (void) GET_STAT(DEV_HWIF); /* ensure drive irq is clear */
- } else {
- rc = 3; /* not present or maybe ATAPI */
+ extern void ide_probe_for_cmd640x (void);
+ ide_probe_for_cmd640x();
}
- if (dev->select.b.drive == 1) {
- OUT_BYTE(0xa0,HD_CURRENT); /* exit with drive0 selected */
- delay_10ms();
- OUT_BYTE(dev->ctl|2,HD_CMD); /* disable device irq */
- delay_10ms();
- (void) GET_STAT(DEV_HWIF); /* ensure drive irq is clear */
- }
- return rc;
+#endif
+#ifdef CONFIG_BLK_DEV_PROMISE
+ init_dc4030();
+#endif
}
-static byte probe_for_drive (ide_dev_t *dev)
-/*
- * Returns: 0 no device was found
- * 1 device was found (note: dev->present might still be 0)
- */
+void ide_init_builtin_drivers (void)
{
- if (dev->dont_probe) /* skip probing? */
- return dev->present;
- if (do_probe(dev, WIN_IDENTIFY) >= 2) { /* if !(success || timed-out) */
-#ifdef CONFIG_BLK_DEV_IDECD
- (void) do_probe(dev, WIN_PIDENTIFY); /* look for ATAPI device */
-#endif /* CONFIG_BLK_DEV_IDECD */
+ /*
+ * Probe for special "known" interface chipsets
+ */
+ probe_for_hwifs ();
+
+#ifdef CONFIG_BLK_DEV_IDE
+#ifdef __mc68000__
+ if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
+ ide_get_lock(&ide_lock, ide_intr, NULL);
+ disable_irq(ide_hwifs[0].irq);
}
- if (!dev->present)
- return 0; /* drive not found */
- if (dev->id == NULL) { /* identification failed? */
- if (dev->type == disk) {
- printk ("%s: non-IDE device, CHS=%d/%d/%d\n",
- dev->name, dev->cyl, dev->head, dev->sect);
- }
-#ifdef CONFIG_BLK_DEV_IDECD
- else if (dev->type == cdrom) {
- printk("%s: ATAPI cdrom (?)\n", dev->name);
- }
-#endif /* CONFIG_BLK_DEV_IDECD */
- else {
- dev->present = 0; /* nuke it */
- return 1; /* drive was found */
- }
+#endif /* __mc68000__ */
+
+ (void) ideprobe_init();
+
+#ifdef __mc68000__
+ if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
+ enable_irq(ide_hwifs[0].irq);
+ ide_release_lock(&ide_lock);
}
+#endif /* __mc68000__ */
+#endif /* CONFIG_BLK_DEV_IDE */
+
+#ifdef CONFIG_BLK_DEV_IDEDISK
+ (void) idedisk_init();
+#endif /* CONFIG_BLK_DEV_IDEDISK */
#ifdef CONFIG_BLK_DEV_IDECD
- if (dev->type == cdrom)
- cdrom_setup(dev);
-#endif /* CONFIG_BLK_DEV_IDECD */
- if (dev->type == disk && !dev->select.b.lba) {
- if (!dev->head || dev->head > 16) {
- printk("%s: cannot handle disk with %d physical heads\n",
- dev->name, dev->head);
- dev->present = 0;
- }
- }
- return 1; /* drive was found */
+ (void) ide_cdrom_init();
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ (void) idetape_init();
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+ (void) idefloppy_init();
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
}
-static void probe_for_drives (byte hwif)
+static int default_cleanup (ide_drive_t *drive)
{
- ide_dev_t *devs = &ide_dev[HWIF][0]; /* for convenience */
+ return ide_unregister_subdriver(drive);
+}
- if (check_region(IDE_PORT(HD_DATA,HWIF),8)
- || check_region(IDE_PORT(HD_CMD,HWIF),1))
- {
- if (devs[0].present || devs[1].present)
- printk("ERROR: ");
- printk("%s: port(s) already in use\n", ide_name[HWIF]);
- devs[0].present = 0;
- devs[1].present = 0;
- } else {
- unsigned long flags;
- save_flags(flags);
- sti(); /* needed for jiffies and irq probing */
-
- /* second drive should only exist if first drive was found */
- if (probe_for_drive(&devs[0]) || devs[1].present)
- (void) probe_for_drive(&devs[1]);
-#if PROBE_FOR_IRQS
- (void) probe_irq_off(probe_irq_on()); /* clear dangling irqs */
-#endif /* PROBE_FOR_IRQS */
- if (devs[0].present || devs[1].present) {
- request_region(IDE_PORT(HD_DATA,HWIF),8,ide_name[HWIF]);
- request_region(IDE_PORT(HD_CMD,HWIF),1,ide_name[HWIF]);
- }
- restore_flags(flags);
- }
+static void default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ ide_end_request(0, HWGROUP(drive));
+}
+
+static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+ ide_end_request(uptodate, hwgroup);
+}
+
+static int default_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return -EIO;
}
-static int next_drive = 0; /* used by the ide_setup() routines below */
+static int default_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+ drive->usage--;
+ return -EIO;
+}
-void ide_setup(char *str, int *ints)
+static void default_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
{
- ide_dev_t *dev;
- const char *p[] = {"cyls","heads","sects","wpcom","irq"};
- int i, hwif, drive = next_drive++;
-#ifdef CONFIG_BLK_DEV_HD
- extern void hd_setup(char *, int *);
+}
- if (drive < 2) {
- hd_setup (str, ints);
- return;
- }
-#endif /* CONFIG_BLK_DEV_HD */
- hwif = (drive > 1);
- printk("%s: ", ide_name[hwif]);
- if (drive > 3) {
- printk("too many drives defined\n");
- return;
- }
- drive = drive & 1;
- printk("%s: ", ide_devname[hwif][drive]);
- if (!SUPPORT_TWO_INTERFACES && hwif != HWIF) {
- printk(unsupported);
- return;
- }
- dev = &ide_dev[hwif][drive];
- if (dev->present)
- printk("(redefined) ");
- if (ints[0] == 0) {
-#if SUPPORT_DTC2278
- if (!strcmp(str,"dtc2278")) {
- printk("%s\n",str);
- probe_dtc2278 = 1; /* try to init DTC-2278 at boot */
- return;
- }
-#endif /* SUPPORT_DTC2278 */
-#if SUPPORT_SERIALIZE
- if (!strcmp(str,"serialize") || !strcmp(str,"cmd")) {
- printk("%s\n",str);
- single_threaded = 1; /* serialize all drive access */
- return;
- }
-#endif /* SUPPORT_SERIALIZE */
- if (!strcmp(str,"noprobe")) {
- printk("%s\n",str);
- dev->dont_probe = 1; /* don't probe for this drive */
- return;
- }
-#ifdef CONFIG_BLK_DEV_IDECD
- if (!strcmp(str,"cdrom")) {
- printk("cdrom\n");
- dev->present = 1; /* force autoprobe to find it */
- dev->type = cdrom;
- return;
- }
-#endif /* CONFIG_BLK_DEV_IDECD */
- }
- if (ints[0] < 3 || ints[0] > 5) {
- printk("bad parms, expected: cyls,heads,sects[,wpcom[,irq]]\n");
- } else {
- for (i=0; i++ < ints[0];)
- printk("%s=%d%c",p[i-1],ints[i],i<ints[0]?',':'\n');
- dev->type = disk;
- dev->cyl = dev->bios_cyl = ints[1];
- dev->head = dev->bios_head = ints[2];
- dev->ctl = (ints[2] > 8 ? 8 : 0);
- dev->sect = dev->bios_sect = ints[3];
- dev->wpcom = (ints[0] >= 4) ? ints[4] : 0;
- if (ints[0] >= 5)
- ide_irq[HWIF] = ints[5];
- ide_capacity[HWIF][drive] = BIOS_SECTORS(dev);
- dev->present = 1;
- }
+static int default_check_media_change (ide_drive_t *drive)
+{
+ return 1;
}
-void hda_setup(char *str, int *ints)
+static void default_pre_reset (ide_drive_t *drive)
{
- next_drive = 0;
- ide_setup (str, ints);
}
-void hdb_setup(char *str, int *ints)
+static unsigned long default_capacity (ide_drive_t *drive)
{
- next_drive = 1;
- ide_setup (str, ints);
+ return 0x7fffffff; /* cdrom or tape */
}
-void hdc_setup(char *str, int *ints)
+static void default_special (ide_drive_t *drive)
{
- next_drive = 2;
- ide_setup (str, ints);
+ special_t *s = &drive->special;
+
+ s->all = 0;
+ drive->mult_req = 0;
}
-void hdd_setup(char *str, int *ints)
+static void setup_driver_defaults (ide_drive_t *drive)
{
- next_drive = 3;
- ide_setup (str, ints);
+ ide_driver_t *d = drive->driver;
+
+ if (d->cleanup == NULL) d->cleanup = default_cleanup;
+ if (d->do_request == NULL) d->do_request = default_do_request;
+ if (d->end_request == NULL) d->end_request = default_end_request;
+ if (d->ioctl == NULL) d->ioctl = default_ioctl;
+ if (d->open == NULL) d->open = default_open;
+ if (d->release == NULL) d->release = default_release;
+ if (d->media_change == NULL) d->media_change = default_check_media_change;
+ if (d->pre_reset == NULL) d->pre_reset = default_pre_reset;
+ if (d->capacity == NULL) d->capacity = default_capacity;
+ if (d->special == NULL) d->special = default_special;
}
-#ifndef CONFIG_BLK_DEV_HD
-/*
- * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
- * controller that is BIOS compatible with ST-506, and thus showing up in our
- * BIOS table, but not register compatible, and therefore not present in CMOS.
- *
- * Furthermore, we will assume that our ST-506 drives <if any> are the primary
- * drives in the system -- the ones reflected as drive 1 or 2. The first
- * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
- * nibble. This will be either a 4 bit drive type or 0xf indicating use byte
- * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
- * means we have an AT controller hard disk for that drive.
- */
-extern struct drive_info_struct drive_info;
-static void probe_cmos_for_drives (void)
-{
- byte drive, cmos_disks, *BIOS = (byte *) &drive_info;
-
- outb_p(0x12,0x70); /* specify CMOS address 0x12 */
- cmos_disks = inb_p(0x71); /* read the data from 0x12 */
- /* Extract drive geometry from CMOS+BIOS if not already setup */
- for (drive = 0; drive < MAX_DRIVES; drive++) {
- ide_dev_t *dev = &ide_dev[0][drive];
- if ((cmos_disks & (0xf0 >> (drive*4))) && !dev->present) {
- dev->cyl = dev->bios_cyl = *(unsigned short *)BIOS;
- dev->head = dev->bios_head = * (BIOS+2);
- dev->sect = dev->bios_sect = * (BIOS+14);
- dev->wpcom = (*(unsigned short *)(BIOS+5))>>2;
- dev->ctl = *(BIOS+8);
- dev->wpcom = 0;
- dev->type = disk;
- dev->present = 1;
- ide_capacity[0][drive] = BIOS_SECTORS(dev);
+ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n)
+{
+ unsigned int unit, index, i;
+ ide_drive_t *drive;
+
+ for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ drive = &ide_hwifs[index].drives[unit];
+ if (drive->present && drive->media == media &&
+ drive->driver == driver && ++i > n)
+ return drive;
}
- BIOS += 16;
- }
-}
-#endif /* CONFIG_BLK_DEV_HD */
-
-static void init_ide_data (byte hwif)
-{
- int drive;
-
- for (drive = 0; drive < (MAX_DRIVES<<PARTN_BITS); drive++)
- ide_blksizes[hwif][drive] = 1024;
- blksize_size[ide_major[hwif]] = ide_blksizes[hwif];
-
- /* Initialize non-geometry fields -- ide_setup() runs before we do */
- for (drive = 0; drive < MAX_DRIVES; drive++) {
- ide_dev_t *dev = &ide_dev[hwif][drive];
- dev->select.all = (drive<<4)|0xa0;
- dev->hwif = hwif;
- dev->unmask = 0;
- dev->busy = 0;
- dev->mult_count = 0; /* set by do_identify() */
- dev->mult_req = 0; /* set by do_identify() */
- dev->usage = 0;
- dev->vlb_32bit = 0;
- dev->vlb_sync = 0;
- dev->id = NULL;
- dev->ctl = 0x08;
- dev->wqueue = NULL;
- dev->special.all = 0;
- dev->special.b.recalibrate = 1;
- dev->special.b.set_geometry = 1;
- dev->keep_settings = 0;
- ide_hd[hwif][drive<<PARTN_BITS].start_sect = 0;
- dev->name = ide_devname[hwif][drive];
}
+ return NULL;
}
-/*
- * This is the harddisk IRQ description. The SA_INTERRUPT in sa_flags
- * means we enter the IRQ-handler with interrupts disabled: this is bad for
- * interrupt latency, but anything else has led to problems on some
- * machines. We enable interrupts as much as we can safely do in most places.
- */
-static byte setup_irq (byte hwif)
+int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version)
{
- static byte rc = 0;
unsigned long flags;
- const char *msg = "", *primary_secondary[] = {"primary", "secondary"};
- void (*handler)(int, struct pt_regs *) = HWIF ? &ide1_intr : &ide0_intr;
-
-#if SUPPORT_SHARING_IRQ
- if (sharing_single_irq) {
- if (HWIF != 0 && !rc) { /* IRQ already allocated? */
- msg = " (shared with ide0)";
- goto done;
- }
- handler = &ide_shared_intr;
- }
-#if SUPPORT_SERIALIZE
- else if (single_threaded) {
- handler = &ide_seq_intr;
- if (HWIF != 0)
- msg = " (single-threaded with ide0)";
- }
-#endif /* SUPPORT_SERIALIZE */
-#endif /* SUPPORT_SHARING_IRQ */
+
save_flags(flags);
cli();
- if ((rc = request_irq(ide_irq[HWIF],handler,SA_INTERRUPT,ide_name[HWIF])))
- msg = ": FAILED! unable to allocate IRQ";
+ if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL ||
+ drive->busy || drive->usage || drive->media != driver->media) {
+ restore_flags(flags);
+ return 1;
+ }
+ drive->driver = driver;
+ setup_driver_defaults(drive);
restore_flags(flags);
-#if SUPPORT_SHARING_IRQ
-done:
-#endif /* SUPPORT_SHARING_IRQ */
- printk("%s: %s interface on irq %d%s\n",
- ide_name[HWIF], primary_secondary[HWIF], ide_irq[HWIF], msg);
- return rc;
+ if (driver->supports_dma && !drive->using_dma && drive->autotune != 2 && HWIF(drive)->dmaproc != NULL)
+ (void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
+ drive->revalidate = 1;
+ return 0;
}
-static void ide_geninit(byte hwif)
+int ide_unregister_subdriver (ide_drive_t *drive)
{
- static int drive;
+ unsigned long flags;
- for (drive = 0; drive < MAX_DRIVES; drive++) {
- ide_dev_t *dev = &ide_dev[HWIF][drive];
- if (dev->present) {
- ide_hd[HWIF][drive<<PARTN_BITS].nr_sects = ide_capacity[HWIF][drive];
- /* Skip partition check for cdroms. */
- if (dev->type == cdrom)
- ide_hd[HWIF][drive<<PARTN_BITS].start_sect = -1;
- }
+ save_flags(flags);
+ cli();
+ if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) {
+ restore_flags(flags);
+ return 1;
}
+ drive->driver = NULL;
+ restore_flags(flags);
+ return 0;
}
-static void ide0_geninit(void)
+int ide_register_module (ide_module_t *module)
{
- ide_geninit(0);
+ ide_module_t *p = ide_modules;
+
+ while (p) {
+ if (p == module)
+ return 1;
+ p = p->next;
+ }
+ module->next = ide_modules;
+ ide_modules = module;
+ revalidate_drives();
+ return 0;
}
-static void ide1_geninit(void)
+void ide_unregister_module (ide_module_t *module)
{
- ide_geninit(1);
+ ide_module_t **p;
+
+ for (p = &ide_modules; (*p) && (*p) != module; p = &((*p)->next));
+ if (*p)
+ *p = (*p)->next;
}
-static struct file_operations ide_fops = {
+struct file_operations ide_fops[] = {{
NULL, /* lseek - default */
block_read, /* read - general block-dev read */
block_write, /* write - general block-dev write */
@@ -2302,137 +2675,94 @@ static struct file_operations ide_fops = {
NULL, /* mmap */
ide_open, /* open */
ide_release, /* release */
- block_fsync /* fsync */
-#ifdef CONFIG_BLK_DEV_IDECD
- ,NULL, /* fasync */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
ide_check_media_change, /* check_media_change */
- NULL /* revalidate */
-#endif CONFIG_BLK_DEV_IDECD
-};
+ ide_revalidate_disk /* revalidate */
+}};
+static struct symbol_table ide_syms = {
+#include <linux/symtab_begin.h>
+ X(ide_hwifs),
+ X(ide_register_module), X(ide_unregister_module),
+
+ /*
+ * Probe module
+ */
+ X(ide_timer_expiry), X(ide_intr),
+ X(ide_geninit), X(ide_fops),
+ X(do_ide0_request),
+#if MAX_HWIFS > 1
+ X(do_ide1_request),
+#endif /* MAX_HWIFS > 1 */
+#if MAX_HWIFS > 2
+ X(do_ide2_request),
+#endif /* MAX_HWIFS > 2 */
+#if MAX_HWIFS > 3
+ X(do_ide3_request),
+#endif /* MAX_HWIFS > 3 */
+
+ /*
+ * Driver module
+ */
+ X(ide_scan_devices), X(ide_register_subdriver),
+ X(ide_unregister_subdriver), X(ide_input_data),
+ X(ide_output_data), X(atapi_input_bytes),
+ X(atapi_output_bytes), X(ide_set_handler),
+ X(ide_dump_status), X(ide_error),
+ X(ide_fixstring), X(ide_wait_stat),
+ X(ide_do_reset), X(ide_init_drive_cmd),
+ X(ide_do_drive_cmd), X(ide_end_drive_cmd),
+ X(ide_end_request), X(ide_revalidate_disk),
+ X(ide_cmd),
+
+ X(ide_register), X(ide_unregister),
+#include <linux/symtab_end.h>
+};
-#if SUPPORT_DTC2278
/*
- * From: andy@cercle.cts.com (Dyan Wile)
- *
- * Below is a patch for DTC-2278 - alike software-programmable controllers
- * The code enables the secondary IDE controller and the PIO4 (3?) timings on
- * the primary (EIDE). You may probably have to enable the 32-bit support to
- * get the full speed. You better get the disk interrupts disabled ( hdparm -u0
- * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my
- * filesystem corrupted with -u 1, but under heavy disk load only :-)
+ * This is gets invoked once during initialization, to set *everything* up
*/
-
-static void sub22 (char b, char c)
+int ide_init (void)
{
- int i;
+ init_ide_data ();
- for(i = 0; i < 3; i++) {
- __inb(0x3f6);
- outb_p(b,0xb0);
- __inb(0x3f6);
- outb_p(c,0xb4);
- __inb(0x3f6);
- if(__inb(0xb4) == c) {
- outb_p(7,0xb0);
- __inb(0x3f6);
- return; /* success */
- }
- }
+ initializing = 1;
+ ide_init_builtin_drivers();
+ initializing = 0;
+
+ (void) register_symtab(&ide_syms);
+ return 0;
}
-static void try_to_init_dtc2278 (void)
-{
-/* This (presumably) enables PIO mode4 (3?) on the first interface */
- cli();
- sub22(1,0xc3);
- sub22(0,0xa0);
- sti();
+#ifdef MODULE
+char *options = NULL;
-/* This enables the second interface */
+static void parse_options (char *line)
+{
+ char *next = line;
- outb_p(4,0xb0);
- __inb(0x3f6);
- outb_p(0x20,0xb4);
- __inb(0x3f6);
+ if (line == NULL || !*line)
+ return;
+ while ((line = next) != NULL) {
+ if ((next = strchr(line,' ')) != NULL)
+ *next++ = 0;
+ if (!strncmp(line,"ide",3) || (!strncmp(line,"hd",2) && line[2] != '='))
+ ide_setup(line);
+ }
}
-#endif /* SUPPORT_DTC2278 */
-/*
- * This is gets invoked once during initialization, to set *everything* up
- */
-unsigned long ide_init (unsigned long mem_start, unsigned long mem_end)
-{
- byte hwif;
-
-#if SUPPORT_DTC2278
- if (probe_dtc2278)
- try_to_init_dtc2278();
-#endif /* SUPPORT_DTC2278 */
- /* single_threaded = 0; */ /* zero by default, override at boot */
- for (hwif = 0; hwif < 2; hwif++) {
- init_ide_data (hwif);
- if (SUPPORT_TWO_INTERFACES || hwif == HWIF) {
- if (hwif == 0)
-#ifdef CONFIG_BLK_DEV_HD
- continue;
-#else
- probe_cmos_for_drives ();
-#endif /* CONFIG_BLJ_DEV_HD */
- probe_mem_start = (mem_start + 3uL) & ~3uL;
- probe_for_drives (hwif);
- mem_start = probe_mem_start;
- }
- }
+int init_module (void)
+{
+ parse_options(options);
+ return ide_init();
+}
- /* At this point, all methods of drive detection have completed */
- ide_gendisk[0].nr_real = ide_dev[0][0].present + ide_dev[0][1].present;
- ide_gendisk[1].nr_real = ide_dev[1][0].present + ide_dev[1][1].present;
- if (ide_gendisk[1].nr_real && (ide_irq[0] == ide_irq[1])) {
- if (!ide_gendisk[0].nr_real) {
- ide_irq[0] = 0; /* needed by ide_intr() */
- } else {
-#if SUPPORT_SHARING_IRQ
- sharing_single_irq = 1;
- single_threaded = 1;
-#else /* SUPPORT_SHARING_IRQ */
- printk("%s: ide irq-sharing%s", ide_name[1], unsupported);
- return mem_start;
-#endif /* SUPPORT_SHARING_IRQ */
- }
- }
-#ifdef CONFIG_BLK_DEV_HD
-#if SUPPORT_SHARING_IRQ
- if (ide_irq[1] == 14 || sharing_single_irq) {
-#else
- if (ide_irq[1] == 14) {
-#endif /* SUPPORT_SHARING_IRQ */
- printk("%s: irq-sharing not possible with old harddisk driver (hd.c)\n", ide_name[1]);
- return mem_start;
- }
-#endif /* CONFIG_BLK_DEV_HD */
+void cleanup_module (void)
+{
+ int index;
- for (hwif = 2; hwif-- > 0;) {
- if (ide_gendisk[hwif].nr_real != 0 && !setup_irq(hwif)) {
- const char *name = ide_name[HWIF];
- unsigned int major = ide_major[HWIF];
- if (register_blkdev(major, name, &ide_fops)) {
- printk("%s: unable to get major number %d\n", name, major);
- } else {
- timer_table[ide_timer[HWIF]].fn
- = HWIF ? ide1_timer_expiry : ide0_timer_expiry;
-#if SUPPORT_SHARING_IRQ
- if (single_threaded)
- blk_dev[major].request_fn = &do_shared_request;
- else
-#endif /* SUPPORT_SHARING_IRQ */
- blk_dev[major].request_fn =
- HWIF ? &do_ide1_request : &do_ide0_request;
- read_ahead[major] = 8; /* (4kB) */
- ide_gendisk[HWIF].next = gendisk_head;
- gendisk_head = &ide_gendisk[HWIF];
- }
- }
- }
- return mem_start;
+ for (index = 0; index < MAX_HWIFS; ++index)
+ ide_unregister(index);
}
+#endif /* MODULE */
diff --git a/drivers/block/ide.h b/drivers/block/ide.h
new file mode 100644
index 000000000..c1e82718e
--- /dev/null
+++ b/drivers/block/ide.h
@@ -0,0 +1,604 @@
+#ifndef _IDE_H
+#define _IDE_H
+/*
+ * linux/drivers/block/ide.h
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors
+ */
+
+#include <linux/config.h>
+#include <asm/ide.h>
+
+/*
+ * This is the multiple IDE interface driver, as evolved from hd.c.
+ * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * There can be up to two drives per interface, as per the ATA-2 spec.
+ *
+ * Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
+ * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0; (hdd or hd1b) minor=64
+ * Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
+ * Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
+ */
+
+/******************************************************************************
+ * IDE driver configuration options (play with these as desired):
+ *
+ * REALLY_SLOW_IO can be defined in ide.c and ide-cd.c, if necessary
+ */
+#undef REALLY_FAST_IO /* define if ide ports are perfect */
+#define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */
+
+#ifndef SUPPORT_SLOW_DATA_PORTS /* 1 to support slow data ports */
+#define SUPPORT_SLOW_DATA_PORTS 1 /* 0 to reduce kernel size */
+#endif
+#ifndef SUPPORT_VLB_SYNC /* 1 to support weird 32-bit chips */
+#define SUPPORT_VLB_SYNC 1 /* 0 to reduce kernel size */
+#endif
+#ifndef DISK_RECOVERY_TIME /* off=0; on=access_delay_time */
+#define DISK_RECOVERY_TIME 0 /* for hardware that needs it */
+#endif
+#ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */
+#define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */
+#endif
+#ifndef FAKE_FDISK_FOR_EZDRIVE /* 1 to help linux fdisk with EZDRIVE */
+#define FAKE_FDISK_FOR_EZDRIVE 1 /* 0 to reduce kernel size */
+#endif
+#ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */
+#define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */
+#endif
+
+#ifdef CONFIG_BLK_DEV_CMD640
+#if 0 /* change to 1 when debugging cmd640 problems */
+void cmd640_dump_regs (void);
+#define CMD640_DUMP_REGS cmd640_dump_regs() /* for debugging cmd640 chipset */
+#endif
+#endif /* CONFIG_BLK_DEV_CMD640 */
+
+/*
+ * IDE_DRIVE_CMD is used to implement many features of the hdparm utility
+ */
+#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/
+
+/*
+ * "No user-serviceable parts" beyond this point :)
+ *****************************************************************************/
+
+typedef unsigned char byte; /* used everywhere */
+
+/*
+ * Probably not wise to fiddle with these
+ */
+#define ERROR_MAX 8 /* Max read/write errors per sector */
+#define ERROR_RESET 3 /* Reset controller every 4th retry */
+#define ERROR_RECAL 1 /* Recalibrate every 2nd retry */
+
+/*
+ * Ensure that various configuration flags have compatible settings
+ */
+#ifdef REALLY_SLOW_IO
+#undef REALLY_FAST_IO
+#endif
+
+#define HWIF(drive) ((ide_hwif_t *)((drive)->hwif))
+#define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup))
+
+/*
+ * Definitions for accessing IDE controller registers
+ */
+#define IDE_NR_PORTS (10)
+
+#define IDE_DATA_OFFSET (0)
+#define IDE_ERROR_OFFSET (1)
+#define IDE_NSECTOR_OFFSET (2)
+#define IDE_SECTOR_OFFSET (3)
+#define IDE_LCYL_OFFSET (4)
+#define IDE_HCYL_OFFSET (5)
+#define IDE_SELECT_OFFSET (6)
+#define IDE_STATUS_OFFSET (7)
+#define IDE_CONTROL_OFFSET (8)
+#define IDE_IRQ_OFFSET (9)
+
+#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET
+#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET
+
+#define IDE_DATA_REG (HWIF(drive)->io_ports[IDE_DATA_OFFSET])
+#define IDE_ERROR_REG (HWIF(drive)->io_ports[IDE_ERROR_OFFSET])
+#define IDE_NSECTOR_REG (HWIF(drive)->io_ports[IDE_NSECTOR_OFFSET])
+#define IDE_SECTOR_REG (HWIF(drive)->io_ports[IDE_SECTOR_OFFSET])
+#define IDE_LCYL_REG (HWIF(drive)->io_ports[IDE_LCYL_OFFSET])
+#define IDE_HCYL_REG (HWIF(drive)->io_ports[IDE_HCYL_OFFSET])
+#define IDE_SELECT_REG (HWIF(drive)->io_ports[IDE_SELECT_OFFSET])
+#define IDE_STATUS_REG (HWIF(drive)->io_ports[IDE_STATUS_OFFSET])
+#define IDE_CONTROL_REG (HWIF(drive)->io_ports[IDE_CONTROL_OFFSET])
+#define IDE_IRQ_REG (HWIF(drive)->io_ports[IDE_IRQ_OFFSET])
+
+#define IDE_FEATURE_REG IDE_ERROR_REG
+#define IDE_COMMAND_REG IDE_STATUS_REG
+#define IDE_ALTSTATUS_REG IDE_CONTROL_REG
+#define IDE_IREASON_REG IDE_NSECTOR_REG
+#define IDE_BCOUNTL_REG IDE_LCYL_REG
+#define IDE_BCOUNTH_REG IDE_HCYL_REG
+
+#ifdef REALLY_FAST_IO
+#define OUT_BYTE(b,p) outb((b),(p))
+#define IN_BYTE(p) (byte)inb(p)
+#else
+#define OUT_BYTE(b,p) outb_p((b),(p))
+#define IN_BYTE(p) (byte)inb_p(p)
+#endif /* REALLY_FAST_IO */
+
+#define GET_ERR() IN_BYTE(IDE_ERROR_REG)
+#define GET_STAT() IN_BYTE(IDE_STATUS_REG)
+#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
+#define BAD_R_STAT (BUSY_STAT | ERR_STAT)
+#define BAD_W_STAT (BAD_R_STAT | WRERR_STAT)
+#define BAD_STAT (BAD_R_STAT | DRQ_STAT)
+#define DRIVE_READY (READY_STAT | SEEK_STAT)
+#define DATA_READY (DRIVE_READY | DRQ_STAT)
+
+/*
+ * Some more useful definitions
+ */
+#define IDE_MAJOR_NAME "ide" /* the same for all i/f; see also genhd.c */
+#define MAJOR_NAME IDE_MAJOR_NAME
+#define PARTN_BITS 6 /* number of minor dev bits for partitions */
+#define PARTN_MASK ((1<<PARTN_BITS)-1) /* a useful bit mask */
+#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
+#define SECTOR_WORDS (512 / 4) /* number of 32bit words per sector */
+
+/*
+ * Timeouts for various operations:
+ */
+#define WAIT_DRQ (5*HZ/100) /* 50msec - spec allows up to 20ms */
+#ifdef CONFIG_APM
+#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 */
+#define WAIT_PIDENTIFY (1*HZ) /* 1sec - should be less than 3ms (?) */
+#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */
+#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */
+
+#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE)
+#define SELECT_DRIVE(hwif,drive) \
+{ \
+ if (hwif->selectproc) \
+ hwif->selectproc(drive); \
+ else \
+ OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); \
+}
+#else
+#define SELECT_DRIVE(hwif,drive) OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]);
+#endif /* CONFIG_BLK_DEV_HT6560B || CONFIG_BLK_DEV_PROMISE */
+
+/*
+ * Now for the data we need to maintain per-drive: ide_drive_t
+ */
+
+#define ide_disk 0x20
+#define ide_cdrom 0x5
+#define ide_tape 0x1
+#define ide_floppy 0x0
+
+typedef union {
+ unsigned all : 8; /* all of the bits together */
+ struct {
+ unsigned set_geometry : 1; /* respecify drive geometry */
+ unsigned recalibrate : 1; /* seek to cyl 0 */
+ unsigned set_multmode : 1; /* set multmode count */
+ unsigned set_tune : 1; /* tune interface for drive */
+ unsigned reserved : 4; /* unused */
+ } b;
+ } special_t;
+
+typedef struct ide_drive_s {
+ special_t special; /* special action flags */
+ unsigned present : 1; /* drive is physically present */
+ unsigned noprobe : 1; /* from: hdx=noprobe */
+ unsigned keep_settings : 1; /* restore settings after drive reset */
+ unsigned busy : 1; /* currently doing revalidate_disk() */
+ unsigned removable : 1; /* 1 if need to do check_media_change */
+ unsigned using_dma : 1; /* disk is using dma for read/write */
+ unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */
+ unsigned unmask : 1; /* flag: okay to unmask other irqs */
+ unsigned no_unmask : 1; /* disallow setting unmask bit */
+ unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */
+ unsigned nobios : 1; /* flag: do not probe bios for drive */
+ unsigned slow : 1; /* flag: slow data port */
+ unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
+ unsigned revalidate : 1; /* request revalidation */
+ unsigned bswap : 1; /* flag: byte swap data */
+#if FAKE_FDISK_FOR_EZDRIVE
+ unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ byte media; /* disk, cdrom, tape, floppy, ... */
+ select_t select; /* basic drive/head select reg value */
+ byte ctl; /* "normal" value for IDE_CONTROL_REG */
+ byte ready_stat; /* min status value for drive ready */
+ byte mult_count; /* current multiple sector setting */
+ byte mult_req; /* requested multiple sector setting */
+ byte tune_req; /* requested drive tuning setting */
+ byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
+ byte bad_wstat; /* used for ignoring WRERR_STAT */
+ byte sect0; /* offset of first sector for DM6:DDO */
+ byte usage; /* current "open()" count for drive */
+ byte head; /* "real" number of heads */
+ byte sect; /* "real" sectors per track */
+ /*
+ * HACK: enforce alignment for sake of IDE CDROM driver on
+ * architectures with strict alignment rules.
+ */
+ byte bios_head __attribute__ ((aligned (8))); /* BIOS/fdisk/LILO number of heads */
+ byte bios_sect __attribute__ ((aligned (8))); /* BIOS/fdisk/LILO sectors per track */
+ unsigned short bios_cyl; /* BIOS/fdisk/LILO number of cyls */
+ unsigned short cyl; /* "real" number of cyls */
+ void *hwif; /* actually (ide_hwif_t *) */
+ struct wait_queue *wqueue; /* used to wait for drive in open() */
+ struct hd_driveid *id; /* drive model identification info */
+ struct hd_struct *part; /* drive partition table */
+ char name[4]; /* drive name, such as "hda" */
+ void *driver; /* (ide_driver_t *) */
+ void *driver_data; /* extra driver data */
+ } ide_drive_t;
+
+/*
+ * An ide_dmaproc_t() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA. All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case the caller
+ * should either try again later, or revert to PIO for the current request.
+ */
+typedef enum { ide_dma_read = 0, ide_dma_write = 1,
+ ide_dma_abort = 2, ide_dma_check = 3,
+ ide_dma_status_bad = 4, ide_dma_transferred = 5,
+ ide_dma_begin = 6 }
+ ide_dma_action_t;
+
+typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
+
+
+/*
+ * An ide_tuneproc_t() is used to set the speed of an IDE interface
+ * to a particular PIO mode. The "byte" parameter is used
+ * to select the PIO mode by number (0,1,2,3,4,5), and a value of 255
+ * indicates that the interface driver should "auto-tune" the PIO mode
+ * according to the drive capabilities in drive->id;
+ *
+ * Not all interface types support tuning, and not all of those
+ * support all possible PIO settings. They may silently ignore
+ * or round values as they see fit.
+ */
+typedef void (ide_tuneproc_t)(ide_drive_t *, byte);
+
+/*
+ * This is used to provide HT6560B & PROMISE interface support.
+ */
+typedef void (ide_selectproc_t) (ide_drive_t *);
+
+/*
+ * hwif_chipset_t is used to keep track of the specific hardware
+ * chipset used by each IDE interface, if known.
+ */
+typedef enum { ide_unknown, ide_generic, ide_triton,
+ ide_cmd640, ide_dtc2278, ide_ali14xx,
+ ide_qd6580, ide_umc8672, ide_ht6560b,
+ ide_promise }
+ hwif_chipset_t;
+
+typedef struct hwif_s {
+ struct hwif_s *next; /* for linked-list in ide_hwgroup_t */
+ void *hwgroup; /* actually (ide_hwgroup_t *) */
+ ide_ioreg_t io_ports[IDE_NR_PORTS]; /* task file registers */
+ ide_drive_t drives[MAX_DRIVES]; /* drive info */
+ struct gendisk *gd; /* gendisk structure */
+ ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */
+#if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE)
+ ide_selectproc_t *selectproc; /* tweaks hardware to select drive */
+#endif
+ ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */
+ unsigned long *dmatable; /* dma physical region descriptor table */
+ unsigned short dma_base; /* base addr for dma ports (triton) */
+ int irq; /* our irq number */
+ byte major; /* our major number */
+ char name[6]; /* name of interface, eg. "ide0" */
+ byte index; /* 0 for ide0; 1 for ide1; ... */
+ hwif_chipset_t chipset; /* sub-module for tuning.. */
+ unsigned noprobe : 1; /* don't probe for this interface */
+ unsigned present : 1; /* this interface exists */
+ unsigned serialized : 1; /* serialized operation with mate hwif */
+ unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
+#ifdef CONFIG_BLK_DEV_PROMISE
+ unsigned is_promise2: 1; /* 2nd i/f on promise DC4030 */
+#endif /* CONFIG_BLK_DEV_PROMISE */
+ unsigned reset : 1; /* reset after probe */
+#if (DISK_RECOVERY_TIME > 0)
+ unsigned long last_time; /* time when previous rq was done */
+#endif
+ } ide_hwif_t;
+
+/*
+ * internal ide interrupt handler type
+ */
+typedef void (ide_handler_t)(ide_drive_t *);
+
+typedef struct hwgroup_s {
+ ide_handler_t *handler;/* irq handler, if active */
+ ide_drive_t *drive; /* current drive */
+ ide_hwif_t *hwif; /* ptr to current hwif in linked-list */
+ ide_hwif_t *next_hwif; /* next selected hwif (for tape) */
+ struct request *rq; /* current request */
+ struct timer_list timer; /* failsafe timer */
+ struct request wrq; /* local copy of current write rq */
+ unsigned long poll_timeout; /* timeout value during long polls */
+ } ide_hwgroup_t;
+
+/*
+ * Subdrivers support.
+ */
+#define IDE_SUBDRIVER_VERSION 0
+
+typedef int (ide_cleanup_proc)(ide_drive_t *);
+typedef void (ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long);
+typedef void (ide_end_request_proc)(byte, ide_hwgroup_t *);
+typedef int (ide_ioctl_proc)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long);
+typedef int (ide_open_proc)(struct inode *, struct file *, ide_drive_t *);
+typedef void (ide_release_proc)(struct inode *, struct file *, ide_drive_t *);
+typedef int (ide_check_media_change_proc)(ide_drive_t *);
+typedef void (ide_pre_reset_proc)(ide_drive_t *);
+typedef unsigned long (ide_capacity_proc)(ide_drive_t *);
+typedef void (ide_special_proc)(ide_drive_t *);
+
+typedef struct ide_driver_s {
+ byte media;
+ unsigned busy : 1;
+ unsigned supports_dma : 1;
+ ide_cleanup_proc *cleanup;
+ ide_do_request_proc *do_request;
+ ide_end_request_proc *end_request;
+ ide_ioctl_proc *ioctl;
+ ide_open_proc *open;
+ ide_release_proc *release;
+ ide_check_media_change_proc *media_change;
+ ide_pre_reset_proc *pre_reset;
+ ide_capacity_proc *capacity;
+ ide_special_proc *special;
+ } ide_driver_t;
+
+#define DRIVER(drive) ((ide_driver_t *)((drive)->driver))
+
+/*
+ * IDE modules.
+ */
+#define IDE_CHIPSET_MODULE 0 /* not supported yet */
+#define IDE_PROBE_MODULE 1
+#define IDE_DRIVER_MODULE 2
+
+typedef int (ide_module_init_proc)(void);
+
+typedef struct ide_module_s {
+ int type;
+ ide_module_init_proc *init;
+ struct ide_module_s *next;
+} ide_module_t;
+
+/*
+ * ide_hwifs[] is the master data structure used to keep track
+ * of just about everything in ide.c. Whenever possible, routines
+ * should be using pointers to a drive (ide_drive_t *) or
+ * pointers to a hwif (ide_hwif_t *), rather than indexing this
+ * structure directly (the allocation/layout may change!).
+ *
+ */
+#ifndef _IDE_C
+extern ide_hwif_t ide_hwifs[]; /* master data repository */
+#endif
+
+/*
+ * One final include file, which references some of the data/defns from above
+ */
+#define IDE_DRIVER /* "parameter" for blk.h */
+#include <linux/blk.h>
+
+/*
+ * This is used for (nearly) all data transfers from/to the IDE interface
+ */
+void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
+void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
+
+/*
+ * This is used for (nearly) all ATAPI data transfers from/to the IDE interface
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+
+/*
+ * This is used on exit from the driver, to designate the next irq handler
+ * and also to start the safety timer.
+ */
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout);
+
+/*
+ * Error reporting, in human readable form (luxurious, but a memory hog).
+ */
+byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat);
+
+/*
+ * ide_error() takes action based on the error returned by the controller.
+ * The calling function must return afterwards, to restart the request.
+ */
+void ide_error (ide_drive_t *drive, const char *msg, byte stat);
+
+/*
+ * Issue a simple drive command
+ * The drive must be selected beforehand.
+ */
+void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler);
+
+/*
+ * ide_fixstring() cleans up and (optionally) byte-swaps a text string,
+ * removing leading/trailing blanks and compressing internal blanks.
+ * It is primarily used to tidy up the model name/number fields as
+ * returned by the WIN_[P]IDENTIFY commands.
+ */
+void ide_fixstring (byte *s, const int bytecount, const int byteswap);
+
+/*
+ * This routine busy-waits for the drive status to be not "busy".
+ * It then checks the status for all of the "good" bits and none
+ * of the "bad" bits, and if all is okay it returns 0. All other
+ * cases return 1 after invoking ide_error() -- caller should return.
+ *
+ */
+int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
+
+/*
+ * This routine is called from the partition-table code in genhd.c
+ * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
+ *
+ * The second parameter, "xparm", determines exactly how the translation
+ * will be handled:
+ * 0 = convert to CHS with fewer than 1024 cyls
+ * using the same method as Ontrack DiskManager.
+ * 1 = same as "0", plus offset everything by 63 sectors.
+ * -1 = similar to "0", plus redirect sector 0 to sector 1.
+ * >1 = convert to a CHS geometry with "xparm" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
+ */
+int ide_xlate_1024 (kdev_t, int, const char *);
+
+/*
+ * Start a reset operation for an IDE interface.
+ * The caller should return immediately after invoking this.
+ */
+void ide_do_reset (ide_drive_t *);
+
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd (struct request *rq);
+
+/*
+ * "action" parameter type for ide_do_drive_cmd() below.
+ */
+typedef enum
+ {ide_wait, /* insert rq at end of list, and wait for it */
+ ide_next, /* insert rq immediately after current request */
+ ide_preempt, /* insert rq in front of current request */
+ ide_end} /* insert rq at end of list, but don't wait for it */
+ ide_action_t;
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed. This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed. This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed. As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the
+ * request queue, and the function returns immediately without waiting
+ * for the new rq to be completed. This is again intended for careful
+ * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
+ * when operating in the pipelined operation mode).
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
+
+/*
+ * Clean up after success/failure of an explicit drive cmd.
+ * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
+
+/*
+ * ide_system_bus_speed() returns what we think is the system VESA/PCI
+ * bus speed (in MHz). This is used for calculating interface PIO timings.
+ * The default is 40 for known PCI systems, 50 otherwise.
+ * The "idebus=xx" parameter can be used to override this value.
+ */
+int ide_system_bus_speed (void);
+
+/*
+ * ide_multwrite() transfers a block of up to mcount sectors of data
+ * to a drive as part of a disk multwrite operation.
+ */
+void ide_multwrite (ide_drive_t *drive, unsigned int mcount);
+
+void ide_revalidate_drives (void);
+
+void ide_timer_expiry (unsigned long data);
+void ide_intr (int irq, void *dev_id, struct pt_regs *regs);
+void ide_geninit (struct gendisk *gd);
+void do_ide0_request (void);
+#if MAX_HWIFS > 1
+void do_ide1_request (void);
+#endif
+#if MAX_HWIFS > 2
+void do_ide2_request (void);
+#endif
+#if MAX_HWIFS > 3
+void do_ide3_request (void);
+#endif
+void ide_init_subdrivers (void);
+
+#ifndef _IDE_C
+extern struct file_operations ide_fops[];
+#endif
+
+#ifdef CONFIG_BLK_DEV_IDECD
+int ide_cdrom_init (void);
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+int idetape_init (void);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+int idefloppy_init (void);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDEDISK
+int idedisk_init (void);
+#endif /* CONFIG_BLK_DEV_IDEDISK */
+
+int ide_register_module (ide_module_t *module);
+void ide_unregister_module (ide_module_t *module);
+ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n);
+int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version);
+int ide_unregister_subdriver (ide_drive_t *drive);
+
+#ifdef CONFIG_BLK_DEV_TRITON
+void ide_init_triton (byte, byte);
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+#ifdef CONFIG_BLK_DEV_OPTI621
+void ide_init_opti621 (byte, byte);
+#endif /* CONFIG_BLK_DEV_OPTI621 */
+
+#ifdef CONFIG_BLK_DEV_IDE
+int ideprobe_init (void);
+#endif /* CONFIG_BLK_DEV_IDE */
+
+#ifdef CONFIG_BLK_DEV_PROMISE
+#include "promise.h"
+#define IS_PROMISE_DRIVE (HWIF(drive)->chipset == ide_promise)
+#else
+#define IS_PROMISE_DRIVE (0) /* auto-NULLs out Promise code */
+#endif /* CONFIG_BLK_DEV_PROMISE */
+
+#endif /* _IDE_H */
diff --git a/drivers/block/ide_modes.h b/drivers/block/ide_modes.h
new file mode 100644
index 000000000..589fbfaa7
--- /dev/null
+++ b/drivers/block/ide_modes.h
@@ -0,0 +1,226 @@
+#ifndef _IDE_MODES_H
+#define _IDE_MODES_H
+/*
+ * linux/drivers/block/ide_modes.h
+ *
+ * Copyright (C) 1996 Linus Torvalds, Igor Abramov, and Mark Lord
+ */
+
+#include <linux/config.h>
+
+/*
+ * Shared data/functions for determining best PIO mode for an IDE drive.
+ * Most of this stuff originally lived in cmd640.c, and changes to the
+ * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid
+ * breaking the fragile cmd640.c support.
+ */
+
+#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS)
+
+/*
+ * Standard (generic) timings for PIO modes, from ATA2 specification.
+ * These timings are for access to the IDE data port register *only*.
+ * Some drives may specify a mode, while also specifying a different
+ * value for cycle_time (from drive identification data).
+ */
+typedef struct ide_pio_timings_s {
+ int setup_time; /* Address setup (ns) minimum */
+ int active_time; /* Active pulse (ns) minimum */
+ int cycle_time; /* Cycle time (ns) minimum = (setup + active + recovery) */
+} ide_pio_timings_t;
+
+typedef struct ide_pio_data_s {
+ byte pio_mode;
+ byte use_iordy;
+ byte overridden;
+ byte blacklisted;
+ unsigned int cycle_time;
+} ide_pio_data_t;
+
+#ifndef _IDE_C
+
+int ide_scan_pio_blacklist (char *model);
+byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d);
+extern const ide_pio_timings_t ide_pio_timings[6];
+
+#else /* _IDE_C */
+
+const ide_pio_timings_t ide_pio_timings[6] = {
+ { 70, 165, 600 }, /* PIO Mode 0 */
+ { 50, 125, 383 }, /* PIO Mode 1 */
+ { 30, 100, 240 }, /* PIO Mode 2 */
+ { 30, 80, 180 }, /* PIO Mode 3 with IORDY */
+ { 25, 70, 120 }, /* PIO Mode 4 with IORDY */
+ { 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */
+};
+
+/*
+ * Black list. Some drives incorrectly report their maximal PIO mode,
+ * at least in respect to CMD640. Here we keep info on some known drives.
+ */
+static struct ide_pio_info {
+ const char *name;
+ int pio;
+} ide_pio_blacklist [] = {
+/* { "Conner Peripherals 1275MB - CFS1275A", 4 }, */
+ { "Conner Peripherals 540MB - CFS540A", 3 },
+
+ { "WDC AC2700", 3 },
+ { "WDC AC2540", 3 },
+ { "WDC AC2420", 3 },
+ { "WDC AC2340", 3 },
+ { "WDC AC2250", 0 },
+ { "WDC AC2200", 0 },
+ { "WDC AC21200", 4 },
+ { "WDC AC2120", 0 },
+ { "WDC AC2850", 3 },
+ { "WDC AC1270", 3 },
+ { "WDC AC1170", 1 },
+ { "WDC AC1210", 1 },
+ { "WDC AC280", 0 },
+/* { "WDC AC21000", 4 }, */
+ { "WDC AC31000", 3 },
+ { "WDC AC31200", 3 },
+/* { "WDC AC31600", 4 }, */
+
+ { "Maxtor 7131 AT", 1 },
+ { "Maxtor 7171 AT", 1 },
+ { "Maxtor 7213 AT", 1 },
+ { "Maxtor 7245 AT", 1 },
+ { "Maxtor 7345 AT", 1 },
+ { "Maxtor 7546 AT", 3 },
+ { "Maxtor 7540 AV", 3 },
+
+ { "SAMSUNG SHD-3121A", 1 },
+ { "SAMSUNG SHD-3122A", 1 },
+ { "SAMSUNG SHD-3172A", 1 },
+
+/* { "ST51080A", 4 },
+ * { "ST51270A", 4 },
+ * { "ST31220A", 4 },
+ * { "ST31640A", 4 },
+ * { "ST32140A", 4 },
+ * { "ST3780A", 4 },
+ */
+ { "ST5660A", 3 },
+ { "ST3660A", 3 },
+ { "ST3630A", 3 },
+ { "ST3655A", 3 },
+ { "ST3391A", 3 },
+ { "ST3390A", 1 },
+ { "ST3600A", 1 },
+ { "ST3290A", 0 },
+ { "ST3144A", 0 },
+
+ { "QUANTUM ELS127A", 0 },
+ { "QUANTUM ELS170A", 0 },
+ { "QUANTUM LPS240A", 0 },
+ { "QUANTUM LPS210A", 3 },
+ { "QUANTUM LPS270A", 3 },
+ { "QUANTUM LPS365A", 3 },
+ { "QUANTUM LPS540A", 3 },
+ { "QUANTUM LIGHTNING 540A", 3 },
+ { "QUANTUM LIGHTNING 730A", 3 },
+ { "QUANTUM FIREBALL", 3 }, /* For models 540/640/1080/1280 */
+ /* 1080A works fine in mode4 with triton */
+ { NULL, 0 }
+};
+
+/*
+ * This routine searches the ide_pio_blacklist for an entry
+ * matching the start/whole of the supplied model name.
+ *
+ * Returns -1 if no match found.
+ * Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
+ */
+int ide_scan_pio_blacklist (char *model)
+{
+ struct ide_pio_info *p;
+
+ for (p = ide_pio_blacklist; p->name != NULL; p++) {
+ if (strncmp(p->name, model, strlen(p->name)) == 0)
+ return p->pio;
+ }
+ return -1;
+}
+
+/*
+ * This routine returns the recommended PIO settings for a given drive,
+ * based on the drive->id information and the ide_pio_blacklist[].
+ * This is used by most chipset support modules when "auto-tuning".
+ */
+
+/*
+ * Drive PIO mode auto selection
+ */
+byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d)
+{
+ int pio_mode;
+ int cycle_time = 0;
+ int use_iordy = 0;
+ struct hd_driveid* id = drive->id;
+ int overridden = 0;
+ int blacklisted = 0;
+
+ if (mode_wanted != 255) {
+ pio_mode = mode_wanted;
+ } else if (!drive->id) {
+ pio_mode = 0;
+ } else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) {
+ overridden = 1;
+ blacklisted = 1;
+ use_iordy = (pio_mode > 2);
+ } else {
+ pio_mode = id->tPIO;
+ if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */
+ pio_mode = 2;
+ overridden = 1;
+ }
+ if (id->field_valid & 2) { /* drive implements ATA2? */
+ if (id->capability & 8) { /* drive supports use_iordy? */
+ use_iordy = 1;
+ cycle_time = id->eide_pio_iordy;
+ if (id->eide_pio_modes & 7) {
+ overridden = 0;
+ if (id->eide_pio_modes & 4)
+ pio_mode = 5;
+ else if (id->eide_pio_modes & 2)
+ pio_mode = 4;
+ else
+ pio_mode = 3;
+ }
+ } else {
+ cycle_time = id->eide_pio;
+ }
+ }
+
+ /*
+ * Conservative "downgrade" for all pre-ATA2 drives
+ */
+ if (pio_mode && pio_mode < 4) {
+ pio_mode--;
+ overridden = 1;
+#if 0
+ use_iordy = (pio_mode > 2);
+#endif
+ if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time)
+ cycle_time = 0; /* use standard timing */
+ }
+ }
+ if (pio_mode > max_mode) {
+ pio_mode = max_mode;
+ cycle_time = 0;
+ }
+ if (d) {
+ d->pio_mode = pio_mode;
+ d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time;
+ d->use_iordy = use_iordy;
+ d->overridden = overridden;
+ d->blacklisted = blacklisted;
+ }
+ return pio_mode;
+}
+
+#endif /* _IDE_C */
+#endif /* defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) */
+#endif /* _IDE_MODES_H */
diff --git a/drivers/block/linear.c b/drivers/block/linear.c
new file mode 100644
index 000000000..6fc6960aa
--- /dev/null
+++ b/drivers/block/linear.c
@@ -0,0 +1,200 @@
+
+/*
+ linear.c : Multiple Devices driver for Linux
+ Copyright (C) 1994-96 Marc ZYNGIER
+ <zyngier@ufr-info-p7.ibp.fr> or
+ <maz@gloups.fdn.fr>
+
+ Linear mode management functions.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ You should have received a copy of the GNU General Public License
+ (for example /usr/src/linux/COPYING); if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+
+#include <linux/md.h>
+#include <linux/malloc.h>
+
+#include "linear.h"
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+static int linear_run (int minor, struct md_dev *mddev)
+{
+ int cur=0, i, size, dev0_size, nb_zone;
+ struct linear_data *data;
+
+ MOD_INC_USE_COUNT;
+
+ mddev->private=kmalloc (sizeof (struct linear_data), GFP_KERNEL);
+ data=(struct linear_data *) mddev->private;
+
+ /*
+ Find out the smallest device. This was previously done
+ at registry time, but since it violates modularity,
+ I moved it here... Any comment ? ;-)
+ */
+
+ data->smallest=mddev->devices;
+ for (i=1; i<mddev->nb_dev; i++)
+ if (data->smallest->size > mddev->devices[i].size)
+ data->smallest=mddev->devices+i;
+
+ nb_zone=data->nr_zones=
+ md_size[minor]/data->smallest->size +
+ (md_size[minor]%data->smallest->size ? 1 : 0);
+
+ data->hash_table=kmalloc (sizeof (struct linear_hash)*nb_zone, GFP_KERNEL);
+
+ size=mddev->devices[cur].size;
+
+ i=0;
+ while (cur<mddev->nb_dev)
+ {
+ data->hash_table[i].dev0=mddev->devices+cur;
+
+ if (size>=data->smallest->size) /* If we completely fill the slot */
+ {
+ data->hash_table[i++].dev1=NULL;
+ size-=data->smallest->size;
+
+ if (!size)
+ {
+ if (++cur==mddev->nb_dev) continue;
+ size=mddev->devices[cur].size;
+ }
+
+ continue;
+ }
+
+ if (++cur==mddev->nb_dev) /* Last dev, set dev1 as NULL */
+ {
+ data->hash_table[i].dev1=NULL;
+ continue;
+ }
+
+ dev0_size=size; /* Here, we use a 2nd dev to fill the slot */
+ size=mddev->devices[cur].size;
+ data->hash_table[i++].dev1=mddev->devices+cur;
+ size-=(data->smallest->size - dev0_size);
+ }
+
+ return 0;
+}
+
+static int linear_stop (int minor, struct md_dev *mddev)
+{
+ struct linear_data *data=(struct linear_data *) mddev->private;
+
+ kfree (data->hash_table);
+ kfree (data);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+
+static int linear_map (struct md_dev *mddev, kdev_t *rdev,
+ unsigned long *rsector, unsigned long size)
+{
+ struct linear_data *data=(struct linear_data *) mddev->private;
+ struct linear_hash *hash;
+ struct real_dev *tmp_dev;
+ long block;
+
+ block=*rsector >> 1;
+ hash=data->hash_table+(block/data->smallest->size);
+
+ if (block >= (hash->dev0->size + hash->dev0->offset))
+ {
+ if (!hash->dev1)
+ {
+ printk ("linear_map : hash->dev1==NULL for block %ld\n", block);
+ return (-1);
+ }
+
+ tmp_dev=hash->dev1;
+ }
+ else
+ tmp_dev=hash->dev0;
+
+ if (block >= (tmp_dev->size + tmp_dev->offset) || block < tmp_dev->offset)
+ printk ("Block %ld out of bounds on dev %s size %d offset %d\n",
+ block, kdevname(tmp_dev->dev), tmp_dev->size, tmp_dev->offset);
+
+ *rdev=tmp_dev->dev;
+ *rsector=(block-(tmp_dev->offset)) << 1;
+
+ return (0);
+}
+
+static int linear_status (char *page, int minor, struct md_dev *mddev)
+{
+ int sz=0;
+
+#undef MD_DEBUG
+#ifdef MD_DEBUG
+ int j;
+ struct linear_data *data=(struct linear_data *) mddev->private;
+
+ sz+=sprintf (page+sz, " ");
+ for (j=0; j<data->nr_zones; j++)
+ {
+ sz+=sprintf (page+sz, "[%s",
+ partition_name (data->hash_table[j].dev0->dev));
+
+ if (data->hash_table[j].dev1)
+ sz+=sprintf (page+sz, "/%s] ",
+ partition_name(data->hash_table[j].dev1->dev));
+ else
+ sz+=sprintf (page+sz, "] ");
+ }
+
+ sz+=sprintf (page+sz, "\n");
+#endif
+ return sz;
+}
+
+
+static struct md_personality linear_personality=
+{
+ "linear",
+ linear_map,
+ linear_run,
+ linear_stop,
+ linear_status,
+ NULL, /* no ioctls */
+ 0
+};
+
+
+#ifndef MODULE
+
+void linear_init (void)
+{
+ register_md_personality (LINEAR, &linear_personality);
+}
+
+#else
+
+int init_module (void)
+{
+ return (register_md_personality (LINEAR, &linear_personality));
+}
+
+void cleanup_module (void)
+{
+ unregister_md_personality (LINEAR);
+}
+
+#endif
diff --git a/drivers/block/linear.h b/drivers/block/linear.h
new file mode 100644
index 000000000..1146d8329
--- /dev/null
+++ b/drivers/block/linear.h
@@ -0,0 +1,16 @@
+#ifndef _LINEAR_H
+#define _LINEAR_H
+
+struct linear_hash
+{
+ struct real_dev *dev0, *dev1;
+};
+
+struct linear_data
+{
+ struct linear_hash *hash_table; /* Dynamically allocated */
+ struct real_dev *smallest;
+ int nr_zones;
+};
+
+#endif
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 53c1f97a0..e7ad12604 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 1994, Karl Keyte: Added support for disk statistics
+ * Copyright (C) 1996, ACN S.A: Added support for flash devices
*/
/*
@@ -19,7 +20,7 @@
#include <asm/system.h>
#include <asm/io.h>
-#include "blk.h"
+#include <linux/blk.h>
/*
* The request-struct contains all necessary data
@@ -28,6 +29,12 @@
static struct request all_requests[NR_REQUEST];
/*
+ * The "disk" task queue is used to start the actual requests
+ * after a plug
+ */
+DECLARE_TASK_QUEUE(tq_disk);
+
+/*
* used to wait on when there are no free requests
*/
struct wait_queue * wait_for_request = NULL;
@@ -37,34 +44,10 @@ struct wait_queue * wait_for_request = NULL;
int read_ahead[MAX_BLKDEV] = {0, };
/* blk_dev_struct is:
- * do_request-address
- * next-request
+ * *request_fn
+ * *current_request
*/
-struct blk_dev_struct blk_dev[MAX_BLKDEV] = {
- { NULL, NULL }, /* 0 no_dev */
- { NULL, NULL }, /* 1 dev mem */
- { NULL, NULL }, /* 2 dev fd */
- { NULL, NULL }, /* 3 dev ide0 or hd */
- { NULL, NULL }, /* 4 dev ttyx */
- { NULL, NULL }, /* 5 dev tty */
- { NULL, NULL }, /* 6 dev lp */
- { NULL, NULL }, /* 7 dev pipes */
- { NULL, NULL }, /* 8 dev sd */
- { NULL, NULL }, /* 9 dev st */
- { NULL, NULL }, /* 10 */
- { NULL, NULL }, /* 11 */
- { NULL, NULL }, /* 12 */
- { NULL, NULL }, /* 13 */
- { NULL, NULL }, /* 14 */
- { NULL, NULL }, /* 15 */
- { NULL, NULL }, /* 16 */
- { NULL, NULL }, /* 17 */
- { NULL, NULL }, /* 18 */
- { NULL, NULL }, /* 19 */
- { NULL, NULL }, /* 20 */
- { NULL, NULL }, /* 21 */
- { NULL, NULL } /* 22 dev ide1 */
-};
+struct blk_dev_struct blk_dev[MAX_BLKDEV]; /* initialized by blk_dev_init() */
/*
* blk_size contains the size of all block-devices in units of 1024 byte
@@ -100,40 +83,37 @@ int * blksize_size[MAX_BLKDEV] = { NULL, NULL, };
int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
/*
- * "plug" the device if there are no outstanding requests: this will
- * force the transfer to start only after we have put all the requests
- * on the list.
+ * remove the plug and let it rip..
*/
-static void plug_device(struct blk_dev_struct * dev, struct request * plug)
+void unplug_device(void * data)
{
+ struct blk_dev_struct * dev = (struct blk_dev_struct *) data;
unsigned long flags;
- plug->dev = -1;
- plug->cmd = -1;
- plug->next = NULL;
save_flags(flags);
cli();
- if (!dev->current_request)
- dev->current_request = plug;
+ if (dev->current_request == &dev->plug) {
+ struct request * next = dev->plug.next;
+ dev->current_request = next;
+ if (next) {
+ dev->plug.next = NULL;
+ (dev->request_fn)();
+ }
+ }
restore_flags(flags);
}
/*
- * remove the plug and let it rip..
+ * "plug" the device if there are no outstanding requests: this will
+ * force the transfer to start only after we have put all the requests
+ * on the list.
+ *
+ * This is called with interrupts off and no requests on the queue.
*/
-static void unplug_device(struct blk_dev_struct * dev)
+static inline void plug_device(struct blk_dev_struct * dev)
{
- struct request * req;
- unsigned long flags;
-
- save_flags(flags);
- cli();
- req = dev->current_request;
- if (req && req->dev == -1 && req->cmd == -1) {
- dev->current_request = req->next;
- (dev->request_fn)();
- }
- restore_flags(flags);
+ dev->current_request = &dev->plug;
+ queue_task_irq_off(&dev->plug_tq, &tq_disk);
}
/*
@@ -141,7 +121,7 @@ static void unplug_device(struct blk_dev_struct * dev)
* NOTE: interrupts must be disabled on the way in, and will still
* be disabled on the way out.
*/
-static inline struct request * get_request(int n, int dev)
+static inline struct request * get_request(int n, kdev_t dev)
{
static struct request *prev_found = NULL, *prev_limit = NULL;
register struct request *req, *limit;
@@ -157,33 +137,34 @@ static inline struct request * get_request(int n, int dev)
req = prev_found;
for (;;) {
req = ((req > all_requests) ? req : limit) - 1;
- if (req->dev < 0)
+ if (req->rq_status == RQ_INACTIVE)
break;
if (req == prev_found)
return NULL;
}
prev_found = req;
- req->dev = dev;
+ req->rq_status = RQ_ACTIVE;
+ req->rq_dev = dev;
return req;
}
/*
* wait until a free request in the first N entries is available.
*/
-static struct request * __get_request_wait(int n, int dev)
+static struct request * __get_request_wait(int n, kdev_t dev)
{
register struct request *req;
struct wait_queue wait = { current, NULL };
add_wait_queue(&wait_for_request, &wait);
for (;;) {
- unplug_device(MAJOR(dev)+blk_dev);
current->state = TASK_UNINTERRUPTIBLE;
cli();
req = get_request(n, dev);
sti();
if (req)
break;
+ run_task_queue(&tq_disk);
schedule();
}
remove_wait_queue(&wait_for_request, &wait);
@@ -191,7 +172,7 @@ static struct request * __get_request_wait(int n, int dev)
return req;
}
-static inline struct request * get_request_wait(int n, int dev)
+static inline struct request * get_request_wait(int n, kdev_t dev)
{
register struct request *req;
@@ -207,7 +188,7 @@ static inline struct request * get_request_wait(int n, int dev)
static long ro_bits[MAX_BLKDEV][8];
-int is_read_only(int dev)
+int is_read_only(kdev_t dev)
{
int minor,major;
@@ -217,7 +198,7 @@ int is_read_only(int dev)
return ro_bits[major][minor >> 5] & (1 << (minor & 31));
}
-void set_device_ro(int dev,int flag)
+void set_device_ro(kdev_t dev,int flag)
{
int minor,major;
@@ -228,28 +209,50 @@ void set_device_ro(int dev,int flag)
else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
}
+static inline void drive_stat_acct(int cmd, unsigned long nr_sectors,
+ short disk_index)
+{
+ kstat.dk_drive[disk_index]++;
+ if (cmd == READ) {
+ kstat.dk_drive_rio[disk_index]++;
+ kstat.dk_drive_rblk[disk_index] += nr_sectors;
+ } else if (cmd == WRITE) {
+ kstat.dk_drive_wio[disk_index]++;
+ kstat.dk_drive_wblk[disk_index] += nr_sectors;
+ } else
+ printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n");
+}
+
/*
* add-request adds a request to the linked list.
* It disables interrupts so that it can muck with the
* request-lists in peace.
+ *
+ * By this point, req->cmd is always either READ/WRITE, never READA/WRITEA,
+ * which is important for drive_stat_acct() above.
*/
-static void add_request(struct blk_dev_struct * dev, struct request * req)
+
+void add_request(struct blk_dev_struct * dev, struct request * req)
{
struct request * tmp;
short disk_index;
- switch (MAJOR(req->dev)) {
- case SCSI_DISK_MAJOR: disk_index = (MINOR(req->dev) & 0x0070) >> 4;
- if (disk_index < 4)
- kstat.dk_drive[disk_index]++;
- break;
- case HD_MAJOR:
- case XT_DISK_MAJOR: disk_index = (MINOR(req->dev) & 0x0040) >> 6;
- kstat.dk_drive[disk_index]++;
- break;
- case IDE1_MAJOR: disk_index = ((MINOR(req->dev) & 0x0040) >> 6) + 2;
- kstat.dk_drive[disk_index]++;
- default: break;
+ switch (MAJOR(req->rq_dev)) {
+ case SCSI_DISK_MAJOR:
+ disk_index = (MINOR(req->rq_dev) & 0x0070) >> 4;
+ if (disk_index < 4)
+ drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
+ break;
+ case IDE0_MAJOR: /* same as HD_MAJOR */
+ case XT_DISK_MAJOR:
+ disk_index = (MINOR(req->rq_dev) & 0x0040) >> 6;
+ drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
+ break;
+ case IDE1_MAJOR:
+ disk_index = ((MINOR(req->rq_dev) & 0x0040) >> 6) + 2;
+ drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
+ default:
+ break;
}
req->next = NULL;
@@ -272,7 +275,7 @@ static void add_request(struct blk_dev_struct * dev, struct request * req)
tmp->next = req;
/* for SCSI devices, call request_fn unconditionally */
- if (scsi_major(MAJOR(req->dev)))
+ if (scsi_blk_major(MAJOR(req->rq_dev)))
(dev->request_fn)();
sti();
@@ -284,103 +287,138 @@ static void make_request(int major,int rw, struct buffer_head * bh)
struct request * req;
int rw_ahead, max_req;
-/* WRITEA/READA is special case - it is not really needed, so if the */
-/* buffer is locked, we just forget about it, else it's a normal read */
- rw_ahead = (rw == READA || rw == WRITEA);
- if (rw_ahead) {
- if (bh->b_lock)
- return;
- if (rw == READA)
- rw = READ;
- else
- rw = WRITE;
- }
- if (rw!=READ && rw!=WRITE) {
- printk("Bad block dev command, must be R/W/RA/WA\n");
- return;
- }
count = bh->b_size >> 9;
- sector = bh->b_blocknr * count;
- if (blk_size[major])
- if (blk_size[major][MINOR(bh->b_dev)] < (sector + count)>>1) {
- bh->b_dirt = bh->b_uptodate = 0;
- bh->b_req = 0;
- return;
- }
+ sector = bh->b_rsector;
+
/* Uhhuh.. Nasty dead-lock possible here.. */
- if (bh->b_lock)
+ if (buffer_locked(bh))
return;
/* Maybe the above fixes it, and maybe it doesn't boot. Life is interesting */
+
lock_buffer(bh);
- if ((rw == WRITE && !bh->b_dirt) || (rw == READ && bh->b_uptodate)) {
- unlock_buffer(bh);
- return;
- }
-/* we don't allow the write-requests to fill up the queue completely:
- * we want some room for reads: they take precedence. The last third
- * of the requests are only for reads.
- */
- max_req = (rw == READ) ? NR_REQUEST : ((NR_REQUEST*2)/3);
+ if (blk_size[major])
+ if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
+ bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO);
+ /* This may well happen - the kernel calls bread()
+ without checking the size of the device, e.g.,
+ when mounting a device. */
+ printk(KERN_INFO
+ "attempt to access beyond end of device\n");
+ printk(KERN_INFO "%s: rw=%d, want=%d, limit=%d\n",
+ kdevname(bh->b_rdev), rw,
+ (sector + count)>>1,
+ blk_size[major][MINOR(bh->b_rdev)]);
+ unlock_buffer(bh);
+ return;
+ }
+
+ rw_ahead = 0; /* normal case; gets changed below for READA/WRITEA */
+ switch (rw) {
+ case READA:
+ rw_ahead = 1;
+ rw = READ; /* drop into READ */
+ case READ:
+ if (buffer_uptodate(bh)) {
+ unlock_buffer(bh); /* Hmmph! Already have it */
+ return;
+ }
+ kstat.pgpgin++;
+ max_req = NR_REQUEST; /* reads take precedence */
+ break;
+ case WRITEA:
+ rw_ahead = 1;
+ rw = WRITE; /* drop into WRITE */
+ case WRITE:
+ if (!buffer_dirty(bh)) {
+ unlock_buffer(bh); /* Hmmph! Nothing to write */
+ return;
+ }
+ /* We don't allow the write-requests to fill up the
+ * queue completely: we want some room for reads,
+ * as they take precedence. The last third of the
+ * requests are only for reads.
+ */
+ kstat.pgpgout++;
+ max_req = (NR_REQUEST * 2) / 3;
+ break;
+ default:
+ printk(KERN_ERR "make_request: bad block dev cmd,"
+ " must be R/W/RA/WA\n");
+ unlock_buffer(bh);
+ return;
+ }
/* look for a free request. */
+ /* Loop uses two requests, 1 for loop and 1 for the real device.
+ * Cut max_req in half to avoid running out and deadlocking. */
+ if (major == LOOP_MAJOR)
+ max_req >>= 1;
+
+ /*
+ * Try to coalesce the new request with old requests
+ */
cli();
-
-/* The scsi disk drivers and the IDE driver completely remove the request
- * from the queue when they start processing an entry. For this reason
- * it is safe to continue to add links to the top entry for those devices.
- */
- if (( major == IDE0_MAJOR /* same as HD_MAJOR */
- || major == IDE1_MAJOR
- || major == FLOPPY_MAJOR
- || major == SCSI_DISK_MAJOR
- || major == SCSI_CDROM_MAJOR)
- && (req = blk_dev[major].current_request))
- {
-#ifdef CONFIG_BLK_DEV_HD
- if (major == HD_MAJOR || major == FLOPPY_MAJOR)
-#else
- if (major == FLOPPY_MAJOR)
-#endif CONFIG_BLK_DEV_HD
- req = req->next;
- while (req) {
- if (req->dev == bh->b_dev &&
- !req->sem &&
- req->cmd == rw &&
- req->sector + req->nr_sectors == sector &&
- req->nr_sectors < 244)
- {
+ req = blk_dev[major].current_request;
+ if (!req) {
+ /* MD and loop can't handle plugging without deadlocking */
+ if (major != MD_MAJOR && major != LOOP_MAJOR)
+ plug_device(blk_dev + major);
+ } else switch (major) {
+ case IDE0_MAJOR: /* same as HD_MAJOR */
+ case IDE1_MAJOR:
+ case FLOPPY_MAJOR:
+ case IDE2_MAJOR:
+ case IDE3_MAJOR:
+ /*
+ * The scsi disk and cdrom drivers completely remove the request
+ * from the queue when they start processing an entry. For this
+ * reason it is safe to continue to add links to the top entry for
+ * those devices.
+ *
+ * All other drivers need to jump over the first entry, as that
+ * entry may be busy being processed and we thus can't change it.
+ */
+ req = req->next;
+ if (!req)
+ break;
+ /* fall through */
+
+ case SCSI_DISK_MAJOR:
+ case SCSI_CDROM_MAJOR:
+
+ do {
+ if (req->sem)
+ continue;
+ if (req->cmd != rw)
+ continue;
+ if (req->nr_sectors >= 244)
+ continue;
+ if (req->rq_dev != bh->b_rdev)
+ continue;
+ /* Can we add it to the end of this request? */
+ if (req->sector + req->nr_sectors == sector) {
req->bhtail->b_reqnext = bh;
req->bhtail = bh;
- req->nr_sectors += count;
- mark_buffer_clean(bh);
- sti();
- return;
- }
-
- if (req->dev == bh->b_dev &&
- !req->sem &&
- req->cmd == rw &&
- req->sector - count == sector &&
- req->nr_sectors < 244)
- {
- req->nr_sectors += count;
+ /* or to the beginning? */
+ } else if (req->sector - count == sector) {
bh->b_reqnext = req->bh;
+ req->bh = bh;
req->buffer = bh->b_data;
req->current_nr_sectors = count;
req->sector = sector;
- mark_buffer_clean(bh);
- req->bh = bh;
- sti();
- return;
- }
-
- req = req->next;
- }
+ } else
+ continue;
+
+ req->nr_sectors += count;
+ mark_buffer_clean(bh);
+ sti();
+ return;
+ } while ((req = req->next) != NULL);
}
/* find an unused request. */
- req = get_request(max_req, bh->b_dev);
+ req = get_request(max_req, bh->b_rdev);
sti();
/* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
@@ -389,7 +427,7 @@ static void make_request(int major,int rw, struct buffer_head * bh)
unlock_buffer(bh);
return;
}
- req = __get_request_wait(max_req, bh->b_dev);
+ req = __get_request_wait(max_req, bh->b_rdev);
}
/* fill up the request-info, and add it to the queue */
@@ -406,38 +444,6 @@ static void make_request(int major,int rw, struct buffer_head * bh)
add_request(major+blk_dev,req);
}
-void ll_rw_page(int rw, int dev, unsigned long page, char * buffer)
-{
- struct request * req;
- unsigned int major = MAJOR(dev);
- unsigned long sector = page * (PAGE_SIZE / 512);
- struct semaphore sem = MUTEX_LOCKED;
-
- if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
- printk("Trying to read nonexistent block-device %04x (%ld)\n",dev,sector);
- return;
- }
- if (rw!=READ && rw!=WRITE)
- panic("Bad block dev command, must be R/W");
- if (rw == WRITE && is_read_only(dev)) {
- printk("Can't page to read-only device 0x%X\n",dev);
- return;
- }
- req = get_request_wait(NR_REQUEST, dev);
-/* fill up the request-info, and add it to the queue */
- req->cmd = rw;
- req->errors = 0;
- req->sector = sector;
- req->nr_sectors = PAGE_SIZE / 512;
- req->current_nr_sectors = PAGE_SIZE / 512;
- req->buffer = buffer;
- req->sem = &sem;
- req->bh = NULL;
- req->next = NULL;
- add_request(major+blk_dev,req);
- down(&sem);
-}
-
/* This function can be used to request a number of buffers from a block
device. Currently the only restriction is that all buffers must belong to
the same device */
@@ -445,7 +451,6 @@ void ll_rw_page(int rw, int dev, unsigned long page, char * buffer)
void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
{
unsigned int major;
- struct request plug;
int correct_size;
struct blk_dev_struct * dev;
int i;
@@ -455,15 +460,15 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
bh++;
if (--nr <= 0)
return;
- };
+ }
dev = NULL;
if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
dev = blk_dev + major;
if (!dev || !dev->request_fn) {
- printk(
- "ll_rw_block: Trying to read nonexistent block-device %04lX (%ld)\n",
- (unsigned long) bh[0]->b_dev, bh[0]->b_blocknr);
+ printk(KERN_ERR
+ "ll_rw_block: Trying to read nonexistent block-device %s (%ld)\n",
+ kdevname(bh[0]->b_dev), bh[0]->b_blocknr);
goto sorry;
}
@@ -478,127 +483,210 @@ void ll_rw_block(int rw, int nr, struct buffer_head * bh[])
/* Verify requested block sizes. */
for (i = 0; i < nr; i++) {
if (bh[i] && bh[i]->b_size != correct_size) {
- printk(
- "ll_rw_block: only %d-char blocks implemented (%lu)\n",
+ printk(KERN_NOTICE "ll_rw_block: device %s: "
+ "only %d-char blocks implemented (%lu)\n",
+ kdevname(bh[0]->b_dev),
correct_size, bh[i]->b_size);
goto sorry;
}
+
+ /* Md remaps blocks now */
+ bh[i]->b_rdev = bh[i]->b_dev;
+ bh[i]->b_rsector=bh[i]->b_blocknr*(bh[i]->b_size >> 9);
+#ifdef CONFIG_BLK_DEV_MD
+ if (major==MD_MAJOR &&
+ md_map (MINOR(bh[i]->b_dev), &bh[i]->b_rdev,
+ &bh[i]->b_rsector, bh[i]->b_size >> 9)) {
+ printk (KERN_ERR
+ "Bad md_map in ll_rw_block\n");
+ goto sorry;
+ }
+#endif
}
if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
- printk("Can't write to read-only device 0x%X\n",bh[0]->b_dev);
+ printk(KERN_NOTICE "Can't write to read-only device %s\n",
+ kdevname(bh[0]->b_dev));
goto sorry;
}
- /* If there are no pending requests for this device, then we insert
- a dummy request for that device. This will prevent the request
- from starting until we have shoved all of the blocks into the
- queue, and then we let it rip. */
-
- if (nr > 1)
- plug_device(dev, &plug);
for (i = 0; i < nr; i++) {
if (bh[i]) {
- bh[i]->b_req = 1;
- make_request(major, rw, bh[i]);
- if (rw == READ || rw == READA)
- kstat.pgpgin++;
- else
- kstat.pgpgout++;
+ set_bit(BH_Req, &bh[i]->b_state);
+ make_request(MAJOR(bh[i]->b_rdev), rw, bh[i]);
}
}
- unplug_device(dev);
return;
sorry:
for (i = 0; i < nr; i++) {
- if (bh[i])
- bh[i]->b_dirt = bh[i]->b_uptodate = 0;
+ if (bh[i]) {
+ clear_bit(BH_Dirty, &bh[i]->b_state);
+ clear_bit(BH_Uptodate, &bh[i]->b_state);
+ }
}
return;
}
-void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buf)
+void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buf)
{
- int i;
+ int i, j;
int buffersize;
- struct request * req;
+ int max_req;
+ unsigned long rsector;
+ kdev_t rdev;
+ struct request * req[8];
unsigned int major = MAJOR(dev);
struct semaphore sem = MUTEX_LOCKED;
if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
- printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
- return;
- }
-
- if (rw!=READ && rw!=WRITE) {
- printk("ll_rw_swap: bad block dev command, must be R/W");
+ printk(KERN_NOTICE "ll_rw_swap_file: trying to swap to"
+ " nonexistent block-device\n");
return;
}
- if (rw == WRITE && is_read_only(dev)) {
- printk("Can't swap to read-only device 0x%X\n",dev);
- return;
+ max_req = NR_REQUEST;
+ switch (rw) {
+ case READ:
+ break;
+ case WRITE:
+ max_req = (NR_REQUEST * 2) / 3;
+ if (is_read_only(dev)) {
+ printk(KERN_NOTICE
+ "Can't swap to read-only device %s\n",
+ kdevname(dev));
+ return;
+ }
+ break;
+ default:
+ panic("ll_rw_swap: bad block dev cmd, must be R/W");
}
-
buffersize = PAGE_SIZE / nb;
- for (i=0; i<nb; i++, buf += buffersize)
+ if (major == LOOP_MAJOR)
+ max_req >>= 1;
+ for (j=0, i=0; i<nb;)
{
- req = get_request_wait(NR_REQUEST, dev);
- req->cmd = rw;
- req->errors = 0;
- req->sector = (b[i] * buffersize) >> 9;
- req->nr_sectors = buffersize >> 9;
- req->current_nr_sectors = buffersize >> 9;
- req->buffer = buf;
- req->sem = &sem;
- req->bh = NULL;
- req->next = NULL;
- add_request(major+blk_dev,req);
- down(&sem);
+ for (; j < 8 && i < nb; j++, i++, buf += buffersize)
+ {
+ rdev = dev;
+ rsector = (b[i] * buffersize) >> 9;
+#ifdef CONFIG_BLK_DEV_MD
+ if (major==MD_MAJOR &&
+ md_map (MINOR(dev), &rdev,
+ &rsector, buffersize >> 9)) {
+ printk (KERN_ERR
+ "Bad md_map in ll_rw_swap_file\n");
+ return;
+ }
+#endif
+
+ if (j == 0) {
+ req[j] = get_request_wait(max_req, rdev);
+ } else {
+ cli();
+ req[j] = get_request(max_req, rdev);
+ sti();
+ if (req[j] == NULL)
+ break;
+ }
+ req[j]->cmd = rw;
+ req[j]->errors = 0;
+ req[j]->sector = rsector;
+ req[j]->nr_sectors = buffersize >> 9;
+ req[j]->current_nr_sectors = buffersize >> 9;
+ req[j]->buffer = buf;
+ req[j]->sem = &sem;
+ req[j]->bh = NULL;
+ req[j]->next = NULL;
+ add_request(MAJOR(rdev)+blk_dev,req[j]);
+ }
+ run_task_queue(&tq_disk);
+ while (j > 0) {
+ j--;
+ down(&sem);
+ }
}
}
-long blk_dev_init(long mem_start, long mem_end)
+int blk_dev_init(void)
{
struct request * req;
+ struct blk_dev_struct *dev;
+
+ for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) {
+ dev->request_fn = NULL;
+ dev->current_request = NULL;
+ dev->plug.rq_status = RQ_INACTIVE;
+ dev->plug.cmd = -1;
+ dev->plug.next = NULL;
+ dev->plug_tq.routine = &unplug_device;
+ dev->plug_tq.data = dev;
+ }
req = all_requests + NR_REQUEST;
while (--req >= all_requests) {
- req->dev = -1;
+ req->rq_status = RQ_INACTIVE;
req->next = NULL;
}
memset(ro_bits,0,sizeof(ro_bits));
-#ifdef CONFIG_BLK_DEV_HD
- mem_start = hd_init(mem_start,mem_end);
+#ifdef CONFIG_BLK_DEV_RAM
+ rd_init();
#endif
-#ifdef CONFIG_BLK_DEV_IDE
- mem_start = ide_init(mem_start,mem_end);
-#endif
-#ifdef CONFIG_BLK_DEV_XD
- mem_start = xd_init(mem_start,mem_end);
-#endif
-#ifdef CONFIG_CDU31A
- mem_start = cdu31a_init(mem_start,mem_end);
+#ifdef CONFIG_BLK_DEV_LOOP
+ loop_init();
#endif
-#ifdef CONFIG_CDU535
- mem_start = sony535_init(mem_start,mem_end);
+#ifdef CONFIG_CDI_INIT
+ cdi_init(); /* this MUST precede ide_init */
+#endif CONFIG_CDI_INIT
+#ifdef CONFIG_BLK_DEV_IDE
+ ide_init(); /* this MUST precede hd_init */
#endif
-#ifdef CONFIG_MCD
- mem_start = mcd_init(mem_start,mem_end);
+#ifdef CONFIG_BLK_DEV_HD
+ hd_init();
#endif
-#ifdef CONFIG_AZTCD
- mem_start = aztcd_init(mem_start,mem_end);
+#ifdef CONFIG_BLK_DEV_XD
+ xd_init();
#endif
#ifdef CONFIG_BLK_DEV_FD
floppy_init();
#else
outb_p(0xc, 0x3f2);
#endif
+#ifdef CONFIG_ACN_MIPS_BOARD
+ flash_devs_init();
+#endif
+#ifdef CONFIG_CDU31A
+ cdu31a_init();
+#endif CONFIG_CDU31A
+#ifdef CONFIG_MCD
+ mcd_init();
+#endif CONFIG_MCD
+#ifdef CONFIG_MCDX
+ mcdx_init();
+#endif CONFIG_MCDX
#ifdef CONFIG_SBPCD
- mem_start = sbpcd_init(mem_start, mem_end);
+ sbpcd_init();
#endif CONFIG_SBPCD
- if (ramdisk_size)
- mem_start += rd_init(mem_start, ramdisk_size*1024);
- return mem_start;
+#ifdef CONFIG_AZTCD
+ aztcd_init();
+#endif CONFIG_AZTCD
+#ifdef CONFIG_CDU535
+ sony535_init();
+#endif CONFIG_CDU535
+#ifdef CONFIG_GSCD
+ gscd_init();
+#endif CONFIG_GSCD
+#ifdef CONFIG_CM206
+ cm206_init();
+#endif
+#ifdef CONFIG_OPTCD
+ optcd_init();
+#endif CONFIG_OPTCD
+#ifdef CONFIG_SJCD
+ sjcd_init();
+#endif CONFIG_SJCD
+#ifdef CONFIG_BLK_DEV_MD
+ md_init();
+#endif CONFIG_BLK_DEV_MD
+ return 0;
}
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
new file mode 100644
index 000000000..1030a53a3
--- /dev/null
+++ b/drivers/block/loop.c
@@ -0,0 +1,568 @@
+/*
+ * linux/drivers/block/loop.c
+ *
+ * Written by Theodore Ts'o, 3/29/93
+ *
+ * Copyright 1993 by Theodore Ts'o. Redistribution of this file is
+ * permitted under the GNU Public License.
+ *
+ * more DES encryption plus IDEA encryption by Nicholas J. Leon, June 20, 1996
+ * DES encryption plus some minor changes by Werner Almesberger, 30-MAY-1993
+ *
+ * Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May 1994
+ *
+ * Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996
+ */
+
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/major.h>
+
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_BLK_DEV_LOOP_DES
+#include <linux/des.h>
+#endif
+
+#ifdef CONFIG_BLK_DEV_LOOP_IDEA
+#include <linux/idea.h>
+#endif
+
+#include <linux/loop.h> /* must follow des.h */
+
+#define MAJOR_NR LOOP_MAJOR
+
+#define DEVICE_NAME "loop"
+#define DEVICE_REQUEST do_lo_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+#define DEVICE_NO_RANDOM
+#define TIMEOUT_VALUE (6 * HZ)
+#include <linux/blk.h>
+
+#define MAX_LOOP 8
+static struct loop_device loop_dev[MAX_LOOP];
+static int loop_sizes[MAX_LOOP];
+static int loop_blksizes[MAX_LOOP];
+
+/*
+ * Transfer functions
+ */
+static int transfer_none(struct loop_device *lo, int cmd, char *raw_buf,
+ char *loop_buf, int size)
+{
+ if (cmd == READ)
+ memcpy(loop_buf, raw_buf, size);
+ else
+ memcpy(raw_buf, loop_buf, size);
+ return 0;
+}
+
+static int transfer_xor(struct loop_device *lo, int cmd, char *raw_buf,
+ char *loop_buf, int size)
+{
+ char *in, *out, *key;
+ int i, keysize;
+
+ if (cmd == READ) {
+ in = raw_buf;
+ out = loop_buf;
+ } else {
+ in = loop_buf;
+ out = raw_buf;
+ }
+ key = lo->lo_encrypt_key;
+ keysize = lo->lo_encrypt_key_size;
+ for (i=0; i < size; i++)
+ *out++ = *in++ ^ key[(i & 511) % keysize];
+ return 0;
+}
+
+#ifdef DES_AVAILABLE
+static int transfer_des(struct loop_device *lo, int cmd, char *raw_buf,
+ char *loop_buf, int size)
+{
+ unsigned long tmp[2];
+ unsigned long x0,x1,p0,p1;
+
+ if (size & 7)
+ return -EINVAL;
+ x0 = lo->lo_des_init[0];
+ x1 = lo->lo_des_init[1];
+ while (size) {
+ if (cmd == READ) {
+ tmp[0] = (p0 = ((unsigned long *) raw_buf)[0])^x0;
+ tmp[1] = (p1 = ((unsigned long *) raw_buf)[1])^x1;
+ des_ecb_encrypt((des_cblock *) tmp,(des_cblock *)
+ loop_buf,lo->lo_des_key,DES_ENCRYPT);
+ x0 = p0^((unsigned long *) loop_buf)[0];
+ x1 = p1^((unsigned long *) loop_buf)[1];
+ }
+ else {
+ p0 = ((unsigned long *) loop_buf)[0];
+ p1 = ((unsigned long *) loop_buf)[1];
+ des_ecb_encrypt((des_cblock *) loop_buf,(des_cblock *)
+ raw_buf,lo->lo_des_key,DES_DECRYPT);
+ ((unsigned long *) raw_buf)[0] ^= x0;
+ ((unsigned long *) raw_buf)[1] ^= x1;
+ x0 = p0^((unsigned long *) raw_buf)[0];
+ x1 = p1^((unsigned long *) raw_buf)[1];
+ }
+ size -= 8;
+ raw_buf += 8;
+ loop_buf += 8;
+ }
+ return 0;
+}
+#endif
+
+#ifdef IDEA_AVAILABLE
+
+extern void idea_encrypt_block(idea_key,char *,char *,int);
+
+static int transfer_idea(struct loop_device *lo, int cmd, char *raw_buf,
+ char *loop_buf, int size)
+{
+ if (cmd==READ) {
+ idea_encrypt_block(lo->lo_idea_en_key,raw_buf,loop_buf,size);
+ }
+ else {
+ idea_encrypt_block(lo->lo_idea_de_key,loop_buf,raw_buf,size);
+ }
+ return 0;
+}
+#endif
+
+static transfer_proc_t xfer_funcs[MAX_LOOP] = {
+ transfer_none, /* LO_CRYPT_NONE */
+ transfer_xor, /* LO_CRYPT_XOR */
+#ifdef DES_AVAILABLE
+ transfer_des, /* LO_CRYPT_DES */
+#else
+ NULL, /* LO_CRYPT_DES */
+#endif
+#ifdef IDEA_AVAILABLE /* LO_CRYPT_IDEA */
+ transfer_idea
+#else
+ NULL
+#endif
+};
+
+
+#define MAX_DISK_SIZE 1024*1024*1024
+
+
+static void figure_loop_size(struct loop_device *lo)
+{
+ int size;
+
+ if (S_ISREG(lo->lo_inode->i_mode))
+ size = (lo->lo_inode->i_size - lo->lo_offset) / BLOCK_SIZE;
+ else {
+ kdev_t lodev = lo->lo_device;
+ if (blk_size[MAJOR(lodev)])
+ size = blk_size[MAJOR(lodev)][MINOR(lodev)] -
+ lo->lo_offset / BLOCK_SIZE;
+ else
+ size = MAX_DISK_SIZE;
+ }
+
+ loop_sizes[lo->lo_number] = size;
+}
+
+static void do_lo_request(void)
+{
+ int real_block, block, offset, len, blksize, size;
+ char *dest_addr;
+ struct loop_device *lo;
+ struct buffer_head *bh;
+
+repeat:
+ INIT_REQUEST;
+ if (MINOR(CURRENT->rq_dev) >= MAX_LOOP)
+ goto error_out;
+ lo = &loop_dev[MINOR(CURRENT->rq_dev)];
+ if (!lo->lo_inode || !lo->transfer)
+ goto error_out;
+
+ blksize = BLOCK_SIZE;
+ if (blksize_size[MAJOR(lo->lo_device)]) {
+ blksize = blksize_size[MAJOR(lo->lo_device)][MINOR(lo->lo_device)];
+ if (!blksize)
+ blksize = BLOCK_SIZE;
+ }
+
+ dest_addr = CURRENT->buffer;
+
+ if (blksize < 512) {
+ block = CURRENT->sector * (512/blksize);
+ offset = 0;
+ } else {
+ block = CURRENT->sector / (blksize >> 9);
+ offset = (CURRENT->sector % (blksize >> 9)) << 9;
+ }
+ block += lo->lo_offset / blksize;
+ offset += lo->lo_offset % blksize;
+ if (offset > blksize) {
+ block++;
+ offset -= blksize;
+ }
+ len = CURRENT->current_nr_sectors << 9;
+
+ if (CURRENT->cmd == WRITE) {
+ if (lo->lo_flags & LO_FLAGS_READ_ONLY)
+ goto error_out;
+ } else if (CURRENT->cmd != READ) {
+ printk("unknown loop device command (%d)?!?", CURRENT->cmd);
+ goto error_out;
+ }
+ while (len > 0) {
+ real_block = block;
+ if (lo->lo_flags & LO_FLAGS_DO_BMAP) {
+ real_block = bmap(lo->lo_inode, block);
+ if (!real_block) {
+ printk("loop: block %d not present\n", block);
+ goto error_out;
+ }
+ }
+ bh = getblk(lo->lo_device, real_block, blksize);
+ if (!bh) {
+ printk("loop: device %s: getblk(-, %d, %d) returned NULL",
+ kdevname(lo->lo_device),
+ block, blksize);
+ goto error_out;
+ }
+ if (!buffer_uptodate(bh) && ((CURRENT->cmd == READ) ||
+ (offset || (len < blksize)))) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ brelse(bh);
+ goto error_out;
+ }
+ }
+ size = blksize - offset;
+ if (size > len)
+ size = len;
+
+ if ((lo->transfer)(lo, CURRENT->cmd, bh->b_data + offset,
+ dest_addr, size)) {
+ printk("loop: transfer error block %d\n", block);
+ brelse(bh);
+ goto error_out;
+ }
+ if (CURRENT->cmd == WRITE) {
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 1);
+ }
+ brelse(bh);
+ dest_addr += size;
+ len -= size;
+ offset = 0;
+ block++;
+ }
+ end_request(1);
+ goto repeat;
+error_out:
+ end_request(0);
+ goto repeat;
+}
+
+static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg)
+{
+ struct file *file;
+ struct inode *inode;
+
+ if (arg >= NR_OPEN || !(file = current->files->fd[arg]))
+ return -EBADF;
+ if (lo->lo_inode)
+ return -EBUSY;
+ inode = file->f_inode;
+ if (!inode) {
+ printk("loop_set_fd: NULL inode?!?\n");
+ return -EINVAL;
+ }
+ if (S_ISBLK(inode->i_mode)) {
+ int error = blkdev_open(inode, file);
+ if (error)
+ return error;
+ lo->lo_device = inode->i_rdev;
+ lo->lo_flags = 0;
+ } else if (S_ISREG(inode->i_mode)) {
+ lo->lo_device = inode->i_dev;
+ lo->lo_flags = LO_FLAGS_DO_BMAP;
+ } else
+ return -EINVAL;
+
+ if (IS_RDONLY (inode) || is_read_only(lo->lo_device)) {
+ lo->lo_flags |= LO_FLAGS_READ_ONLY;
+ set_device_ro(dev, 1);
+ } else {
+ invalidate_inode_pages (inode);
+ set_device_ro(dev, 0);
+ }
+
+ lo->lo_inode = inode;
+ lo->lo_inode->i_count++;
+ lo->transfer = NULL;
+ figure_loop_size(lo);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int loop_clr_fd(struct loop_device *lo, kdev_t dev)
+{
+ if (!lo->lo_inode)
+ return -ENXIO;
+ if (lo->lo_refcnt > 1) /* we needed one fd for the ioctl */
+ return -EBUSY;
+ if (S_ISBLK(lo->lo_inode->i_mode))
+ blkdev_release (lo->lo_inode);
+ iput(lo->lo_inode);
+ lo->lo_device = 0;
+ lo->lo_inode = NULL;
+ lo->lo_encrypt_type = 0;
+ lo->lo_offset = 0;
+ lo->lo_encrypt_key_size = 0;
+ memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
+ memset(lo->lo_name, 0, LO_NAME_SIZE);
+ loop_sizes[lo->lo_number] = 0;
+ invalidate_buffers(dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int loop_set_status(struct loop_device *lo, struct loop_info *arg)
+{
+ struct loop_info info;
+ int err;
+
+ if (!lo->lo_inode)
+ return -ENXIO;
+ if (!arg)
+ return -EINVAL;
+ err = verify_area(VERIFY_READ, arg, sizeof(info));
+ if (err)
+ return err;
+ copy_from_user(&info, arg, sizeof(info));
+ if ((unsigned int) info.lo_encrypt_key_size > LO_KEY_SIZE)
+ return -EINVAL;
+ switch (info.lo_encrypt_type) {
+ case LO_CRYPT_NONE:
+ break;
+ case LO_CRYPT_XOR:
+ if (info.lo_encrypt_key_size < 0)
+ return -EINVAL;
+ break;
+#ifdef DES_AVAILABLE
+ case LO_CRYPT_DES:
+ if (info.lo_encrypt_key_size != 8)
+ return -EINVAL;
+ des_set_key((des_cblock *) lo->lo_encrypt_key,
+ lo->lo_des_key);
+ memcpy(lo->lo_des_init,info.lo_init,8);
+ break;
+#endif
+#ifdef IDEA_AVAILABLE
+ case LO_CRYPT_IDEA:
+ {
+ uint16 tmpkey[8];
+
+ if (info.lo_encrypt_key_size != IDEAKEYSIZE)
+ return -EINVAL;
+ /* create key in lo-> from info.lo_encrypt_key */
+ memcpy(tmpkey,info.lo_encrypt_key,sizeof(tmpkey));
+ en_key_idea(tmpkey,lo->lo_idea_en_key);
+ de_key_idea(lo->lo_idea_en_key,lo->lo_idea_de_key);
+ break;
+ }
+#endif
+ default:
+ return -EINVAL;
+ }
+ lo->lo_offset = info.lo_offset;
+ strncpy(lo->lo_name, info.lo_name, LO_NAME_SIZE);
+ lo->lo_encrypt_type = info.lo_encrypt_type;
+ lo->transfer = xfer_funcs[lo->lo_encrypt_type];
+ lo->lo_encrypt_key_size = info.lo_encrypt_key_size;
+ if (info.lo_encrypt_key_size)
+ memcpy(lo->lo_encrypt_key, info.lo_encrypt_key,
+ info.lo_encrypt_key_size);
+ figure_loop_size(lo);
+ return 0;
+}
+
+static int loop_get_status(struct loop_device *lo, struct loop_info *arg)
+{
+ struct loop_info info;
+ int err;
+
+ if (!lo->lo_inode)
+ return -ENXIO;
+ if (!arg)
+ return -EINVAL;
+ err = verify_area(VERIFY_WRITE, arg, sizeof(info));
+ if (err)
+ return err;
+ memset(&info, 0, sizeof(info));
+ info.lo_number = lo->lo_number;
+ info.lo_device = kdev_t_to_nr(lo->lo_inode->i_dev);
+ info.lo_inode = lo->lo_inode->i_ino;
+ info.lo_rdevice = kdev_t_to_nr(lo->lo_device);
+ info.lo_offset = lo->lo_offset;
+ info.lo_flags = lo->lo_flags;
+ strncpy(info.lo_name, lo->lo_name, LO_NAME_SIZE);
+ info.lo_encrypt_type = lo->lo_encrypt_type;
+ if (lo->lo_encrypt_key_size && suser()) {
+ info.lo_encrypt_key_size = lo->lo_encrypt_key_size;
+ memcpy(info.lo_encrypt_key, lo->lo_encrypt_key,
+ lo->lo_encrypt_key_size);
+ }
+ copy_to_user(arg, &info, sizeof(info));
+ return 0;
+}
+
+static int lo_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct loop_device *lo;
+ int dev;
+
+ if (!inode)
+ return -EINVAL;
+ if (MAJOR(inode->i_rdev) != MAJOR_NR) {
+ printk("lo_ioctl: pseudo-major != %d\n", MAJOR_NR);
+ return -ENODEV;
+ }
+ dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_LOOP)
+ return -ENODEV;
+ lo = &loop_dev[dev];
+ switch (cmd) {
+ case LOOP_SET_FD:
+ return loop_set_fd(lo, inode->i_rdev, arg);
+ case LOOP_CLR_FD:
+ return loop_clr_fd(lo, inode->i_rdev);
+ case LOOP_SET_STATUS:
+ return loop_set_status(lo, (struct loop_info *) arg);
+ case LOOP_GET_STATUS:
+ return loop_get_status(lo, (struct loop_info *) arg);
+ case BLKGETSIZE: /* Return device size */
+ if (!lo->lo_inode)
+ return -ENXIO;
+ if (!arg)
+ return -EINVAL;
+ return put_user(loop_sizes[lo->lo_number] << 1, (long *) arg);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int lo_open(struct inode *inode, struct file *file)
+{
+ struct loop_device *lo;
+ int dev;
+
+ if (!inode)
+ return -EINVAL;
+ if (MAJOR(inode->i_rdev) != MAJOR_NR) {
+ printk("lo_open: pseudo-major != %d\n", MAJOR_NR);
+ return -ENODEV;
+ }
+ dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_LOOP)
+ return -ENODEV;
+ lo = &loop_dev[dev];
+ lo->lo_refcnt++;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void lo_release(struct inode *inode, struct file *file)
+{
+ struct loop_device *lo;
+ int dev;
+
+ if (!inode)
+ return;
+ if (MAJOR(inode->i_rdev) != MAJOR_NR) {
+ printk("lo_release: pseudo-major != %d\n", MAJOR_NR);
+ return;
+ }
+ dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_LOOP)
+ return;
+ fsync_dev(inode->i_rdev);
+ lo = &loop_dev[dev];
+ if (lo->lo_refcnt <= 0)
+ printk("lo_release: refcount(%d) <= 0\n", lo->lo_refcnt);
+ else {
+ lo->lo_refcnt--;
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+static struct file_operations lo_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ lo_ioctl, /* ioctl */
+ NULL, /* mmap */
+ lo_open, /* open */
+ lo_release /* release */
+};
+
+/*
+ * And now the modules code and kernel interface.
+ */
+#ifdef MODULE
+#define loop_init init_module
+#endif
+
+int
+loop_init( void ) {
+ int i;
+
+ if (register_blkdev(MAJOR_NR, "loop", &lo_fops)) {
+ printk("Unable to get major number %d for loop device\n",
+ MAJOR_NR);
+ return -EIO;
+ }
+#ifndef MODULE
+ printk("loop: registered device at major %d\n", MAJOR_NR);
+#ifdef DES_AVAILABLE
+ printk("loop: DES encryption available\n");
+#endif
+#ifdef IDEA_AVAILABLE
+ printk("loop: IDEA encryption available\n");
+#endif
+#endif
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ for (i=0; i < MAX_LOOP; i++) {
+ memset(&loop_dev[i], 0, sizeof(struct loop_device));
+ loop_dev[i].lo_number = i;
+ }
+ memset(&loop_sizes, 0, sizeof(loop_sizes));
+ memset(&loop_blksizes, 0, sizeof(loop_blksizes));
+ blk_size[MAJOR_NR] = loop_sizes;
+ blksize_size[MAJOR_NR] = loop_blksizes;
+
+ return 0;
+}
+
+#ifdef MODULE
+void
+cleanup_module( void ) {
+ if (unregister_blkdev(MAJOR_NR, "loop") != 0)
+ printk("loop: cleanup_module failed\n");
+}
+#endif
diff --git a/drivers/block/mcd.c b/drivers/block/mcd.c
deleted file mode 100644
index f20b4d0ab..000000000
--- a/drivers/block/mcd.c
+++ /dev/null
@@ -1,1522 +0,0 @@
-/*
- linux/kernel/blk_drv/mcd.c - Mitsumi CDROM driver
-
- Copyright (C) 1992 Martin Harriss
-
- martin@bdsi.com
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- HISTORY
-
- 0.1 First attempt - internal use only
- 0.2 Cleaned up delays and use of timer - alpha release
- 0.3 Audio support added
- 0.3.1 Changes for mitsumi CRMC LU005S march version
- (stud11@cc4.kuleuven.ac.be)
- 0.3.2 bug fixes to the ioctls and merged with ALPHA0.99-pl12
- (Jon Tombs <jon@robots.ox.ac.uk>)
- 0.3.3 Added more #defines and mcd_setup()
- (Jon Tombs <jon@gtex02.us.es>)
-
- October 1993 Bernd Huebner and Ruediger Helsch, Unifix Software GmbH,
- Braunschweig, Germany: Total rework to speed up data read operation.
- Also enabled definition of irq and address from bootstrap, using the
- environment. linux/init/main.c must be patched to export the env.
- November 93 added code for FX001 S,D (single & double speed).
- February 94 added code for broken M 5/6 series of 16-bit single speed.
-*/
-
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-
-/* #define REALLY_SLOW_IO */
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
-#define MAJOR_NR MITSUMI_CDROM_MAJOR
-#include "blk.h"
-#include <linux/mcd.h>
-
-#if 0
-static int mcd_sizes[] = { 0 };
-#endif
-
-static int mcdPresent = 0;
-
-#if 0
-#define TEST1 /* <int-..> */
-#define TEST2 /* do_mcd_req */
-#define TEST3 */ /* MCD_S_state */
-#define TEST4 /* QUICK_LOOP-counter */
-#define TEST5 */ /* port(1) state */
-#endif
-
-#if 1
-#define QUICK_LOOP_DELAY udelay(45) /* use udelay */
-#define QUICK_LOOP_COUNT 20
-#else
-#define QUICK_LOOP_DELAY
-#define QUICK_LOOP_COUNT 140 /* better wait constant time */
-#endif
-/* #define DOUBLE_QUICK_ONLY */
-
-#define CURRENT_VALID \
- (CURRENT && MAJOR(CURRENT -> dev) == MAJOR_NR && CURRENT -> cmd == READ \
- && CURRENT -> sector != -1)
-#define MFL_STATUSorDATA (MFL_STATUS | MFL_DATA)
-#define MCD_BUF_SIZ 16
-static volatile int mcd_transfer_is_active;
-static char mcd_buf[2048*MCD_BUF_SIZ]; /* buffer for block size conversion */
-static volatile int mcd_buf_bn[MCD_BUF_SIZ], mcd_next_bn;
-static volatile int mcd_buf_in, mcd_buf_out = -1;
-static volatile int mcd_error;
-static int mcd_open_count;
-enum mcd_state_e {
- MCD_S_IDLE, /* 0 */
- MCD_S_START, /* 1 */
- MCD_S_MODE, /* 2 */
- MCD_S_READ, /* 3 */
- MCD_S_DATA, /* 4 */
- MCD_S_STOP, /* 5 */
- MCD_S_STOPPING /* 6 */
-};
-static volatile enum mcd_state_e mcd_state = MCD_S_IDLE;
-static int mcd_mode = -1;
-static int MCMD_DATA_READ= MCMD_PLAY_READ;
-#define READ_TIMEOUT 3000
-#define WORK_AROUND_MITSUMI_BUG_92
-#define WORK_AROUND_MITSUMI_BUG_93
-#ifdef WORK_AROUND_MITSUMI_BUG_93
-int mitsumi_bug_93_wait = 0;
-#endif /* WORK_AROUND_MITSUMI_BUG_93 */
-
-static short mcd_port = MCD_BASE_ADDR;
-static int mcd_irq = MCD_INTR_NR;
-
-static int McdTimeout, McdTries;
-static struct wait_queue *mcd_waitq = NULL;
-
-static struct mcd_DiskInfo DiskInfo;
-static struct mcd_Toc Toc[MAX_TRACKS];
-static struct mcd_Play_msf mcd_Play;
-
-static int audioStatus;
-static char mcdDiskChanged;
-static char tocUpToDate;
-static char mcdVersion;
-
-static void mcd_transfer(void);
-static void mcd_poll(void);
-static void mcd_invalidate_buffers(void);
-static void do_mcd_request(void);
-static void hsg2msf(long hsg, struct msf *msf);
-static void bin2bcd(unsigned char *p);
-static int bcd2bin(unsigned char bcd);
-static int mcdStatus(void);
-static void sendMcdCmd(int cmd, struct mcd_Play_msf *params);
-static int getMcdStatus(int timeout);
-static int GetQChannelInfo(struct mcd_Toc *qp);
-static int updateToc(void);
-static int GetDiskInfo(void);
-static int GetToc(void);
-static int getValue(unsigned char *result);
-
-
-void mcd_setup(char *str, int *ints)
-{
- if (ints[0] > 0)
- mcd_port = ints[1];
- if (ints[0] > 1)
- mcd_irq = ints[2];
-#ifdef WORK_AROUND_MITSUMI_BUG_93
- if (ints[0] > 2)
- mitsumi_bug_93_wait = ints[3];
-#endif /* WORK_AROUND_MITSUMI_BUG_93 */
-}
-
-
-static int
-check_mcd_change(dev_t full_dev)
-{
- int retval, target;
-
-
-#if 1 /* the below is not reliable */
- return 0;
-#endif
- target = MINOR(full_dev);
-
- if (target > 0) {
- printk("mcd: Mitsumi CD-ROM request error: invalid device.\n");
- return 0;
- }
-
- retval = mcdDiskChanged;
- mcdDiskChanged = 0;
-
- return retval;
-}
-
-
-/*
- * Do a 'get status' command and get the result. Only use from the top half
- * because it calls 'getMcdStatus' which sleeps.
- */
-
-static int
-statusCmd(void)
-{
- int st, retry;
-
- for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
- {
-
- outb(MCMD_GET_STATUS, MCDPORT(0)); /* send get-status cmd */
- st = getMcdStatus(MCD_STATUS_DELAY);
- if (st != -1)
- break;
- }
-
- return st;
-}
-
-
-/*
- * Send a 'Play' command and get the status. Use only from the top half.
- */
-
-static int
-mcdPlay(struct mcd_Play_msf *arg)
-{
- int retry, st;
-
- for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
- {
- sendMcdCmd(MCMD_PLAY_READ, arg);
- st = getMcdStatus(2 * MCD_STATUS_DELAY);
- if (st != -1)
- break;
- }
-
- return st;
-}
-
-
-long
-msf2hsg(struct msf *mp)
-{
- return bcd2bin(mp -> frame)
- + bcd2bin(mp -> sec) * 75
- + bcd2bin(mp -> min) * 4500
- - 150;
-}
-
-
-static int
-mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
- unsigned long arg)
-{
- int i, st;
- struct mcd_Toc qInfo;
- struct cdrom_ti ti;
- struct cdrom_tochdr tocHdr;
- struct cdrom_msf msf;
- struct cdrom_tocentry entry;
- struct mcd_Toc *tocPtr;
- struct cdrom_subchnl subchnl;
- struct cdrom_volctrl volctrl;
-
- if (!ip)
- return -EINVAL;
-
- st = statusCmd();
- if (st < 0)
- return -EIO;
-
- if (!tocUpToDate)
- {
- i = updateToc();
- if (i < 0)
- return i; /* error reading TOC */
- }
-
- switch (cmd)
- {
- case CDROMSTART: /* Spin up the drive */
- /* Don't think we can do this. Even if we could,
- * I think the drive times out and stops after a while
- * anyway. For now, ignore it.
- */
-
- return 0;
-
- case CDROMSTOP: /* Spin down the drive */
- outb(MCMD_STOP, MCDPORT(0));
- i = getMcdStatus(MCD_STATUS_DELAY);
-
- /* should we do anything if it fails? */
-
- audioStatus = CDROM_AUDIO_NO_STATUS;
- return 0;
-
- case CDROMPAUSE: /* Pause the drive */
- if (audioStatus != CDROM_AUDIO_PLAY)
- return -EINVAL;
-
- outb(MCMD_STOP, MCDPORT(0));
- i = getMcdStatus(MCD_STATUS_DELAY);
-
- if (GetQChannelInfo(&qInfo) < 0)
- {
- /* didn't get q channel info */
-
- audioStatus = CDROM_AUDIO_NO_STATUS;
- return 0;
- }
-
- mcd_Play.start = qInfo.diskTime; /* remember restart point */
-
- audioStatus = CDROM_AUDIO_PAUSED;
- return 0;
-
- case CDROMRESUME: /* Play it again, Sam */
- if (audioStatus != CDROM_AUDIO_PAUSED)
- return -EINVAL;
-
- /* restart the drive at the saved position. */
-
- i = mcdPlay(&mcd_Play);
- if (i < 0)
- {
- audioStatus = CDROM_AUDIO_ERROR;
- return -EIO;
- }
-
- audioStatus = CDROM_AUDIO_PLAY;
- return 0;
-
- case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
-
- st = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
- if (st)
- return st;
-
- memcpy_fromfs(&ti, (void *) arg, sizeof ti);
-
- if (ti.cdti_trk0 < DiskInfo.first
- || ti.cdti_trk0 > DiskInfo.last
- || ti.cdti_trk1 < ti.cdti_trk0)
- {
- return -EINVAL;
- }
-
- if (ti.cdti_trk1 > DiskInfo.last)
- ti. cdti_trk1 = DiskInfo.last;
-
- mcd_Play.start = Toc[ti.cdti_trk0].diskTime;
- mcd_Play.end = Toc[ti.cdti_trk1 + 1].diskTime;
-
-#ifdef MCD_DEBUG
-printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
- mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
- mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
-#endif
-
- i = mcdPlay(&mcd_Play);
- if (i < 0)
- {
- audioStatus = CDROM_AUDIO_ERROR;
- return -EIO;
- }
-
- audioStatus = CDROM_AUDIO_PLAY;
- return 0;
-
- case CDROMPLAYMSF: /* Play starting at the given MSF address. */
-
- if (audioStatus == CDROM_AUDIO_PLAY) {
- outb(MCMD_STOP, MCDPORT(0));
- i = getMcdStatus(MCD_STATUS_DELAY);
- audioStatus = CDROM_AUDIO_NO_STATUS;
- }
-
- st = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
- if (st)
- return st;
-
- memcpy_fromfs(&msf, (void *) arg, sizeof msf);
-
- /* convert to bcd */
-
- bin2bcd(&msf.cdmsf_min0);
- bin2bcd(&msf.cdmsf_sec0);
- bin2bcd(&msf.cdmsf_frame0);
- bin2bcd(&msf.cdmsf_min1);
- bin2bcd(&msf.cdmsf_sec1);
- bin2bcd(&msf.cdmsf_frame1);
-
- mcd_Play.start.min = msf.cdmsf_min0;
- mcd_Play.start.sec = msf.cdmsf_sec0;
- mcd_Play.start.frame = msf.cdmsf_frame0;
- mcd_Play.end.min = msf.cdmsf_min1;
- mcd_Play.end.sec = msf.cdmsf_sec1;
- mcd_Play.end.frame = msf.cdmsf_frame1;
-
-#ifdef MCD_DEBUG
-printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n",
-mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame,
-mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame);
-#endif
-
- i = mcdPlay(&mcd_Play);
- if (i < 0)
- {
- audioStatus = CDROM_AUDIO_ERROR;
- return -EIO;
- }
-
- audioStatus = CDROM_AUDIO_PLAY;
- return 0;
-
- case CDROMREADTOCHDR: /* Read the table of contents header */
- st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr);
- if (st)
- return st;
-
- tocHdr.cdth_trk0 = DiskInfo.first;
- tocHdr.cdth_trk1 = DiskInfo.last;
- memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr);
- return 0;
-
- case CDROMREADTOCENTRY: /* Read an entry in the table of contents */
-
- st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
- if (st)
- return st;
-
- memcpy_fromfs(&entry, (void *) arg, sizeof entry);
- if (entry.cdte_track == CDROM_LEADOUT)
- /* XXX */
- tocPtr = &Toc[DiskInfo.last + 1];
-
- else if (entry.cdte_track > DiskInfo.last
- || entry.cdte_track < DiskInfo.first)
- return -EINVAL;
-
- else
- tocPtr = &Toc[entry.cdte_track];
-
- entry.cdte_adr = tocPtr -> ctrl_addr;
- entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4;
-
- if (entry.cdte_format == CDROM_LBA)
- entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime);
-
- else if (entry.cdte_format == CDROM_MSF)
- {
- entry.cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min);
- entry.cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec);
- entry.cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame);
- }
-
- else
- return -EINVAL;
-
- memcpy_tofs((void *) arg, &entry, sizeof entry);
- return 0;
-
- case CDROMSUBCHNL: /* Get subchannel info */
-
- st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl);
- if (st)
- return st;
-
- memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl);
-
- if (GetQChannelInfo(&qInfo) < 0)
- return -EIO;
-
- subchnl.cdsc_audiostatus = audioStatus;
- subchnl.cdsc_adr = qInfo.ctrl_addr;
- subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4;
- subchnl.cdsc_trk = bcd2bin(qInfo.track);
- subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex);
-
- if (subchnl.cdsc_format == CDROM_LBA)
- {
- subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime);
- subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime);
- }
-
- else if (subchnl.cdsc_format == CDROM_MSF)
- {
- subchnl.cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min);
- subchnl.cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec);
- subchnl.cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame);
-
- subchnl.cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min);
- subchnl.cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec);
- subchnl.cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame);
- }
-
- else
- return -EINVAL;
-
- memcpy_tofs((void *) arg, &subchnl, sizeof subchnl);
- return 0;
-
- case CDROMVOLCTRL: /* Volume control */
- st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl));
- if (st)
- return st;
-
- memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl));
- outb(MCMD_SET_VOLUME, MCDPORT(0));
- outb(volctrl.channel0, MCDPORT(0));
- outb(255, MCDPORT(0));
- outb(volctrl.channel1, MCDPORT(0));
- outb(255, MCDPORT(0));
-
- i = getMcdStatus(MCD_STATUS_DELAY);
- if (i < 0)
- return -EIO;
-
- {
- char a, b, c, d;
-
- getValue(&a);
- getValue(&b);
- getValue(&c);
- getValue(&d);
- }
-
- return 0;
-
- case CDROMEJECT:
- /* all drives can at least stop! */
- if (audioStatus == CDROM_AUDIO_PLAY) {
- outb(MCMD_STOP, MCDPORT(0));
- i = getMcdStatus(MCD_STATUS_DELAY);
- }
-
- audioStatus = CDROM_AUDIO_NO_STATUS;
-
- outb(MCMD_EJECT, MCDPORT(0));
- /*
- * the status (i) shows failure on all but the FX drives.
- * But nothing we can do about that in software!
- * So just read the status and forget it. - Jon.
- */
- i = getMcdStatus(MCD_STATUS_DELAY);
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-
-/*
- * Take care of the different block sizes between cdrom and Linux.
- * When Linux gets variable block sizes this will probably go away.
- */
-
-static void
-mcd_transfer(void)
-{
- if (CURRENT_VALID) {
- while (CURRENT -> nr_sectors) {
- int bn = CURRENT -> sector / 4;
- int i;
- for (i = 0; i < MCD_BUF_SIZ && mcd_buf_bn[i] != bn; ++i)
- ;
- if (i < MCD_BUF_SIZ) {
- int offs = (i * 4 + (CURRENT -> sector & 3)) * 512;
- int nr_sectors = 4 - (CURRENT -> sector & 3);
- if (mcd_buf_out != i) {
- mcd_buf_out = i;
- if (mcd_buf_bn[i] != bn) {
- mcd_buf_out = -1;
- continue;
- }
- }
- if (nr_sectors > CURRENT -> nr_sectors)
- nr_sectors = CURRENT -> nr_sectors;
- memcpy(CURRENT -> buffer, mcd_buf + offs, nr_sectors * 512);
- CURRENT -> nr_sectors -= nr_sectors;
- CURRENT -> sector += nr_sectors;
- CURRENT -> buffer += nr_sectors * 512;
- } else {
- mcd_buf_out = -1;
- break;
- }
- }
- }
-}
-
-
-/*
- * We only seem to get interrupts after an error.
- * Just take the interrupt and clear out the status reg.
- */
-
-static void
-mcd_interrupt(int irq, struct pt_regs * regs)
-{
- int st;
-
- st = inb(MCDPORT(1)) & 0xFF;
-#ifdef TEST1
- printk("<int1-%02X>", st);
-#endif
- if (!(st & MFL_STATUS))
- {
- st = inb(MCDPORT(0)) & 0xFF;
-#ifdef TEST1
- printk("<int0-%02X>", st);
-#endif
- if ((st & 0xFF) != 0xFF)
- mcd_error = st ? st & 0xFF : -1;
- }
-}
-
-
-static void
-do_mcd_request(void)
-{
-#ifdef TEST2
- printk(" do_mcd_request(%ld+%ld)\n", CURRENT -> sector, CURRENT -> nr_sectors);
-#endif
- mcd_transfer_is_active = 1;
- while (CURRENT_VALID) {
- if (CURRENT->bh) {
- if (!CURRENT->bh->b_lock)
- panic(DEVICE_NAME ": block not locked");
- }
- mcd_transfer();
- if (CURRENT -> nr_sectors == 0) {
- end_request(1);
- } else {
- mcd_buf_out = -1; /* Want to read a block not in buffer */
- if (mcd_state == MCD_S_IDLE) {
- if (!tocUpToDate) {
- if (updateToc() < 0) {
- while (CURRENT_VALID)
- end_request(0);
- break;
- }
- }
- mcd_state = MCD_S_START;
- McdTries = 5;
- SET_TIMER(mcd_poll, 1);
- }
- break;
- }
- }
- mcd_transfer_is_active = 0;
-#ifdef TEST2
- printk(" do_mcd_request ends\n");
-#endif
-}
-
-
-
-static void
-mcd_poll(void)
-{
- int st;
-
-
- if (mcd_error) {
- if (mcd_error & 0xA5) {
- printk("mcd: I/O error 0x%02x", mcd_error);
- if (mcd_error & 0x80)
- printk(" (Door open)");
- if (mcd_error & 0x20)
- printk(" (Disk changed)");
- if (mcd_error & 0x04)
- printk(" (Read error)");
- printk("\n");
- mcd_invalidate_buffers();
-#ifdef WARN_IF_READ_FAILURE
- if (McdTries == 5)
- printk("mcd: read of block %d failed\n", mcd_next_bn);
-#endif
- if (!McdTries--) {
- printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
- if (mcd_transfer_is_active) {
- McdTries = 0;
- goto ret;
- }
- if (CURRENT_VALID)
- end_request(0);
- McdTries = 5;
- }
- }
- mcd_error = 0;
- mcd_state = MCD_S_STOP;
- }
-
-
-
- immediately:
- switch (mcd_state) {
-
-
-
- case MCD_S_IDLE:
-#ifdef TEST3
- printk("MCD_S_IDLE\n");
-#endif
- return;
-
-
-
- case MCD_S_START:
-#ifdef TEST3
- printk("MCD_S_START\n");
-#endif
-
- outb(MCMD_GET_STATUS, MCDPORT(0));
- mcd_state = mcd_mode == 1 ? MCD_S_READ : MCD_S_MODE;
- McdTimeout = 3000;
- break;
-
-
-
- case MCD_S_MODE:
-#ifdef TEST3
- printk("MCD_S_MODE\n");
-#endif
-
- if ((st = mcdStatus()) != -1) {
-
- if (st & MST_DSK_CHG) {
- mcdDiskChanged = 1;
- tocUpToDate = 0;
- mcd_invalidate_buffers();
- }
-
- set_mode_immediately:
-
- if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
- mcdDiskChanged = 1;
- tocUpToDate = 0;
- if (mcd_transfer_is_active) {
- mcd_state = MCD_S_START;
- goto immediately;
- }
- printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
- mcd_state = MCD_S_IDLE;
- while (CURRENT_VALID)
- end_request(0);
- return;
- }
-
- outb(MCMD_SET_MODE, MCDPORT(0));
- outb(1, MCDPORT(0));
- mcd_mode = 1;
- mcd_state = MCD_S_READ;
- McdTimeout = 3000;
-
- }
- break;
-
-
-
- case MCD_S_READ:
-#ifdef TEST3
- printk("MCD_S_READ\n");
-#endif
-
- if ((st = mcdStatus()) != -1) {
-
- if (st & MST_DSK_CHG) {
- mcdDiskChanged = 1;
- tocUpToDate = 0;
- mcd_invalidate_buffers();
- }
-
- read_immediately:
-
- if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) {
- mcdDiskChanged = 1;
- tocUpToDate = 0;
- if (mcd_transfer_is_active) {
- mcd_state = MCD_S_START;
- goto immediately;
- }
- printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n");
- mcd_state = MCD_S_IDLE;
- while (CURRENT_VALID)
- end_request(0);
- return;
- }
-
- if (CURRENT_VALID) {
- struct mcd_Play_msf msf;
- mcd_next_bn = CURRENT -> sector / 4;
- hsg2msf(mcd_next_bn, &msf.start);
- msf.end.min = ~0;
- msf.end.sec = ~0;
- msf.end.frame = ~0;
- sendMcdCmd(MCMD_DATA_READ, &msf);
- mcd_state = MCD_S_DATA;
- McdTimeout = READ_TIMEOUT;
- } else {
- mcd_state = MCD_S_STOP;
- goto immediately;
- }
-
- }
- break;
-
-
- case MCD_S_DATA:
-#ifdef TEST3
- printk("MCD_S_DATA\n");
-#endif
-
- st = inb(MCDPORT(1)) & (MFL_STATUSorDATA);
- data_immediately:
-#ifdef TEST5
- printk("Status %02x\n",st);
-#endif
- switch (st) {
-
- case MFL_DATA:
-#ifdef WARN_IF_READ_FAILURE
- if (McdTries == 5)
- printk("mcd: read of block %d failed\n", mcd_next_bn);
-#endif
- if (!McdTries--) {
- printk("mcd: read of block %d failed, giving up\n", mcd_next_bn);
- if (mcd_transfer_is_active) {
- McdTries = 0;
- break;
- }
- if (CURRENT_VALID)
- end_request(0);
- McdTries = 5;
- }
- mcd_state = MCD_S_START;
- McdTimeout = READ_TIMEOUT;
- goto immediately;
-
- case MFL_STATUSorDATA:
- break;
-
- default:
- McdTries = 5;
- if (!CURRENT_VALID && mcd_buf_in == mcd_buf_out) {
- mcd_state = MCD_S_STOP;
- goto immediately;
- }
- mcd_buf_bn[mcd_buf_in] = -1;
- READ_DATA(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in, 2048);
- mcd_buf_bn[mcd_buf_in] = mcd_next_bn++;
- if (mcd_buf_out == -1)
- mcd_buf_out = mcd_buf_in;
- mcd_buf_in = mcd_buf_in + 1 == MCD_BUF_SIZ ? 0 : mcd_buf_in + 1;
- if (!mcd_transfer_is_active) {
- while (CURRENT_VALID) {
- mcd_transfer();
- if (CURRENT -> nr_sectors == 0)
- end_request(1);
- else
- break;
- }
- }
-
- if (CURRENT_VALID
- && (CURRENT -> sector / 4 < mcd_next_bn ||
- CURRENT -> sector / 4 > mcd_next_bn + 16)) {
- mcd_state = MCD_S_STOP;
- goto immediately;
- }
- McdTimeout = READ_TIMEOUT;
-#ifdef DOUBLE_QUICK_ONLY
- if (MCMD_DATA_READ != MCMD_PLAY_READ)
-#endif
- {
- int count= QUICK_LOOP_COUNT;
- while (count--) {
- QUICK_LOOP_DELAY;
- if ((st = (inb(MCDPORT(1))) & (MFL_STATUSorDATA)) != (MFL_STATUSorDATA)) {
-# ifdef TEST4
-/* printk("Quickloop success at %d\n",QUICK_LOOP_COUNT-count); */
- printk(" %d ",QUICK_LOOP_COUNT-count);
-# endif
- goto data_immediately;
- }
- }
-# ifdef TEST4
-/* printk("Quickloop ended at %d\n",QUICK_LOOP_COUNT); */
- printk("ended ");
-# endif
- }
- break;
- }
- break;
-
-
-
- case MCD_S_STOP:
-#ifdef TEST3
- printk("MCD_S_STOP\n");
-#endif
-
-#ifdef WORK_AROUND_MITSUMI_BUG_93
- if (!mitsumi_bug_93_wait)
- goto do_not_work_around_mitsumi_bug_93_1;
-
- McdTimeout = mitsumi_bug_93_wait;
- mcd_state = 9+3+1;
- break;
-
- case 9+3+1:
- if (McdTimeout)
- break;
-
- do_not_work_around_mitsumi_bug_93_1:
-#endif /* WORK_AROUND_MITSUMI_BUG_93 */
-
- outb(MCMD_STOP, MCDPORT(0));
-
-#ifdef WORK_AROUND_MITSUMI_BUG_92
- if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
- int i = 4096;
- do {
- inb(MCDPORT(0));
- } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
- outb(MCMD_STOP, MCDPORT(0));
- if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) {
- i = 4096;
- do {
- inb(MCDPORT(0));
- } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i);
- outb(MCMD_STOP, MCDPORT(0));
- }
- }
-#endif /* WORK_AROUND_MITSUMI_BUG_92 */
-
- mcd_state = MCD_S_STOPPING;
- McdTimeout = 1000;
- break;
-
- case MCD_S_STOPPING:
-#ifdef TEST3
- printk("MCD_S_STOPPING\n");
-#endif
-
- if ((st = mcdStatus()) == -1 && McdTimeout)
- break;
-
- if ((st != -1) && (st & MST_DSK_CHG)) {
- mcdDiskChanged = 1;
- tocUpToDate = 0;
- mcd_invalidate_buffers();
- }
-
-#ifdef WORK_AROUND_MITSUMI_BUG_93
- if (!mitsumi_bug_93_wait)
- goto do_not_work_around_mitsumi_bug_93_2;
-
- McdTimeout = mitsumi_bug_93_wait;
- mcd_state = 9+3+2;
- break;
-
- case 9+3+2:
- if (McdTimeout)
- break;
-
- st = -1;
-
- do_not_work_around_mitsumi_bug_93_2:
-#endif /* WORK_AROUND_MITSUMI_BUG_93 */
-
-#ifdef TEST3
- printk("CURRENT_VALID %d mcd_mode %d\n",
- CURRENT_VALID, mcd_mode);
-#endif
-
- if (CURRENT_VALID) {
- if (st != -1) {
- if (mcd_mode == 1)
- goto read_immediately;
- else
- goto set_mode_immediately;
- } else {
- mcd_state = MCD_S_START;
- McdTimeout = 1;
- }
- } else {
- mcd_state = MCD_S_IDLE;
- return;
- }
- break;
-
- default:
- printk("mcd: invalid state %d\n", mcd_state);
- return;
- }
-
- ret:
- if (!McdTimeout--) {
- printk("mcd: timeout in state %d\n", mcd_state);
- mcd_state = MCD_S_STOP;
- }
-
- SET_TIMER(mcd_poll, 1);
-}
-
-
-
-static void
-mcd_invalidate_buffers(void)
-{
- int i;
- for (i = 0; i < MCD_BUF_SIZ; ++i)
- mcd_buf_bn[i] = -1;
- mcd_buf_out = -1;
-}
-
-
-/*
- * Open the device special file. Check that a disk is in.
- */
-
-int
-mcd_open(struct inode *ip, struct file *fp)
-{
- int st;
-
- if (mcdPresent == 0)
- return -ENXIO; /* no hardware */
-
- if (fp->f_mode & 2) /* write access? */
- return -EROFS;
-
- if (!mcd_open_count && mcd_state == MCD_S_IDLE) {
-
- mcd_invalidate_buffers();
-
- st = statusCmd(); /* check drive status */
- if (st == -1)
- return -EIO; /* drive doesn't respond */
-
- if ((st & MST_READY) == 0) /* no disk in drive */
- {
- printk("mcd: no disk in drive\n");
- return -EIO;
- }
-
- if (updateToc() < 0)
- return -EIO;
-
- }
- ++mcd_open_count;
-
- return 0;
-}
-
-
-/*
- * On close, we flush all mcd blocks from the buffer cache.
- */
-
-static void
-mcd_release(struct inode * inode, struct file * file)
-{
- if (!--mcd_open_count) {
- mcd_invalidate_buffers();
- sync_dev(inode->i_rdev);
- invalidate_buffers(inode -> i_rdev);
- }
-}
-
-
-static struct file_operations mcd_fops = {
- NULL, /* lseek - default */
- block_read, /* read - general block-dev read */
- block_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- mcd_ioctl, /* ioctl */
- NULL, /* mmap */
- mcd_open, /* open */
- mcd_release, /* release */
- NULL, /* fsync */
- NULL, /* fasync */
- check_mcd_change, /* media change */
- NULL /* revalidate */
-};
-
-
-/*
- * Test for presence of drive and initialize it. Called at boot time.
- */
-
-unsigned long
-mcd_init(unsigned long mem_start, unsigned long mem_end)
-{
- int count;
- unsigned char result[3];
-
- if (mcd_port <= 0 || mcd_irq <= 0) {
- printk("skip mcd_init\n");
- return mem_start;
- }
-
- printk("mcd=0x%x,%d: ", mcd_port, mcd_irq);
-
- if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
- {
- printk("Unable to get major %d for Mitsumi CD-ROM\n",
- MAJOR_NR);
- return mem_start;
- }
-
- if (check_region(mcd_port, 4)) {
- printk("Init failed, I/O port (%X) already in use\n",
- mcd_port);
- return mem_start;
- }
-
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- read_ahead[MAJOR_NR] = 4;
-
- /* check for card */
-
- outb(0, MCDPORT(1)); /* send reset */
- for (count = 0; count < 2000000; count++)
- (void) inb(MCDPORT(1)); /* delay a bit */
-
- outb(0x40, MCDPORT(0)); /* send get-stat cmd */
- for (count = 0; count < 2000000; count++)
- if (!(inb(MCDPORT(1)) & MFL_STATUS))
- break;
-
- if (count >= 2000000) {
- printk("Init failed. No mcd device at 0x%x irq %d\n",
- mcd_port, mcd_irq);
- return mem_start;
- }
- count = inb(MCDPORT(0)); /* pick up the status */
-
- outb(MCMD_GET_VERSION,MCDPORT(0));
- for(count=0;count<3;count++)
- if(getValue(result+count)) {
- printk("mitsumi get version failed at 0x%d\n",
- mcd_port);
- return mem_start;
- }
-
- if (result[0] == result[1] && result[1] == result[2])
- return mem_start;
-
- printk("Mitsumi status, type and version : %02X %c %x\n",
- result[0],result[1],result[2]);
-
- if (result[1] == 'D') MCMD_DATA_READ= 0xC1;
-
- mcdVersion=result[2];
-
- if (mcdVersion >=4)
- outb(4,MCDPORT(2)); /* magic happens */
-
- /* don't get the IRQ until we know for sure the drive is there */
-
- if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD"))
- {
- printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq);
- return mem_start;
- }
- request_region(mcd_port, 4,"mcd");
-
- outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
- outb(0x02,MCDPORT(0));
- outb(0x00,MCDPORT(0));
- getValue(result);
-
- outb(MCMD_CONFIG_DRIVE, MCDPORT(0));
- outb(0x10,MCDPORT(0));
- outb(0x04,MCDPORT(0));
- getValue(result);
-
- mcd_invalidate_buffers();
- mcdPresent = 1;
- return mem_start;
-}
-
-
-static void
-hsg2msf(long hsg, struct msf *msf)
-{
- hsg += 150;
- msf -> min = hsg / 4500;
- hsg %= 4500;
- msf -> sec = hsg / 75;
- msf -> frame = hsg % 75;
-
- bin2bcd(&msf -> min); /* convert to BCD */
- bin2bcd(&msf -> sec);
- bin2bcd(&msf -> frame);
-}
-
-
-static void
-bin2bcd(unsigned char *p)
-{
- int u, t;
-
- u = *p % 10;
- t = *p / 10;
- *p = u | (t << 4);
-}
-
-static int
-bcd2bin(unsigned char bcd)
-{
- return (bcd >> 4) * 10 + (bcd & 0xF);
-}
-
-
-/*
- * See if a status is ready from the drive and return it
- * if it is ready.
- */
-
-static int
-mcdStatus(void)
-{
- int i;
- int st;
-
- st = inb(MCDPORT(1)) & MFL_STATUS;
- if (!st)
- {
- i = inb(MCDPORT(0)) & 0xFF;
- return i;
- }
- else
- return -1;
-}
-
-
-/*
- * Send a play or read command to the drive
- */
-
-static void
-sendMcdCmd(int cmd, struct mcd_Play_msf *params)
-{
- outb(cmd, MCDPORT(0));
- outb(params -> start.min, MCDPORT(0));
- outb(params -> start.sec, MCDPORT(0));
- outb(params -> start.frame, MCDPORT(0));
- outb(params -> end.min, MCDPORT(0));
- outb(params -> end.sec, MCDPORT(0));
- outb(params -> end.frame, MCDPORT(0));
-}
-
-
-/*
- * Timer interrupt routine to test for status ready from the drive.
- * (see the next routine)
- */
-
-static void
-mcdStatTimer(void)
-{
- if (!(inb(MCDPORT(1)) & MFL_STATUS))
- {
- wake_up(&mcd_waitq);
- return;
- }
-
- McdTimeout--;
- if (McdTimeout <= 0)
- {
- wake_up(&mcd_waitq);
- return;
- }
-
- SET_TIMER(mcdStatTimer, 1);
-}
-
-
-/*
- * Wait for a status to be returned from the drive. The actual test
- * (see routine above) is done by the timer interrupt to avoid
- * excessive rescheduling.
- */
-
-static int
-getMcdStatus(int timeout)
-{
- int st;
-
- McdTimeout = timeout;
- SET_TIMER(mcdStatTimer, 1);
- sleep_on(&mcd_waitq);
- if (McdTimeout <= 0)
- return -1;
-
- st = inb(MCDPORT(0)) & 0xFF;
- if (st == 0xFF)
- return -1;
-
- if ((st & MST_BUSY) == 0 && audioStatus == CDROM_AUDIO_PLAY)
- /* XXX might be an error? look at q-channel? */
- audioStatus = CDROM_AUDIO_COMPLETED;
-
- if (st & MST_DSK_CHG)
- {
- mcdDiskChanged = 1;
- tocUpToDate = 0;
- audioStatus = CDROM_AUDIO_NO_STATUS;
- }
-
- return st;
-}
-
-
-/*
- * Read a value from the drive. Should return quickly, so a busy wait
- * is used to avoid excessive rescheduling.
- */
-
-static int
-getValue(unsigned char *result)
-{
- int count;
- int s;
-
- for (count = 0; count < 2000; count++)
- if (!(inb(MCDPORT(1)) & MFL_STATUS))
- break;
-
- if (count >= 2000)
- {
- printk("mcd: getValue timeout\n");
- return -1;
- }
-
- s = inb(MCDPORT(0)) & 0xFF;
- *result = (unsigned char) s;
- return 0;
-}
-
-
-/*
- * Read the current Q-channel info. Also used for reading the
- * table of contents.
- */
-
-int
-GetQChannelInfo(struct mcd_Toc *qp)
-{
- unsigned char notUsed;
- int retry;
-
- for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
- {
- outb(MCMD_GET_Q_CHANNEL, MCDPORT(0));
- if (getMcdStatus(MCD_STATUS_DELAY) != -1)
- break;
- }
-
- if (retry >= MCD_RETRY_ATTEMPTS)
- return -1;
-
- if (getValue(&qp -> ctrl_addr) < 0) return -1;
- if (getValue(&qp -> track) < 0) return -1;
- if (getValue(&qp -> pointIndex) < 0) return -1;
- if (getValue(&qp -> trackTime.min) < 0) return -1;
- if (getValue(&qp -> trackTime.sec) < 0) return -1;
- if (getValue(&qp -> trackTime.frame) < 0) return -1;
- if (getValue(&notUsed) < 0) return -1;
- if (getValue(&qp -> diskTime.min) < 0) return -1;
- if (getValue(&qp -> diskTime.sec) < 0) return -1;
- if (getValue(&qp -> diskTime.frame) < 0) return -1;
-
- return 0;
-}
-
-
-/*
- * Read the table of contents (TOC) and TOC header if necessary
- */
-
-static int
-updateToc()
-{
- if (tocUpToDate)
- return 0;
-
- if (GetDiskInfo() < 0)
- return -EIO;
-
- if (GetToc() < 0)
- return -EIO;
-
- tocUpToDate = 1;
- return 0;
-}
-
-
-/*
- * Read the table of contents header
- */
-
-static int
-GetDiskInfo()
-{
- int retry;
-
- for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
- {
- outb(MCMD_GET_DISK_INFO, MCDPORT(0));
- if (getMcdStatus(MCD_STATUS_DELAY) != -1)
- break;
- }
-
- if (retry >= MCD_RETRY_ATTEMPTS)
- return -1;
-
- if (getValue(&DiskInfo.first) < 0) return -1;
- if (getValue(&DiskInfo.last) < 0) return -1;
-
- DiskInfo.first = bcd2bin(DiskInfo.first);
- DiskInfo.last = bcd2bin(DiskInfo.last);
-
- if (getValue(&DiskInfo.diskLength.min) < 0) return -1;
- if (getValue(&DiskInfo.diskLength.sec) < 0) return -1;
- if (getValue(&DiskInfo.diskLength.frame) < 0) return -1;
- if (getValue(&DiskInfo.firstTrack.min) < 0) return -1;
- if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1;
- if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1;
-
-#ifdef MCD_DEBUG
-printk("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n",
- DiskInfo.first,
- DiskInfo.last,
- DiskInfo.diskLength.min,
- DiskInfo.diskLength.sec,
- DiskInfo.diskLength.frame,
- DiskInfo.firstTrack.min,
- DiskInfo.firstTrack.sec,
- DiskInfo.firstTrack.frame);
-#endif
-
- return 0;
-}
-
-
-/*
- * Read the table of contents (TOC)
- */
-
-static int
-GetToc()
-{
- int i, px;
- int limit;
- int retry;
- struct mcd_Toc qInfo;
-
- for (i = 0; i < MAX_TRACKS; i++)
- Toc[i].pointIndex = 0;
-
- i = DiskInfo.last + 3;
-
- for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
- {
- outb(MCMD_STOP, MCDPORT(0));
- if (getMcdStatus(MCD_STATUS_DELAY) != -1)
- break;
- }
-
- if (retry >= MCD_RETRY_ATTEMPTS)
- return -1;
-
- for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
- {
- outb(MCMD_SET_MODE, MCDPORT(0));
- outb(0x05, MCDPORT(0)); /* mode: toc */
- mcd_mode = 0x05;
- if (getMcdStatus(MCD_STATUS_DELAY) != -1)
- break;
- }
-
- if (retry >= MCD_RETRY_ATTEMPTS)
- return -1;
-
- for (limit = 300; limit > 0; limit--)
- {
- if (GetQChannelInfo(&qInfo) < 0)
- break;
-
- px = bcd2bin(qInfo.pointIndex);
- if (px > 0 && px < MAX_TRACKS && qInfo.track == 0)
- if (Toc[px].pointIndex == 0)
- {
- Toc[px] = qInfo;
- i--;
- }
-
- if (i <= 0)
- break;
- }
-
- Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength;
-
- for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++)
- {
- outb(MCMD_SET_MODE, MCDPORT(0));
- outb(0x01, MCDPORT(0));
- mcd_mode = 1;
- if (getMcdStatus(MCD_STATUS_DELAY) != -1)
- break;
- }
-
-#ifdef MCD_DEBUG
-for (i = 1; i <= DiskInfo.last; i++)
-printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n",
-i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
-for (i = 100; i < 103; i++)
-printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n",
-i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex,
-Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame,
-Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame);
-#endif
-
- return limit > 0 ? 0 : -1;
-}
-
diff --git a/drivers/block/md.c b/drivers/block/md.c
new file mode 100644
index 000000000..a5c8d262e
--- /dev/null
+++ b/drivers/block/md.c
@@ -0,0 +1,581 @@
+
+/*
+ md.c : Multiple Devices driver for Linux
+ Copyright (C) 1994-96 Marc ZYNGIER
+ <zyngier@ufr-info-p7.ibp.fr> or
+ <maz@gloups.fdn.fr>
+
+ A lot of inspiration came from hd.c ...
+
+ kerneld support by Boris Tobotras <boris@xtalk.msk.su>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ You should have received a copy of the GNU General Public License
+ (for example /usr/src/linux/COPYING); if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/md.h>
+#include <linux/hdreg.h>
+#include <linux/stat.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+#include <linux/errno.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+
+#include <linux/blk.h>
+#include <asm/uaccess.h>
+
+static struct hd_struct md_hd_struct[MAX_MD_DEV];
+static int md_blocksizes[MAX_MD_DEV];
+
+int md_size[MAX_MD_DEV]={0, };
+
+static void md_geninit (struct gendisk *);
+
+static struct gendisk md_gendisk=
+{
+ MD_MAJOR,
+ "md",
+ 0,
+ 1,
+ MAX_MD_DEV,
+ md_geninit,
+ md_hd_struct,
+ md_size,
+ MAX_MD_DEV,
+ NULL,
+ NULL
+};
+
+static struct md_personality *pers[MAX_PERSONALITY]={NULL, };
+
+struct md_dev md_dev[MAX_MD_DEV];
+
+static struct gendisk *find_gendisk (kdev_t dev)
+{
+ struct gendisk *tmp=gendisk_head;
+
+ while (tmp != NULL)
+ {
+ if (tmp->major==MAJOR(dev))
+ return (tmp);
+
+ tmp=tmp->next;
+ }
+
+ return (NULL);
+}
+
+
+char *partition_name (kdev_t dev)
+{
+ static char name[40]; /* This should be long
+ enough for a device name ! */
+ struct gendisk *hd = find_gendisk (dev);
+
+ if (!hd)
+ {
+ printk ("No gendisk entry for dev %s\n", kdevname(dev));
+ sprintf (name, "dev %s", kdevname(dev));
+ return (name);
+ }
+
+ return disk_name (hd, MINOR(dev), name); /* routine in genhd.c */
+}
+
+
+static void set_ra (void)
+{
+ int i, j, minra=INT_MAX;
+
+ for (i=0; i<MAX_MD_DEV; i++)
+ {
+ if (!md_dev[i].pers)
+ continue;
+
+ for (j=0; j<md_dev[i].nb_dev; j++)
+ if (read_ahead[MAJOR(md_dev[i].devices[j].dev)]<minra)
+ minra=read_ahead[MAJOR(md_dev[i].devices[j].dev)];
+ }
+
+ read_ahead[MD_MAJOR]=minra;
+}
+
+
+static int do_md_run (int minor, int repart)
+{
+ int pnum, i, min, current_ra, err;
+
+ if (!md_dev[minor].nb_dev)
+ return -EINVAL;
+
+ if (md_dev[minor].pers)
+ return -EBUSY;
+
+ md_dev[minor].repartition=repart;
+
+ if ((pnum=PERSONALITY(md_dev+minor) >> (PERSONALITY_SHIFT))
+ >= MAX_PERSONALITY)
+ return -EINVAL;
+
+ if (!pers[pnum])
+ {
+#ifdef CONFIG_KERNELD
+ char module_name[80];
+ sprintf (module_name, "md-personality-%d", pnum);
+ request_module (module_name);
+ if (!pers[pnum])
+#endif
+ return -EINVAL;
+ }
+
+ min=1 << FACTOR_SHIFT(FACTOR((md_dev+minor)));
+
+ for (i=0; i<md_dev[minor].nb_dev; i++)
+ if (md_dev[minor].devices[i].size<min)
+ {
+ printk ("Dev %s smaller than %dk, cannot shrink\n",
+ partition_name (md_dev[minor].devices[i].dev), min);
+ return -EINVAL;
+ }
+
+ /* Resize devices according to the factor. It is used to align
+ partitions size on a given chunk size. */
+ md_size[minor]=0;
+
+ for (i=0; i<md_dev[minor].nb_dev; i++)
+ {
+ md_dev[minor].devices[i].size &= ~(min - 1);
+ md_size[minor] += md_dev[minor].devices[i].size;
+ md_dev[minor].devices[i].offset=i ? (md_dev[minor].devices[i-1].offset + md_dev[minor].devices[i-1].size) : 0;
+ }
+
+ md_dev[minor].pers=pers[pnum];
+
+ if ((err=md_dev[minor].pers->run (minor, md_dev+minor)))
+ {
+ md_dev[minor].pers=NULL;
+ return (err);
+ }
+
+ /* FIXME : We assume here we have blocks
+ that are twice as large as sectors.
+ THIS MAY NOT BE TRUE !!! */
+ md_hd_struct[minor].start_sect=0;
+ md_hd_struct[minor].nr_sects=md_size[minor]<<1;
+
+ /* It would be better to have a per-md-dev read_ahead. Currently,
+ we only use the smallest read_ahead among md-attached devices */
+
+ current_ra=read_ahead[MD_MAJOR];
+
+ for (i=0; i<md_dev[minor].nb_dev; i++)
+ if (current_ra>read_ahead[MAJOR(md_dev[minor].devices[i].dev)])
+ current_ra=read_ahead[MAJOR(md_dev[minor].devices[i].dev)];
+
+ read_ahead[MD_MAJOR]=current_ra;
+
+ printk ("START_DEV md%x %s\n", minor, md_dev[minor].pers->name);
+ return (0);
+}
+
+
+static int do_md_stop (int minor, struct inode *inode)
+{
+ int i;
+
+ if (inode->i_count>1 || md_dev[minor].busy>1) /* ioctl : one open channel */
+ {
+ printk ("STOP_MD md%x failed : i_count=%d, busy=%d\n", minor, inode->i_count, md_dev[minor].busy);
+ return -EBUSY;
+ }
+
+ if (md_dev[minor].pers)
+ {
+ /* The device won't exist anymore -> flush it now */
+ fsync_dev (inode->i_rdev);
+ invalidate_buffers (inode->i_rdev);
+ md_dev[minor].pers->stop (minor, md_dev+minor);
+ }
+
+ /* Remove locks. */
+ for (i=0; i<md_dev[minor].nb_dev; i++)
+ clear_inode (md_dev[minor].devices[i].inode);
+
+ md_dev[minor].nb_dev=md_size[minor]=0;
+ md_hd_struct[minor].nr_sects=0;
+ md_dev[minor].pers=NULL;
+
+ set_ra (); /* calculate new read_ahead */
+
+ printk ("STOP_DEV md%x\n", minor);
+ return (0);
+}
+
+
+static int do_md_add (int minor, kdev_t dev)
+{
+ struct gendisk *gen_real;
+ int i;
+
+ if (MAJOR(dev)==MD_MAJOR || md_dev[minor].nb_dev==MAX_REAL)
+ return -EINVAL;
+
+ if (!fs_may_mount (dev) || md_dev[minor].pers)
+ return -EBUSY;
+
+ if (!(gen_real=find_gendisk (dev)))
+ return -ENOENT;
+
+ i=md_dev[minor].nb_dev++;
+ md_dev[minor].devices[i].dev=dev;
+
+ /* Lock the device by inserting a dummy inode. This doesn't
+ smell very good, but I need to be consistent with the
+ mount stuff, specially with fs_may_mount. If someone have
+ a better idea, please help ! */
+
+ md_dev[minor].devices[i].inode=get_empty_inode ();
+ md_dev[minor].devices[i].inode->i_dev=dev; /* don't care about
+ other fields */
+ insert_inode_hash (md_dev[minor].devices[i].inode);
+
+ /* Sizes are now rounded at run time */
+
+ md_dev[minor].devices[i].size=gen_real->sizes[MINOR(dev)];
+
+ printk ("REGISTER_DEV %s to md%x done\n", partition_name(dev), minor);
+ return (0);
+}
+
+
+static int md_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int minor, err;
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+
+ if (!suser())
+ return -EACCES;
+
+ if (((minor=MINOR(inode->i_rdev)) & 0x80) &&
+ (minor & 0x7f) < MAX_PERSONALITY &&
+ pers[minor & 0x7f] &&
+ pers[minor & 0x7f]->ioctl)
+ return (pers[minor & 0x7f]->ioctl (inode, file, cmd, arg));
+
+ if (minor >= MAX_MD_DEV)
+ return -EINVAL;
+
+ switch (cmd)
+ {
+ case REGISTER_DEV:
+ return do_md_add (minor, to_kdev_t ((dev_t) arg));
+
+ case START_MD:
+ return do_md_run (minor, (int) arg);
+
+ case STOP_MD:
+ return do_md_stop (minor, inode);
+
+ case BLKGETSIZE: /* Return device size */
+ if (!arg) return -EINVAL;
+ err = put_user (md_hd_struct[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
+ if (err)
+ return err;
+ break;
+
+ case BLKFLSBUF:
+ fsync_dev (inode->i_rdev);
+ invalidate_buffers (inode->i_rdev);
+ break;
+
+ case BLKRASET:
+ if (arg > 0xff)
+ return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+
+ case BLKRAGET:
+ if (!arg) return -EINVAL;
+ err = put_user (read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
+ if (err)
+ return err;
+ break;
+
+ /* We have a problem here : there is no easy way to give a CHS
+ virtual geometry. We currently pretend that we have a 2 heads
+ 4 sectors (with a BIG number of cylinders...). This drives dosfs
+ just mad... ;-) */
+
+ case HDIO_GETGEO:
+ if (!loc) return -EINVAL;
+ err = put_user (2, (char *) &loc->heads);
+ if (err)
+ return err;
+ err = put_user (4, (char *) &loc->sectors);
+ if (err)
+ return err;
+ err = put_user (md_hd_struct[minor].nr_sects/8, (short *) &loc->cylinders);
+ if (err)
+ return err;
+ err = put_user (md_hd_struct[MINOR(inode->i_rdev)].start_sect,
+ (long *) &loc->start);
+ if (err)
+ return err;
+ break;
+
+ RO_IOCTLS(inode->i_rdev,arg);
+
+ default:
+ printk ("Unknown md_ioctl %d\n", cmd);
+ return -EINVAL;
+ }
+
+ return (0);
+}
+
+
+static int md_open (struct inode *inode, struct file *file)
+{
+ int minor=MINOR(inode->i_rdev);
+
+ md_dev[minor].busy++;
+ return (0); /* Always succeed */
+}
+
+
+static void md_release (struct inode *inode, struct file *file)
+{
+ int minor=MINOR(inode->i_rdev);
+
+ sync_dev (inode->i_rdev);
+ md_dev[minor].busy--;
+}
+
+
+static long md_read (struct inode *inode, struct file *file,
+ char *buf, unsigned long count)
+{
+ int minor=MINOR(inode->i_rdev);
+
+ if (!md_dev[minor].pers) /* Check if device is being run */
+ return -ENXIO;
+
+ return block_read (inode, file, buf, count);
+}
+
+static long md_write (struct inode *inode, struct file *file,
+ const char *buf, unsigned long count)
+{
+ int minor=MINOR(inode->i_rdev);
+
+ if (!md_dev[minor].pers) /* Check if device is being run */
+ return -ENXIO;
+
+ return block_write (inode, file, buf, count);
+}
+
+static struct file_operations md_fops=
+{
+ NULL,
+ md_read,
+ md_write,
+ NULL,
+ NULL,
+ md_ioctl,
+ NULL,
+ md_open,
+ md_release,
+ block_fsync
+};
+
+int md_map (int minor, kdev_t *rdev, unsigned long *rsector, unsigned long size)
+{
+ if ((unsigned int) minor >= MAX_MD_DEV)
+ {
+ printk ("Bad md device %d\n", minor);
+ return (-1);
+ }
+
+ if (!md_dev[minor].pers)
+ {
+ printk ("Oops ! md%d not running, giving up !\n", minor);
+ return (-1);
+ }
+
+ return (md_dev[minor].pers->map(md_dev+minor, rdev, rsector, size));
+}
+
+
+static void do_md_request (void)
+{
+ printk ("Got md request, not good...");
+ return;
+}
+
+static struct symbol_table md_symbol_table=
+{
+#include <linux/symtab_begin.h>
+
+ X(md_size),
+ X(register_md_personality),
+ X(unregister_md_personality),
+ X(partition_name),
+
+#include <linux/symtab_end.h>
+};
+
+static struct proc_dir_entry proc_md = {
+ PROC_MD, 6, "mdstat",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
+
+static void md_geninit (struct gendisk *gdisk)
+{
+ int i;
+
+ for(i=0;i<MAX_MD_DEV;i++)
+ {
+ md_blocksizes[i] = 1024;
+ md_gendisk.part[i].start_sect=-1; /* avoid partition check */
+ md_gendisk.part[i].nr_sects=0;
+ md_dev[i].pers=NULL;
+ }
+
+ blksize_size[MAJOR_NR] = md_blocksizes;
+ register_symtab (&md_symbol_table);
+
+ proc_register(&proc_root, &proc_md);
+}
+
+
+int get_md_status (char *page)
+{
+ int sz=0, i, j, size;
+
+ sz+=sprintf( page+sz, "Personalities : ");
+ for (i=0; i<MAX_PERSONALITY; i++)
+ if (pers[i])
+ sz+=sprintf (page+sz, "[%d %s] ", i, pers[i]->name);
+
+ page[sz-1]='\n';
+
+ sz+=sprintf (page+sz, "read_ahead ");
+ if (read_ahead[MD_MAJOR]==INT_MAX)
+ sz+=sprintf (page+sz, "not set\n");
+ else
+ sz+=sprintf (page+sz, "%d sectors\n", read_ahead[MD_MAJOR]);
+
+ for (i=0; i<MAX_MD_DEV; i++)
+ {
+ sz+=sprintf (page+sz, "md%d : %sactive", i, md_dev[i].pers ? "" : "in");
+
+ if (md_dev[i].pers)
+ sz+=sprintf (page+sz, " %s", md_dev[i].pers->name);
+
+ size=0;
+ for (j=0; j<md_dev[i].nb_dev; j++)
+ {
+ sz+=sprintf (page+sz, " %s",
+ partition_name(md_dev[i].devices[j].dev));
+ size+=md_dev[i].devices[j].size;
+ }
+
+ if (md_dev[i].nb_dev)
+ sz+=sprintf (page+sz, " %d blocks", size);
+
+ if (!md_dev[i].pers)
+ {
+ sz+=sprintf (page+sz, "\n");
+ continue;
+ }
+
+ if (md_dev[i].pers->max_invalid_dev)
+ sz+=sprintf (page+sz, " maxfault=%ld", MAX_FAULT(md_dev+i));
+
+ sz+=sprintf (page+sz, " %dk %s\n", 1<<FACTOR_SHIFT(FACTOR(md_dev+i)),
+ md_dev[i].pers == pers[LINEAR>>PERSONALITY_SHIFT] ?
+ "rounding" : "chunks");
+
+ sz+=md_dev[i].pers->status (page+sz, i, md_dev+i);
+ }
+
+ return (sz);
+}
+
+int register_md_personality (int p_num, struct md_personality *p)
+{
+ int i=(p_num >> PERSONALITY_SHIFT);
+
+ if (i >= MAX_PERSONALITY)
+ return -EINVAL;
+
+ if (pers[i])
+ return -EBUSY;
+
+ pers[i]=p;
+ printk ("%s personality registered\n", p->name);
+ return 0;
+}
+
+int unregister_md_personality (int p_num)
+{
+ int i=(p_num >> PERSONALITY_SHIFT);
+
+ if (i >= MAX_PERSONALITY)
+ return -EINVAL;
+
+ printk ("%s personality unregistered\n", pers[i]->name);
+ pers[i]=NULL;
+ return 0;
+}
+
+void linear_init (void);
+void raid0_init (void);
+void raid1_init (void);
+void raid5_init (void);
+
+int md_init (void)
+{
+ printk ("md driver %s MAX_MD_DEV=%d, MAX_REAL=%d\n", MD_VERSION, MAX_MD_DEV, MAX_REAL);
+
+ if (register_blkdev (MD_MAJOR, "md", &md_fops))
+ {
+ printk ("Unable to get major %d for md\n", MD_MAJOR);
+ return (-1);
+ }
+
+ blk_dev[MD_MAJOR].request_fn=DEVICE_REQUEST;
+ blk_dev[MD_MAJOR].current_request=NULL;
+ read_ahead[MD_MAJOR]=INT_MAX;
+ md_gendisk.next=gendisk_head;
+
+ gendisk_head=&md_gendisk;
+
+#ifdef CONFIG_MD_LINEAR
+ linear_init ();
+#endif
+#ifdef CONFIG_MD_STRIPED
+ raid0_init ();
+#endif
+
+ return (0);
+}
diff --git a/drivers/block/opti621.c b/drivers/block/opti621.c
new file mode 100644
index 000000000..2e7bd733c
--- /dev/null
+++ b/drivers/block/opti621.c
@@ -0,0 +1,337 @@
+/*
+ * linux/drivers/block/opti621.c Version 0.1 Oct 26, 1996
+ *
+ * Copyright (C) 1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * OPTi 82C621 chipset EIDE controller driver
+ * Author: Jaromir Koutek (E-mail: Jaromir.Koutek@st.mff.cuni.cz)
+ *
+ * Some parts of code are from ali14xx.c and from rz1000.c.
+ * I used docs from OPTi databook, from ftp.opti.com, file 9123-0002.ps
+ * and disassembled/traced setupvic.exe (DOS program).
+ * It increases kernel code about 2 kB.
+ * My card is Octek PIDE 1.01 (on card) or OPTiViC (program).
+ * It has a place for a secondary connector in circuit, but nothing
+ * is there. It cost about $25. Also BIOS says no address for
+ * secondary controller (see bellow in ide_init_opti621).
+ * I've only tested this on my system, which only has one disk.
+ * It's Western Digital WDAC2850, with PIO mode 3. The PCI bus
+ * is at 20 MHz (I have DX2/80, I tried PCI at 40, but I got random
+ * lockups). I tried the OCTEK double speed CD-ROM and
+ * it does not work! But I can't boot DOS also, so it's probably
+ * hardware fault. I have connected Conner 80MB, the Seagate 850MB (no
+ * problems) and Seagate 1GB (as slave, WD as master). My experiences
+ * with the third, 1GB drive: I got 3MB/s (hdparm), but sometimes
+ * it slows to about 100kB/s! I don't know why and I have
+ * not this drive now, so I can't try it again.
+ * If you have two disk, please boot in single mode and carefully
+ * (you can boot on read-only fs) try to set PIO mode 0 etc.
+ * The main problem with OPTi is that some timings for master
+ * and slave must be the same. For example, if you have master
+ * PIO 3 and slave PIO 0, driver have to set some timings of
+ * master for PIO 0. Second problem is that opti621_tune_drive
+ * got only one drive to set, but have to set both drives.
+ * This is solved in opti621_compute_pios. If you don't set
+ * the second drive, opti621_compute_pios use ide_get_best_pio_mode
+ * for autoselect mode (you can change it to PIO 0, if you want).
+ * If you then set the second drive to another PIO, the old value
+ * (automatically selected) will be overrided by yours.
+ * I don't know what there is a 25/33MHz switch in configuration
+ * register, driver is written for use at any frequency which get
+ * (use idebus=xx to select PCI bus speed).
+ * Use ide0=autotune for automatical tune of the PIO modes.
+ * If you get strange results, do not use this and set PIO manually
+ * by hdparm.
+ * I write this driver because I lost the paper ("manual") with
+ * settings of jumpers on the card and I have to boot Linux with
+ * Loadlin except LILO, cause I have to run the setupvic.exe program
+ * already or I get disk errors (my test: rpm -Vf
+ * /usr/X11R6/bin/XF86_SVGA - or any big file).
+ * Some numbers from hdparm -t /dev/hda:
+ * Timing buffer-cache reads: 32 MB in 3.02 seconds =10.60 MB/sec
+ * Timing buffered disk reads: 16 MB in 5.52 seconds = 2.90 MB/sec
+ * I have 4 Megs/s before, but I don't know why (maybe bad hdparm).
+ * If you tried this driver, please send me a E-mail of your experiences.
+ * My E-mail address is Jaromir.Koutek@st.mff.cuni.cz (I hope
+ * till 30. 6. 2000), otherwise you can try miri@atrey.karlin.mff.cuni.cz.
+ * I think OPTi is trademark of OPTi, Octek is trademark of Octek and so on.
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+#define OPTI621_DEBUG /* define for debug messages */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "ide_modes.h"
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+#define OPTI621_MAX_PIO 3
+/* In fact, I do not have any PIO 4 drive
+ * (address: 25 ns, data: 70 ns, recovery: 35 ns),
+ * but OPTi 82C621 is programmable and it can do (minimal values):
+ * on 40MHz PCI bus (pulse 25 ns):
+ * address: 25 ns, data: 25 ns, recovery: 50 ns;
+ * on 20MHz PCI bus (pulse 50 ns):
+ * address: 50 ns, data: 50 ns, recovery: 100 ns.
+ */
+
+/* #define READ_PREFETCH 0 */
+/* Uncommnent for disable read prefetch.
+ * There is some readprefetch capatibility in hdparm,
+ * but when I type hdparm -P 1 /dev/hda, I got errors
+ * and till reset drive is inacessible.
+ * This (hw) read prefetch is safe on my drive.
+ */
+
+#ifndef READ_PREFETCH
+#define READ_PREFETCH 0x40 /* read prefetch is enabled */
+#endif /* else read prefetch is disabled */
+
+#define READ_REG 0 /* index of Read cycle timing register */
+#define WRITE_REG 1 /* index of Write cycle timing register */
+#define MISC_REG 6 /* index of Miscellaneous register */
+#define CNTRL_REG 3 /* index of Control register */
+int reg_base;
+int opti621_primary_base, opti621_secondary_base;
+
+#define PIO_NOT_EXIST 254
+#define PIO_DONT_KNOW 255
+int opti621_drive_pio_modes[4];
+/* there are stored pio numbers from other calls of opti621_tune_drive */
+
+void opti621_compute_pios(ide_hwif_t *drv, int second_contr, int slave_drive, byte pio)
+/* Store values into opti621_drive_pio_modes:
+ * second_contr - 0 for primary controller, 1 for secondary
+ * slave_drive - 0 -> pio is for master, 1 -> pio is for slave
+ * pio - PIO mode for selected drive (for other we don't know)
+ */
+{
+ ide_drive_t *p1, *p2, *drive;
+ int i;
+
+ i = 2*second_contr;
+ p1 = &drv->drives[0];
+ p2 = &drv->drives[1];
+ drive = &drv->drives[slave_drive];
+ pio = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL);
+ opti621_drive_pio_modes[i+slave_drive]=pio;
+
+ if (p1->present) {
+ if (opti621_drive_pio_modes[i]==PIO_DONT_KNOW)
+ opti621_drive_pio_modes[i]=ide_get_best_pio_mode(p1,
+ 255, OPTI621_MAX_PIO, NULL);
+ /* we don't know the selected PIO mode, so we have to autoselect */
+ } else
+ opti621_drive_pio_modes[i]=PIO_NOT_EXIST;
+ if (p2->present) {
+ if (opti621_drive_pio_modes[i+1]==PIO_DONT_KNOW)
+ opti621_drive_pio_modes[i+1]=ide_get_best_pio_mode(p2,
+ 255, OPTI621_MAX_PIO, NULL);
+ /* we don't know the selected PIO mode, so we have to autoselect */
+ } else
+ opti621_drive_pio_modes[i+1]=PIO_NOT_EXIST;
+ /* in opti621_drive_pio_modes[i] and [i+1] are valid PIO modes (or PIO_NOT_EXIST,
+ if drive is not connected), we can continue */
+#ifdef OPTI621_DEBUG
+ printk("%s: (master): ", p1->name);
+ if (p1->present)
+ printk("PIO mode %d\n", opti621_drive_pio_modes[i]);
+ else
+ printk("not present\n");
+ printk("%s: (slave): ", p2->name);
+ if (p2->present)
+ printk("PIO mode %d\n", opti621_drive_pio_modes[i+1]);
+ else
+ printk("not present\n");
+#endif
+}
+
+int cmpt_clk(int time, int bus_speed)
+/* Returns (rounded up) time in clocks for time in ns,
+ * with bus_speed in MHz.
+ * Example: bus_speed = 40 MHz, time = 80 ns
+ * 1000/40 = 25 ns (clk value),
+ * 80/25 = 3.2, rounded up to 4 (I hope ;-)).
+ * Use idebus=xx to select right frequency.
+ */
+{
+ return ((time*bus_speed+999)/1000);
+}
+
+void write_reg(byte value, int reg)
+/* Write value to register reg, base of register
+ * is at reg_base (0x1f0 primary, 0x170 secondary,
+ * if not changed by PCI configuration).
+ * This is from setupvic.exe program.
+ */
+{
+ inw(reg_base+1);
+ inw(reg_base+1);
+ outb(3, reg_base+2);
+ outb(value, reg_base+reg);
+ outb(0x83, reg_base+2);
+}
+
+byte read_reg(int reg)
+/* Read value from register reg, base of register
+ * is at reg_base (0x1f0 primary, 0x170 secondary,
+ * if not changed by PCI configuration).
+ * This is from setupvic.exe program.
+ */
+{
+ byte ret;
+ inw(reg_base+1);
+ inw(reg_base+1);
+ outb(3, reg_base+2);
+ ret=inb(reg_base+reg);
+ outb(0x83, reg_base+2);
+ return ret;
+}
+
+typedef struct pio_clocks_s {
+ int address_time; /* Address setup (clocks) */
+ int data_time; /* Active/data pulse (clocks) */
+ int recovery_time; /* Recovery time (clocks) */
+} pio_clocks_t;
+
+void compute_clocks(int pio, pio_clocks_t *clks)
+{
+ if (pio!=PIO_NOT_EXIST) {
+ int adr_setup, data_pls, bus_speed;
+ bus_speed = ide_system_bus_speed();
+ adr_setup = ide_pio_timings[pio].setup_time;
+ data_pls = ide_pio_timings[pio].active_time;
+ clks->address_time = cmpt_clk(adr_setup, bus_speed);
+ clks->data_time = cmpt_clk(data_pls, bus_speed);
+ clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time
+ -adr_setup-data_pls, bus_speed);
+ if (clks->address_time<1) clks->address_time = 1;
+ if (clks->address_time>4) clks->address_time = 4;
+ if (clks->data_time<1) clks->data_time = 1;
+ if (clks->data_time>16) clks->data_time = 16;
+ if (clks->recovery_time<2) clks->recovery_time = 2;
+ if (clks->recovery_time>17) clks->recovery_time = 17;
+ } else {
+ clks->address_time = 1;
+ clks->data_time = 1;
+ clks->recovery_time = 2;
+ /* minimal values */
+ }
+}
+
+static void opti621_tune_drive (ide_drive_t *drive, byte pio)
+/* Main tune procedure, hooked by tuneproc. */
+{
+ /* primary and secondary drives share some (but not same) registers,
+ so we have to program both drives */
+ unsigned long flags;
+ byte pio1, pio2;
+ int second_contr, slave_drive;
+ pio_clocks_t first, second;
+ int ax, drdy;
+ byte cycle1, cycle2, misc;
+
+ second_contr=HWIF(drive)->index;
+ if ((second_contr!=0) && (second_contr!=1))
+ return; /* invalid controller number */
+ if (((second_contr==0) && (opti621_primary_base==0)) ||
+ ((second_contr==1) && (opti621_secondary_base==0)))
+ return; /* controller is unaccessible/not exist */
+ slave_drive = drive->select.b.unit;
+ /* set opti621_drive_pio_modes[] */
+ opti621_compute_pios(HWIF(drive), second_contr, slave_drive, pio);
+
+ reg_base = second_contr ? opti621_primary_base : opti621_secondary_base;
+
+ pio1 = opti621_drive_pio_modes[second_contr*2];
+ pio2 = opti621_drive_pio_modes[second_contr*2+1];
+
+ compute_clocks(pio1, &first);
+ compute_clocks(pio2, &second);
+
+ ax = (first.address_time<second.address_time) ?
+ (second.address_time) : (first.address_time); /* in ax is max(a1,a2) */
+ drdy = 2; /* DRDY is default 2 (by OPTi Databook) */
+
+ cycle1 = ((first.data_time-1)<<4) | (first.recovery_time-2);
+ cycle2 = ((second.data_time-1)<<4) | (second.recovery_time-2);
+ misc = READ_PREFETCH | ((ax-1)<<4) | ((drdy-2)<<1);
+
+#ifdef OPTI621_DEBUG
+ printk("%s: master: address: %d, data: %d, recovery: %d, drdy: %d [clk]\n",
+ HWIF(drive)->name, ax, first.data_time, first.recovery_time, drdy);
+ printk("%s: slave: address: %d, data: %d, recovery: %d, drdy: %d [clk]\n",
+ HWIF(drive)->name, ax, second.data_time, second.recovery_time, drdy);
+#endif
+
+ save_flags(flags);
+ cli();
+
+ outb(0xc0, reg_base+CNTRL_REG); /* allow Register-B */
+ outb(0xff, reg_base+5); /* hmm, setupvic.exe does this ;-) */
+ inb(reg_base+CNTRL_REG); /* if reads 0xff, adapter not exist? */
+ read_reg(CNTRL_REG); /* if reads 0xc0, no interface exist? */
+ read_reg(5); /* read version, probably 0 */
+
+ /* programming primary drive - 0 or 2 */
+ write_reg(0, MISC_REG); /* select Index-0 for Register-A */
+ write_reg(cycle1, READ_REG); /* set read cycle timings */
+ write_reg(cycle1, WRITE_REG); /* set write cycle timings */
+
+ /* programming secondary drive - 1 or 3 */
+ write_reg(1, MISC_REG); /* select Index-1 for Register-B */
+ write_reg(cycle2, READ_REG); /* set read cycle timings */
+ write_reg(cycle2, WRITE_REG); /* set write cycle timings */
+
+ write_reg(0x85, CNTRL_REG); /* use Register-A for drive 0 (or 2) and
+ Register-B for drive 1 (or 3) */
+
+ write_reg(misc, MISC_REG); /* set address setup, DRDY timings
+ and read prefetch for both drives */
+
+ restore_flags(flags);
+}
+
+void ide_init_opti621 (byte bus, byte fn)
+/* Init controller. Called on kernel boot. */
+{
+ int rc, i;
+ unsigned char sreg;
+ unsigned short reg;
+ unsigned int dreg;
+ unsigned char revision;
+ for (i=0; i<4; i++)
+ opti621_drive_pio_modes[i] = PIO_DONT_KNOW;
+ printk("ide: OPTi 82C621 on PCI bus %d function %d\n", bus, fn);
+ if ((rc = pcibios_read_config_byte (bus, fn, 0x08, &sreg)))
+ goto quit;
+ revision = sreg;
+ if ((rc = pcibios_read_config_dword (bus, fn, 0x10, &dreg)))
+ goto quit;
+ opti621_primary_base = ((dreg==0) || (dreg>0xffff)) ? 0 : dreg-1;
+ if ((rc = pcibios_read_config_dword (bus, fn, 0x18, &dreg)))
+ goto quit;
+ opti621_secondary_base = ((dreg==0) || (dreg>0xffff)) ? 0 : dreg-1;
+ printk("ide: revision %d, primary: 0x%04x, secondary: 0x%04x\n",
+ revision, opti621_primary_base, opti621_secondary_base);
+ if ((rc = pcibios_read_config_word (bus, fn, PCI_COMMAND, &reg)))
+ goto quit;
+ if (!(reg & 1)) {
+ printk("ide: ports are not enabled (BIOS)\n");
+ } else {
+ ide_hwifs[0].tuneproc = &opti621_tune_drive;
+ ide_hwifs[1].tuneproc = &opti621_tune_drive;
+ }
+ quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
+}
diff --git a/drivers/block/promise.c b/drivers/block/promise.c
new file mode 100644
index 000000000..f3a8d5529
--- /dev/null
+++ b/drivers/block/promise.c
@@ -0,0 +1,362 @@
+/* -*- linux-c -*-
+ * linux/drivers/block/promise.c Version 0.07 Mar 26, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: peterd@pnd-pc.demon.co.uk
+ *
+ * This file provides support for the second port and cache of Promise
+ * IDE interfaces, e.g. DC4030, DC5030.
+ *
+ * Thanks are due to Mark Lord for advice and patiently answering stupid
+ * questions, and all those mugs^H^H^H^Hbrave souls who've tested this.
+ *
+ * Version 0.01 Initial version, #include'd in ide.c rather than
+ * compiled separately.
+ * Reads use Promise commands, writes as before. Drives
+ * on second channel are read-only.
+ * Version 0.02 Writes working on second channel, reads on both
+ * channels. Writes fail under high load. Suspect
+ * transfers of >127 sectors don't work.
+ * Version 0.03 Brought into line with ide.c version 5.27.
+ * Other minor changes.
+ * Version 0.04 Updated for ide.c version 5.30
+ * Changed initialization strategy
+ * Version 0.05 Kernel integration. -ml
+ * Version 0.06 Ooops. Add hwgroup to direct call of ide_intr() -ml
+ * Version 0.07 Added support for DC4030 variants
+ * Secondary interface autodetection
+ */
+
+/*
+ * Once you've compiled it in, you'll have to also enable the interface
+ * setup routine from the kernel command line, as in
+ *
+ * 'linux ide0=dc4030'
+ *
+ * As before, it seems that somewhere around 3Megs when writing, bad things
+ * start to happen [timeouts/retries -ml]. If anyone can give me more feedback,
+ * I'd really appreciate it. [email: peterd@pnd-pc.demon.co.uk]
+ *
+ */
+
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "ide.h"
+#include "promise.h"
+
+/* This is needed as the controller may not interrupt if the required data is
+available in the cache. We have to simulate an interrupt. Ugh! */
+
+extern void ide_intr(int, void *dev_id, struct pt_regs*);
+
+/*
+ * promise_selectproc() is invoked by ide.c
+ * in preparation for access to the specified drive.
+ */
+static void promise_selectproc (ide_drive_t *drive)
+{
+ unsigned int number;
+
+ OUT_BYTE(drive->select.all,IDE_SELECT_REG);
+ udelay(1); /* paranoia */
+ number = ((HWIF(drive)->is_promise2)<<1) + drive->select.b.unit;
+ OUT_BYTE(number,IDE_FEATURE_REG);
+}
+
+/*
+ * promise_cmd handles the set of vendor specific commands that are initiated
+ * by command F0. They all have the same success/failure notification.
+ */
+int promise_cmd(ide_drive_t *drive, byte cmd)
+{
+ unsigned long timeout, timer;
+ byte status_val;
+
+ promise_selectproc(drive); /* redundant? */
+ OUT_BYTE(0xF3,IDE_SECTOR_REG);
+ OUT_BYTE(cmd,IDE_SELECT_REG);
+ OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG);
+ timeout = HZ * 10;
+ timeout += jiffies;
+ do {
+ if(jiffies > timeout) {
+ return 2; /* device timed out */
+ }
+ /* This is out of delay_10ms() */
+ /* Delays at least 10ms to give interface a chance */
+ timer = jiffies + (HZ + 99)/100 + 1;
+ while (timer > jiffies);
+ status_val = IN_BYTE(IDE_SECTOR_REG);
+ } while (status_val != 0x50 && status_val != 0x70);
+
+ if(status_val == 0x50)
+ return 0; /* device returned success */
+ else
+ return 1; /* device returned failure */
+}
+
+ide_hwif_t *hwif_required = NULL;
+
+void setup_dc4030 (ide_hwif_t *hwif)
+{
+ hwif_required = hwif;
+}
+
+/*
+init_dc4030: Test for presence of a Promise caching controller card.
+Returns: 0 if no Promise card present at this io_base
+ 1 if Promise card found
+*/
+int init_dc4030 (void)
+{
+ ide_hwif_t *hwif = hwif_required;
+ ide_drive_t *drive;
+ ide_hwif_t *second_hwif;
+ struct dc_ident ident;
+ int i;
+
+ if (!hwif) return 0;
+
+ drive = &hwif->drives[0];
+ second_hwif = &ide_hwifs[hwif->index+1];
+ if(hwif->is_promise2) /* we've already been found ! */
+ return 1;
+
+ if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF)
+ {
+ return 0;
+ }
+ OUT_BYTE(0x08,IDE_CONTROL_REG);
+ if(promise_cmd(drive,PROMISE_GET_CONFIG)) {
+ return 0;
+ }
+ if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
+ printk("%s: Failed Promise read config!\n",hwif->name);
+ return 0;
+ }
+ ide_input_data(drive,&ident,SECTOR_WORDS);
+ if(ident.id[1] != 'P' || ident.id[0] != 'T') {
+ return 0;
+ }
+ printk("%s: Promise caching controller, ",hwif->name);
+ switch(ident.type) {
+ case 0x43: printk("DC4030VL-2, "); break;
+ case 0x41: printk("DC4030VL-1, "); break;
+ case 0x40: printk("DC4030VL, "); break;
+ default: printk("unknown - type 0x%02x - please report!\n"
+ ,ident.type);
+ return 0;
+ }
+ printk("%dKB cache, ",(int)ident.cache_mem);
+ switch(ident.irq) {
+ case 0x00: hwif->irq = 14; break;
+ case 0x01: hwif->irq = 12; break;
+ default: hwif->irq = 15; break;
+ }
+ printk("on IRQ %d\n",hwif->irq);
+ hwif->chipset = second_hwif->chipset = ide_promise;
+ hwif->selectproc = second_hwif->selectproc = &promise_selectproc;
+/* Shift the remaining interfaces down by one */
+ for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
+ ide_hwif_t *h = &ide_hwifs[i];
+
+ printk("Shifting i/f %d values to i/f %d\n",i-1,i);
+ ide_init_hwif_ports(h->io_ports, (h-1)->io_ports[IDE_DATA_OFFSET], NULL);
+ h->io_ports[IDE_CONTROL_OFFSET] = (h-1)->io_ports[IDE_CONTROL_OFFSET];
+ h->noprobe = (h-1)->noprobe;
+ }
+ second_hwif->is_promise2 = 1;
+ ide_init_hwif_ports(second_hwif->io_ports, hwif->io_ports[IDE_DATA_OFFSET], NULL);
+ second_hwif->io_ports[IDE_CONTROL_OFFSET] = hwif->io_ports[IDE_CONTROL_OFFSET];
+ second_hwif->irq = hwif->irq;
+ for (i=0; i<2 ; i++) {
+ hwif->drives[i].io_32bit = 3;
+ second_hwif->drives[i].io_32bit = 3;
+ if(!ident.current_tm[i+2].cyl) second_hwif->drives[i].noprobe=1;
+ }
+ return 1;
+}
+
+/*
+ * promise_read_intr() is the handler for disk read/multread interrupts
+ */
+static void promise_read_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ unsigned int sectors_left, sectors_avail, nsect;
+ struct request *rq;
+
+ if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+ ide_error(drive, "promise_read_intr", stat);
+ return;
+ }
+
+read_again:
+ do {
+ sectors_left = IN_BYTE(IDE_NSECTOR_REG);
+ IN_BYTE(IDE_SECTOR_REG);
+ } while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left);
+ rq = HWGROUP(drive)->rq;
+ sectors_avail = rq->nr_sectors - sectors_left;
+
+read_next:
+ rq = HWGROUP(drive)->rq;
+ if ((nsect = rq->current_nr_sectors) > sectors_avail)
+ nsect = sectors_avail;
+ sectors_avail -= nsect;
+ ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG
+ printk("%s: promise_read: sectors(%ld-%ld), buffer=0x%08lx, "
+ "remaining=%ld\n", drive->name, rq->sector, rq->sector+nsect-1,
+ (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
+#endif
+ rq->sector += nsect;
+ rq->buffer += nsect<<9;
+ rq->errors = 0;
+ i = (rq->nr_sectors -= nsect);
+ if ((rq->current_nr_sectors -= nsect) <= 0)
+ ide_end_request(1, HWGROUP(drive));
+ if (i > 0) {
+ if (sectors_avail)
+ goto read_next;
+ stat = GET_STAT();
+ if(stat & DRQ_STAT)
+ goto read_again;
+ if(stat & BUSY_STAT) {
+ ide_set_handler (drive, &promise_read_intr, WAIT_CMD);
+ return;
+ }
+ printk("Ah! promise read intr: sectors left !DRQ !BUSY\n");
+ ide_error(drive, "promise read intr", stat);
+ }
+}
+
+/*
+ * promise_write_pollfunc() is the handler for disk write completion polling.
+ */
+static void promise_write_pollfunc (ide_drive_t *drive)
+{
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq;
+
+ if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
+ if (jiffies < hwgroup->poll_timeout) {
+ ide_set_handler (drive, &promise_write_pollfunc, 1);
+ return; /* continue polling... */
+ }
+ printk("%s: write timed-out!\n",drive->name);
+ ide_error (drive, "write timeout", GET_STAT());
+ return;
+ }
+
+ ide_multwrite(drive, 4);
+ rq = hwgroup->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, hwgroup);
+ }
+ return;
+}
+
+/*
+ * promise_write() transfers a block of one or more sectors of data to a
+ * drive as part of a disk write operation. All but 4 sectors are transfered
+ * in the first attempt, then the interface is polled (nicely!) for completion
+ * before the final 4 sectors are transfered. Don't ask me why, but this is
+ * how it's done in the drivers for other O/Ses. There is no interrupt
+ * generated on writes, which is why we have to do it like this.
+ */
+static void promise_write (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = &hwgroup->wrq;
+ int i;
+
+ if (rq->nr_sectors > 4) {
+ ide_multwrite(drive, rq->nr_sectors - 4);
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &promise_write_pollfunc, 1);
+ return;
+ } else {
+ ide_multwrite(drive, rq->nr_sectors);
+ rq = hwgroup->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, hwgroup);
+ }
+ }
+}
+
+/*
+ * do_promise_io() is called from do_rw_disk, having had the block number
+ * already set up. It issues a READ or WRITE command to the Promise
+ * controller, assuming LBA has been used to set up the block number.
+ */
+void do_promise_io (ide_drive_t *drive, struct request *rq)
+{
+ unsigned long timeout;
+ byte stat;
+
+ if (rq->cmd == READ) {
+ ide_set_handler(drive, &promise_read_intr, WAIT_CMD);
+ OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG);
+/* The card's behaviour is odd at this point. If the data is
+ available, DRQ will be true, and no interrupt will be
+ generated by the card. If this is the case, we need to simulate
+ an interrupt. Ugh! Otherwise, if an interrupt will occur, bit0
+ of the SELECT register will be high, so we can just return and
+ be interrupted.*/
+ timeout = jiffies + HZ/20; /* 50ms wait */
+ do {
+ stat=GET_STAT();
+ if(stat & DRQ_STAT) {
+/* unsigned long flags;
+ save_flags(flags);
+ cli();
+ disable_irq(HWIF(drive)->irq);
+*/
+ ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL);
+/* enable_irq(HWIF(drive)->irq);
+ restore_flags(flags);
+*/
+ return;
+ }
+ if(IN_BYTE(IDE_SELECT_REG) & 0x01)
+ return;
+ udelay(1);
+ } while (jiffies < timeout);
+ printk("%s: reading: No DRQ and not waiting - Odd!\n",
+ drive->name);
+ return;
+ }
+ if (rq->cmd == WRITE) {
+ OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG);
+ if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+ printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name);
+ return;
+ }
+ if (!drive->unmask)
+ cli();
+ HWGROUP(drive)->wrq = *rq; /* scratchpad */
+ promise_write(drive);
+ return;
+ }
+ printk("%s: bad command: %d\n", drive->name, rq->cmd);
+ ide_end_request(0, HWGROUP(drive));
+}
diff --git a/drivers/block/promise.h b/drivers/block/promise.h
new file mode 100644
index 000000000..e82541d2a
--- /dev/null
+++ b/drivers/block/promise.h
@@ -0,0 +1,52 @@
+/*
+ * linux/drivers/block/promise.h
+ *
+ * Copyright (C) 1995-6 Linus Torvalds & authors
+ */
+
+/*
+ * Principal author: Peter Denison <peterd@pnd-pc.demon.co.uk>
+ */
+
+#ifndef IDE_PROMISE_H
+#define IDE_PROMISE_H
+
+#define PROMISE_EXTENDED_COMMAND 0xF0
+#define PROMISE_READ 0xF2
+#define PROMISE_WRITE 0xF3
+/* Extended commands - main command code = 0xf0 */
+#define PROMISE_GET_CONFIG 0x10
+#define PROMISE_IDENTIFY 0x20
+
+struct translation_mode {
+ u16 cyl;
+ u8 head;
+ u8 sect;
+};
+
+struct dc_ident {
+ u8 type;
+ u8 unknown1;
+ u8 hw_revision;
+ u8 firmware_major;
+ u8 firmware_minor;
+ u8 bios_address;
+ u8 irq;
+ u8 unknown2;
+ u16 cache_mem;
+ u16 unknown3;
+ u8 id[2];
+ u16 info;
+ struct translation_mode current_tm[4];
+ u8 pad[SECTOR_WORDS*4 - 32];
+};
+
+/*
+ * Routines exported to ide.c:
+ */
+void do_promise_io (ide_drive_t *, struct request *);
+int promise_cmd(ide_drive_t *, byte);
+void setup_dc4030 (ide_hwif_t *);
+int init_dc4030 (void);
+
+#endif IDE_PROMISE_H
diff --git a/drivers/block/qd6580.c b/drivers/block/qd6580.c
new file mode 100644
index 000000000..78c73cf39
--- /dev/null
+++ b/drivers/block/qd6580.c
@@ -0,0 +1,66 @@
+/*
+ * linux/drivers/block/qd6580.c Version 0.02 Feb 09, 1996
+ *
+ * Copyright (C) 1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * QDI QD6580 EIDE controller fast support by Colten Edwards.
+ * No net access, but (maybe) can be reached at pje120@cs.usask.ca
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "ide_modes.h"
+
+/*
+ * Register 0xb3 looks like:
+ * 0x4f is fast mode3 ?
+ * 0x3f is medium mode2 ?
+ * 0x2f is slower mode1 ?
+ * 0x1f is slower yet mode0 ?
+ * 0x0f ??? ???
+ *
+ * Don't know whether this sets BOTH drives, or just the first drive.
+ * Don't know if there is a separate setting for the second drive.
+ *
+ * Feel free to patch this if you have one of these beasts
+ * and can work out the answers!
+ *
+ * I/O ports are 0xb0 0xb2 and 0xb3
+ *
+ * More research on qd6580 being done by willmore@cig.mot.com (David)
+ * -- this is apparently a *dual* IDE interface
+ */
+
+static void tune_qd6580 (ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+
+ pio = ide_get_best_pio_mode(drive, pio, 3, NULL);
+
+ save_flags(flags);
+ cli();
+ outb_p(0x8d,0xb0);
+ outb_p(0x0 ,0xb2);
+ outb_p(((pio+1)<<4)|0x0f,0xb3);
+ inb(0x3f6);
+ restore_flags(flags);
+}
+
+void init_qd6580 (void)
+{
+ ide_hwifs[0].chipset = ide_qd6580;
+ ide_hwifs[1].chipset = ide_qd6580;
+ ide_hwifs[0].tuneproc = &tune_qd6580;
+}
diff --git a/drivers/block/raid0.c b/drivers/block/raid0.c
new file mode 100644
index 000000000..dddf5dd8d
--- /dev/null
+++ b/drivers/block/raid0.c
@@ -0,0 +1,278 @@
+
+/*
+ raid0.c : Multiple Devices driver for Linux
+ Copyright (C) 1994-96 Marc ZYNGIER
+ <zyngier@ufr-info-p7.ibp.fr> or
+ <maz@gloups.fdn.fr>
+
+ RAID-0 management functions.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ You should have received a copy of the GNU General Public License
+ (for example /usr/src/linux/COPYING); if not, write to the Free
+ Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/module.h>
+#include <linux/md.h>
+#include <linux/raid0.h>
+#include <linux/malloc.h>
+
+#define MAJOR_NR MD_MAJOR
+#define MD_DRIVER
+#define MD_PERSONALITY
+
+static void create_strip_zones (int minor, struct md_dev *mddev)
+{
+ int i, j, c=0;
+ int current_offset=0;
+ struct real_dev *smallest_by_zone;
+ struct raid0_data *data=(struct raid0_data *) mddev->private;
+
+ data->nr_strip_zones=1;
+
+ for (i=1; i<mddev->nb_dev; i++)
+ {
+ for (j=0; j<i; j++)
+ if (mddev->devices[i].size==mddev->devices[j].size)
+ {
+ c=1;
+ break;
+ }
+
+ if (!c)
+ data->nr_strip_zones++;
+
+ c=0;
+ }
+
+ data->strip_zone=kmalloc (sizeof(struct strip_zone)*data->nr_strip_zones,
+ GFP_KERNEL);
+
+ data->smallest=NULL;
+
+ for (i=0; i<data->nr_strip_zones; i++)
+ {
+ data->strip_zone[i].dev_offset=current_offset;
+ smallest_by_zone=NULL;
+ c=0;
+
+ for (j=0; j<mddev->nb_dev; j++)
+ if (mddev->devices[j].size>current_offset)
+ {
+ data->strip_zone[i].dev[c++]=mddev->devices+j;
+ if (!smallest_by_zone ||
+ smallest_by_zone->size > mddev->devices[j].size)
+ smallest_by_zone=mddev->devices+j;
+ }
+
+ data->strip_zone[i].nb_dev=c;
+ data->strip_zone[i].size=(smallest_by_zone->size-current_offset)*c;
+
+ if (!data->smallest ||
+ data->smallest->size > data->strip_zone[i].size)
+ data->smallest=data->strip_zone+i;
+
+ data->strip_zone[i].zone_offset=i ? (data->strip_zone[i-1].zone_offset+
+ data->strip_zone[i-1].size) : 0;
+ current_offset=smallest_by_zone->size;
+ }
+}
+
+static int raid0_run (int minor, struct md_dev *mddev)
+{
+ int cur=0, i=0, size, zone0_size, nb_zone;
+ struct raid0_data *data;
+
+ MOD_INC_USE_COUNT;
+
+ mddev->private=kmalloc (sizeof (struct raid0_data), GFP_KERNEL);
+ data=(struct raid0_data *) mddev->private;
+
+ create_strip_zones (minor, mddev);
+
+ nb_zone=data->nr_zones=
+ md_size[minor]/data->smallest->size +
+ (md_size[minor]%data->smallest->size ? 1 : 0);
+
+ data->hash_table=kmalloc (sizeof (struct raid0_hash)*nb_zone, GFP_KERNEL);
+
+ size=data->strip_zone[cur].size;
+
+ i=0;
+ while (cur<data->nr_strip_zones)
+ {
+ data->hash_table[i].zone0=data->strip_zone+cur;
+
+ if (size>=data->smallest->size)/* If we completely fill the slot */
+ {
+ data->hash_table[i++].zone1=NULL;
+ size-=data->smallest->size;
+
+ if (!size)
+ {
+ if (++cur==data->nr_strip_zones) continue;
+ size=data->strip_zone[cur].size;
+ }
+
+ continue;
+ }
+
+ if (++cur==data->nr_strip_zones) /* Last dev, set unit1 as NULL */
+ {
+ data->hash_table[i].zone1=NULL;
+ continue;
+ }
+
+ zone0_size=size; /* Here, we use a 2nd dev to fill the slot */
+ size=data->strip_zone[cur].size;
+ data->hash_table[i++].zone1=data->strip_zone+cur;
+ size-=(data->smallest->size - zone0_size);
+ }
+
+ return (0);
+}
+
+
+static int raid0_stop (int minor, struct md_dev *mddev)
+{
+ struct raid0_data *data=(struct raid0_data *) mddev->private;
+
+ kfree (data->hash_table);
+ kfree (data->strip_zone);
+ kfree (data);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * FIXME - We assume some things here :
+ * - requested buffers NEVER bigger than chunk size,
+ * - requested buffers NEVER cross stripes limits.
+ * Of course, those facts may not be valid anymore (and surely won't...)
+ * Hey guys, there's some work out there ;-)
+ */
+static int raid0_map (struct md_dev *mddev, kdev_t *rdev,
+ unsigned long *rsector, unsigned long size)
+{
+ struct raid0_data *data=(struct raid0_data *) mddev->private;
+ static struct raid0_hash *hash;
+ struct strip_zone *zone;
+ struct real_dev *tmp_dev;
+ int blk_in_chunk, factor, chunk, chunk_size;
+ long block, rblock;
+
+ factor=FACTOR(mddev);
+ chunk_size=(1UL << FACTOR_SHIFT(factor));
+ block=*rsector >> 1;
+ hash=data->hash_table+(block/data->smallest->size);
+
+ /* Sanity check */
+ if ((chunk_size*2)<(*rsector % (chunk_size*2))+size)
+ {
+ printk ("raid0_convert : can't convert block across chunks or bigger than %dk %ld %ld\n", chunk_size, *rsector, size);
+ return (-1);
+ }
+
+ if (block >= (hash->zone0->size +
+ hash->zone0->zone_offset))
+ {
+ if (!hash->zone1)
+ {
+ printk ("raid0_convert : hash->zone1==NULL for block %ld\n", block);
+ return (-1);
+ }
+
+ zone=hash->zone1;
+ }
+ else
+ zone=hash->zone0;
+
+ blk_in_chunk=block & (chunk_size -1);
+ chunk=(block - zone->zone_offset) / (zone->nb_dev<<FACTOR_SHIFT(factor));
+ tmp_dev=zone->dev[(block >> FACTOR_SHIFT(factor)) % zone->nb_dev];
+ rblock=(chunk << FACTOR_SHIFT(factor)) + blk_in_chunk + zone->dev_offset;
+
+ *rdev=tmp_dev->dev;
+ *rsector=rblock<<1;
+
+ return (0);
+}
+
+
+static int raid0_status (char *page, int minor, struct md_dev *mddev)
+{
+ int sz=0;
+#undef MD_DEBUG
+#ifdef MD_DEBUG
+ int j, k;
+ struct raid0_data *data=(struct raid0_data *) mddev->private;
+
+ sz+=sprintf (page+sz, " ");
+ for (j=0; j<data->nr_zones; j++)
+ {
+ sz+=sprintf (page+sz, "[z%d",
+ data->hash_table[j].zone0-data->strip_zone);
+ if (data->hash_table[j].zone1)
+ sz+=sprintf (page+sz, "/z%d] ",
+ data->hash_table[j].zone1-data->strip_zone);
+ else
+ sz+=sprintf (page+sz, "] ");
+ }
+
+ sz+=sprintf (page+sz, "\n");
+
+ for (j=0; j<data->nr_strip_zones; j++)
+ {
+ sz+=sprintf (page+sz, " z%d=[", j);
+ for (k=0; k<data->strip_zone[j].nb_dev; k++)
+ sz+=sprintf (page+sz, "%s/",
+ partition_name(data->strip_zone[j].dev[k]->dev));
+ sz--;
+ sz+=sprintf (page+sz, "] zo=%d do=%d s=%d\n",
+ data->strip_zone[j].zone_offset,
+ data->strip_zone[j].dev_offset,
+ data->strip_zone[j].size);
+ }
+#endif
+ return sz;
+}
+
+
+static struct md_personality raid0_personality=
+{
+ "raid0",
+ raid0_map,
+ raid0_run,
+ raid0_stop,
+ raid0_status,
+ NULL, /* no ioctls */
+ 0
+};
+
+
+#ifndef MODULE
+
+void raid0_init (void)
+{
+ register_md_personality (RAID0, &raid0_personality);
+}
+
+#else
+
+int init_module (void)
+{
+ return (register_md_personality (RAID0, &raid0_personality));
+}
+
+void cleanup_module (void)
+{
+ unregister_md_personality (RAID0);
+}
+
+#endif
diff --git a/drivers/block/ramdisk.c b/drivers/block/ramdisk.c
deleted file mode 100644
index 8d0d8552d..000000000
--- a/drivers/block/ramdisk.c
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * linux/kernel/blk_drv/ramdisk.c
- *
- * Written by Theodore Ts'o, 12/2/91
- *
- * Modifications by Fred N. van Kempen to allow for bootable root
- * disks (which are used in LINUX/Pro). Also some cleanups. 03/03/93
- */
-
-
-#include <linux/sched.h>
-#include <linux/minix_fs.h>
-#include <linux/ext2_fs.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-
-#ifdef __mips__
-#include <asm/bootinfo.h>
-#endif
-#include <asm/system.h>
-#include <asm/segment.h>
-
-#define MAJOR_NR MEM_MAJOR
-#include "blk.h"
-
-#define RAMDISK_MINOR 1
-
-extern void wait_for_keypress(void);
-
-char *rd_start;
-int rd_length = 0;
-static int rd_blocksizes[2] = {0, 0};
-extern char _end;
-
-static void do_rd_request(void)
-{
- int len;
- char *addr;
-
-repeat:
- INIT_REQUEST;
- addr = rd_start + (CURRENT->sector << 9);
- len = CURRENT->current_nr_sectors << 9;
-
- if ((MINOR(CURRENT->dev) != RAMDISK_MINOR) ||
- (addr+len > rd_start+rd_length)) {
- end_request(0);
- goto repeat;
- }
- if (CURRENT-> cmd == WRITE) {
- (void ) memcpy(addr,
- CURRENT->buffer,
- len);
- } else if (CURRENT->cmd == READ) {
- (void) memcpy(CURRENT->buffer,
- addr,
- len);
- } else
- panic("RAMDISK: unknown RAM disk command !\n");
- end_request(1);
- goto repeat;
-}
-
-static struct file_operations rd_fops = {
- NULL, /* lseek - default */
- block_read, /* read - general block-dev read */
- block_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- NULL, /* ioctl */
- NULL, /* mmap */
- NULL, /* no special open code */
- NULL, /* no special release code */
- block_fsync /* fsync */
-};
-
-/*
- * Returns amount of memory which needs to be reserved.
- */
-long rd_init(long mem_start, int length)
-{
- int i;
-#if __i386__
- char *cp;
-#endif
-
- if (register_blkdev(MEM_MAJOR,"rd",&rd_fops)) {
- printk("RAMDISK: Unable to get major %d.\n", MEM_MAJOR);
- return 0;
- }
- blk_dev[MEM_MAJOR].request_fn = DEVICE_REQUEST;
-#if __i386__
- rd_start = (char *) mem_start;
- rd_length = length;
- cp = rd_start;
- for (i=0; i < length; i++)
- *cp++ = '\0';
-#elif defined(__mips__)
- rd_start = (char *) &_end;
- rd_length = length;
- /*
- * Don't reserve memory - this has already been done
- * in arch/mips/kernel/setup.c.
- */
- length = 0;
-#endif
-
- for(i=0;i<2;i++) rd_blocksizes[i] = 1024;
- blksize_size[MAJOR_NR] = rd_blocksizes;
-
- return(length);
-}
-
-static void do_load(void)
-{
- struct buffer_head *bh;
- struct super_block {
- union
- {
- char minix [sizeof (struct minix_super_block)];
- char ext2 [sizeof (struct ext2_super_block)];
- } record;
- } sb;
- struct minix_super_block *minixsb =
- (struct minix_super_block *)&sb;
- struct ext2_super_block *ext2sb =
- (struct ext2_super_block *)&sb;
- int block, tries;
- int i = 1;
- int nblocks;
- char *cp;
-
- /*
- * Check for a super block on the diskette.
- * The old-style boot/root diskettes had their RAM image
- * starting at block 512 of the boot diskette. LINUX/Pro
- * uses the entire diskette as a file system, so in that
- * case, we have to look at block 0. Be intelligent about
- * this, and check both... - FvK
- */
- for (tries = 0; tries < 1000; tries += 512) {
- block = tries;
-#if defined (__i386__)
- bh = breada(ROOT_DEV,block+1,BLOCK_SIZE, 0, PAGE_SIZE);
- if (!bh) {
- printk("RAMDISK: I/O error while looking for super block!\n");
- return;
- }
-
- /* This is silly- why do we require it to be a MINIX FS? */
- *((struct super_block *) &sb) =
- *((struct super_block *) bh->b_data);
- brelse(bh);
-#else /* !defined (__i386__) */
- /*
- * Linux/MIPS loads ramdisk images via the host machine's
- * bootroms. This enables us not only to load the ramdisk
- * from floppy - we might also use CDROM or via network
- * from BOOTP/TFTP, DLC/RIPL boot servers.
- * The same applies for Linux/68k machines.
- */
- *((struct super_block *) &sb) =
- *((struct super_block *) rd_start + BLOCK_SIZE);
-#endif /* !defined (__i386__) */
-
- /* Try Minix */
- nblocks = -1;
- if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
- minixsb->s_magic == MINIX_SUPER_MAGIC2) {
- printk("RAMDISK: Minix filesystem found at block %d\n",
- block);
- nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
- }
-
- /* Try ext2 */
- if (nblocks == -1 && (ext2sb->s_magic ==
- EXT2_PRE_02B_MAGIC ||
- ext2sb->s_magic == EXT2_SUPER_MAGIC))
- {
- printk("RAMDISK: Ext2 filesystem found at block %d\n",
- block);
- nblocks = ext2sb->s_blocks_count;
- }
-
- if (nblocks == -1)
- {
- printk("RAMDISK: trying old-style RAM image.\n");
- continue;
- }
-
- if (nblocks > (rd_length >> BLOCK_SIZE_BITS)) {
- printk("RAMDISK: image too big! (%d/%d blocks)\n",
- nblocks, rd_length >> BLOCK_SIZE_BITS);
- return;
- }
-#ifdef __i386__
- printk("RAMDISK: Loading %d blocks into RAM disk", nblocks);
-
- /* We found an image file system. Load it into core! */
- cp = rd_start;
- while (nblocks) {
- if (nblocks > 2)
- bh = breada(ROOT_DEV, block, BLOCK_SIZE, 0, PAGE_SIZE);
- else
- bh = bread(ROOT_DEV, block, BLOCK_SIZE);
- if (!bh) {
- printk("RAMDISK: I/O error on block %d, aborting!\n",
- block);
- return;
- }
- (void) memcpy(cp, bh->b_data, BLOCK_SIZE);
- brelse(bh);
- if (!(nblocks-- & 15)) printk(".");
- cp += BLOCK_SIZE;
- block++;
- i++;
- }
- printk("\ndone\n");
-#endif /* __i386__ */
-
- /* We loaded the file system image. Prepare for mounting it. */
- ROOT_DEV = ((MEM_MAJOR << 8) | RAMDISK_MINOR);
- return;
- }
-}
-
-/*
- * If the root device is the RAM disk, try to load it.
- * In order to do this, the root device is originally set to the
- * floppy, and we later change it to be RAM disk.
- */
-void rd_load(void)
-{
- struct inode inode;
- struct file filp;
-
- /* If no RAM disk specified, give up early. */
- if (!rd_length)
- return;
- printk("RAMDISK: %d bytes, starting at 0x%p\n",
- rd_length, rd_start);
-
- /* If we are doing a diskette boot, we might have to pre-load it. */
- if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR)
- return;
-
- /* for Slackware install disks */
- printk(KERN_NOTICE "VFS: Insert ramdisk floppy and press ENTER\n");
- wait_for_keypress();
-
- memset(&filp, 0, sizeof(filp));
- memset(&inode, 0, sizeof(inode));
- inode.i_rdev = ROOT_DEV;
- filp.f_mode = 1; /* read only */
- filp.f_inode = &inode;
- if(blkdev_open(&inode, &filp) == 0 ){
- do_load();
- if(filp.f_op && filp.f_op->release)
- filp.f_op->release(&inode,&filp);
- }
-}
diff --git a/drivers/block/rd.c b/drivers/block/rd.c
new file mode 100644
index 000000000..0150bf404
--- /dev/null
+++ b/drivers/block/rd.c
@@ -0,0 +1,686 @@
+/*
+ * ramdisk.c - Multiple ramdisk driver - gzip-loading version - v. 0.8 beta.
+ *
+ * (C) Chad Page, Theodore Ts'o, et. al, 1995.
+ *
+ * This ramdisk is designed to have filesystems created on it and mounted
+ * just like a regular floppy disk.
+ *
+ * It also does something suggested by Linus: use the buffer cache as the
+ * ramdisk data. This makes it possible to dynamically allocate the ramdisk
+ * buffer - with some consequences I have to deal with as I write this.
+ *
+ * This code is based on the original ramdisk.c, written mostly by
+ * Theodore Ts'o (TYT) in 1991. The code was largely rewritten by
+ * Chad Page to use the buffer cache to store the ramdisk data in
+ * 1995; Theodore then took over the driver again, and cleaned it up
+ * for inclusion in the mainline kernel.
+ *
+ * The original CRAMDISK code was written by Richard Lyons, and
+ * adapted by Chad Page to use the new ramdisk interface. Theodore
+ * Ts'o rewrote it so that both the compressed ramdisk loader and the
+ * kernel decompressor uses the same inflate.c codebase. The ramdisk
+ * loader now also loads into a dynamic (buffer cache based) ramdisk,
+ * not the old static ramdisk. Support for the old static ramdisk has
+ * been completely removed.
+ *
+ * Loadable module support added by Tom Dyas.
+ *
+ * Further cleanups by Chad Page (page0588@sundance.sjsu.edu):
+ * Cosmetic changes in #ifdef MODULE, code movement, etc...
+ * When the ramdisk is rmmod'ed, free the protected buffers
+ * Default ramdisk size changed to 2.88MB
+ *
+ * Added initrd: Werner Almesberger & Hans Lermen, Feb '96
+ *
+ * 4/25/96 : Made ramdisk size a parameter (default is now 4MB)
+ * - Chad Page
+ * 7/23/96 : Support for systems without PC-style consoles - <dfrick@dial.eunet.ch>
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/minix_fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/malloc.h>
+#include <linux/ioctl.h>
+#include <linux/fd.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+extern void wait_for_keypress(void);
+
+/*
+ * 35 has been officially registered as the RAMDISK major number, but
+ * so is the original MAJOR number of 1. We're using 1 in
+ * include/linux/major.h for now
+ */
+#define MAJOR_NR RAMDISK_MAJOR
+#include <linux/blk.h>
+
+/* The ramdisk size is now a parameter */
+#define NUM_RAMDISKS 16 /* This cannot be overridden (yet) */
+
+#ifndef MODULE
+/* We don't have to load ramdisks or gunzip them in a module... */
+#define RD_LOADER
+#define BUILD_CRAMDISK
+
+void rd_load(void);
+static int crd_load(struct file *fp, struct file *outfp);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+static int initrd_users = 0;
+#endif
+#endif
+
+/* Various static variables go here... mostly used within the ramdisk code only. */
+
+static int rd_length[NUM_RAMDISKS];
+static int rd_blocksizes[NUM_RAMDISKS];
+
+/*
+ * Parameters for the boot-loading of the ramdisk. These are set by
+ * init/main.c (from arguments to the kernel command line) or from the
+ * architecture-specific setup routine (from the stored bootsector
+ * information).
+ */
+int rd_size = 4096; /* Size of the ramdisks */
+
+#ifndef MODULE
+int rd_doload = 0; /* 1 = load ramdisk, 0 = don't load */
+int rd_prompt = 1; /* 1 = prompt for ramdisk, 0 = don't prompt */
+int rd_image_start = 0; /* starting block # of image */
+#ifdef CONFIG_BLK_DEV_INITRD
+unsigned long initrd_start,initrd_end;
+int mount_initrd = 1; /* zero if initrd should not be mounted */
+#endif
+#endif
+
+/*
+ * Basically, my strategy here is to set up a buffer-head which can't be
+ * deleted, and make that my Ramdisk. If the request is outside of the
+ * allocated size, we must get rid of it...
+ *
+ */
+static void rd_request(void)
+{
+ unsigned int minor;
+ int offset, len;
+
+repeat:
+ INIT_REQUEST;
+
+ minor = MINOR(CURRENT->rq_dev);
+
+ if (minor >= NUM_RAMDISKS) {
+ end_request(0);
+ goto repeat;
+ }
+
+ offset = CURRENT->sector << 9;
+ len = CURRENT->current_nr_sectors << 9;
+
+ if ((offset + len) > rd_length[minor]) {
+ end_request(0);
+ goto repeat;
+ }
+
+ /*
+ * If we're reading, fill the buffer with 0's. This is okay since
+ * we're using protected buffers which should never get freed...
+ *
+ * If we're writing, we protect the buffer.
+ */
+
+ if (CURRENT->cmd == READ)
+ memset(CURRENT->buffer, 0, len);
+ else
+ set_bit(BH_Protected, &CURRENT->bh->b_state);
+
+ end_request(1);
+ goto repeat;
+}
+
+static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int err;
+
+ if (!inode || !inode->i_rdev)
+ return -EINVAL;
+
+ switch (cmd) {
+ case BLKFLSBUF:
+ if (!suser()) return -EACCES;
+ invalidate_buffers(inode->i_rdev);
+ break;
+ case BLKGETSIZE: /* Return device size */
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE, (long *) arg,
+ sizeof(long));
+ if (err)
+ return err;
+ put_user(rd_length[MINOR(inode->i_rdev)] / 512,
+ (long *) arg);
+ return 0;
+
+ default:
+ break;
+ };
+
+ return 0;
+}
+
+
+#ifdef CONFIG_BLK_DEV_INITRD
+
+static long initrd_read(struct inode *inode,struct file *file,
+ char *buf, unsigned long count)
+{
+ int left;
+
+ left = initrd_end-initrd_start-file->f_pos;
+ if (count > left) count = left;
+ if (count == 0) return 0;
+ copy_to_user(buf,(char *) initrd_start+file->f_pos,count);
+ file->f_pos += count;
+ return count;
+}
+
+
+static void initrd_release(struct inode *inode,struct file *file)
+{
+ unsigned long i;
+
+ if (--initrd_users) return;
+ for (i = initrd_start; i < initrd_end; i += PAGE_SIZE)
+ free_page(i);
+ initrd_start = 0;
+}
+
+
+static struct file_operations initrd_fops = {
+ NULL, /* lseek */
+ initrd_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ initrd_release, /* release */
+ NULL /* fsync */
+};
+
+#endif
+
+
+static int rd_open(struct inode * inode, struct file * filp)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (DEVICE_NR(inode->i_rdev) == INITRD_MINOR) {
+ if (!initrd_start) return -ENODEV;
+ initrd_users++;
+ filp->f_op = &initrd_fops;
+ return 0;
+ }
+#endif
+
+ if (DEVICE_NR(inode->i_rdev) >= NUM_RAMDISKS)
+ return -ENXIO;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef MODULE
+static void rd_release(struct inode * inode, struct file * filp)
+{
+ MOD_DEC_USE_COUNT;
+}
+#endif
+
+static struct file_operations fd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - block dev read */
+ block_write, /* write - block dev write */
+ NULL, /* readdir - not here! */
+ NULL, /* select */
+ rd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ rd_open, /* open */
+#ifndef MODULE
+ NULL, /* no special release code... */
+#else
+ rd_release, /* module needs to decrement use count */
+#endif
+ block_fsync /* fsync */
+};
+
+/* This is the registration and initialization section of the ramdisk driver */
+int rd_init(void)
+{
+ int i;
+
+ if (register_blkdev(MAJOR_NR, "ramdisk", &fd_fops)) {
+ printk("RAMDISK: Could not get major %d", MAJOR_NR);
+ return -EIO;
+ }
+
+ blk_dev[MAJOR_NR].request_fn = &rd_request;
+
+ for (i = 0; i < NUM_RAMDISKS; i++) {
+ rd_length[i] = (rd_size * 1024);
+ rd_blocksizes[i] = 1024;
+ }
+
+ blksize_size[MAJOR_NR] = rd_blocksizes;
+
+ printk("Ramdisk driver initialized : %d ramdisks of %dK size\n",
+ NUM_RAMDISKS, rd_size);
+
+ return 0;
+}
+
+/* loadable module support */
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ int error = rd_init();
+ if (!error)
+ printk(KERN_INFO "RAMDISK: Loaded as module.\n");
+ return error;
+}
+
+/* Before freeing the module, invalidate all of the protected buffers! */
+void cleanup_module(void)
+{
+ int i;
+
+ for (i = 0 ; i < NUM_RAMDISKS; i++)
+ invalidate_buffers(MKDEV(MAJOR_NR, i));
+
+ unregister_blkdev( MAJOR_NR, "ramdisk" );
+ blk_dev[MAJOR_NR].request_fn = 0;
+}
+
+#endif /* MODULE */
+
+/* End of non-loading portions of the ramdisk driver */
+
+#ifdef RD_LOADER
+/*
+ * This routine tries to a ramdisk image to load, and returns the
+ * number of blocks to read for a non-compressed image, 0 if the image
+ * is a compressed image, and -1 if an image with the right magic
+ * numbers could not be found.
+ *
+ * We currently check for the following magic numbers:
+ * minix
+ * ext2
+ * gzip
+ */
+int
+identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)
+{
+ const int size = 512;
+ struct minix_super_block *minixsb;
+ struct ext2_super_block *ext2sb;
+ int nblocks = -1;
+ int max_blocks;
+ unsigned char *buf;
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (buf == 0)
+ return -1;
+
+ minixsb = (struct minix_super_block *) buf;
+ ext2sb = (struct ext2_super_block *) buf;
+ memset(buf, 0xe5, size);
+
+ /*
+ * Read block 0 to test for gzipped kernel
+ */
+ if (fp->f_op->llseek)
+ fp->f_op->llseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0);
+ fp->f_pos = start_block * BLOCK_SIZE;
+
+ fp->f_op->read(fp->f_inode, fp, buf, size);
+
+ /*
+ * If it matches the gzip magic numbers, return -1
+ */
+ if (buf[0] == 037 && ((buf[1] == 0213) || (buf[1] == 0236))) {
+ printk(KERN_NOTICE
+ "RAMDISK: Compressed image found at block %d\n",
+ start_block);
+ nblocks = 0;
+ goto done;
+ }
+
+ /*
+ * Read block 1 to test for minix and ext2 superblock
+ */
+ if (fp->f_op->llseek)
+ fp->f_op->llseek(fp->f_inode, fp,
+ (start_block+1) * BLOCK_SIZE, 0);
+ fp->f_pos = (start_block+1) * BLOCK_SIZE;
+
+ fp->f_op->read(fp->f_inode, fp, buf, size);
+
+ /* Try minix */
+ if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
+ minixsb->s_magic == MINIX_SUPER_MAGIC2) {
+ printk(KERN_NOTICE
+ "RAMDISK: Minix filesystem found at block %d\n",
+ start_block);
+ nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
+ goto done;
+ }
+
+ /* Try ext2 */
+ if (ext2sb->s_magic == EXT2_SUPER_MAGIC) {
+ printk(KERN_NOTICE
+ "RAMDISK: Ext2 filesystem found at block %d\n",
+ start_block);
+ nblocks = ext2sb->s_blocks_count;
+ goto done;
+ }
+ printk(KERN_NOTICE
+ "RAMDISK: Couldn't find valid ramdisk image starting at %d.\n",
+ start_block);
+
+done:
+ if (fp->f_op->llseek)
+ fp->f_op->llseek(fp->f_inode, fp, start_block * BLOCK_SIZE, 0);
+ fp->f_pos = start_block * BLOCK_SIZE;
+
+ if ((nblocks > 0) && blk_size[MAJOR(device)]) {
+ max_blocks = blk_size[MAJOR(device)][MINOR(device)];
+ max_blocks -= start_block;
+ if (nblocks > max_blocks) {
+ printk(KERN_NOTICE
+ "RAMDISK: Restricting filesystem size "
+ "from %d to %d blocks.\n",
+ nblocks, max_blocks);
+ nblocks = max_blocks;
+ }
+ }
+ kfree(buf);
+ return nblocks;
+}
+
+/*
+ * This routine loads in the ramdisk image.
+ */
+static void rd_load_image(kdev_t device,int offset)
+{
+ struct inode inode, out_inode;
+ struct file infile, outfile;
+ unsigned short fs;
+ kdev_t ram_device;
+ int nblocks, i;
+ char *buf;
+ unsigned short rotate = 0;
+ char rotator[4] = { '|' , '/' , '-' , '\\' };
+
+ ram_device = MKDEV(MAJOR_NR, 0);
+
+ memset(&infile, 0, sizeof(infile));
+ memset(&inode, 0, sizeof(inode));
+ inode.i_rdev = device;
+ infile.f_mode = 1; /* read only */
+ infile.f_inode = &inode;
+
+ memset(&outfile, 0, sizeof(outfile));
+ memset(&out_inode, 0, sizeof(out_inode));
+ out_inode.i_rdev = ram_device;
+ outfile.f_mode = 3; /* read/write */
+ outfile.f_inode = &out_inode;
+
+ if (blkdev_open(&inode, &infile) != 0) return;
+ if (blkdev_open(&out_inode, &outfile) != 0) return;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ nblocks = identify_ramdisk_image(device, &infile, offset);
+ if (nblocks < 0)
+ goto done;
+
+ if (nblocks == 0) {
+#ifdef BUILD_CRAMDISK
+ if (crd_load(&infile, &outfile) == 0)
+ goto successful_load;
+#else
+ printk(KERN_NOTICE
+ "RAMDISK: Kernel does not support compressed "
+ "ramdisk images\n");
+#endif
+ goto done;
+ }
+
+ if (nblocks > (rd_length[0] >> BLOCK_SIZE_BITS)) {
+ printk("RAMDISK: image too big! (%d/%d blocks)\n",
+ nblocks, rd_length[0] >> BLOCK_SIZE_BITS);
+ goto done;
+ }
+
+ /*
+ * OK, time to copy in the data
+ */
+ buf = kmalloc(BLOCK_SIZE, GFP_KERNEL);
+ if (buf == 0) {
+ printk(KERN_ERR "RAMDISK: could not allocate buffer\n");
+ goto done;
+ }
+
+ printk(KERN_NOTICE "RAMDISK: Loading %d blocks into ram disk... ", nblocks);
+ for (i=0; i < nblocks; i++) {
+ infile.f_op->read(infile.f_inode, &infile, buf,
+ BLOCK_SIZE);
+ outfile.f_op->write(outfile.f_inode, &outfile, buf,
+ BLOCK_SIZE);
+ if (!(i % 16)) {
+ printk("%c\b", rotator[rotate & 0x3]);
+ rotate++;
+ }
+ }
+ printk("done.\n");
+ kfree(buf);
+
+successful_load:
+ invalidate_buffers(device);
+ ROOT_DEV = MKDEV(MAJOR_NR,0);
+
+done:
+ if (infile.f_op->release)
+ infile.f_op->release(&inode, &infile);
+ set_fs(fs);
+}
+
+
+void rd_load()
+{
+ if (rd_doload == 0)
+ return;
+
+ if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR) return;
+
+ if (rd_prompt) {
+#ifdef CONFIG_BLK_DEV_FD
+ floppy_eject();
+#endif
+#ifndef CONFIG_SERIAL_ONLY_CONSOLE
+ printk(KERN_NOTICE
+ "VFS: Insert root floppy disk to be loaded into ramdisk and press ENTER\n");
+ wait_for_keypress();
+#endif
+ }
+
+ rd_load_image(ROOT_DEV,rd_image_start);
+
+}
+
+
+#ifdef CONFIG_BLK_DEV_INITRD
+void initrd_load(void)
+{
+ rd_load_image(MKDEV(MAJOR_NR, INITRD_MINOR),0);
+}
+#endif
+
+#endif /* RD_LOADER */
+
+#ifdef BUILD_CRAMDISK
+
+/*
+ * gzip declarations
+ */
+
+#define OF(args) args
+
+#define memzero(s, n) memset ((s), 0, (n))
+
+
+typedef unsigned char uch;
+typedef unsigned short ush;
+typedef unsigned long ulg;
+
+#define INBUFSIZ 4096
+#define WSIZE 0x8000 /* window size--must be a power of two, and */
+ /* at least 32K for zip's deflate method */
+
+static uch *inbuf;
+static uch *window;
+
+static unsigned insize = 0; /* valid bytes in inbuf */
+static unsigned inptr = 0; /* index of next byte to be processed in inbuf */
+static unsigned outcnt = 0; /* bytes in output buffer */
+static exit_code = 0;
+static long bytes_out = 0;
+static struct file *crd_infp, *crd_outfp;
+
+#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf())
+
+/* Diagnostic functions (stubbed out) */
+#define Assert(cond,msg)
+#define Trace(x)
+#define Tracev(x)
+#define Tracevv(x)
+#define Tracec(c,x)
+#define Tracecv(c,x)
+
+#define STATIC static
+
+static int fill_inbuf(void);
+static void flush_window(void);
+static void *malloc(int size);
+static void free(void *where);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+#include "../../lib/inflate.c"
+
+static void *malloc(int size)
+{
+ return kmalloc(size, GFP_KERNEL);
+}
+
+static void free(void *where)
+{
+ kfree(where);
+}
+
+static void gzip_mark(void **ptr)
+{
+}
+
+static void gzip_release(void **ptr)
+{
+}
+
+
+/* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
+ */
+static int fill_inbuf()
+{
+ if (exit_code) return -1;
+
+ insize = crd_infp->f_op->read(crd_infp->f_inode, crd_infp,
+ inbuf, INBUFSIZ);
+ if (insize == 0) return -1;
+
+ inptr = 1;
+
+ return inbuf[0];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+static void flush_window()
+{
+ ulg c = crc; /* temporary variable */
+ unsigned n;
+ uch *in, ch;
+
+ crd_outfp->f_op->write(crd_outfp->f_inode, crd_outfp, window,
+ outcnt);
+ in = window;
+ for (n = 0; n < outcnt; n++) {
+ ch = *in++;
+ c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
+ }
+ crc = c;
+ bytes_out += (ulg)outcnt;
+ outcnt = 0;
+}
+
+static void error(char *x)
+{
+ printk(KERN_ERR "%s", x);
+ exit_code = 1;
+}
+
+static int
+crd_load(struct file * fp, struct file *outfp)
+{
+ int result;
+
+ crd_infp = fp;
+ crd_outfp = outfp;
+ inbuf = kmalloc(INBUFSIZ, GFP_KERNEL);
+ if (inbuf == 0) {
+ printk(KERN_ERR "RAMDISK: Couldn't allocate gzip buffer\n");
+ return -1;
+ }
+ window = kmalloc(WSIZE, GFP_KERNEL);
+ if (window == 0) {
+ printk(KERN_ERR "RAMDISK: Couldn't allocate gzip window\n");
+ kfree(inbuf);
+ return -1;
+ }
+ makecrc();
+ result = gunzip();
+ kfree(inbuf);
+ kfree(window);
+ return result;
+}
+
+#endif /* BUILD_CRAMDISK */
+
diff --git a/drivers/block/rz1000.c b/drivers/block/rz1000.c
new file mode 100644
index 000000000..41b26f277
--- /dev/null
+++ b/drivers/block/rz1000.c
@@ -0,0 +1,59 @@
+/*
+ * linux/drivers/block/rz1000.c Version 0.03 Mar 20, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: mlord@pobox.com (Mark Lord)
+ *
+ * This file provides support for disabling the buggy read-ahead
+ * mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include "ide.h"
+
+static void ide_pci_access_error (int rc)
+{
+ printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
+}
+
+void init_rz1000 (byte bus, byte fn)
+{
+ int rc;
+ unsigned short reg;
+
+ printk("ide0: buggy RZ1000 interface: ");
+ if ((rc = pcibios_read_config_word (bus, fn, PCI_COMMAND, &reg))) {
+ ide_pci_access_error (rc);
+ } else if (!(reg & 1)) {
+ printk("not enabled\n");
+ } else {
+ if ((rc = pcibios_read_config_word(bus, fn, 0x40, &reg))
+ || (rc = pcibios_write_config_word(bus, fn, 0x40, reg & 0xdfff)))
+ {
+ ide_hwifs[0].drives[0].no_unmask = 1;
+ ide_hwifs[0].drives[1].no_unmask = 1;
+ ide_hwifs[1].drives[0].no_unmask = 1;
+ ide_hwifs[1].drives[1].no_unmask = 1;
+ ide_hwifs[0].serialized = 1;
+ ide_hwifs[1].serialized = 1;
+ ide_pci_access_error (rc);
+ printk("serialized, disabled unmasking\n");
+ } else
+ printk("disabled read-ahead\n");
+ }
+}
diff --git a/drivers/block/sbpcd.c b/drivers/block/sbpcd.c
deleted file mode 100644
index 5f536e941..000000000
--- a/drivers/block/sbpcd.c
+++ /dev/null
@@ -1,5399 +0,0 @@
-/*
- * sbpcd.c CD-ROM device driver for the whole family of IDE-style
- * Kotobuki/Matsushita/Panasonic CR-5xx drives for
- * SoundBlaster ("Pro" or "16 ASP" or compatible) cards
- * and for "no-sound" interfaces like Lasermate and the
- * Panasonic CI-101P.
- * Also for the Longshine LCS-7260 drive.
- * Also for the IBM "External ISA CD-Rom" drive.
- * Also for the CreativeLabs CD200 drive (but I still need some
- * detailed bug reports).
- * Also for the TEAC CD-55A drive.
- * Not for Funai or Sanyo drives.
- *
- * NOTE: This is release 3.7.
- *
- * VERSION HISTORY
- *
- * 0.1 initial release, April/May 93, after mcd.c (Martin Harriss)
- *
- * 0.2 the "repeat:"-loop in do_sbpcd_request did not check for
- * end-of-request_queue (resulting in kernel panic).
- * Flow control seems stable, but throughput is not better.
- *
- * 0.3 interrupt locking totally eliminated (maybe "inb" and "outb"
- * are still locking) - 0.2 made keyboard-type-ahead losses.
- * check_sbpcd_media_change added (to use by isofs/inode.c)
- * - but it detects almost nothing.
- *
- * 0.4 use MAJOR 25 definitely.
- * Almost total re-design to support double-speed drives and
- * "naked" (no sound) interface cards ("LaserMate" interface type).
- * Flow control should be exact now.
- * Don't occupy the SbPro IRQ line (not needed either); will
- * live together with Hannu Savolainen's sndkit now.
- * Speeded up data transfer to 150 kB/sec, with help from Kai
- * Makisara, the "provider" of the "mt" tape utility.
- * Give "SpinUp" command if necessary.
- * First steps to support up to 4 drives (but currently only one).
- * Implemented audio capabilities - workman should work, xcdplayer
- * gives some problems.
- * This version is still consuming too much CPU time, and
- * sleeping still has to be worked on.
- * During "long" implied seeks, it seems possible that a
- * ReadStatus command gets ignored. That gives the message
- * "ResponseStatus timed out" (happens about 6 times here during
- * a "ls -alR" of the YGGDRASIL LGX-Beta CD). Such a case is
- * handled without data error, but it should get done better.
- *
- * 0.5 Free CPU during waits (again with help from Kai Makisara).
- * Made it work together with the LILO/kernel setup standard.
- * Included auto-probing code, as suggested by YGGDRASIL.
- * Formal redesign to add DDI debugging.
- * There are still flaws in IOCTL (workman with double speed drive).
- *
- * 1.0 Added support for all drive IDs (0...3, no longer only 0)
- * and up to 4 drives on one controller.
- * Added "#define MANY_SESSION" for "old" multi session CDs.
- *
- * 1.1 Do SpinUp for new drives, too.
- * Revised for clean compile under "old" kernels (0.99pl9).
- *
- * 1.2 Found the "workman with double-speed drive" bug: use the driver's
- * audio_state, not what the drive is reporting with ReadSubQ.
- *
- * 1.3 Minor cleanups.
- * Refinements regarding Workman.
- *
- * 1.4 Read XA disks (PhotoCDs) with "old" drives, too (but only the first
- * session - no chance to fully access a "multi-session" CD).
- * This currently still is too slow (50 kB/sec) - but possibly
- * the old drives won't do it faster.
- * Implemented "door (un)lock" for new drives (still does not work
- * as wanted - no lock possible after an unlock).
- * Added some debugging printout for the UPC/EAN code - but my drives
- * return only zeroes. Is there no UPC/EAN code written?
- *
- * 1.5 Laborate with UPC/EAN code (not better yet).
- * Adapt to kernel 1.1.8 change (have to explicitly include
- * <linux/string.h> now).
- *
- * 1.6 Trying to read audio frames as data. Impossible with the current
- * drive firmware levels, as it seems. Awaiting any hint. ;-)
- * Changed "door unlock": repeat it until success.
- * Changed CDROMSTOP routine (stop somewhat "softer" so that Workman
- * won't get confused).
- * Added a third interface type: Sequoia S-1000, as used with the SPEA
- * Media FX sound card. This interface (usable for Sony and Mitsumi
- * drives, too) needs a special configuration setup and behaves like a
- * LaserMate type after that. Still experimental - I do not have such
- * an interface.
- * Use the "variable BLOCK_SIZE" feature (2048). But it does only work
- * if you give the mount option "block=2048".
- * The media_check routine is currently disabled; now that it gets
- * called as it should I fear it must get synchronized for not to
- * disturb the normal driver's activity.
- *
- * 2.0 Version number bumped - two reasons:
- * - reading audio tracks as data works now with CR-562 and CR-563. We
- * currently do it by an IOCTL (yet has to get standardized), one frame
- * at a time; that is pretty slow. But it works.
- * - we are maintaining now up to 4 interfaces (each up to 4 drives):
- * did it the easy way - a different MAJOR (25, 26, ...) and a different
- * copy of the driver (sbpcd.c, sbpcd2.c, sbpcd3.c, sbpcd4.c - only
- * distinguished by the value of SBPCD_ISSUE and the driver's name),
- * and a common sbpcd.h file.
- * Bettered the "ReadCapacity error" problem with old CR-52x drives (the
- * drives sometimes need a manual "eject/insert" before work): just
- * reset the drive and do again. Needs lots of resets here and sometimes
- * that does not cure, so this can't be the solution.
- *
- * 2.1 Found bug with multisession CDs (accessing frame 16).
- * "read audio" works now with address type CDROM_MSF, too.
- * Bigger audio frame buffer: allows reading max. 4 frames at time; this
- * gives a significant speedup, but reading more than one frame at once
- * gives missing chunks at each single frame boundary.
- *
- * 2.2 Kernel interface cleanups: timers, init, setup, media check.
- *
- * 2.3 Let "door lock" and "eject" live together.
- * Implemented "close tray" (done automatically during open).
- *
- * 2.4 Use different names for device registering.
- *
- * 2.5 Added "#if EJECT" code (default: enabled) to automatically eject
- * the tray during last call to "sbpcd_release".
- * Added "#if JUKEBOX" code (default: disabled) to automatically eject
- * the tray during call to "sbpcd_open" if no disk is in.
- * Turn on the CD volume of "compatible" sound cards, too; just define
- * SOUND_BASE (in sbpcd.h) accordingly (default: disabled).
- *
- * 2.6 Nothing new.
- *
- * 2.7 Added CDROMEJECT_SW ioctl to set the "EJECT" behavior on the fly:
- * 0 disables, 1 enables auto-ejecting. Useful to keep the tray in
- * during shutdown.
- *
- * 2.8 Added first support (still BETA, I need feedback or a drive) for
- * the Longshine LCS-7260 drives. They appear as double-speed drives
- * using the "old" command scheme, extended by tray control and door
- * lock functions.
- * Found (and fixed preliminary) a flaw with some multisession CDs: we
- * have to re-direct not only the accesses to frame 16 (the isofs
- * routines drive it up to max. 100), but also those to the continuation
- * (repetition) frames (as far as they exist - currently set fix as
- * 16..20).
- * Changed default of the "JUKEBOX" define. If you use this default,
- * your tray will eject if you try to mount without a disk in. Next
- * mount command will insert the tray - so, just fill in a disk. ;-)
- *
- * 2.9 Fulfilled the Longshine LCS-7260 support; with great help and
- * experiments by Serge Robyns.
- * First attempts to support the TEAC CD-55A drives; but still not
- * usable yet.
- * Implemented the CDROMMULTISESSION ioctl; this is an attempt to handle
- * multi session CDs more "transparent" (redirection handling has to be
- * done within the isofs routines, and only for the special purpose of
- * obtaining the "right" volume descriptor; accesses to the raw device
- * should not get redirected).
- *
- * 3.0 Just a "normal" increment, with some provisions to do it better. ;-)
- * Introduced "#define READ_AUDIO" to specify the maximum number of
- * audio frames to grab with one request. This defines a buffer size
- * within kernel space; a value of 0 will reserve no such space and
- * disable the CDROMREADAUDIO ioctl. A value of 75 enables the reading
- * of a whole second with one command, but will use a buffer of more
- * than 172 kB.
- * Started CD200 support. Drive detection should work, but nothing
- * more.
- *
- * 3.1 Working to support the CD200 and the Teac CD-55A drives.
- * AT-BUS style device numbering no longer used: use SCSI style now.
- * So, the first "found" device has MINOR 0, regardless of the
- * jumpered drive ID. This implies modifications to the /dev/sbpcd*
- * entries for some people, but will help the DAU (german TLA, english:
- * "newbie", maybe ;-) to install his "first" system from a CD.
- *
- * 3.2 Still testing with CD200 and CD-55A drives.
- *
- * 3.3 Working with CD200 support.
- *
- * 3.4 Auto-probing stops if an address of 0 is seen (to be entered with
- * the kernel command line).
- * Made the driver "loadable". If used as a module, "audio copy" is
- * disabled, and the internal read ahead data buffer has a reduced size
- * of 4 kB; so, throughput may be reduced a little bit with slow CPUs.
- *
- * 3.5 Provisions to handle weird photoCDs which have an interrupted
- * "formatting" immediately after the last frames of some files: simply
- * never "read ahead" with MultiSession CDs. By this, CPU usage may be
- * increased with those CDs, and there may be a loss in speed.
- * Re-structured the messaging system.
- * The "loadable" version no longer has a limited READ_AUDIO buffer
- * size.
- * Removed "MANY_SESSION" handling for "old" multi session CDs.
- * Added "private" IOCTLs CDROMRESET and CDROMVOLREAD.
- * Started again to support the TEAC CD-55A drives, now that I found
- * the money for "my own" drive. ;-)
- * The TEAC CD-55A support is fairly working now.
- * I have measured that the drive "delivers" at 600 kB/sec (even with
- * bigger requests than the drive's 64 kB buffer can satisfy), but
- * the "real" rate does not exceed 520 kB/sec at the moment.
- * Caused by the various changes to build in TEAC support, the timed
- * loops are de-optimized at the moment (less throughput with CR-52x
- * drives, and the TEAC will give speed only with SBP_BUFFER_FRAMES 64).
- *
- * 3.6 Fixed TEAC data read problems with SbPro interfaces.
- * Initial size of the READ_AUDIO buffer is 0. Can get set to any size
- * during runtime.
- *
- * 3.7 Introduced MAX_DRIVES for some poor interface cards (seen with TEAC
- * drives) which allow only one drive (ID 0); this avoids repetitive
- * detection under IDs 1..3.
- * Elongated cmd_out_T response waiting; necessary for photo CDs with
- * a lot of sessions.
- * Bettered the sbpcd_open() behavior with TEAC drives.
- *
- * TODO
- *
- * disk change detection
- * allow & synchronize multi-activity
- * (data + audio + ioctl + disk change, multiple drives)
- * implement "read all subchannel data" (96 bytes per frame)
- *
- * special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
- * elaborated speed-up experiments (and the fabulous results!), for
- * the "push" towards load-free wait loops, and for the extensive mail
- * thread which brought additional hints and bug fixes.
- *
- * Copyright (C) 1993, 1994, 1995 Eberhard Moenkeberg <emoenke@gwdg.de>
- *
- * If you change this software, you should mail a .diff
- * file with some description lines to emoenke@gwdg.de.
- * I want to know about it.
- *
- * If you are the editor of a Linux CD, you should
- * enable sbpcd.c within your boot floppy kernel and
- * send me one of your CDs for free.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example /usr/src/linux/COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef SBPCD_ISSUE
-#define SBPCD_ISSUE 1
-#endif SBPCD_ISSUE
-
-#include <linux/config.h>
-
-#ifdef MODULE
-#include <linux/module.h>
-#include <linux/version.h>
-#ifndef CONFIG_MODVERSIONS
-char kernel_version[]=UTS_RELEASE;
-#endif
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif MODULE
-
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/cdrom.h>
-#include <linux/ioport.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-#include <stdarg.h>
-#include <linux/sbpcd.h>
-
-#if !(SBPCD_ISSUE-1)
-#define MAJOR_NR MATSUSHITA_CDROM_MAJOR
-#endif
-#if !(SBPCD_ISSUE-2)
-#define MAJOR_NR MATSUSHITA_CDROM2_MAJOR /* second driver issue */
-#endif
-#if !(SBPCD_ISSUE-3)
-#define MAJOR_NR MATSUSHITA_CDROM3_MAJOR /* third driver issue */
-#endif
-#if !(SBPCD_ISSUE-4)
-#define MAJOR_NR MATSUSHITA_CDROM4_MAJOR /* fourth driver issue */
-#endif
-
-#include "blk.h"
-
-#define VERSION "v3.7 Eberhard Moenkeberg <emoenke@gwdg.de>"
-
-/*==========================================================================*/
-/*
- * provisions for more than 1 driver issues
- * currently up to 4 drivers, expandable
- */
-#if !(SBPCD_ISSUE-1)
-#define DO_SBPCD_REQUEST(a) do_sbpcd_request(a)
-#define SBPCD_INIT(a,b) sbpcd_init(a,b)
-#endif
-#if !(SBPCD_ISSUE-2)
-#define DO_SBPCD_REQUEST(a) do_sbpcd2_request(a)
-#define SBPCD_INIT(a,b) sbpcd2_init(a,b)
-#endif
-#if !(SBPCD_ISSUE-3)
-#define DO_SBPCD_REQUEST(a) do_sbpcd3_request(a)
-#define SBPCD_INIT(a,b) sbpcd3_init(a,b)
-#endif
-#if !(SBPCD_ISSUE-4)
-#define DO_SBPCD_REQUEST(a) do_sbpcd4_request(a)
-#define SBPCD_INIT(a,b) sbpcd4_init(a,b)
-#endif
-/*==========================================================================*/
-#if SBPCD_DIS_IRQ
-#define SBPCD_CLI cli()
-#define SBPCD_STI sti()
-#else
-#define SBPCD_CLI
-#define SBPCD_STI
-#endif SBPCD_DIS_IRQ
-/*==========================================================================*/
-/*
- * auto-probing address list
- * inspired by Adam J. Richter from Yggdrasil
- *
- * still not good enough - can cause a hang.
- * example: a NE 2000 ethernet card at 300 will cause a hang probing 310.
- * if that happens, reboot and use the LILO (kernel) command line.
- * The possibly conflicting ethernet card addresses get NOT probed
- * by default - to minimize the hang possibilities.
- *
- * The SB Pro addresses get "mirrored" at 0x6xx and some more locations - to
- * avoid a type error, the 0x2xx-addresses must get checked before 0x6xx.
- *
- * send mail to emoenke@gwdg.de if your interface card is not FULLY
- * represented here.
- */
-#if !(SBPCD_ISSUE-1)
-static int sbpcd[] =
-{
- CDROM_PORT, SBPRO, /* probe with user's setup first */
-#if DISTRIBUTION
- 0x230, 1, /* Soundblaster Pro and 16 (default) */
- 0x300, 0, /* CI-101P (default), WDH-7001C (default),
- Galaxy (default), Reveal (one default) */
- 0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
- 0x260, 1, /* OmniCD */
- 0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default),
- Longshine LCS-6853 (default) */
- 0x338, 0, /* Reveal Sound Wave 32 card model #SC600 */
- 0x340, 0, /* Mozart sound card (default), Lasermate, CI-101P */
- 0x360, 0, /* Lasermate, CI-101P */
- 0x270, 1, /* Soundblaster 16 */
- 0x670, 0, /* "sound card #9" */
- 0x690, 0, /* "sound card #9" */
- 0x330, 2, /* SPEA Media FX (default) */
- 0x320, 2, /* SPEA Media FX */
- 0x340, 2, /* SPEA Media FX */
- 0x634, 0, /* some newer sound cards */
- 0x638, 0, /* some newer sound cards */
- 0x230, 1, /* some newer sound cards */
- /* due to incomplete address decoding of the SbPro card, these must be last */
- 0x630, 0, /* "sound card #9" (default) */
- 0x650, 0, /* "sound card #9" */
-#ifdef MODULE
- /*
- * some "hazardous" locations (no harm with the loadable version)
- * (will stop the bus if a NE2000 ethernet card resides at offset -0x10)
- */
- 0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
- 0x350, 0, /* Lasermate, CI-101P */
- 0x350, 2, /* SPEA Media FX */
- 0x370, 0, /* Lasermate, CI-101P */
- 0x290, 1, /* Soundblaster 16 */
- 0x310, 0, /* Lasermate, CI-101P, WDH-7001C */
-#endif MODULE
-#endif DISTRIBUTION
-};
-#else
-static int sbpcd[] = {CDROM_PORT, SBPRO}; /* probe with user's setup only */
-#endif
-
-#define NUM_PROBE (sizeof(sbpcd) / sizeof(int))
-
-/*==========================================================================*/
-/*
- * the external references:
- */
-#if !(SBPCD_ISSUE-1)
-#ifdef CONFIG_SBPCD2
-extern unsigned long sbpcd2_init(unsigned long, unsigned long);
-#endif
-#ifdef CONFIG_SBPCD3
-extern unsigned long sbpcd3_init(unsigned long, unsigned long);
-#endif
-#ifdef CONFIG_SBPCD4
-extern unsigned long sbpcd4_init(unsigned long, unsigned long);
-#endif
-#endif
-
-/*==========================================================================*/
-
-#define INLINE inline
-
-/*==========================================================================*/
-/*
- * the forward references:
- */
-static void sbp_sleep(u_int);
-static void mark_timeout_delay(u_long);
-static void mark_timeout_data(u_long);
-#if 0
-static void mark_timeout_audio(u_long);
-#endif
-static void sbp_read_cmd(void);
-static int sbp_data(void);
-static int cmd_out(void);
-static int DiskInfo(void);
-static int sbpcd_chk_disk_change(dev_t);
-
-/*==========================================================================*/
-
-/*
- * pattern for printk selection:
- *
- * (1<<DBG_INF) necessary information
- * (1<<DBG_BSZ) BLOCK_SIZE trace
- * (1<<DBG_REA) "read" status trace
- * (1<<DBG_CHK) "media check" trace
- * (1<<DBG_TIM) datarate timer test
- * (1<<DBG_INI) initialization trace
- * (1<<DBG_TOC) tell TocEntry values
- * (1<<DBG_IOC) ioctl trace
- * (1<<DBG_STA) "ResponseStatus" trace
- * (1<<DBG_ERR) "cc_ReadError" trace
- * (1<<DBG_CMD) "cmd_out" trace
- * (1<<DBG_WRN) give explanation before auto-probing
- * (1<<DBG_MUL) multi session code test
- * (1<<DBG_IDX) "drive_id != 0" test code
- * (1<<DBG_IOX) some special information
- * (1<<DBG_DID) drive ID test
- * (1<<DBG_RES) drive reset info
- * (1<<DBG_SPI) SpinUp test info
- * (1<<DBG_IOS) ioctl trace: "subchannel"
- * (1<<DBG_IO2) ioctl trace: general
- * (1<<DBG_UPC) show UPC info
- * (1<<DBG_XA1) XA mode debugging
- * (1<<DBG_LCK) door (un)lock info
- * (1<<DBG_SQ1) dump SubQ frame
- * (1<<DBG_AUD) "read audio" debugging
- * (1<<DBG_SEQ) Sequoia interface configuration trace
- * (1<<DBG_LCS) Longshine LCS-7260 debugging trace
- * (1<<DBG_CD2) MKE CD200 debugging trace
- * (1<<DBG_TEA) TEAC CD-55A debugging trace
- * (1<<DBG_TE2) TEAC CD-55A debugging trace, 2nd level
- * (1<<DBG_000) unnecessary information
- */
-#if DISTRIBUTION
-static int sbpcd_debug = (1<<DBG_INF);
-#else
-static int sbpcd_debug = ((1<<DBG_INF) |
- (1<<DBG_TOC) |
- (1<<DBG_MUL) |
- (1<<DBG_UPC));
-#endif DISTRIBUTION
-
-static int sbpcd_ioaddr = CDROM_PORT; /* default I/O base address */
-static int sbpro_type = SBPRO;
-static unsigned char setup_done = 0;
-static int CDo_command, CDo_reset;
-static int CDo_sel_i_d, CDo_enable;
-static int CDi_info, CDi_status, CDi_data;
-static int MIXER_addr, MIXER_data;
-static struct cdrom_msf msf;
-static struct cdrom_ti ti;
-static struct cdrom_tochdr tochdr;
-static struct cdrom_tocentry tocentry;
-static struct cdrom_subchnl SC;
-static struct cdrom_volctrl volctrl;
-static struct cdrom_read_audio read_audio;
-static struct cdrom_multisession ms_info;
-
-static unsigned char msgnum=0;
-static char msgbuf[80];
-
-static char *str_sb = "SoundBlaster";
-static char *str_sb_l = "soundblaster";
-static char *str_lm = "LaserMate";
-static char *str_sp = "SPEA";
-static char *str_sp_l = "spea";
-char *type;
-
-#if !(SBPCD_ISSUE-1)
-static char *major_name="sbpcd";
-#endif
-#if !(SBPCD_ISSUE-2)
-static char *major_name="sbpcd2";
-#endif
-#if !(SBPCD_ISSUE-3)
-static char *major_name="sbpcd3";
-#endif
-#if !(SBPCD_ISSUE-4)
-static char *major_name="sbpcd4";
-#endif
-
-/*==========================================================================*/
-
-#if FUTURE
-static struct wait_queue *sbp_waitq = NULL;
-#endif FUTURE
-
-/*==========================================================================*/
-#define SBP_BUFFER_FRAMES 8 /* driver's own read_ahead, data mode */
-/*==========================================================================*/
-
-static u_char family0[]="MATSHITA"; /* MKE CR-52x */
-static u_char family1[]="CR-56"; /* MKE CR-56x */
-static u_char family2[]="CD200"; /* MKE CD200 */
-static u_char familyL[]="LCS-7260"; /* Longshine LCS-7260 */
-static u_char familyT[]="CD-55"; /* TEAC CD-55A */
-
-static u_int recursion=0; /* internal testing only */
-static u_int fatal_err=0; /* internal testing only */
-static u_int response_count=0;
-static u_int flags_cmd_out;
-static u_char cmd_type=0;
-static u_char drvcmd[10];
-static u_char infobuf[20];
-static u_char xa_head_buf[CD_XA_HEAD];
-static u_char xa_tail_buf[CD_XA_TAIL];
-
-static volatile u_char busy_data=0;
-static volatile u_char busy_audio=0; /* true semaphores would be safer */
-static u_long timeout;
-static volatile u_char timed_out_delay=0;
-static volatile u_char timed_out_data=0;
-#if 0
-static volatile u_char timed_out_audio=0;
-#endif
-static u_int datarate= 1000000;
-static u_int maxtim16=16000000;
-static u_int maxtim04= 4000000;
-static u_int maxtim02= 2000000;
-static u_int maxtim_8= 30000;
-#if LONG_TIMING
-static u_int maxtim_data= 9000;
-#else
-static u_int maxtim_data= 3000;
-#endif LONG_TIMING
-#if DISTRIBUTION
-static int n_retries=3;
-#else
-static int n_retries=1;
-#endif
-/*==========================================================================*/
-
-static int ndrives=0;
-static u_char drv_pattern[NR_SBPCD]={speed_auto,speed_auto,speed_auto,speed_auto};
-static int sbpcd_blocksizes[NR_SBPCD] = {0, };
-
-/*==========================================================================*/
-/*
- * drive space begins here (needed separate for each unit)
- */
-static int d=0; /* DriveStruct index: drive number */
-
-static struct {
- char drv_id; /* "jumpered" drive ID or -1 */
- char drv_sel; /* drive select lines bits */
-
- char drive_model[9];
- u_char firmware_version[4];
- char f_eject; /* auto-eject flag: 0 or 1 */
- u_char *sbp_buf; /* Pointer to internal data buffer,
- space allocated during sbpcd_init() */
- u_int sbp_bufsiz; /* size of sbp_buf (# of frames) */
- int sbp_first_frame; /* First frame in buffer */
- int sbp_last_frame; /* Last frame in buffer */
- int sbp_read_frames; /* Number of frames being read to buffer */
- int sbp_current; /* Frame being currently read */
-
- u_char mode; /* read_mode: READ_M1, READ_M2, READ_SC, READ_AU */
- u_char *aud_buf; /* Pointer to audio data buffer,
- space allocated during sbpcd_init() */
- u_int sbp_audsiz; /* size of aud_buf (# of raw frames) */
- u_char drv_type;
- u_char drv_options;
- int status_bits;
- u_char diskstate_flags;
- u_char sense_byte;
-
- u_char CD_changed;
- char open_count;
- u_char error_byte;
-
- u_char f_multisession;
- u_int lba_multi;
- int first_session;
- int last_session;
-
- u_char audio_state;
- u_int pos_audio_start;
- u_int pos_audio_end;
- char vol_chan0;
- u_char vol_ctrl0;
- char vol_chan1;
- u_char vol_ctrl1;
-#if 000 /* no supported drive has it */
- char vol_chan2;
- u_char vol_ctrl2;
- char vol_chan3;
- u_char vol_ctrl3;
-#endif 000
- u_char volume_control; /* TEAC on/off bits */
-
- u_char SubQ_ctl_adr;
- u_char SubQ_trk;
- u_char SubQ_pnt_idx;
- u_int SubQ_run_tot;
- u_int SubQ_run_trk;
- u_char SubQ_whatisthis;
-
- u_char UPC_ctl_adr;
- u_char UPC_buf[7];
-
- int CDsize_blk;
- int frame_size;
- int CDsize_frm;
-
- u_char xa_byte; /* 0x20: XA capabilities */
- u_char n_first_track; /* binary */
- u_char n_last_track; /* binary (not bcd), 0x01...0x63 */
- u_int size_msf; /* time of whole CD, position of LeadOut track */
- u_int size_blk;
-
- u_char TocEnt_nixbyte; /* em */
- u_char TocEnt_ctl_adr;
- u_char TocEnt_number;
- u_char TocEnt_format; /* em */
- u_int TocEnt_address;
- u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */
-
- struct {
- u_char nixbyte; /* em */
- u_char ctl_adr; /* 0x4x: data, 0x0x: audio */
- u_char number;
- u_char format; /* em */ /* 0x00: lba, 0x01: msf */
- u_int address;
- } TocBuffer[MAX_TRACKS+1]; /* last entry faked */
-
- int in_SpinUp; /* CR-52x test flag */
- int n_bytes; /* TEAC awaited response count */
- u_char error_state, b3, b4; /* TEAC command error state */
- u_char f_drv_error; /* TEAC command error flag */
- u_char speed_byte;
- int frmsiz;
- u_char f_XA; /* 1: XA */
- u_char type_byte; /* 0, 1, 3 */
- u_char mode_xb_6;
- u_char mode_yb_7;
- u_char mode_xb_8;
- u_char delay;
-
-} D_S[NR_SBPCD];
-
-/*
- * drive space ends here (needed separate for each unit)
- */
-/*==========================================================================*/
-#if 0
-unsigned long cli_sti; /* for saving the processor flags */
-#endif
-/*==========================================================================*/
-static struct timer_list delay_timer = { NULL, NULL, 0, 0, mark_timeout_delay};
-static struct timer_list data_timer = { NULL, NULL, 0, 0, mark_timeout_data};
-#if 0
-static struct timer_list audio_timer = { NULL, NULL, 0, 0, mark_timeout_audio};
-#endif
-/*==========================================================================*/
-/*
- * DDI interface
- */
-static void msg(int level, char *fmt, ...)
-{
- char buf[256];
- va_list args;
- extern int vsprintf(char *, const char *, va_list);
-
- if (!(sbpcd_debug&(1<<level))) return;
-
- msgnum++;
- if (msgnum>99) msgnum=0;
- sprintf(buf, "%s-%d [%02d]: ", major_name, d, msgnum);
- va_start(args, fmt);
- vsprintf(&buf[15], fmt, args);
- va_end(args);
- printk(buf);
- sbp_sleep(55); /* else messages get lost */
- return;
-}
-/*==========================================================================*/
-/*
- * DDI interface: runtime trace bit pattern maintenance
- */
-static int sbpcd_dbg_ioctl(unsigned long arg, int level)
-{
- switch(arg)
- {
- case 0: /* OFF */
- sbpcd_debug = DBG_INF;
- break;
-
- default:
- if (arg>=128) sbpcd_debug &= ~(1<<(arg-128));
- else sbpcd_debug |= (1<<arg);
- }
- return (arg);
-}
-/*==========================================================================*/
-static void mark_timeout_delay(u_long i)
-{
- timed_out_delay=1;
- msg(DBG_TIM,"delay timer expired.\n");
-}
-/*==========================================================================*/
-static void mark_timeout_data(u_long i)
-{
- timed_out_data=1;
- msg(DBG_TIM,"data timer expired.\n");
-}
-/*==========================================================================*/
-#if 0
-static void mark_timeout_audio(u_long i)
-{
- timed_out_audio=1;
- msg(DBG_TIM,"audio timer expired.\n");
-}
-#endif
-/*==========================================================================*/
-/*
- * Wait a little while (used for polling the drive).
- */
-static void sbp_sleep(u_int time)
-{
-#ifndef MODULE
- if (current == task[0])
- {
- del_timer(&delay_timer);
- delay_timer.expires=time;
- timed_out_delay=0;
- add_timer(&delay_timer);
- while (!timed_out_delay) ;
- return;
- }
-#endif MODULE
- sti();
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + time;
- schedule();
- sti();
-}
-/*==========================================================================*/
-/*
- * convert logical_block_address to m-s-f_number (3 bytes only)
- */
-static INLINE void lba2msf(int lba, u_char *msf)
-{
- lba += CD_BLOCK_OFFSET;
- msf[0] = lba / (CD_SECS*CD_FRAMES);
- lba %= CD_SECS*CD_FRAMES;
- msf[1] = lba / CD_FRAMES;
- msf[2] = lba % CD_FRAMES;
-}
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * convert msf-bin to msf-bcd
- */
-static INLINE void bin2bcdx(u_char *p) /* must work only up to 75 or 99 */
-{
- *p=((*p/10)<<4)|(*p%10);
-}
-/*==========================================================================*/
-static INLINE u_int blk2msf(u_int blk)
-{
- MSF msf;
- u_int mm;
-
- msf.c[3] = 0;
- msf.c[2] = (blk + CD_BLOCK_OFFSET) / (CD_SECS * CD_FRAMES);
- mm = (blk + CD_BLOCK_OFFSET) % (CD_SECS * CD_FRAMES);
- msf.c[1] = mm / CD_FRAMES;
- msf.c[0] = mm % CD_FRAMES;
- return (msf.n);
-}
-/*==========================================================================*/
-static INLINE u_int make16(u_char rh, u_char rl)
-{
- return ((rh<<8)|rl);
-}
-/*==========================================================================*/
-static INLINE u_int make32(u_int rh, u_int rl)
-{
- return ((rh<<16)|rl);
-}
-/*==========================================================================*/
-static INLINE u_char swap_nibbles(u_char i)
-{
- return ((i<<4)|(i>>4));
-}
-/*==========================================================================*/
-static INLINE u_char byt2bcd(u_char i)
-{
- return (((i/10)<<4)+i%10);
-}
-/*==========================================================================*/
-static INLINE u_char bcd2bin(u_char bcd)
-{
- return ((bcd>>4)*10+(bcd&0x0F));
-}
-/*==========================================================================*/
-static INLINE int msf2blk(int msfx)
-{
- MSF msf;
- int i;
-
- msf.n=msfx;
- i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_BLOCK_OFFSET;
- if (i<0) return (0);
- return (i);
-}
-/*==========================================================================*/
-/*
- * convert m-s-f_number (3 bytes only) to logical_block_address
- */
-static INLINE int msf2lba(u_char *msf)
-{
- int i;
-
- i=(msf[0] * CD_SECS + msf[1]) * CD_FRAMES + msf[2] - CD_BLOCK_OFFSET;
- if (i<0) return (0);
- return (i);
-}
-/*==========================================================================*/
-/* evaluate cc_ReadError code */
-static int sta2err(int sta)
-{
- if (famT_drive)
- {
- if (sta==0x00) return (0);
- if (sta==0x01) return (-604); /* CRC error */
- if (sta==0x02) return (-602); /* drive not ready */
- if (sta==0x03) return (-607); /* unknown media */
- if (sta==0x04) return (-612); /* general failure */
- if (sta==0x05) return (0);
- if (sta==0x06) return (-615); /* invalid disk change */
- if (sta==0x0b) return (-612); /* general failure */
- if (sta==0xff) return (-612); /* general failure */
- return (0);
- }
- else
- {
- if (sta<=2) return (sta);
- if (sta==0x05) return (-604); /* CRC error */
- if (sta==0x06) return (-606); /* seek error */
- if (sta==0x0d) return (-606); /* seek error */
- if (sta==0x0e) return (-603); /* unknown command */
- if (sta==0x14) return (-603); /* unknown command */
- if (sta==0x0c) return (-611); /* read fault */
- if (sta==0x0f) return (-611); /* read fault */
- if (sta==0x10) return (-611); /* read fault */
- if (sta>=0x16) return (-612); /* general failure */
- D_S[d].CD_changed=0xFF;
- if (sta==0x11) return (-615); /* invalid disk change (LCS: removed) */
- if (famL_drive)
- if (sta==0x12) return (-615); /* invalid disk change (inserted) */
- return (-602); /* drive not ready */
- }
-}
-/*==========================================================================*/
-static INLINE void clr_cmdbuf(void)
-{
- int i;
-
- for (i=0;i<10;i++) drvcmd[i]=0;
- cmd_type=0;
-}
-/*==========================================================================*/
-static void flush_status(void)
-{
- int i;
-
-#ifdef MODULE
- sbp_sleep(150);
- for (i=maxtim_data;i!=0;i--) inb(CDi_status);
-#else
- if (current == task[0])
- for (i=maxtim02;i!=0;i--) inb(CDi_status);
- else
- {
- sbp_sleep(150);
- for (i=maxtim_data;i!=0;i--) inb(CDi_status);
- }
-#endif MODULE
-}
-/*==========================================================================*/
-static int CDi_stat_loop(void)
-{
- int i,j;
-
-#ifdef MODULE
- for(timeout = jiffies + 1000, i=maxtim_data; timeout > jiffies; )
- {
- for ( ;i!=0;i--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) return (j);
- if (!(j&s_not_result_ready)) return (j);
- if (fam0L_drive) if (j&s_attention) return (j);
- }
- sbp_sleep(1);
- i = 1;
- }
-#else
- if (current == task[0])
- for(i=maxtim16;i!=0;i--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) return (j);
- if (!(j&s_not_result_ready)) return (j);
- if (fam0L_drive) if (j&s_attention) return (j);
- }
- else
- for(timeout = jiffies + 1000, i=maxtim_data; timeout > jiffies; )
- {
- for ( ;i!=0;i--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) return (j);
- if (!(j&s_not_result_ready)) return (j);
- if (fam0L_drive) if (j&s_attention) return (j);
- }
- sbp_sleep(1);
- i = 1;
- }
-#endif MODULE
- msg(DBG_LCS,"CDi_stat_loop failed\n");
- return (-1);
-}
-/*==========================================================================*/
-#if 00000
-/*==========================================================================*/
-static int tst_DataReady(void)
-{
- int i;
-
- i=inb(CDi_status);
- if (i&s_not_data_ready) return (0);
- return (1);
-}
-/*==========================================================================*/
-static int tst_ResultReady(void)
-{
- int i;
-
- i=inb(CDi_status);
- if (i&s_not_result_ready) return (0);
- return (1);
-}
-/*==========================================================================*/
-static int tst_Attention(void)
-{
- int i;
-
- i=inb(CDi_status);
- if (i&s_attention) return (1);
- return (0);
-}
-/*==========================================================================*/
-#endif 00000
-/*==========================================================================*/
-static int ResponseInfo(void)
-{
- int i,j,st=0;
- u_long timeout;
-
-#ifdef MODULE
- if (0)
-#else
- if (current == task[0])
-#endif MODULE
- for (i=0;i<response_count;i++)
- {
- for (j=maxtim_8;j!=0;j--)
- {
- st=inb(CDi_status);
- if (!(st&s_not_result_ready)) break;
- }
- if (j==0)
- {
- msg(DBG_SEQ,"ResponseInfo: not_result_ready (got %d of %d bytes).\n", i, response_count);
- break;
- }
- infobuf[i]=inb(CDi_info);
- }
- else
- {
- for (i=0,timeout=jiffies+100;i<response_count;i++)
- {
- for (j=maxtim_data; ; )
- {
- for ( ;j!=0;j-- )
- {
- st=inb(CDi_status);
- if (!(st&s_not_result_ready)) break;
- }
- if ((j!=0)||(timeout<=jiffies)) break;
- sbp_sleep(1);
- j = 1;
- }
- if (timeout<=jiffies) break;
- infobuf[i]=inb(CDi_info);
- }
- }
-#if 000
- while (!(inb(CDi_status)&s_not_result_ready))
- {
- infobuf[i++]=inb(CDi_info);
- }
- j=i-response_count;
- if (j>0) msg(DBG_INF,"ResponseInfo: got %d trailing bytes.\n",j);
-#endif 000
- for (j=0;j<i;j++)
- sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
- msgbuf[j*3]=0;
- msg(DBG_CMD,"ResponseInfo:%s (%d,%d)\n",msgbuf,response_count,i);
- j=response_count-i;
- if (j>0) return (-j);
- else return (i);
-}
-/*==========================================================================*/
-static void EvaluateStatus(int st)
-{
- D_S[d].status_bits=0;
- if (fam1_drive) D_S[d].status_bits=st|p_success;
- else if (fam0_drive)
- {
- if (st&p_caddin_old) D_S[d].status_bits |= p_door_closed|p_caddy_in;
- if (st&p_spinning) D_S[d].status_bits |= p_spinning;
- if (st&p_check) D_S[d].status_bits |= p_check;
- if (st&p_success_old) D_S[d].status_bits |= p_success;
- if (st&p_busy_old) D_S[d].status_bits |= p_busy_new;
- if (st&p_disk_ok) D_S[d].status_bits |= p_disk_ok;
- }
- else if (famL_drive)
- {
- D_S[d].status_bits |= p_success;
- if (st&p_caddin_old) D_S[d].status_bits |= p_disk_ok|p_caddy_in;
- if (st&p_spinning) D_S[d].status_bits |= p_spinning;
- if (st&p_check) D_S[d].status_bits |= p_check;
- if (st&p_busy_old) D_S[d].status_bits |= p_busy_new;
- if (st&p_lcs_door_closed) D_S[d].status_bits |= p_door_closed;
- if (st&p_lcs_door_locked) D_S[d].status_bits |= p_door_locked;
- }
- else if (fam2_drive)
- {
- D_S[d].status_bits |= p_success;
- if (st&p2_check) D_S[d].status_bits |= p1_check;
- if (st&p2_door_closed) D_S[d].status_bits |= p1_door_closed;
- if (st&p2_disk_in) D_S[d].status_bits |= p1_disk_in;
- if (st&p2_busy1) D_S[d].status_bits |= p1_busy;
- if (st&p2_busy2) D_S[d].status_bits |= p1_busy;
- if (st&p2_spinning) D_S[d].status_bits |= p1_spinning;
- if (st&p2_door_locked) D_S[d].status_bits |= p1_door_locked;
- if (st&p2_disk_ok) D_S[d].status_bits |= p1_disk_ok;
- }
- else if (famT_drive)
- {
- return; /* still needs to get coded */
- D_S[d].status_bits |= p_success;
- if (st&p2_check) D_S[d].status_bits |= p1_check;
- if (st&p2_door_closed) D_S[d].status_bits |= p1_door_closed;
- if (st&p2_disk_in) D_S[d].status_bits |= p1_disk_in;
- if (st&p2_busy1) D_S[d].status_bits |= p1_busy;
- if (st&p2_busy2) D_S[d].status_bits |= p1_busy;
- if (st&p2_spinning) D_S[d].status_bits |= p1_spinning;
- if (st&p2_door_locked) D_S[d].status_bits |= p1_door_locked;
- if (st&p2_disk_ok) D_S[d].status_bits |= p1_disk_ok;
- }
- return;
-}
-/*==========================================================================*/
-static int get_state_T(void)
-{
- int i;
-
- static int cmd_out_T(void);
-
- msg(DBG_TE2,"doing get_state_T...\n");
- clr_cmdbuf();
- D_S[d].n_bytes=1;
- drvcmd[0]=CMDT_STATUS;
- i=cmd_out_T();
- if (i>=0) i=infobuf[0];
- else
- {
- msg(DBG_TEA,"get_state_T error %d\n", i);
- return (i);
- }
- if (i>=0)
- /* 2: closed, disk in */
- D_S[d].status_bits=p1_door_closed|p1_disk_in|p1_spinning|p1_disk_ok;
- else if (D_S[d].error_state==6)
- /* 3: closed, disk in, changed ("06 xx xx") */
- D_S[d].status_bits=p1_door_closed|p1_disk_in;
- else if ((D_S[d].error_state!=2)||(D_S[d].b3!=0x3A)||(D_S[d].b4==0x00))
- {
- /* 1: closed, no disk ("xx yy zz"or "02 3A 00") */
- D_S[d].status_bits=p1_door_closed;
- D_S[d].open_count=0;
- }
- else if (D_S[d].b4==0x01)
- {
- /* 0: open ("02 3A 01") */
- D_S[d].status_bits=0;
- D_S[d].open_count=0;
- }
- else
- {
- /* 1: closed, no disk ("02 3A xx") */
- D_S[d].status_bits=p1_door_closed;
- D_S[d].open_count=0;
- }
- msg(DBG_TE2,"get_state_T done (%02X)...\n", D_S[d].status_bits);
- return (D_S[d].status_bits);
-}
-/*==========================================================================*/
-static int ResponseStatus(void)
-{
- int i,j;
- u_long timeout;
-
- msg(DBG_STA,"doing ResponseStatus...\n");
- if (famT_drive) return (get_state_T());
-#ifdef MODULE
- if (0)
-#else
- if (current == task[0])
-#endif MODULE
- {
- if (flags_cmd_out & f_respo3) j = maxtim_8;
- else if (flags_cmd_out&f_respo2) j=maxtim16;
- else j=maxtim04;
- for (;j!=0;j--)
- {
- i=inb(CDi_status);
- if (!(i&s_not_result_ready)) break;
- }
- }
- else
- {
- if (flags_cmd_out & f_respo3) timeout = jiffies;
- else if (flags_cmd_out & f_respo2) timeout = jiffies + 1600;
- else timeout = jiffies + 400;
- j=maxtim_8;
- do
- {
- for ( ;j!=0;j--)
- {
- i=inb(CDi_status);
- if (!(i&s_not_result_ready)) break;
- }
- if ((j!=0)||(timeout<jiffies)) break;
- sbp_sleep(1);
- j = 1;
- }
- while (1);
- }
- if (j==0)
- {
- if ((flags_cmd_out & f_respo3) == 0)
- msg(DBG_STA,"ResponseStatus: timeout.\n");
- D_S[d].status_bits=0;
- return (-401);
- }
- i=inb(CDi_info);
- msg(DBG_STA,"ResponseStatus: response %02X.\n", i);
- EvaluateStatus(i);
-#if 0
- if (fam0_drive)
-#endif
- msg(DBG_STA,"status_bits=%02X, i=%02X\n",D_S[d].status_bits,i);
-#if 1
- return (D_S[d].status_bits);
-#else
- return (i);
-#endif 0
-}
-/*==========================================================================*/
-static void cc_ReadStatus(void)
-{
- int i;
-
- msg(DBG_STA,"giving cc_ReadStatus command\n");
- if (famT_drive) return;
- SBPCD_CLI;
- if (fam0L_drive) OUT(CDo_command,CMD0_STATUS);
- else if (fam1_drive) OUT(CDo_command,CMD1_STATUS);
- else if (fam2_drive) OUT(CDo_command,CMD2_STATUS);
- if (!fam0L_drive) for (i=0;i<6;i++) OUT(CDo_command,0);
- SBPCD_STI;
-}
-/*==========================================================================*/
-static int cc_ReadError(void)
-{
- int i;
-
- clr_cmdbuf();
- msg(DBG_ERR,"giving cc_ReadError command.\n");
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_READ_ERR;
- response_count=8;
- flags_cmd_out=f_putcmd|f_ResponseStatus;
- }
- else if (fam0L_drive)
- {
- drvcmd[0]=CMD0_READ_ERR;
- response_count=6;
- if (famL_drive)
- flags_cmd_out=f_putcmd;
- else
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_READ_ERR;
- response_count=6;
- flags_cmd_out=f_putcmd;
- }
- else if (famT_drive)
- {
- response_count=5;
- drvcmd[0]=CMDT_READ_ERR;
- }
- i=cmd_out();
- D_S[d].error_byte=0;
- msg(DBG_ERR,"cc_ReadError: cmd_out(CMDx_READ_ERR) returns %d (%02X)\n",i,i);
- if (i<0) return (i);
- if (fam0_drive) i=1;
- else i=2;
- D_S[d].error_byte=infobuf[i];
- msg(DBG_ERR,"cc_ReadError: infobuf[%d] is %d (%02X)\n",i,D_S[d].error_byte,D_S[d].error_byte);
- i=sta2err(infobuf[i]);
- return (i);
-}
-/*==========================================================================*/
-static int cmd_out_T(void)
-{
-#undef CMDT_TRIES
-#define CMDT_TRIES 1000
-
- static int cc_DriveReset(void);
- int i, j, l, ntries;
-
- D_S[d].error_state=0;
- D_S[d].b3=0;
- D_S[d].b4=0;
- D_S[d].f_drv_error=0;
- for (i=0;i<10;i++) sprintf(&msgbuf[i*3]," %02X",drvcmd[i]);
- msgbuf[i*3]=0;
- msg(DBG_CMD,"cmd_out_T:%s\n",msgbuf);
-
- OUT(CDo_sel_i_d,0);
- OUT(CDo_enable,D_S[d].drv_sel);
- i=inb(CDi_status);
- if (!(i&s_not_result_ready))
- do
- {
- j=inb(CDi_info);
- i=inb(CDi_status);
- sbp_sleep(0);
- msg(DBG_TEA,"cmd_out_T: spurious !s_not_result_ready. (%02X)\n", j);
- }
- while (!(i&s_not_result_ready));
- cli();
- for (i=0;i<10;i++) OUT(CDo_command,drvcmd[i]);
- sti();
- for (ntries=CMDT_TRIES;ntries>0;ntries--)
- {
- if (drvcmd[0]==CMDT_READ_VER) sbp_sleep(100);
-#if 1
- OUT(CDo_sel_i_d,0);
-#endif
- i=inb(CDi_status);
- if (!(i&s_not_data_ready)) /* f.e. CMDT_DISKINFO */
- {
- OUT(CDo_sel_i_d,1);
- if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
- if (drvcmd[0]==CMDT_DISKINFO)
- {
- l=0;
- do
- {
- infobuf[l++]=inb(CDi_data);
- i=inb(CDi_status);
- }
- while (!(i&s_not_data_ready));
- for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
- msgbuf[j*3]=0;
- msg(DBG_CMD,"cmd_out_T data response:%s\n", msgbuf);
- }
- else
- {
- msg(DBG_TEA,"cmd_out_T: data response with cmd_%02X !!!!!!!!!!!!!!!!!!!!\n", drvcmd[0]);
- j=0;
- do
- {
- i=inb(CDi_data);
- j++;
- i=inb(CDi_status);
- }
- while (!(i&s_not_data_ready));
- msg(DBG_TEA,"cmd_out_T: data response: discarded %d bytes.\n", j);
- fatal_err++;
- }
- }
- i=inb(CDi_status);
- if (!(i&s_not_result_ready))
- {
- OUT(CDo_sel_i_d,0);
- l=0;
- do
- {
- infobuf[l++]=inb(CDi_info);
- i=inb(CDi_status);
- }
- while (!(i&s_not_result_ready));
- for (j=0;j<l;j++) sprintf(&msgbuf[j*3]," %02X",infobuf[j]);
- msgbuf[j*3]=0;
- msg(DBG_CMD,"cmd_out_T info response:%s\n", msgbuf);
- if (infobuf[0]!=0x02) return (l); /* info length */
- do
- {
- ++recursion;
- if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (%02X): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", drvcmd[0], recursion);
- clr_cmdbuf();
- drvcmd[0]=CMDT_READ_ERR;
- j=cmd_out_T(); /* !!! recursive here !!! */
- --recursion;
- sbp_sleep(1);
- }
- while (j<0);
- D_S[d].error_state=infobuf[2];
- D_S[d].b3=infobuf[3];
- D_S[d].b4=infobuf[4];
- if (D_S[d].f_drv_error)
- {
- D_S[d].f_drv_error=0;
- cc_DriveReset();
- D_S[d].error_state=2;
- }
- return (-D_S[d].error_state-400);
- }
- if (drvcmd[0]==CMDT_READ) return (0); /* handled elsewhere */
- sbp_sleep(10);
- if (ntries>(CMDT_TRIES-50)) continue;
- msg(DBG_TEA,"cmd_out_T: next CMDT_TRIES (%02X): %d.\n", drvcmd[0], ntries-1);
- }
- D_S[d].f_drv_error=1;
- cc_DriveReset();
- D_S[d].error_state=2;
- return (-99);
-}
-/*==========================================================================*/
-static int cmd_out(void)
-{
- int i=0;
-
- if (famT_drive) return(cmd_out_T());
-
- if (flags_cmd_out&f_putcmd)
- {
- for (i=0;i<7;i++)
- sprintf(&msgbuf[i*3], " %02X", drvcmd[i]);
- msgbuf[i*3]=0;
- msg(DBG_CMD,"cmd_out:%s\n", msgbuf);
- cli();
- for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
- sti();
- }
- if (response_count!=0)
- {
- if (cmd_type!=0)
- {
- if (sbpro_type==1) OUT(CDo_sel_i_d,1);
- msg(DBG_INF,"misleaded to try ResponseData.\n");
- if (sbpro_type==1) OUT(CDo_sel_i_d,0);
- return (-22);
- }
- else i=ResponseInfo();
- if (i<0) return (i);
- }
- if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to CDi_stat_loop.\n");
- if (flags_cmd_out&f_lopsta)
- {
- i=CDi_stat_loop();
- if ((i<0)||!(i&s_attention)) return (-8);
- }
- if (!(flags_cmd_out&f_getsta)) goto LOC_229;
-
- LOC_228:
- if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadStatus.\n");
- cc_ReadStatus();
-
- LOC_229:
- if (flags_cmd_out&f_ResponseStatus)
- {
- if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to ResponseStatus.\n");
- i=ResponseStatus();
- /* builds status_bits, returns orig. status or p_busy_new */
- if (i<0) return (i);
- if (flags_cmd_out&(f_bit1|f_wait_if_busy))
- {
- if (!st_check)
- {
- if ((flags_cmd_out&f_bit1)&&(i&p_success)) goto LOC_232;
- if ((!(flags_cmd_out&f_wait_if_busy))||(!st_busy)) goto LOC_228;
- }
- }
- }
- LOC_232:
- if (!(flags_cmd_out&f_obey_p_check)) return (0);
- if (!st_check) return (0);
- if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cc_ReadError.\n");
- i=cc_ReadError();
- if (D_S[d].in_SpinUp) msg(DBG_SPI,"in_SpinUp: to cmd_out OK.\n");
- msg(DBG_000,"cmd_out: cc_ReadError=%d\n", i);
- return (i);
-}
-/*==========================================================================*/
-static int cc_Seek(u_int pos, char f_blk_msf)
-{
- int i;
-
- clr_cmdbuf();
- if (f_blk_msf>1) return (-3);
- if (fam0_drive)
- {
- drvcmd[0]=CMD0_SEEK;
- if (f_blk_msf==1) pos=msf2blk(pos);
- drvcmd[2]=(pos>>16)&0x00FF;
- drvcmd[3]=(pos>>8)&0x00FF;
- drvcmd[4]=pos&0x00FF;
- flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
- f_ResponseStatus | f_obey_p_check | f_bit1;
- }
- else if (fam1L_drive)
- {
- drvcmd[0]=CMD1_SEEK; /* same as CMD1_ and CMDL_ */
- if (f_blk_msf==0) pos=blk2msf(pos);
- drvcmd[1]=(pos>>16)&0x00FF;
- drvcmd[2]=(pos>>8)&0x00FF;
- drvcmd[3]=pos&0x00FF;
- if (famL_drive)
- flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
- else
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_SEEK;
- if (f_blk_msf==0) pos=blk2msf(pos);
- drvcmd[2]=(pos>>24)&0x00FF;
- drvcmd[3]=(pos>>16)&0x00FF;
- drvcmd[4]=(pos>>8)&0x00FF;
- drvcmd[5]=pos&0x00FF;
- flags_cmd_out=f_putcmd|f_ResponseStatus;
- }
- else if (famT_drive)
- {
- drvcmd[0]=CMDT_SEEK;
- if (f_blk_msf==1) pos=msf2blk(pos);
- drvcmd[2]=(pos>>24)&0x00FF;
- drvcmd[3]=(pos>>16)&0x00FF;
- drvcmd[4]=(pos>>8)&0x00FF;
- drvcmd[5]=pos&0x00FF;
- D_S[d].n_bytes=1;
- }
- response_count=0;
- i=cmd_out();
- return (i);
-}
-/*==========================================================================*/
-static int cc_SpinUp(void)
-{
- int i;
-
- msg(DBG_SPI,"SpinUp.\n");
- D_S[d].in_SpinUp = 1;
- clr_cmdbuf();
- if (fam0L_drive)
- {
- drvcmd[0]=CMD0_SPINUP;
- flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
- f_ResponseStatus|f_obey_p_check|f_bit1;
- }
- else if (fam1_drive)
- {
- drvcmd[0]=CMD1_SPINUP;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_TRAY_CTL;
- drvcmd[4]=0x01; /* "spinup" */
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- }
- else if (famT_drive)
- {
- drvcmd[0]=CMDT_TRAY_CTL;
- drvcmd[4]=0x03; /* "insert", it hopefully spins the drive up */
- }
- response_count=0;
- i=cmd_out();
- D_S[d].in_SpinUp = 0;
- return (i);
-}
-/*==========================================================================*/
-static int cc_SpinDown(void)
-{
- int i;
-
- if (fam0_drive) return (0);
- clr_cmdbuf();
- response_count=0;
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_SPINDOWN;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_TRAY_CTL;
- drvcmd[4]=0x02; /* "eject" */
- flags_cmd_out=f_putcmd|f_ResponseStatus;
- }
- else if (famL_drive)
- {
- drvcmd[0]=CMDL_SPINDOWN;
- drvcmd[1]=1;
- flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
- }
- else if (famT_drive)
- {
- drvcmd[0]=CMDT_TRAY_CTL;
- drvcmd[4]=0x02; /* "eject" */
- }
- i=cmd_out();
- return (i);
-}
-/*==========================================================================*/
-static int cc_get_mode_T(void)
-{
- int i;
-
- clr_cmdbuf();
- response_count=10;
- drvcmd[0]=CMDT_GETMODE;
- drvcmd[4]=response_count;
- i=cmd_out_T();
- return (i);
-}
-/*==========================================================================*/
-static int cc_set_mode_T(void)
-{
- int i;
-
- clr_cmdbuf();
- response_count=1;
- drvcmd[0]=CMDT_SETMODE;
- drvcmd[1]=D_S[d].speed_byte;
- drvcmd[2]=D_S[d].frmsiz>>8;
- drvcmd[3]=D_S[d].frmsiz&0x0FF;
- drvcmd[4]=D_S[d].f_XA; /* 1: XA */
- drvcmd[5]=D_S[d].type_byte; /* 0, 1, 3 */
- drvcmd[6]=D_S[d].mode_xb_6;
- drvcmd[7]=D_S[d].mode_yb_7|D_S[d].volume_control;
- drvcmd[8]=D_S[d].mode_xb_8;
- drvcmd[9]=D_S[d].delay;
- i=cmd_out_T();
- return (i);
-}
-/*==========================================================================*/
-static int cc_prep_mode_T(void)
-{
- int i, j;
-
- i=cc_get_mode_T();
- if (i<0) return (i);
- for (i=0;i<10;i++)
- sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf);
- D_S[d].speed_byte=0x02; /* 0x02: auto quad, 0x82: quad, 0x81: double, 0x80: single */
- D_S[d].frmsiz=make16(infobuf[2],infobuf[3]);
- D_S[d].f_XA=infobuf[4];
- if (D_S[d].f_XA==0) D_S[d].type_byte=0;
- else D_S[d].type_byte=1;
- D_S[d].mode_xb_6=infobuf[6];
- D_S[d].mode_yb_7=1;
- D_S[d].mode_xb_8=infobuf[8];
- D_S[d].delay=0; /* 0, 1, 2, 3 */
- j=cc_set_mode_T();
- i=cc_get_mode_T();
- for (i=0;i<10;i++)
- sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_TEA,"CMDT_GETMODE:%s\n", msgbuf);
- return (j);
-}
-/*==========================================================================*/
-static int cc_SetSpeed(u_char speed, u_char x1, u_char x2)
-{
- int i;
-
- if (fam0L_drive) return (-3);
- clr_cmdbuf();
- response_count=0;
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_SETMODE;
- drvcmd[1]=0x03;
- drvcmd[2]=speed;
- drvcmd[3]=x1;
- drvcmd[4]=x2;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_SETSPEED;
- if (speed&speed_auto)
- {
- drvcmd[2]=0xFF;
- drvcmd[3]=0xFF;
- }
- else
- {
- drvcmd[2]=0;
- drvcmd[3]=150;
- }
- flags_cmd_out=f_putcmd|f_ResponseStatus;
- }
- else if (famT_drive)
- {
- return (0);
- }
- i=cmd_out();
- return (i);
-}
-/*==========================================================================*/
-static int cc_SetVolume(void)
-{
- int i;
- u_char channel0,channel1,volume0,volume1;
- u_char control0,value0,control1,value1;
-
- D_S[d].diskstate_flags &= ~volume_bit;
- clr_cmdbuf();
- channel0=D_S[d].vol_chan0;
- volume0=D_S[d].vol_ctrl0;
- channel1=control1=D_S[d].vol_chan1;
- volume1=value1=D_S[d].vol_ctrl1;
- control0=value0=0;
-
- if (((D_S[d].drv_options&audio_mono)!=0)&&(D_S[d].drv_type>=drv_211))
- {
- if ((volume0!=0)&&(volume1==0))
- {
- volume1=volume0;
- channel1=channel0;
- }
- else if ((volume0==0)&&(volume1!=0))
- {
- volume0=volume1;
- channel0=channel1;
- }
- }
- if (channel0>1)
- {
- channel0=0;
- volume0=0;
- }
- if (channel1>1)
- {
- channel1=1;
- volume1=0;
- }
-
- if (fam1_drive)
- {
- control0=channel0+1;
- control1=channel1+1;
- value0=(volume0>volume1)?volume0:volume1;
- value1=value0;
- if (volume0==0) control0=0;
- if (volume1==0) control1=0;
- drvcmd[0]=CMD1_SETMODE;
- drvcmd[1]=0x05;
- drvcmd[3]=control0;
- drvcmd[4]=value0;
- drvcmd[5]=control1;
- drvcmd[6]=value1;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- control0=channel0+1;
- control1=channel1+1;
- value0=(volume0>volume1)?volume0:volume1;
- value1=value0;
- if (volume0==0) control0=0;
- if (volume1==0) control1=0;
- drvcmd[0]=CMD2_SETMODE;
- drvcmd[1]=0x0E;
- drvcmd[3]=control0;
- drvcmd[4]=value0;
- drvcmd[5]=control1;
- drvcmd[6]=value1;
- flags_cmd_out=f_putcmd|f_ResponseStatus;
- }
- else if (famL_drive)
- {
- if ((volume0==0)||(channel0!=0)) control0 |= 0x80;
- if ((volume1==0)||(channel1!=1)) control0 |= 0x40;
- if (volume0|volume1) value0=0x80;
- drvcmd[0]=CMDL_SETMODE;
- drvcmd[1]=0x03;
- drvcmd[4]=control0;
- drvcmd[5]=value0;
- flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
- }
- else if (fam0_drive) /* different firmware levels */
- {
- if (D_S[d].drv_type>=drv_300)
- {
- control0=volume0&0xFC;
- value0=volume1&0xFC;
- if ((volume0!=0)&&(volume0<4)) control0 |= 0x04;
- if ((volume1!=0)&&(volume1<4)) value0 |= 0x04;
- if (channel0!=0) control0 |= 0x01;
- if (channel1==1) value0 |= 0x01;
- }
- else
- {
- value0=(volume0>volume1)?volume0:volume1;
- if (D_S[d].drv_type<drv_211)
- {
- if (channel0!=0)
- {
- i=channel1;
- channel1=channel0;
- channel0=i;
- i=volume1;
- volume1=volume0;
- volume0=i;
- }
- if (channel0==channel1)
- {
- if (channel0==0)
- {
- channel1=1;
- volume1=0;
- volume0=value0;
- }
- else
- {
- channel0=0;
- volume0=0;
- volume1=value0;
- }
- }
- }
-
- if ((volume0!=0)&&(volume1!=0))
- {
- if (volume0==0xFF) volume1=0xFF;
- else if (volume1==0xFF) volume0=0xFF;
- }
- else if (D_S[d].drv_type<drv_201) volume0=volume1=value0;
-
- if (D_S[d].drv_type>=drv_201)
- {
- if (volume0==0) control0 |= 0x80;
- if (volume1==0) control0 |= 0x40;
- }
- if (D_S[d].drv_type>=drv_211)
- {
- if (channel0!=0) control0 |= 0x20;
- if (channel1!=1) control0 |= 0x10;
- }
- }
- drvcmd[0]=CMD0_SETMODE;
- drvcmd[1]=0x83;
- drvcmd[4]=control0;
- drvcmd[5]=value0;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- else if (famT_drive)
- {
- D_S[d].volume_control=0;
- if (!volume0) D_S[d].volume_control|=0x10;
- if (!volume1) D_S[d].volume_control|=0x20;
- i=cc_prep_mode_T();
- if (i<0) return (i);
- }
- if (!famT_drive)
- {
- response_count=0;
- i=cmd_out();
- if (i<0) return (i);
- }
- D_S[d].diskstate_flags |= volume_bit;
- return (0);
-}
-/*==========================================================================*/
-static int GetStatus(void)
-{
- int i;
-
- if (famT_drive) return (0);
- flags_cmd_out=f_getsta|f_ResponseStatus|f_obey_p_check;
- response_count=0;
- cmd_type=0;
- i=cmd_out();
- return (i);
-}
-/*==========================================================================*/
-static int cc_DriveReset(void)
-{
- int i;
-
- msg(DBG_RES,"cc_DriveReset called.\n");
- clr_cmdbuf();
- response_count=0;
- if (fam0L_drive) OUT(CDo_reset,0x00);
- else if (fam1_drive)
- {
- drvcmd[0]=CMD1_RESET;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_RESET;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- OUT(CDo_reset,0x00);
- }
- else if (famT_drive)
- {
- OUT(CDo_sel_i_d,0);
- OUT(CDo_enable,D_S[d].drv_sel);
- OUT(CDo_command,CMDT_RESET);
- for (i=1;i<10;i++) OUT(CDo_command,0);
- }
- if (fam0L_drive) sbp_sleep(500); /* wait 5 seconds */
- else sbp_sleep(100); /* wait a second */
-#if 1
- if (famT_drive)
- {
- msg(DBG_TEA, "================CMDT_RESET given=================.\n");
- sbp_sleep(300);
- }
-#endif 1
- flush_status();
- i=GetStatus();
- if (i<0) return i;
- if (!famT_drive)
- if (D_S[d].error_byte!=aud_12) return -501;
- return (0);
-}
-/*==========================================================================*/
-static int SetSpeed(void)
-{
- int i, speed;
-
- if (!(D_S[d].drv_options&(speed_auto|speed_300|speed_150))) return (0);
- speed=speed_auto;
- if (!(D_S[d].drv_options&speed_auto))
- {
- speed |= speed_300;
- if (!(D_S[d].drv_options&speed_300)) speed=0;
- }
- i=cc_SetSpeed(speed,0,0);
- return (i);
-}
-/*==========================================================================*/
-static int DriveReset(void)
-{
- int i;
-
- i=cc_DriveReset();
- if (i<0) return (-22);
- do
- {
- i=GetStatus();
- if ((i<0)&&(i!=-615)) return (-2); /* i!=-615 is from sta2err */
- if (!st_caddy_in) break;
- sbp_sleep(1);
- }
- while (!st_diskok);
-#if 000
- D_S[d].CD_changed=1;
-#endif
- if ((st_door_closed) && (st_caddy_in))
- {
- i=DiskInfo();
- if (i<0) return (-23);
- }
- return (0);
-}
-/*==========================================================================*/
-static int cc_PlayAudio(int pos_audio_start,int pos_audio_end)
-{
- int i, j, n;
-
- if (D_S[d].audio_state==audio_playing) return (-EINVAL);
- clr_cmdbuf();
- response_count=0;
- if (famL_drive)
- {
- drvcmd[0]=CMDL_PLAY;
- i=msf2blk(pos_audio_start);
- n=msf2blk(pos_audio_end)+1-i;
- drvcmd[1]=(i>>16)&0x00FF;
- drvcmd[2]=(i>>8)&0x00FF;
- drvcmd[3]=i&0x00FF;
- drvcmd[4]=(n>>16)&0x00FF;
- drvcmd[5]=(n>>8)&0x00FF;
- drvcmd[6]=n&0x00FF;
- flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
- f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
- }
- else
- {
- j=1;
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_PLAY_MSF;
- flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus |
- f_obey_p_check | f_wait_if_busy;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_PLAY_MSF;
- flags_cmd_out = f_putcmd | f_ResponseStatus;
- }
- else if (famT_drive)
- {
- drvcmd[0]=CMDT_PLAY_MSF;
- j=3;
- response_count=1;
- }
- else if (fam0_drive)
- {
- drvcmd[0]=CMD0_PLAY_MSF;
- flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
- f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
- }
- drvcmd[j]=(pos_audio_start>>16)&0x00FF;
- drvcmd[j+1]=(pos_audio_start>>8)&0x00FF;
- drvcmd[j+2]=pos_audio_start&0x00FF;
- drvcmd[j+3]=(pos_audio_end>>16)&0x00FF;
- drvcmd[j+4]=(pos_audio_end>>8)&0x00FF;
- drvcmd[j+5]=pos_audio_end&0x00FF;
- }
- i=cmd_out();
- return (i);
-}
-/*==========================================================================*/
-static int cc_Pause_Resume(int pau_res)
-{
- int i;
-
- clr_cmdbuf();
- response_count=0;
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_PAU_RES;
- if (pau_res!=1) drvcmd[1]=0x80;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_PAU_RES;
- if (pau_res!=1) drvcmd[2]=0x01;
- flags_cmd_out=f_putcmd|f_ResponseStatus;
- }
- else if (fam0L_drive)
- {
- drvcmd[0]=CMD0_PAU_RES;
- if (pau_res!=1) drvcmd[1]=0x80;
- if (famL_drive)
- flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
- f_obey_p_check|f_bit1;
- else
- flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
- f_obey_p_check;
- }
- else if (famT_drive)
- {
- if (pau_res==3) return (cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end));
- else if (pau_res==1) drvcmd[0]=CMDT_PAUSE;
- else return (-56);
- }
- i=cmd_out();
- return (i);
-}
-/*==========================================================================*/
-static int cc_LockDoor(char lock)
-{
- int i;
-
- if (fam0_drive) return (0);
- msg(DBG_LCK,"cc_LockDoor: %d (drive %d)\n", lock, d);
- msg(DBG_LCS,"p_door_locked bit %d before\n", st_door_locked);
- clr_cmdbuf();
- response_count=0;
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_LOCK_CTL;
- if (lock==1) drvcmd[1]=0x01;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_LOCK_CTL;
- if (lock==1) drvcmd[4]=0x01;
- flags_cmd_out=f_putcmd|f_ResponseStatus;
- }
- else if (famL_drive)
- {
- drvcmd[0]=CMDL_LOCK_CTL;
- if (lock==1) drvcmd[1]=0x01;
- flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
- }
- else if (famT_drive)
- {
- drvcmd[0]=CMDT_LOCK_CTL;
- if (lock==1) drvcmd[4]=0x01;
- }
- i=cmd_out();
- msg(DBG_LCS,"p_door_locked bit %d after\n", st_door_locked);
- return (i);
-}
-/*==========================================================================*/
-/*==========================================================================*/
-static int UnLockDoor(void)
-{
- int i,j;
-
- j=20;
- do
- {
- i=cc_LockDoor(0);
- --j;
- sbp_sleep(1);
- }
- while ((i<0)&&(j));
- if (i<0)
- {
- cc_DriveReset();
- return -84;
- }
- return (0);
-}
-/*==========================================================================*/
-static int LockDoor(void)
-{
- int i,j;
-
- j=20;
- do
- {
- i=cc_LockDoor(1);
- --j;
- sbp_sleep(1);
- }
- while ((i<0)&&(j));
- if (j==0)
- {
- cc_DriveReset();
- j=20;
- do
- {
- i=cc_LockDoor(1);
- --j;
- sbp_sleep(1);
- }
- while ((i<0)&&(j));
- }
- return (i);
-}
-/*==========================================================================*/
-static int cc_CloseTray(void)
-{
- int i;
-
- if (fam0_drive) return (0);
- msg(DBG_LCK,"cc_CloseTray (drive %d)\n", d);
- msg(DBG_LCS,"p_door_closed bit %d before\n", st_door_closed);
-
- clr_cmdbuf();
- response_count=0;
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_TRAY_CTL;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_TRAY_CTL;
- drvcmd[1]=0x01;
- drvcmd[4]=0x03; /* "insert" */
- flags_cmd_out=f_putcmd|f_ResponseStatus;
- }
- else if (famL_drive)
- {
- drvcmd[0]=CMDL_TRAY_CTL;
- flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
- f_ResponseStatus|f_obey_p_check|f_bit1;
- }
- else if (famT_drive)
- {
- drvcmd[0]=CMDT_TRAY_CTL;
- drvcmd[4]=0x03; /* "insert" */
- }
- i=cmd_out();
- msg(DBG_LCS,"p_door_closed bit %d after\n", st_door_closed);
- return (i);
-}
-/*==========================================================================*/
-static int cc_ReadSubQ(void)
-{
- int i,j;
-
- D_S[d].diskstate_flags &= ~subq_bit;
- for (j=255;j>0;j--)
- {
- clr_cmdbuf();
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_READSUBQ;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- response_count=11;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_READSUBQ;
- drvcmd[1]=0x02;
- drvcmd[3]=0x01;
- flags_cmd_out=f_putcmd;
- response_count=10;
- }
- else if (fam0L_drive)
- {
- drvcmd[0]=CMD0_READSUBQ;
- drvcmd[1]=0x02;
- if (famL_drive)
- flags_cmd_out=f_putcmd;
- else
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- response_count=13;
- }
- else if (famT_drive)
- {
- response_count=12;
- drvcmd[0]=CMDT_READSUBQ;
- drvcmd[1]=0x02;
- drvcmd[2]=0x40;
- drvcmd[3]=0x01;
- drvcmd[8]=response_count;
- }
- i=cmd_out();
- if (i<0) return (i);
- for (i=0;i<response_count;i++)
- {
- sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_SQ1,"cc_ReadSubQ:%s\n", msgbuf);
- }
- if (famT_drive) break;
- if (infobuf[0]!=0) break;
- if ((!st_spinning) || (j==1))
- {
- D_S[d].SubQ_ctl_adr=D_S[d].SubQ_trk=D_S[d].SubQ_pnt_idx=D_S[d].SubQ_whatisthis=0;
- D_S[d].SubQ_run_tot=D_S[d].SubQ_run_trk=0;
- return (0);
- }
- }
- if (famT_drive) D_S[d].SubQ_ctl_adr=infobuf[1];
- else D_S[d].SubQ_ctl_adr=swap_nibbles(infobuf[1]);
- D_S[d].SubQ_trk=byt2bcd(infobuf[2]);
- D_S[d].SubQ_pnt_idx=byt2bcd(infobuf[3]);
- if (fam0L_drive) i=5;
- else if (fam12_drive) i=4;
- else if (famT_drive) i=8;
- D_S[d].SubQ_run_tot=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
- i=7;
- if (fam0L_drive) i=9;
- else if (fam12_drive) i=7;
- else if (famT_drive) i=4;
- D_S[d].SubQ_run_trk=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
- D_S[d].SubQ_whatisthis=infobuf[i+3];
- D_S[d].diskstate_flags |= subq_bit;
- return (0);
-}
-/*==========================================================================*/
-static int cc_ModeSense(void)
-{
- int i;
-
- if (fam2_drive) return (0);
- D_S[d].diskstate_flags &= ~frame_size_bit;
- clr_cmdbuf();
- if (fam1_drive)
- {
- response_count=5;
- drvcmd[0]=CMD1_GETMODE;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam0L_drive)
- {
- response_count=2;
- drvcmd[0]=CMD0_GETMODE;
- if (famL_drive) flags_cmd_out=f_putcmd;
- else flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- else if (famT_drive)
- {
- response_count=10;
- drvcmd[0]=CMDT_GETMODE;
- drvcmd[4]=response_count;
- }
- i=cmd_out();
- if (i<0) return (i);
- i=0;
- if (fam1_drive) D_S[d].sense_byte=infobuf[i++];
- else if (fam0L_drive) D_S[d].sense_byte=0;
- else if (famT_drive)
- {
- D_S[d].sense_byte=0;
- if (infobuf[4]==0x01) D_S[d].xa_byte=0x20; /* wrong!!!! */
- i=2;
- }
- D_S[d].frame_size=make16(infobuf[i],infobuf[i+1]);
- for (i=0;i<response_count;i++)
- sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_XA1,"cc_ModeSense:%s\n", msgbuf);
-
- D_S[d].diskstate_flags |= frame_size_bit;
- return (0);
-}
-/*==========================================================================*/
-/*==========================================================================*/
-static int cc_ModeSelect(int framesize)
-{
- int i;
-
- if (fam2_drive) return (0);
- D_S[d].diskstate_flags &= ~frame_size_bit;
- clr_cmdbuf();
- D_S[d].frame_size=framesize;
- if (framesize==CD_FRAMESIZE_RAW) D_S[d].sense_byte=0x82;
- else D_S[d].sense_byte=0x00;
-
- msg(DBG_XA1,"cc_ModeSelect: %02X %04X\n",
- D_S[d].sense_byte, D_S[d].frame_size);
-
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_SETMODE;
- drvcmd[1]=0x00;
- drvcmd[2]=D_S[d].sense_byte;
- drvcmd[3]=(D_S[d].frame_size>>8)&0xFF;
- drvcmd[4]=D_S[d].frame_size&0xFF;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam0L_drive)
- {
- drvcmd[0]=CMD0_SETMODE;
- drvcmd[1]=0x00;
- drvcmd[2]=(D_S[d].frame_size>>8)&0xFF;
- drvcmd[3]=D_S[d].frame_size&0xFF;
- drvcmd[4]=0x00;
- if(famL_drive)
- flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check;
- else
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- else if (famT_drive)
- {
- return (-1);
- }
- response_count=0;
- i=cmd_out();
- if (i<0) return (i);
- D_S[d].diskstate_flags |= frame_size_bit;
- return (0);
-}
-/*==========================================================================*/
-static int cc_GetVolume(void)
-{
- int i;
- u_char switches;
- u_char chan0=0;
- u_char vol0=0;
- u_char chan1=1;
- u_char vol1=0;
-
- D_S[d].diskstate_flags &= ~volume_bit;
- clr_cmdbuf();
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_GETMODE;
- drvcmd[1]=0x05;
- response_count=5;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_GETMODE;
- drvcmd[1]=0x0E;
- response_count=5;
- flags_cmd_out=f_putcmd;
- }
- else if (fam0L_drive)
- {
- drvcmd[0]=CMD0_GETMODE;
- drvcmd[1]=0x03;
- response_count=2;
- if(famL_drive)
- flags_cmd_out=f_putcmd;
- else
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- else if (famT_drive)
- {
- i=cc_get_mode_T();
- if (i<0) return (i);
- }
- if (!famT_drive)
- {
- i=cmd_out();
- if (i<0) return (i);
- }
- if (fam1_drive)
- {
- chan0=infobuf[1]&0x0F;
- vol0=infobuf[2];
- chan1=infobuf[3]&0x0F;
- vol1=infobuf[4];
- if (chan0==0)
- {
- chan0=1;
- vol0=0;
- }
- if (chan1==0)
- {
- chan1=2;
- vol1=0;
- }
- chan0 >>= 1;
- chan1 >>= 1;
- }
- else if (fam2_drive)
- {
- chan0=infobuf[1];
- vol0=infobuf[2];
- chan1=infobuf[3];
- vol1=infobuf[4];
- }
- else if (famL_drive)
- {
- chan0=0;
- chan1=1;
- vol0=vol1=infobuf[1];
- switches=infobuf[0];
- if ((switches&0x80)!=0) chan0=1;
- if ((switches&0x40)!=0) chan1=0;
- }
- else if (fam0_drive) /* different firmware levels */
- {
- chan0=0;
- chan1=1;
- vol0=vol1=infobuf[1];
- if (D_S[d].drv_type>=drv_201)
- {
- if (D_S[d].drv_type<drv_300)
- {
- switches=infobuf[0];
- if ((switches&0x80)!=0) vol0=0;
- if ((switches&0x40)!=0) vol1=0;
- if (D_S[d].drv_type>=drv_211)
- {
- if ((switches&0x20)!=0) chan0=1;
- if ((switches&0x10)!=0) chan1=0;
- }
- }
- else
- {
- vol0=infobuf[0];
- if ((vol0&0x01)!=0) chan0=1;
- if ((vol1&0x01)==0) chan1=0;
- vol0 &= 0xFC;
- vol1 &= 0xFC;
- if (vol0!=0) vol0 += 3;
- if (vol1!=0) vol1 += 3;
- }
- }
- }
- else if (famT_drive)
- {
- D_S[d].volume_control=infobuf[7];
- chan0=0;
- chan1=1;
- if (D_S[d].volume_control&0x10) vol0=0;
- else vol0=0xff;
- if (D_S[d].volume_control&0x20) vol1=0;
- else vol1=0xff;
- }
- D_S[d].vol_chan0=chan0;
- D_S[d].vol_ctrl0=vol0;
- D_S[d].vol_chan1=chan1;
- D_S[d].vol_ctrl1=vol1;
-#if 000
- D_S[d].vol_chan2=2;
- D_S[d].vol_ctrl2=0xFF;
- D_S[d].vol_chan3=3;
- D_S[d].vol_ctrl3=0xFF;
-#endif 000
- D_S[d].diskstate_flags |= volume_bit;
- return (0);
-}
-/*==========================================================================*/
-static int cc_ReadCapacity(void)
-{
- int i, j;
-
- if (famL_drive) return (0);
- D_S[d].diskstate_flags &= ~cd_size_bit;
- for (j=3;j>0;j--)
- {
- clr_cmdbuf();
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_CAPACITY;
- response_count=5;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_CAPACITY;
- response_count=8;
- flags_cmd_out=f_putcmd;
- }
- else if (fam0_drive)
- {
- drvcmd[0]=CMD0_CAPACITY;
- response_count=5;
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- else if (famT_drive)
- {
- response_count=12;
- drvcmd[0]=CMDT_DISKINFO;
- drvcmd[1]=0x02;
- drvcmd[6]=CDROM_LEADOUT;
- drvcmd[8]=response_count;
- drvcmd[9]=0x00;
- }
- i=cmd_out();
- if (i>=0) break;
- msg(DBG_000,"cc_ReadCapacity: cmd_out: err %d\n", i);
- cc_ReadError();
- }
- if (j==0) return (i);
- if (fam1_drive) D_S[d].CDsize_frm=msf2blk(make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2])))+CD_BLOCK_OFFSET;
- else if (fam0_drive) D_S[d].CDsize_frm=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2]));
- else if (fam2_drive) D_S[d].CDsize_frm=make32(make16(infobuf[0],infobuf[1]),make16(infobuf[2],infobuf[3]));
- else if (famT_drive)
- {
- D_S[d].CDsize_frm=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
- D_S[d].n_first_track=infobuf[2];
- D_S[d].n_last_track=infobuf[3];
-
- }
- D_S[d].diskstate_flags |= cd_size_bit;
- msg(DBG_000,"cc_ReadCapacity: %d frames.\n", D_S[d].CDsize_frm);
- return (0);
-}
-/*==========================================================================*/
-static int cc_ReadTocDescr(void)
-{
- int i;
-
- D_S[d].diskstate_flags &= ~toc_bit;
- clr_cmdbuf();
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_DISKINFO;
- response_count=6;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam0L_drive)
- {
- drvcmd[0]=CMD0_DISKINFO;
- response_count=6;
- if(famL_drive)
- flags_cmd_out=f_putcmd;
- else
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- /* possibly longer timeout periods necessary */
- D_S[d].f_multisession=0;
- drvcmd[0]=CMD2_DISKINFO;
- drvcmd[1]=0x02;
- drvcmd[2]=0xAB;
- drvcmd[3]=0xFF; /* session */
- response_count=8;
- flags_cmd_out=f_putcmd;
- }
- else if (famT_drive)
- {
- D_S[d].f_multisession=0;
- response_count=12;
- drvcmd[0]=CMDT_DISKINFO;
- drvcmd[1]=0x02;
- drvcmd[6]=CDROM_LEADOUT;
- drvcmd[8]=response_count;
- drvcmd[9]=0x00;
- }
- i=cmd_out();
- if (i<0) return (i);
- if ((fam1_drive)||(fam2_drive)||(famL_drive)||(fam0_drive))
- D_S[d].xa_byte=infobuf[0];
- if (fam2_drive)
- {
- D_S[d].first_session=infobuf[1];
- D_S[d].last_session=infobuf[2];
- D_S[d].n_first_track=infobuf[3];
- D_S[d].n_last_track=infobuf[4];
- if (D_S[d].first_session!=D_S[d].last_session)
- {
- D_S[d].f_multisession=1;
- D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7])));
- }
-#if 0
- if (D_S[d].first_session!=D_S[d].last_session)
- {
- if (D_S[d].last_session<=20)
- zwanzig=D_S[d].last_session+1;
- else zwanzig=20;
- for (count=D_S[d].first_session;count<zwanzig;count++)
- {
- drvcmd[0]=CMD2_DISKINFO;
- drvcmd[1]=0x02;
- drvcmd[2]=0xAB;
- drvcmd[3]=count;
- response_count=8;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- if (i<0) return (i);
- D_S[d].msf_multi_n[count]=make32(make16(0,infobuf[5]),make16(infobuf[6],infobuf[7]));
- }
- D_S[d].diskstate_flags |= multisession_bit;
- }
-#endif
- drvcmd[0]=CMD2_DISKINFO;
- drvcmd[1]=0x02;
- drvcmd[2]=0xAA;
- drvcmd[3]=0xFF;
- response_count=5;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- if (i<0) return (i);
- D_S[d].size_msf=make32(make16(0,infobuf[2]),make16(infobuf[3],infobuf[4]));
- D_S[d].size_blk=msf2blk(D_S[d].size_msf);
- }
- else if (famT_drive)
- {
- D_S[d].size_msf=make32(make16(infobuf[8],infobuf[9]),make16(infobuf[10],infobuf[11]));
- D_S[d].size_blk=msf2blk(D_S[d].size_msf);
- D_S[d].n_first_track=infobuf[2];
- D_S[d].n_last_track=infobuf[3];
- }
- else
- {
- D_S[d].n_first_track=infobuf[1];
- D_S[d].n_last_track=infobuf[2];
- D_S[d].size_msf=make32(make16(0,infobuf[3]),make16(infobuf[4],infobuf[5]));
- D_S[d].size_blk=msf2blk(D_S[d].size_msf);
- if (famL_drive) D_S[d].CDsize_frm=D_S[d].size_blk+1;
- }
- D_S[d].diskstate_flags |= toc_bit;
- msg(DBG_TOC,"TocDesc: %02X %02X %02X %08X\n",
- D_S[d].xa_byte,
- D_S[d].n_first_track,
- D_S[d].n_last_track,
- D_S[d].size_msf);
- return (0);
-}
-/*==========================================================================*/
-static int cc_ReadTocEntry(int num)
-{
- int i;
-
- clr_cmdbuf();
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_READTOC;
- drvcmd[2]=num;
- response_count=8;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam2_drive)
- {
- /* possibly longer timeout periods necessary */
- drvcmd[0]=CMD2_DISKINFO;
- drvcmd[1]=0x02;
- drvcmd[2]=num;
- response_count=5;
- flags_cmd_out=f_putcmd;
- }
- else if (fam0L_drive)
- {
- drvcmd[0]=CMD0_READTOC;
- drvcmd[1]=0x02;
- drvcmd[2]=num;
- response_count=8;
- if(famL_drive)
- flags_cmd_out=f_putcmd;
- else
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- }
- else if (famT_drive)
- {
- response_count=12;
- drvcmd[0]=CMDT_DISKINFO;
- drvcmd[1]=0x02;
- drvcmd[6]=num;
- drvcmd[8]=response_count;
- drvcmd[9]=0x00;
- }
- i=cmd_out();
- if (i<0) return (i);
- if ((fam1_drive)||(famL_drive)||(fam0_drive))
- {
- D_S[d].TocEnt_nixbyte=infobuf[0];
- i=1;
- }
- else if (fam2_drive) i=0;
- else if (famT_drive)
- {
- i=5;
- }
- D_S[d].TocEnt_ctl_adr=swap_nibbles(infobuf[i++]);
- if ((fam1_drive)||(famL_drive)||(fam0_drive))
- {
- D_S[d].TocEnt_number=infobuf[i++];
- D_S[d].TocEnt_format=infobuf[i];
- }
- else D_S[d].TocEnt_number=num;
- if (fam1_drive) i=4;
- else if (fam0L_drive) i=5;
- else if (fam2_drive) i=2;
- else if (famT_drive) i=9;
- D_S[d].TocEnt_address=make32(make16(0,infobuf[i]),
- make16(infobuf[i+1],infobuf[i+2]));
- msg(DBG_TOC,"TocEntry: %02X %02X %02X %02X %08X\n",
- D_S[d].TocEnt_nixbyte, D_S[d].TocEnt_ctl_adr,
- D_S[d].TocEnt_number, D_S[d].TocEnt_format,
- D_S[d].TocEnt_address);
- return (0);
-}
-/*==========================================================================*/
-static int cc_ReadPacket(void)
-{
- int i;
-
- clr_cmdbuf();
- drvcmd[0]=CMD0_PACKET;
- drvcmd[1]=response_count;
- if(famL_drive) flags_cmd_out=f_putcmd;
- else if (fam01_drive)
- flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
- else if (fam2_drive) return (-1); /* not implemented yet */
- else if (famT_drive)
- {
- return (-1);
- }
- i=cmd_out();
- return (i);
-}
-/*==========================================================================*/
-static int convert_UPC(u_char *p)
-{
- int i;
-
- p++;
- if (fam0L_drive) p[13]=0;
- for (i=0;i<7;i++)
- {
- if (fam1_drive) D_S[d].UPC_buf[i]=swap_nibbles(*p++);
- else if (fam0L_drive)
- {
- D_S[d].UPC_buf[i]=((*p++)<<4)&0xFF;
- D_S[d].UPC_buf[i] |= *p++;
- }
- else if (famT_drive)
- {
- return (-1);
- }
- else /* CD200 */
- {
- return (-1);
- }
- }
- D_S[d].UPC_buf[6] &= 0xF0;
- return (0);
-}
-/*==========================================================================*/
-static int cc_ReadUPC(void)
-{
- int i;
-#if TEST_UPC
- int block, checksum;
-#endif TEST_UPC
-
- if (fam2_drive) return (0); /* not implemented yet */
- if (famT_drive) return (0); /* not implemented yet */
-#if 1
- if (fam0_drive) return (0); /* but it should work */
-#endif 1
-
- D_S[d].diskstate_flags &= ~upc_bit;
-#if TEST_UPC
- for (block=CD_BLOCK_OFFSET+1;block<CD_BLOCK_OFFSET+200;block++)
- {
-#endif TEST_UPC
- clr_cmdbuf();
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_READ_UPC;
-#if TEST_UPC
- drvcmd[1]=(block>>16)&0xFF;
- drvcmd[2]=(block>>8)&0xFF;
- drvcmd[3]=block&0xFF;
-#endif TEST_UPC
- response_count=8;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- }
- else if (fam0L_drive)
- {
- drvcmd[0]=CMD0_READ_UPC;
-#if TEST_UPC
- drvcmd[2]=(block>>16)&0xFF;
- drvcmd[3]=(block>>8)&0xFF;
- drvcmd[4]=block&0xFF;
-#endif TEST_UPC
- response_count=0;
- flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
- }
- else if (fam2_drive)
- {
- return (-1);
- }
- else if (famT_drive)
- {
- return (-1);
- }
- i=cmd_out();
- if (i<0)
- {
- msg(DBG_000,"cc_ReadUPC cmd_out: err %d\n", i);
- return (i);
- }
- if (fam0L_drive)
- {
- response_count=16;
- if (famL_drive) flags_cmd_out=f_putcmd;
- i=cc_ReadPacket();
- if (i<0)
- {
- msg(DBG_000,"cc_ReadUPC ReadPacket: err %d\n", i);
- return (i);
- }
- }
-#if TEST_UPC
- checksum=0;
-#endif TEST_UPC
- for (i=0;i<(fam1_drive?8:16);i++)
- {
-#if TEST_UPC
- checksum |= infobuf[i];
-#endif TEST_UPC
- sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
- }
- msgbuf[i*3]=0;
- msg(DBG_UPC,"UPC info:%s\n", msgbuf);
-#if TEST_UPC
- if ((checksum&0x7F)!=0) break;
- }
-#endif TEST_UPC
- D_S[d].UPC_ctl_adr=0;
- if (fam1_drive) i=0;
- else i=2;
- if ((infobuf[i]&0x80)!=0)
- {
- convert_UPC(&infobuf[i]);
- D_S[d].UPC_ctl_adr = (D_S[d].TocEnt_ctl_adr & 0xF0) | 0x02;
- }
- for (i=0;i<7;i++)
- sprintf(&msgbuf[i*3], " %02X", D_S[d].UPC_buf[i]);
- sprintf(&msgbuf[i*3], " (%02X)", D_S[d].UPC_ctl_adr);
- msgbuf[i*3+5]=0;
- msg(DBG_UPC,"UPC code:%s\n", msgbuf);
- D_S[d].diskstate_flags |= upc_bit;
- return (0);
-}
-/*==========================================================================*/
-static int cc_CheckMultiSession(void)
-{
- int i;
-
- if (fam2_drive) return (0);
- D_S[d].f_multisession=0;
- D_S[d].lba_multi=0;
- if (fam0_drive) return (0);
- clr_cmdbuf();
- if (fam1_drive)
- {
- drvcmd[0]=CMD1_MULTISESS;
- response_count=6;
- flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
- i=cmd_out();
- if (i<0) return (i);
- if ((infobuf[0]&0x80)!=0)
- {
- D_S[d].f_multisession=1;
- D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[1]),
- make16(infobuf[2],infobuf[3])));
- }
- }
- else if (famL_drive)
- {
- drvcmd[0]=CMDL_MULTISESS;
- drvcmd[1]=3;
- drvcmd[2]=1;
- response_count=8;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- if (i<0) return (i);
- D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),
- make16(infobuf[6],infobuf[7])));
- }
- else if (famT_drive)
- {
- response_count=12;
- drvcmd[0]=CMDT_DISKINFO;
- drvcmd[1]=0x02;
- drvcmd[6]=0;
- drvcmd[8]=response_count;
- drvcmd[9]=0x40;
- i=cmd_out();
- if (i<0) return (i);
- D_S[d].lba_multi=msf2blk(make32(make16(0,infobuf[9]),make16(infobuf[10],infobuf[11])));
- }
- for (i=0;i<response_count;i++)
- sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_MUL,"MultiSession Info:%s (%d)\n", msgbuf, D_S[d].lba_multi);
- if (D_S[d].lba_multi>200)
- {
- D_S[d].f_multisession=1;
- msg(DBG_MUL,"MultiSession base: %06X\n", D_S[d].lba_multi);
- }
- return (0);
-}
-/*==========================================================================*/
-#if FUTURE
-static int cc_SubChanInfo(int frame, int count, u_char *buffer)
- /* "frame" is a RED BOOK (msf-bin) address */
-{
- int i;
-
- if (fam0L_drive) return (-ENOSYS); /* drive firmware lacks it */
- if (famT_drive)
- {
- return (-1);
- }
-#if 0
- if (D_S[d].audio_state!=audio_playing) return (-ENODATA);
-#endif
- clr_cmdbuf();
- drvcmd[0]=CMD1_SUBCHANINF;
- drvcmd[1]=(frame>>16)&0xFF;
- drvcmd[2]=(frame>>8)&0xFF;
- drvcmd[3]=frame&0xFF;
- drvcmd[5]=(count>>8)&0xFF;
- drvcmd[6]=count&0xFF;
- flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
- cmd_type=READ_SC;
- D_S[d].frame_size=CD_FRAMESIZE_SUB;
- i=cmd_out(); /* which buffer to use? */
- return (i);
-}
-#endif FUTURE
-/*==========================================================================*/
-static void check_datarate(void)
-{
- int i=0;
-
- msg(DBG_IOX,"check_datarate entered.\n");
- datarate=0;
-#if TEST_STI
- for (i=0;i<=1000;i++) printk(".");
-#endif
- /* set a timer to make (timed_out_delay!=0) after 1.1 seconds */
-#if 1
- del_timer(&delay_timer);
-#endif
- delay_timer.expires=110;
- timed_out_delay=0;
- add_timer(&delay_timer);
- msg(DBG_TIM,"delay timer started (110).\n");
- do
- {
- i=inb(CDi_status);
- datarate++;
-#if 1
- if (datarate>0x6FFFFFFF) break;
-#endif 00000
- }
- while (!timed_out_delay);
- del_timer(&delay_timer);
- msg(DBG_TIM,"datarate: %04X\n", datarate);
- if (datarate<65536) datarate=65536;
- maxtim16=datarate*16;
- maxtim04=datarate*4;
- maxtim02=datarate*2;
- maxtim_8=datarate/32;
-#if LONG_TIMING
- maxtim_data=datarate/100;
-#else
- maxtim_data=datarate/300;
-#endif LONG_TIMING
- msg(DBG_TIM,"maxtim_8 %d, maxtim_data %d.\n",
- maxtim_8, maxtim_data);
-}
-/*==========================================================================*/
-#if 0
-static int c2_ReadError(int fam)
-{
- int i;
-
- clr_cmdbuf();
- response_count=9;
- clr_respo_buf(9);
- if (fam==1)
- {
- drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */
- i=do_cmd(f_putcmd|f_lopsta|f_getsta|f_ResponseStatus);
- }
- else if (fam==2)
- {
- drvcmd[0]=CMD2_READ_ERR;
- i=do_cmd(f_putcmd);
- }
- else return (-1);
- return (i);
-}
-#endif
-/*==========================================================================*/
-static void ask_mail(void)
-{
- int i;
-
- msg(DBG_INF, "please mail the following lines to emoenke@gwdg.de:\n");
- msg(DBG_INF, "%s\n", VERSION);
- msg(DBG_INF, "address %03X, type %s, drive %s (ID %d)\n",
- CDo_command, type, D_S[d].drive_model, D_S[d].drv_id);
- for (i=0;i<12;i++)
- sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_INF,"infobuf =%s\n", msgbuf);
- for (i=0;i<12;i++)
- sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_INF,"infobuf =%s\n", msgbuf);
-}
-/*==========================================================================*/
-static int check_version(void)
-{
- int i, j, l;
- int teac_possible=0;
-
- msg(DBG_INI,"check_version entered.\n");
- msg(DBG_TE2,"check_version: id=%d, d=%d.\n", D_S[d].drv_id, d);
- D_S[d].drv_type=0;
-
- /* check for CR-52x, CR-56x and LCS-7260 */
- /* clear any pending error state */
- clr_cmdbuf();
- drvcmd[0]=CMD0_READ_ERR; /* same as CMD1_ and CMDL_ */
- response_count=9;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- if (i<0) msg(DBG_INI,"CMD0_READERR returns %d (ok anyway).\n",i);
- /* read drive version */
- clr_cmdbuf();
- for (i=0;i<12;i++) infobuf[i]=0;
- drvcmd[0]=CMD0_READ_VER; /* same as CMD1_ and CMDL_ */
- response_count=12; /* fam1: only 11 */
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- if (i<-1) msg(DBG_INI,"CMD0_READ_VER returns %d\n",i);
- if (i==-11) teac_possible++;
- j=0;
- for (i=0;i<12;i++) j+=infobuf[i];
- if (j)
- {
- for (i=0;i<12;i++)
- sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_IDX,"infobuf =%s\n", msgbuf);
- msg(DBG_000,"infobuf =%s\n", msgbuf);
- for (i=0;i<12;i++)
- sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_IDX,"infobuf =%s\n", msgbuf);
- msg(DBG_000,"infobuf =%s\n", msgbuf);
- }
- for (i=0;i<4;i++) if (infobuf[i]!=family1[i]) break;
- if (i==4)
- {
- D_S[d].drive_model[0]='C';
- D_S[d].drive_model[1]='R';
- D_S[d].drive_model[2]='-';
- D_S[d].drive_model[3]='5';
- D_S[d].drive_model[4]=infobuf[i++];
- D_S[d].drive_model[5]=infobuf[i++];
- D_S[d].drive_model[6]=0;
- D_S[d].drv_type=drv_fam1;
- }
- if (!D_S[d].drv_type)
- {
- for (i=0;i<8;i++) if (infobuf[i]!=family0[i]) break;
- if (i==8)
- {
- D_S[d].drive_model[0]='C';
- D_S[d].drive_model[1]='R';
- D_S[d].drive_model[2]='-';
- D_S[d].drive_model[3]='5';
- D_S[d].drive_model[4]='2';
- D_S[d].drive_model[5]='x';
- D_S[d].drive_model[6]=0;
- D_S[d].drv_type=drv_fam0;
- }
- }
- if (!D_S[d].drv_type)
- {
- for (i=0;i<8;i++) if (infobuf[i]!=familyL[i]) break;
- if (i==8)
- {
- for (j=0;j<8;j++)
- D_S[d].drive_model[j]=infobuf[j];
- D_S[d].drive_model[8]=0;
- D_S[d].drv_type=drv_famL;
- }
- }
- if (!D_S[d].drv_type)
- {
- /* check for CD200 */
- clr_cmdbuf();
- drvcmd[0]=CMD2_READ_ERR;
- response_count=9;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- if (i<0) msg(DBG_INI,"CMD2_READERR returns %d (ok anyway).\n",i);
- if (i<0) msg(DBG_000,"CMD2_READERR returns %d (ok anyway).\n",i);
- /* read drive version */
- clr_cmdbuf();
- for (i=0;i<12;i++) infobuf[i]=0;
- if (sbpro_type==1) OUT(CDo_sel_i_d,0);
-#if 0
- OUT(CDo_reset,0);
- sbp_sleep(600);
- OUT(CDo_enable,D_S[d].drv_sel);
-#endif 0
- drvcmd[0]=CMD2_READ_VER;
- response_count=12;
- flags_cmd_out=f_putcmd;
- i=cmd_out();
- if (i<0) msg(DBG_INI,"CMD2_READ_VER returns %d\n",i);
- if (i==-7) teac_possible++;
- j=0;
- for (i=0;i<12;i++) j+=infobuf[i];
- if (j)
- {
- for (i=0;i<12;i++)
- sprintf(&msgbuf[i*3], " %02X", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_IDX,"infobuf =%s\n", msgbuf);
- for (i=0;i<12;i++)
- sprintf(&msgbuf[i*3], " %c ", infobuf[i]);
- msgbuf[i*3]=0;
- msg(DBG_IDX,"infobuf =%s\n", msgbuf);
- }
- if (i>=0)
- {
- for (i=0;i<5;i++) if (infobuf[i]!=family2[i]) break;
- if (i==5)
- {
- D_S[d].drive_model[0]='C';
- D_S[d].drive_model[1]='D';
- D_S[d].drive_model[2]='2';
- D_S[d].drive_model[3]='0';
- D_S[d].drive_model[4]='0';
- D_S[d].drive_model[5]=infobuf[i++];
- D_S[d].drive_model[6]=infobuf[i++];
- D_S[d].drive_model[7]=0;
- D_S[d].drv_type=drv_fam2;
- }
- }
- }
- if (!D_S[d].drv_type)
- {
- /* check for TEAC CD-55A */
- msg(DBG_TEA,"teac_possible: %d\n",teac_possible);
- for (j=1;j<=((D_S[d].drv_id==0)?3:1);j++)
- {
- for (l=1;l<=((D_S[d].drv_id==0)?10:1);l++)
- {
- msg(DBG_TEA,"TEAC reset #%d-%d.\n", j, l);
- if (sbpro_type==1) OUT(CDo_reset,0);
- else
- {
- OUT(CDo_enable,D_S[d].drv_sel);
- OUT(CDo_sel_i_d,0);
- OUT(CDo_command,CMDT_RESET);
- for (i=0;i<9;i++) OUT(CDo_command,0);
- }
- sbp_sleep(50);
- OUT(CDo_enable,D_S[d].drv_sel);
- OUT(CDo_sel_i_d,0);
- i=inb(CDi_status);
- msg(DBG_TEA,"TEAC CDi_status: %02X.\n",i);
-#if 0
- if (i&s_not_result_ready) continue; /* drive not present or ready */
-#endif
- i=inb(CDi_info);
- msg(DBG_TEA,"TEAC CDi_info: %02X.\n",i);
- if (i==0x55) break; /* drive found */
- }
- if (i==0x55) break; /* drive found */
- }
- if (i==0x55) /* drive found */
- {
- msg(DBG_TEA,"TEAC drive found.\n");
- clr_cmdbuf();
- flags_cmd_out=f_putcmd;
- response_count=12;
- drvcmd[0]=CMDT_READ_VER;
- drvcmd[4]=response_count;
- for (i=0;i<12;i++) infobuf[i]=0;
- i=cmd_out_T();
- if (i!=0) msg(DBG_TEA,"cmd_out_T(CMDT_READ_VER) returns %d.\n",i);
- for (i=1;i<6;i++) if (infobuf[i]!=familyT[i-1]) break;
- if (i==6)
- {
- D_S[d].drive_model[0]='C';
- D_S[d].drive_model[1]='D';
- D_S[d].drive_model[2]='-';
- D_S[d].drive_model[3]='5';
- D_S[d].drive_model[4]='5';
- D_S[d].drive_model[5]=0;
- D_S[d].drv_type=drv_famT;
- }
- }
- }
- if (!D_S[d].drv_type)
- {
- msg(DBG_TEA,"no drive found at address %03X under ID %d.\n",CDo_command,D_S[d].drv_id);
- return (-522);
- }
- for (j=0;j<4;j++) D_S[d].firmware_version[j]=infobuf[i+j];
- if (famL_drive)
- {
- u_char lcs_firm_e1[]="A E1";
- u_char lcs_firm_f4[]="A4F4";
-
- for (j=0;j<4;j++)
- if (D_S[d].firmware_version[j]!=lcs_firm_e1[j]) break;
- if (j==4) D_S[d].drv_type=drv_e1;
-
- for (j=0;j<4;j++)
- if (D_S[d].firmware_version[j]!=lcs_firm_f4[j]) break;
- if (j==4) D_S[d].drv_type=drv_f4;
-
- if (D_S[d].drv_type==drv_famL) ask_mail();
- }
- else if (famT_drive)
- {
- j=infobuf[4]; /* one-byte version??? - here: 0x15 */
- if (j=='5')
- {
- D_S[d].firmware_version[0]=infobuf[7];
- D_S[d].firmware_version[1]=infobuf[8];
- D_S[d].firmware_version[2]=infobuf[10];
- D_S[d].firmware_version[3]=infobuf[11];
- }
- else
- {
- if (j!=0x15) ask_mail();
- D_S[d].firmware_version[0]='0';
- D_S[d].firmware_version[1]='.';
- D_S[d].firmware_version[2]='0'+(j>>4);
- D_S[d].firmware_version[3]='0'+(j&0x0f);
- }
- }
- else /* CR-52x, CR-56x, CD200 */
- {
- j = (D_S[d].firmware_version[0] & 0x0F) * 100 +
- (D_S[d].firmware_version[2] & 0x0F) *10 +
- (D_S[d].firmware_version[3] & 0x0F);
- if (fam0_drive)
- {
- if (j<200) D_S[d].drv_type=drv_199;
- else if (j<201) D_S[d].drv_type=drv_200;
- else if (j<210) D_S[d].drv_type=drv_201;
- else if (j<211) D_S[d].drv_type=drv_210;
- else if (j<300) D_S[d].drv_type=drv_211;
- else if (j>=300) D_S[d].drv_type=drv_300;
- }
- else if (fam1_drive)
- {
- if (j<100) D_S[d].drv_type=drv_099;
- else
- {
- D_S[d].drv_type=drv_100;
- if ((j!=500)||(j!=102)) ask_mail();
- }
- }
- else if (fam2_drive)
- {
- msg(DBG_INF,"new drive CD200 (%s)detected.\n", D_S[d].firmware_version);
- msg(DBG_INF,"support is not fulfilled yet - audio should work.\n");
- if ((j!=101)&&(j!=35)) ask_mail(); /* only 1.01 and 0.35 known at time */
- }
- }
- msg(DBG_LCS,"drive type %02X\n",D_S[d].drv_type);
- msg(DBG_INI,"check_version done.\n");
- return (0);
-}
-/*==========================================================================*/
-static void switch_drive(int i)
-{
- d=i;
- OUT(CDo_enable,D_S[d].drv_sel);
- msg(DBG_DID,"drive %d (ID=%d) activated.\n", i, D_S[d].drv_id);
- return;
-}
-/*==========================================================================*/
-#ifdef PATH_CHECK
-/*
- * probe for the presence of an interface card
- */
-static int check_card(int port)
-{
-#undef N_RESPO
-#define N_RESPO 20
- int i, j, k;
- u_char response[N_RESPO];
- u_char save_port0;
- u_char save_port3;
-
- msg(DBG_INI,"check_card entered.\n");
- save_port0=inb(port+0);
- save_port3=inb(port+3);
-
- for (j=0;j<NR_SBPCD;j++)
- {
- OUT(port+3,j) ; /* enable drive #j */
- OUT(port+0,CMD0_PATH_CHECK);
- for (i=10;i>0;i--) OUT(port+0,0);
- for (k=0;k<N_RESPO;k++) response[k]=0;
- for (k=0;k<N_RESPO;k++)
- {
- for (i=10000;i>0;i--)
- {
- if (inb(port+1)&s_not_result_ready) continue;
- response[k]=inb(port+0);
- break;
- }
- }
- for (i=0;i<N_RESPO;i++)
- sprintf(&msgbuf[i*3], " %02X", response[i]);
- msgbuf[i*3]=0;
- msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf);
- OUT(port+0,CMD0_PATH_CHECK);
- for (i=10;i>0;i--) OUT(port+0,0);
- for (k=0;k<N_RESPO;k++) response[k]=0xFF;
- for (k=0;k<N_RESPO;k++)
- {
- for (i=10000;i>0;i--)
- {
- if (inb(port+1)&s_not_result_ready) continue;
- response[k]=inb(port+0);
- break;
- }
- }
- for (i=0;i<N_RESPO;i++)
- sprintf(&msgbuf[i*3], " %02X", response[i]);
- msgbuf[i*3]=0;
- msg(DBG_TEA,"path check 00 (%d): %s\n", j, msgbuf);
-
- if (response[0]==0xAA)
- if (response[1]==0x55)
- return (0);
- }
- for (j=0;j<NR_SBPCD;j++)
- {
- OUT(port+3,j) ; /* enable drive #j */
- OUT(port+0,CMD2_READ_VER);
- for (i=10;i>0;i--) OUT(port+0,0);
- for (k=0;k<N_RESPO;k++) response[k]=0;
- for (k=0;k<N_RESPO;k++)
- {
- for (i=1000000;i>0;i--)
- {
- if (inb(port+1)&s_not_result_ready) continue;
- response[k]=inb(port+0);
- break;
- }
- }
- for (i=0;i<N_RESPO;i++)
- sprintf(&msgbuf[i*3], " %02X", response[i]);
- msgbuf[i*3]=0;
- msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf);
-
- OUT(port+0,CMD2_READ_VER);
- for (i=10;i>0;i--) OUT(port+0,0);
- for (k=0;k<N_RESPO;k++) response[k]=0xFF;
- for (k=0;k<N_RESPO;k++)
- {
- for (i=1000000;i>0;i--)
- {
- if (inb(port+1)&s_not_result_ready) continue;
- response[k]=inb(port+0);
- break;
- }
- }
- for (i=0;i<N_RESPO;i++)
- sprintf(&msgbuf[i*3], " %02X", response[i]);
- msgbuf[i*3]=0;
- msg(DBG_TEA,"path check 12 (%d): %s\n", j, msgbuf);
-
- if (response[0]==0xAA)
- if (response[1]==0x55)
- return (0);
- }
- OUT(port+0,save_port0);
- OUT(port+3,save_port3);
- return (0); /* in any case - no real "function" at time */
-}
-#endif PATH_CHECK
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * probe for the presence of drives on the selected controller
- */
-static int check_drives(void)
-{
- int i, j;
-
- msg(DBG_INI,"check_drives entered.\n");
- ndrives=0;
- for (j=0;j<MAX_DRIVES;j++)
- {
- D_S[ndrives].drv_id=j;
- if (sbpro_type==1) D_S[ndrives].drv_sel=(j&0x01)<<1|(j&0x02)>>1;
- else D_S[ndrives].drv_sel=j;
- switch_drive(ndrives);
- msg(DBG_INI,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j);
- msg(DBG_000,"check_drives: drive %d (ID=%d) activated.\n",ndrives,j);
- i=check_version();
- if (i<0) msg(DBG_INI,"check_version returns %d.\n",i);
- else
- {
- D_S[d].drv_options=drv_pattern[j];
- if (fam0L_drive) D_S[d].drv_options&=~(speed_auto|speed_300|speed_150);
- msg(DBG_INF, "Drive %d (ID=%d): %.9s (%.4s) at 0x%03X (type %d)\n",
- d,
- D_S[d].drv_id,
- D_S[d].drive_model,
- D_S[d].firmware_version,
- CDo_command,
- sbpro_type);
- ndrives++;
- }
- }
- for (j=ndrives;j<NR_SBPCD;j++) D_S[j].drv_id=-1;
- if (ndrives==0) return (-1);
- return (0);
-}
-/*==========================================================================*/
-#if FUTURE
-/*
- * obtain if requested service disturbs current audio state
- */
-static int obey_audio_state(u_char audio_state, u_char func,u_char subfunc)
-{
- switch (audio_state) /* audio status from controller */
- {
- case aud_11: /* "audio play in progress" */
- case audx11:
- switch (func) /* DOS command code */
- {
- case cmd_07: /* input flush */
- case cmd_0d: /* open device */
- case cmd_0e: /* close device */
- case cmd_0c: /* ioctl output */
- return (1);
- case cmd_03: /* ioctl input */
- switch (subfunc)
- /* DOS ioctl input subfunction */
- {
- case cxi_00:
- case cxi_06:
- case cxi_09:
- return (1);
- default:
- return (ERROR15);
- }
- return (1);
- default:
- return (ERROR15);
- }
- return (1);
- case aud_12: /* "audio play paused" */
- case audx12:
- return (1);
- default:
- return (2);
- }
-}
-/*==========================================================================*/
-/* allowed is only
- * ioctl_o, flush_input, open_device, close_device,
- * tell_address, tell_volume, tell_capabiliti,
- * tell_framesize, tell_CD_changed, tell_audio_posi
- */
-static int check_allowed1(u_char func1, u_char func2)
-{
-#if 000
- if (func1==ioctl_o) return (0);
- if (func1==read_long) return (-1);
- if (func1==read_long_prefetch) return (-1);
- if (func1==seek) return (-1);
- if (func1==audio_play) return (-1);
- if (func1==audio_pause) return (-1);
- if (func1==audio_resume) return (-1);
- if (func1!=ioctl_i) return (0);
- if (func2==tell_SubQ_run_tot) return (-1);
- if (func2==tell_cdsize) return (-1);
- if (func2==tell_TocDescrip) return (-1);
- if (func2==tell_TocEntry) return (-1);
- if (func2==tell_subQ_info) return (-1);
- if (fam1_drive) if (func2==tell_SubChanInfo) return (-1);
- if (func2==tell_UPC) return (-1);
-#else
- return (0);
-#endif 000
-}
-/*==========================================================================*/
-static int check_allowed2(u_char func1, u_char func2)
-{
-#if 000
- if (func1==read_long) return (-1);
- if (func1==read_long_prefetch) return (-1);
- if (func1==seek) return (-1);
- if (func1==audio_play) return (-1);
- if (func1!=ioctl_o) return (0);
- if (fam1_drive)
- {
- if (func2==EjectDisk) return (-1);
- if (func2==CloseTray) return (-1);
- }
-#else
- return (0);
-#endif 000
-}
-/*==========================================================================*/
-static int check_allowed3(u_char func1, u_char func2)
-{
-#if 000
- if (func1==ioctl_i)
- {
- if (func2==tell_address) return (0);
- if (func2==tell_capabiliti) return (0);
- if (func2==tell_CD_changed) return (0);
- if (fam0L_drive) if (func2==tell_SubChanInfo) return (0);
- return (-1);
- }
- if (func1==ioctl_o)
- {
- if (func2==DriveReset) return (0);
- if (fam0L_drive)
- {
- if (func2==EjectDisk) return (0);
- if (func2==LockDoor) return (0);
- if (func2==CloseTray) return (0);
- }
- return (-1);
- }
- if (func1==flush_input) return (-1);
- if (func1==read_long) return (-1);
- if (func1==read_long_prefetch) return (-1);
- if (func1==seek) return (-1);
- if (func1==audio_play) return (-1);
- if (func1==audio_pause) return (-1);
- if (func1==audio_resume) return (-1);
-#else
- return (0);
-#endif 000
-}
-/*==========================================================================*/
-static int seek_pos_audio_end(void)
-{
- int i;
-
- i=msf2blk(D_S[d].pos_audio_end)-1;
- if (i<0) return (-1);
- i=cc_Seek(i,0);
- return (i);
-}
-#endif FUTURE
-/*==========================================================================*/
-static int ReadToC(void)
-{
- int i, j;
- D_S[d].diskstate_flags &= ~toc_bit;
- D_S[d].ored_ctl_adr=0;
- for (j=D_S[d].n_first_track;j<=D_S[d].n_last_track;j++)
- {
- i=cc_ReadTocEntry(j);
- if (i<0)
- {
- msg(DBG_INF,"cc_ReadTocEntry(%d) returns %d.\n",j,i);
- return (i);
- }
- D_S[d].TocBuffer[j].nixbyte=D_S[d].TocEnt_nixbyte;
- D_S[d].TocBuffer[j].ctl_adr=D_S[d].TocEnt_ctl_adr;
- D_S[d].TocBuffer[j].number=D_S[d].TocEnt_number;
- D_S[d].TocBuffer[j].format=D_S[d].TocEnt_format;
- D_S[d].TocBuffer[j].address=D_S[d].TocEnt_address;
- D_S[d].ored_ctl_adr |= D_S[d].TocEnt_ctl_adr;
- }
- /* fake entry for LeadOut Track */
- D_S[d].TocBuffer[j].nixbyte=0;
- D_S[d].TocBuffer[j].ctl_adr=0;
- D_S[d].TocBuffer[j].number=CDROM_LEADOUT;
- D_S[d].TocBuffer[j].format=0;
- D_S[d].TocBuffer[j].address=D_S[d].size_msf;
-
- D_S[d].diskstate_flags |= toc_bit;
- return (0);
-}
-/*==========================================================================*/
-static int DiskInfo(void)
-{
- int i, j;
-
- D_S[d].mode=READ_M1;
-
-#undef LOOP_COUNT
-#define LOOP_COUNT 10 /* needed for some "old" drives */
-
- msg(DBG_000,"DiskInfo entered.\n");
- for (j=1;j<LOOP_COUNT;j++)
- {
- i=SetSpeed();
- if (i<0)
- {
- msg(DBG_INF,"DiskInfo: SetSpeed returns %d\n", i);
- continue;
- }
- i=cc_ModeSense();
- if (i<0)
- {
- msg(DBG_INF,"DiskInfo: cc_ModeSense returns %d\n", i);
- continue;
- }
- i=cc_ReadCapacity();
- if (i>=0) break;
- msg(DBG_INF,"DiskInfo: ReadCapacity #%d returns %d\n", j, i);
- i=cc_DriveReset();
- }
- if (j==LOOP_COUNT) return (-33); /* give up */
-
- i=cc_ReadTocDescr();
- if (i<0)
- {
- msg(DBG_INF,"DiskInfo: ReadTocDescr returns %d\n", i);
- return (i);
- }
- i=ReadToC();
- if (i<0)
- {
- msg(DBG_INF,"DiskInfo: ReadToC returns %d\n", i);
- return (i);
- }
- i=cc_CheckMultiSession();
- if (i<0)
- {
- msg(DBG_INF,"DiskInfo: cc_CheckMultiSession returns %d\n", i);
- return (i);
- }
- if (D_S[d].f_multisession) D_S[d].sbp_bufsiz=1; /* possibly a weird PhotoCD */
- else D_S[d].sbp_bufsiz=SBP_BUFFER_FRAMES;
- i=cc_ReadTocEntry(D_S[d].n_first_track);
- if (i<0)
- {
- msg(DBG_INF,"DiskInfo: cc_ReadTocEntry(1) returns %d\n", i);
- return (i);
- }
- i=cc_ReadUPC();
- if (i<0) msg(DBG_INF,"DiskInfo: cc_ReadUPC returns %d\n", i);
- if ((fam0L_drive) && (D_S[d].xa_byte==0x20))
- {
- /* XA disk with old drive */
- cc_ModeSelect(CD_FRAMESIZE_XA);
- cc_ModeSense();
- }
- if (famT_drive) cc_prep_mode_T();
- msg(DBG_000,"DiskInfo done.\n");
- return (0);
-}
-/*==========================================================================*/
-#if FUTURE
-/*
- * called always if driver gets entered
- * returns 0 or ERROR2 or ERROR15
- */
-static int prepare(u_char func, u_char subfunc)
-{
- int i;
-
- if (fam0L_drive)
- {
- i=inb(CDi_status);
- if (i&s_attention) GetStatus();
- }
- else if (fam1_drive) GetStatus();
- else if (fam2_drive) GetStatus();
- else if (famT_drive) GetStatus();
- if (D_S[d].CD_changed==0xFF)
- {
- D_S[d].diskstate_flags=0;
- D_S[d].audio_state=0;
- if (!st_diskok)
- {
- i=check_allowed1(func,subfunc);
- if (i<0) return (-2);
- }
- else
- {
- i=check_allowed3(func,subfunc);
- if (i<0)
- {
- D_S[d].CD_changed=1;
- return (-15);
- }
- }
- }
- else
- {
- if (!st_diskok)
- {
- D_S[d].diskstate_flags=0;
- D_S[d].audio_state=0;
- i=check_allowed1(func,subfunc);
- if (i<0) return (-2);
- }
- else
- {
- if (st_busy)
- {
- if (D_S[d].audio_state!=audio_pausing)
- {
- i=check_allowed2(func,subfunc);
- if (i<0) return (-2);
- }
- }
- else
- {
- if (D_S[d].audio_state==audio_playing) seek_pos_audio_end();
- D_S[d].audio_state=0;
- }
- if (!frame_size_valid)
- {
- i=DiskInfo();
- if (i<0)
- {
- D_S[d].diskstate_flags=0;
- D_S[d].audio_state=0;
- i=check_allowed1(func,subfunc);
- if (i<0) return (-2);
- }
- }
- }
- }
- return (0);
-}
-#endif FUTURE
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * Check the results of the "get status" command.
- */
-static int sbp_status(void)
-{
- int st;
-
- st=ResponseStatus();
- if (st<0)
- {
- msg(DBG_INF,"sbp_status: timeout.\n");
- return (0);
- }
-
- if (!st_spinning) msg(DBG_SPI,"motor got off - ignoring.\n");
-
- if (st_check)
- {
- msg(DBG_INF,"st_check detected - retrying.\n");
- return (0);
- }
- if (!st_door_closed)
- {
- msg(DBG_INF,"door is open - retrying.\n");
- return (0);
- }
- if (!st_caddy_in)
- {
- msg(DBG_INF,"disk removed - retrying.\n");
- return (0);
- }
- if (!st_diskok)
- {
- msg(DBG_INF,"!st_diskok detected - retrying.\n");
- return (0);
- }
- if (st_busy)
- {
- msg(DBG_INF,"st_busy detected - retrying.\n");
- return (0);
- }
- return (1);
-}
-/*==========================================================================*/
-
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * ioctl support
- */
-static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
- u_long arg)
-{
- int i, st;
-
- msg(DBG_IO2,"ioctl(%d, 0x%08lX, 0x%08lX)\n",
- MINOR(inode->i_rdev), cmd, arg);
- if (!inode) return (-EINVAL);
- i=MINOR(inode->i_rdev);
- if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
- {
- msg(DBG_INF, "ioctl: bad device: %04X\n", inode->i_rdev);
- return (-ENXIO); /* no such drive */
- }
- if (d!=i) switch_drive(i);
-
-#if 0
- st=GetStatus();
- if (st<0) return (-EIO);
-
- if (!toc_valid)
- {
- i=DiskInfo();
- if (i<0) return (-EIO); /* error reading TOC */
- }
-#endif
-
- msg(DBG_IO2,"ioctl: device %d, request %04X\n",i,cmd);
- switch (cmd) /* Sun-compatible */
- {
- case DDIOCSDBG: /* DDI Debug */
- if (!suser()) return (-EPERM);
- i=sbpcd_dbg_ioctl(arg,1);
- return (i);
-
- case CDROMPAUSE: /* Pause the drive */
- msg(DBG_IOC,"ioctl: CDROMPAUSE entered.\n");
- /* pause the drive unit when it is currently in PLAY mode, */
- /* or reset the starting and ending locations when in PAUSED mode. */
- /* If applicable, at the next stopping point it reaches */
- /* the drive will discontinue playing. */
- switch (D_S[d].audio_state)
- {
- case audio_playing:
- if (famL_drive) i=cc_ReadSubQ();
- else i=cc_Pause_Resume(1);
- if (i<0) return (-EIO);
- if (famL_drive) i=cc_Pause_Resume(1);
- else i=cc_ReadSubQ();
- if (i<0) return (-EIO);
- D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
- D_S[d].audio_state=audio_pausing;
- return (0);
- case audio_pausing:
- i=cc_Seek(D_S[d].pos_audio_start,1);
- if (i<0) return (-EIO);
- return (0);
- default:
- return (-EINVAL);
- }
-
- case CDROMRESUME: /* resume paused audio play */
- msg(DBG_IOC,"ioctl: CDROMRESUME entered.\n");
- /* resume playing audio tracks when a previous PLAY AUDIO call has */
- /* been paused with a PAUSE command. */
- /* It will resume playing from the location saved in SubQ_run_tot. */
- if (D_S[d].audio_state!=audio_pausing) return -EINVAL;
- if (famL_drive)
- i=cc_PlayAudio(D_S[d].pos_audio_start,
- D_S[d].pos_audio_end);
- else i=cc_Pause_Resume(3);
- if (i<0) return (-EIO);
- D_S[d].audio_state=audio_playing;
- return (0);
-
- case CDROMPLAYMSF:
- msg(DBG_IOC,"ioctl: CDROMPLAYMSF entered.\n");
- if (D_S[d].audio_state==audio_playing)
- {
- i=cc_Pause_Resume(1);
- if (i<0) return (-EIO);
- i=cc_ReadSubQ();
- if (i<0) return (-EIO);
- D_S[d].pos_audio_start=D_S[d].SubQ_run_tot;
- i=cc_Seek(D_S[d].pos_audio_start,1);
- }
- st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_msf));
- if (st) return (st);
- memcpy_fromfs(&msf, (void *) arg, sizeof(struct cdrom_msf));
- /* values come as msf-bin */
- D_S[d].pos_audio_start = (msf.cdmsf_min0<<16) |
- (msf.cdmsf_sec0<<8) |
- msf.cdmsf_frame0;
- D_S[d].pos_audio_end = (msf.cdmsf_min1<<16) |
- (msf.cdmsf_sec1<<8) |
- msf.cdmsf_frame1;
- msg(DBG_IOX,"ioctl: CDROMPLAYMSF %08X %08X\n",
- D_S[d].pos_audio_start,D_S[d].pos_audio_end);
- i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
- msg(DBG_IOC,"ioctl: cc_PlayAudio returns %d\n",i);
-#if 0
- if (i<0) return (-EIO);
-#endif 0
- D_S[d].audio_state=audio_playing;
- return (0);
-
- case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
- msg(DBG_IOC,"ioctl: CDROMPLAYTRKIND entered.\n");
- if (D_S[d].audio_state==audio_playing)
- {
- msg(DBG_IOX,"CDROMPLAYTRKIND: already audio_playing.\n");
- return (0);
- return (-EINVAL);
- }
- st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti));
- if (st<0)
- {
- msg(DBG_IOX,"CDROMPLAYTRKIND: verify_area error.\n");
- return (st);
- }
- memcpy_fromfs(&ti,(void *) arg,sizeof(struct cdrom_ti));
- msg(DBG_IOX,"ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
- ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1);
- if (ti.cdti_trk0<D_S[d].n_first_track) return (-EINVAL);
- if (ti.cdti_trk0>D_S[d].n_last_track) return (-EINVAL);
- if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
- if (ti.cdti_trk1>D_S[d].n_last_track) ti.cdti_trk1=D_S[d].n_last_track;
- D_S[d].pos_audio_start=D_S[d].TocBuffer[ti.cdti_trk0].address;
- D_S[d].pos_audio_end=D_S[d].TocBuffer[ti.cdti_trk1+1].address;
- i=cc_PlayAudio(D_S[d].pos_audio_start,D_S[d].pos_audio_end);
-#if 0
- if (i<0) return (-EIO);
-#endif 0
- D_S[d].audio_state=audio_playing;
- return (0);
-
- case CDROMREADTOCHDR: /* Read the table of contents header */
- msg(DBG_IOC,"ioctl: CDROMREADTOCHDR entered.\n");
- tochdr.cdth_trk0=D_S[d].n_first_track;
- tochdr.cdth_trk1=D_S[d].n_last_track;
- st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_tochdr));
- if (st) return (st);
- memcpy_tofs((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
- return (0);
-
- case CDROMREADTOCENTRY: /* Read an entry in the table of contents */
- msg(DBG_IOC,"ioctl: CDROMREADTOCENTRY entered.\n");
- st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_tocentry));
- if (st) return (st);
- memcpy_fromfs(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
- i=tocentry.cdte_track;
- if (i==CDROM_LEADOUT) i=D_S[d].n_last_track+1;
- else if (i<D_S[d].n_first_track||i>D_S[d].n_last_track) return (-EINVAL);
- tocentry.cdte_adr=D_S[d].TocBuffer[i].ctl_adr&0x0F;
- tocentry.cdte_ctrl=(D_S[d].TocBuffer[i].ctl_adr>>4)&0x0F;
- tocentry.cdte_datamode=D_S[d].TocBuffer[i].format;
- if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */
- {
- tocentry.cdte_addr.msf.minute=(D_S[d].TocBuffer[i].address>>16)&0x00FF;
- tocentry.cdte_addr.msf.second=(D_S[d].TocBuffer[i].address>>8)&0x00FF;
- tocentry.cdte_addr.msf.frame=D_S[d].TocBuffer[i].address&0x00FF;
- }
- else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
- tocentry.cdte_addr.lba=msf2blk(D_S[d].TocBuffer[i].address);
- else return (-EINVAL);
- st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry));
- if (st) return (st);
- memcpy_tofs((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
- return (0);
-
- case CDROMRESET: /* hard reset the drive */
- msg(DBG_IOC,"ioctl: CDROMRESET entered.\n");
- i=DriveReset();
- D_S[d].audio_state=0;
- return (i);
-
- case CDROMSTOP: /* Spin down the drive */
- msg(DBG_IOC,"ioctl: CDROMSTOP entered.\n");
- i=cc_Pause_Resume(1);
- D_S[d].audio_state=0;
- return (i);
-
- case CDROMSTART: /* Spin up the drive */
- msg(DBG_IOC,"ioctl: CDROMSTART entered.\n");
- cc_SpinUp();
- D_S[d].audio_state=0;
- return (0);
-
- case CDROMEJECT:
- msg(DBG_IOC,"ioctl: CDROMEJECT entered.\n");
- if (fam0_drive) return (0);
- i=UnLockDoor();
- D_S[d].open_count=-9; /* to get it locked next time again */
- i=cc_SpinDown();
- msg(DBG_IOX,"ioctl: cc_SpinDown returned %d.\n", i);
- msg(DBG_TEA,"ioctl: cc_SpinDown returned %d.\n", i);
- if (i<0) return (-EIO);
- D_S[d].CD_changed=0xFF;
- D_S[d].diskstate_flags=0;
- D_S[d].audio_state=0;
- return (0);
-
- case CDROMEJECT_SW:
- msg(DBG_IOC,"ioctl: CDROMEJECT_SW entered.\n");
- if (fam0_drive) return (0);
- D_S[d].f_eject=arg;
- return (0);
-
- case CDROMVOLCTRL: /* Volume control */
- msg(DBG_IOC,"ioctl: CDROMVOLCTRL entered.\n");
- st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl));
- if (st) return (st);
- memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
- D_S[d].vol_chan0=0;
- D_S[d].vol_ctrl0=volctrl.channel0;
- D_S[d].vol_chan1=1;
- D_S[d].vol_ctrl1=volctrl.channel1;
- i=cc_SetVolume();
- return (0);
-
- case CDROMVOLREAD: /* read Volume settings from drive */
- msg(DBG_IOC,"ioctl: CDROMVOLREAD entered.\n");
- st=verify_area(VERIFY_WRITE,(void *)arg,sizeof(volctrl));
- if (st) return (st);
- st=cc_GetVolume();
- if (st<0) return (st);
- volctrl.channel0=D_S[d].vol_ctrl0;
- volctrl.channel1=D_S[d].vol_ctrl1;
- volctrl.channel2=0;
- volctrl.channel2=0;
- memcpy_tofs((void *)arg,&volctrl,sizeof(volctrl));
- return (0);
-
- case CDROMSUBCHNL: /* Get subchannel info */
- msg(DBG_IOS,"ioctl: CDROMSUBCHNL entered.\n");
- if ((st_spinning)||(!subq_valid)) { i=cc_ReadSubQ();
- if (i<0) return (-EIO);
- }
- st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
- if (st) return (st);
- memcpy_fromfs(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
- switch (D_S[d].audio_state)
- {
- case audio_playing:
- SC.cdsc_audiostatus=CDROM_AUDIO_PLAY;
- break;
- case audio_pausing:
- SC.cdsc_audiostatus=CDROM_AUDIO_PAUSED;
- break;
- default:
- SC.cdsc_audiostatus=CDROM_AUDIO_NO_STATUS;
- break;
- }
- SC.cdsc_adr=D_S[d].SubQ_ctl_adr;
- SC.cdsc_ctrl=D_S[d].SubQ_ctl_adr>>4;
- SC.cdsc_trk=bcd2bin(D_S[d].SubQ_trk);
- SC.cdsc_ind=bcd2bin(D_S[d].SubQ_pnt_idx);
- if (SC.cdsc_format==CDROM_LBA)
- {
- SC.cdsc_absaddr.lba=msf2blk(D_S[d].SubQ_run_tot);
- SC.cdsc_reladdr.lba=msf2blk(D_S[d].SubQ_run_trk);
- }
- else /* not only if (SC.cdsc_format==CDROM_MSF) */
- {
- SC.cdsc_absaddr.msf.minute=(D_S[d].SubQ_run_tot>>16)&0x00FF;
- SC.cdsc_absaddr.msf.second=(D_S[d].SubQ_run_tot>>8)&0x00FF;
- SC.cdsc_absaddr.msf.frame=D_S[d].SubQ_run_tot&0x00FF;
- SC.cdsc_reladdr.msf.minute=(D_S[d].SubQ_run_trk>>16)&0x00FF;
- SC.cdsc_reladdr.msf.second=(D_S[d].SubQ_run_trk>>8)&0x00FF;
- SC.cdsc_reladdr.msf.frame=D_S[d].SubQ_run_trk&0x00FF;
- }
- memcpy_tofs((void *) arg, &SC, sizeof(struct cdrom_subchnl));
- msg(DBG_IOS,"CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n",
- SC.cdsc_format,SC.cdsc_audiostatus,
- SC.cdsc_adr,SC.cdsc_ctrl,
- SC.cdsc_trk,SC.cdsc_ind,
- SC.cdsc_absaddr,SC.cdsc_reladdr);
- return (0);
-
- case CDROMREADMODE1:
- msg(DBG_IOC,"ioctl: CDROMREADMODE1 requested.\n");
- cc_ModeSelect(CD_FRAMESIZE);
- cc_ModeSense();
- D_S[d].mode=READ_M1;
- return (0);
-
- case CDROMREADMODE2: /* not usable at the moment */
- msg(DBG_IOC,"ioctl: CDROMREADMODE2 requested.\n");
- cc_ModeSelect(CD_FRAMESIZE_XA);
- cc_ModeSense();
- D_S[d].mode=READ_M2;
- return (0);
-
- case CDROMAUDIOBUFSIZ: /* configure the audio buffer size */
- msg(DBG_IOC,"ioctl: CDROMAUDIOBUFSIZ entered.\n");
-#ifdef MODULE
- if (D_S[d].sbp_audsiz>0)
- vfree(D_S[d].aud_buf);
-#endif MODULE
- D_S[d].aud_buf=NULL;
- D_S[d].sbp_audsiz=arg;
- if (D_S[d].sbp_audsiz>0)
- {
- D_S[d].aud_buf=(u_char *) vmalloc(D_S[d].sbp_audsiz*CD_FRAMESIZE_RAW);
- if (D_S[d].aud_buf==NULL)
- {
- msg(DBG_INF,"audio buffer (%d frames) not available.\n",D_S[d].sbp_audsiz);
- D_S[d].sbp_audsiz=0;
- }
- else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[d].sbp_audsiz);
- }
- return (D_S[d].sbp_audsiz);
-
- case CDROMREADAUDIO:
- { /* start of CDROMREADAUDIO */
- int i=0, j=0, frame, block;
- u_int try=0;
- u_long timeout;
- u_char *p;
- u_int data_tries = 0;
- u_int data_waits = 0;
- u_int data_retrying = 0;
- int status_tries;
- int error_flag;
-
- msg(DBG_IOC,"ioctl: CDROMREADAUDIO entered.\n");
- if (fam0_drive) return (-EINVAL);
- if (famL_drive) return (-EINVAL);
- if (fam2_drive) return (-EINVAL);
- if (famT_drive) return (-EINVAL);
- if (D_S[d].aud_buf==NULL) return (-EINVAL);
- i=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_read_audio));
- if (i) return (i);
- memcpy_fromfs(&read_audio, (void *) arg, sizeof(struct cdrom_read_audio));
- if (read_audio.nframes>D_S[d].sbp_audsiz) return (-EINVAL);
- i=verify_area(VERIFY_WRITE, read_audio.buf,
- read_audio.nframes*CD_FRAMESIZE_RAW);
- if (i) return (i);
-
- if (read_audio.addr_format==CDROM_MSF) /* MSF-bin specification of where to start */
- block=msf2lba(&read_audio.addr.msf.minute);
- else if (read_audio.addr_format==CDROM_LBA) /* lba specification of where to start */
- block=read_audio.addr.lba;
- else return (-EINVAL);
- i=cc_SetSpeed(speed_150,0,0);
- if (i) msg(DBG_AUD,"read_audio: SetSpeed error %d\n", i);
- msg(DBG_AUD,"read_audio: lba: %d, msf: %06X\n",
- block, blk2msf(block));
- msg(DBG_AUD,"read_audio: before cc_ReadStatus.\n");
- while (busy_data) sbp_sleep(10); /* wait a bit */
- busy_audio=1;
- error_flag=0;
- for (data_tries=5; data_tries>0; data_tries--)
- {
- msg(DBG_AUD,"data_tries=%d ...\n", data_tries);
- D_S[d].mode=READ_AU;
- cc_ModeSelect(CD_FRAMESIZE_RAW);
- cc_ModeSense();
- for (status_tries=3; status_tries > 0; status_tries--)
- {
- flags_cmd_out |= f_respo3;
- cc_ReadStatus();
- if (sbp_status() != 0) break;
- sbp_sleep(1); /* wait a bit, try again */
- }
- if (status_tries == 0)
- {
- msg(DBG_AUD,"read_audio: sbp_status: failed after 3 tries.\n");
- continue;
- }
- msg(DBG_AUD,"read_audio: sbp_status: ok.\n");
-
- flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
- if (fam0L_drive)
- {
- flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
- cmd_type=READ_M2;
- drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
- drvcmd[1]=(block>>16)&0x000000ff;
- drvcmd[2]=(block>>8)&0x000000ff;
- drvcmd[3]=block&0x000000ff;
- drvcmd[4]=0;
- drvcmd[5]=read_audio.nframes; /* # of frames */
- drvcmd[6]=0;
- }
- else if (fam1_drive)
- {
- drvcmd[0]=CMD1_READ; /* "read frames", new drives */
- lba2msf(block,&drvcmd[1]); /* msf-bin format required */
- drvcmd[4]=0;
- drvcmd[5]=0;
- drvcmd[6]=read_audio.nframes; /* # of frames */
- }
- else if (fam2_drive) /* CD200: not tested yet */
- {
- }
- else if (famT_drive) /* CD-55A: not tested yet */
- {
- }
- msg(DBG_AUD,"read_audio: before giving \"read\" command.\n");
- for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
- sbp_sleep(0);
- msg(DBG_AUD,"read_audio: after giving \"read\" command.\n");
- for (frame=1;frame<2 && !error_flag; frame++)
- {
- try=maxtim_data;
- for (timeout=jiffies+900; ; )
- {
- for ( ; try!=0;try--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) break;
- if (!(j&s_not_result_ready)) break;
- if (fam0L_drive) if (j&s_attention) break;
- }
- if (try != 0 || timeout <= jiffies) break;
- if (data_retrying == 0) data_waits++;
- data_retrying = 1;
- sbp_sleep(1);
- try = 1;
- }
- if (try==0)
- {
- msg(DBG_INF,"read_audio: sbp_data: CDi_status timeout.\n");
- error_flag++;
- break;
- }
- msg(DBG_AUD,"read_audio: sbp_data: CDi_status ok.\n");
- if (j&s_not_data_ready)
- {
- msg(DBG_INF, "read_audio: sbp_data: DATA_READY timeout.\n");
- error_flag++;
- break;
- }
- msg(DBG_AUD,"read_audio: before reading data.\n");
- error_flag=0;
- p = D_S[d].aud_buf;
- if (sbpro_type==1) OUT(CDo_sel_i_d,1);
- insb(CDi_data, p, read_audio.nframes*CD_FRAMESIZE_RAW);
- if (sbpro_type==1) OUT(CDo_sel_i_d,0);
- data_retrying = 0;
- }
- msg(DBG_AUD,"read_audio: after reading data.\n");
- if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
- {
- msg(DBG_AUD,"read_audio: read aborted by drive\n");
-#if 0000
- i=cc_DriveReset(); /* ugly fix to prevent a hang */
-#else
- i=cc_ReadError();
-#endif 0000
- continue;
- }
- if (fam0L_drive)
- {
- i=maxtim_data;
- for (timeout=jiffies+900; timeout > jiffies; timeout--)
- {
- for ( ;i!=0;i--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) break;
- if (!(j&s_not_result_ready)) break;
- if (j&s_attention) break;
- }
- if (i != 0 || timeout <= jiffies) break;
- sbp_sleep(0);
- i = 1;
- }
- if (i==0) msg(DBG_AUD,"read_audio: STATUS TIMEOUT AFTER READ");
- if (!(j&s_attention))
- {
- msg(DBG_AUD,"read_audio: sbp_data: timeout waiting DRV_ATTN - retrying\n");
- i=cc_DriveReset(); /* ugly fix to prevent a hang */
- continue;
- }
- }
- do
- {
- if (fam0L_drive) cc_ReadStatus();
- i=ResponseStatus(); /* builds status_bits, returns orig. status (old) or faked p_success (new) */
- if (i<0) { msg(DBG_AUD,
- "read_audio: cc_ReadStatus error after read: %02X\n",
- D_S[d].status_bits);
- continue; /* FIXME */
- }
- }
- while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
- if (st_check)
- {
- i=cc_ReadError();
- msg(DBG_AUD,"read_audio: cc_ReadError was necessary after read: %02X\n",i);
- continue;
- }
- memcpy_tofs((u_char *) read_audio.buf,
- (u_char *) D_S[d].aud_buf,
- read_audio.nframes*CD_FRAMESIZE_RAW);
- msg(DBG_AUD,"read_audio: memcpy_tofs done.\n");
- break;
- }
- cc_ModeSelect(CD_FRAMESIZE);
- cc_ModeSense();
- D_S[d].mode=READ_M1;
- busy_audio=0;
- if (data_tries == 0)
- {
- msg(DBG_AUD,"read_audio: failed after 5 tries.\n");
- return (-8);
- }
- msg(DBG_AUD,"read_audio: successful return.\n");
- return (0);
- } /* end of CDROMREADAUDIO */
-
- case CDROMMULTISESSION: /* tell start-of-last-session */
- msg(DBG_IOC,"ioctl: CDROMMULTISESSION entered.\n");
- st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_multisession));
- if (st) return (st);
- memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
- if (ms_info.addr_format==CDROM_MSF) /* MSF-bin requested */
- lba2msf(D_S[d].lba_multi,&ms_info.addr.msf.minute);
- else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
- ms_info.addr.lba=D_S[d].lba_multi;
- else return (-EINVAL);
- if (D_S[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */
- else ms_info.xa_flag=0; /* invalid redirection address */
- st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession));
- if (st) return (st);
- memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
- msg(DBG_MUL,"ioctl: CDROMMULTISESSION done (%d, %08X).\n",
- ms_info.xa_flag, ms_info.addr.lba);
- return (0);
-
- case BLKRASET:
- if(!suser()) return -EACCES;
- if(!inode->i_rdev) return -EINVAL;
- if(arg > 0xff) return -EINVAL;
- read_ahead[MAJOR(inode->i_rdev)] = arg;
- return (0);
-
- default:
- msg(DBG_IOC,"ioctl: unknown function request %04X\n", cmd);
- return (-EINVAL);
- } /* end switch(cmd) */
-}
-/*==========================================================================*/
-/*
- * Take care of the different block sizes between cdrom and Linux.
- */
-static void sbp_transfer(void)
-{
- long offs;
-
- while ( (CURRENT->nr_sectors > 0) &&
- (CURRENT->sector/4 >= D_S[d].sbp_first_frame) &&
- (CURRENT->sector/4 <= D_S[d].sbp_last_frame) )
- {
- offs = (CURRENT->sector - D_S[d].sbp_first_frame * 4) * 512;
- memcpy(CURRENT->buffer, D_S[d].sbp_buf + offs, 512);
- CURRENT->nr_sectors--;
- CURRENT->sector++;
- CURRENT->buffer += 512;
- }
-}
-/*==========================================================================*/
-/*
- * I/O request routine, called from Linux kernel.
- */
-static void DO_SBPCD_REQUEST(void)
-{
- u_int block;
- u_int nsect;
- int i, status_tries, data_tries;
-
- request_loop:
- INIT_REQUEST;
- sti();
-
- if ((CURRENT==NULL)||(CURRENT->dev<0)) goto err_done;
- if (CURRENT -> sector == -1) goto err_done;
- if (CURRENT->cmd != READ)
- {
- msg(DBG_INF, "bad cmd %d\n", CURRENT->cmd);
- goto err_done;
- }
- i = MINOR(CURRENT->dev);
- if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
- {
- msg(DBG_INF, "do_request: bad device: %04X\n", CURRENT->dev);
- goto err_done;
- }
- while (busy_audio) sbp_sleep(100); /* wait a bit */
- busy_data=1;
-
- if (D_S[i].audio_state==audio_playing) goto err_done;
- if (d!=i) switch_drive(i);
-
- block = CURRENT->sector; /* always numbered as 512-byte-pieces */
- nsect = CURRENT->nr_sectors; /* always counted as 512-byte-pieces */
-
- msg(DBG_BSZ,"read sector %d (%d sectors)\n", block, nsect);
-#if 0
- msg(DBG_MUL,"read LBA %d\n", block/4);
-#endif
-
- sbp_transfer();
- /* if we satisfied the request from the buffer, we're done. */
- if (CURRENT->nr_sectors == 0)
- {
- end_request(1);
- goto request_loop;
- }
-
-#if FUTURE
- i=prepare(0,0); /* at moment not really a hassle check, but ... */
- if (i!=0)
- msg(DBG_INF,"\"prepare\" tells error %d -- ignored\n", i);
-#endif FUTURE
-
- if (!st_spinning) cc_SpinUp();
-
- for (data_tries=n_retries; data_tries > 0; data_tries--)
- {
- for (status_tries=3; status_tries > 0; status_tries--)
- {
- flags_cmd_out |= f_respo3;
- cc_ReadStatus();
- if (sbp_status() != 0) break;
- if (st_check) cc_ReadError();
- sbp_sleep(1); /* wait a bit, try again */
- }
- if (status_tries == 0)
- {
- msg(DBG_INF,"sbp_status: failed after 3 tries\n");
- break;
- }
-
- sbp_read_cmd();
- sbp_sleep(0);
- if (sbp_data() != 0)
- {
- end_request(1);
- goto request_loop;
- }
- }
-
- err_done:
- busy_data=0;
- end_request(0);
- sbp_sleep(0); /* wait a bit, try again */
- goto request_loop;
-}
-/*==========================================================================*/
-/*
- * build and send the READ command.
- */
-static void sbp_read_cmd(void)
-{
-#undef OLD
-
- int i;
- int block;
-
- D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1; /* purge buffer */
- D_S[d].sbp_current = 0;
- block=CURRENT->sector/4;
- if (block+D_S[d].sbp_bufsiz <= D_S[d].CDsize_frm)
- D_S[d].sbp_read_frames = D_S[d].sbp_bufsiz;
- else
- {
- D_S[d].sbp_read_frames=D_S[d].CDsize_frm-block;
- /* avoid reading past end of data */
- if (D_S[d].sbp_read_frames < 1)
- {
- msg(DBG_INF,"requested frame %d, CD size %d ???\n",
- block, D_S[d].CDsize_frm);
- D_S[d].sbp_read_frames=1;
- }
- }
-
- flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus | f_obey_p_check;
- clr_cmdbuf();
- if (fam0L_drive)
- {
- flags_cmd_out |= f_lopsta | f_getsta | f_bit1;
- if (D_S[d].xa_byte==0x20)
- {
- cmd_type=READ_M2;
- drvcmd[0]=CMD0_READ_XA; /* "read XA frames", old drives */
- drvcmd[1]=(block>>16)&0x000000ff;
- drvcmd[2]=(block>>8)&0x000000ff;
- drvcmd[3]=block&0x000000ff;
- drvcmd[5]=D_S[d].sbp_read_frames;
- }
- else
- {
- drvcmd[0]=CMD0_READ; /* "read frames", old drives */
- if (D_S[d].drv_type>=drv_201)
- {
- lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
- bin2bcdx(&drvcmd[1]);
- bin2bcdx(&drvcmd[2]);
- bin2bcdx(&drvcmd[3]);
- }
- else
- {
- drvcmd[1]=(block>>16)&0x000000ff;
- drvcmd[2]=(block>>8)&0x000000ff;
- drvcmd[3]=block&0x000000ff;
- }
- drvcmd[5]=D_S[d].sbp_read_frames;
- drvcmd[6]=(D_S[d].drv_type<drv_201)?0:2; /* flag "lba or msf-bcd format" */
- }
- }
- else if (fam1_drive)
- {
- drvcmd[0]=CMD1_READ;
- lba2msf(block,&drvcmd[1]); /* msf-bin format required */
- drvcmd[6]=D_S[d].sbp_read_frames;
- }
- else if (fam2_drive)
- {
- drvcmd[0]=CMD2_READ;
- lba2msf(block,&drvcmd[1]); /* msf-bin format required */
- drvcmd[5]=D_S[d].sbp_read_frames;
- drvcmd[6]=0x02;
- }
- else if (famT_drive)
- {
- drvcmd[0]=CMDT_READ;
- drvcmd[2]=(block>>24)&0x0ff;
- drvcmd[3]=(block>>16)&0x0ff;
- drvcmd[4]=(block>>8)&0x0ff;
- drvcmd[5]=block&0x0ff;
- drvcmd[7]=(D_S[d].sbp_read_frames>>8)&0x0ff;
- drvcmd[8]=D_S[d].sbp_read_frames&0x0ff;
- }
-#ifdef OLD
- SBPCD_CLI;
- for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
- if (famT_drive) for (i=7;i<10;i++) OUT(CDo_command,drvcmd[i]);
- SBPCD_STI;
-#else
- flags_cmd_out=f_putcmd;
- response_count=0;
- i=cmd_out(); /* immediate return here - read data "ourselves" */
- if (i<0) msg(DBG_INF,"error giving READ command: %0d\n", i);
-#endif OLD
- return;
-}
-/*==========================================================================*/
-/*
- * Check the completion of the read-data command. On success, read
- * the D_S[d].sbp_bufsiz * 2048 bytes of data from the disk into buffer.
- */
-static int sbp_data(void)
-{
- int i=0, j=0, l, frame;
- u_int try=0;
- u_long timeout;
- u_char *p;
- u_int data_tries = 0;
- u_int data_waits = 0;
- u_int data_retrying = 0;
- int error_flag;
- int xa_count;
- int max_latency;
- int success;
- int wait;
- int duration;
-
- error_flag=0;
- success=0;
-#if LONG_TIMING
- max_latency=900;
-#else
- if (D_S[d].f_multisession) max_latency=900;
- else if (fam0L_drive) max_latency=300;
- else if (famT_drive) max_latency=300;
- else max_latency=100;
-#endif
- msg(DBG_TE2,"beginning to READ\n");
- duration=jiffies;
- for (frame=0;frame<D_S[d].sbp_read_frames&&!error_flag; frame++)
- {
- SBPCD_CLI;
-
- del_timer(&data_timer);
- data_timer.expires=max_latency;
- timed_out_data=0;
- add_timer(&data_timer);
- while (!timed_out_data)
- {
- if (D_S[d].f_multisession) try=maxtim_data*4;
- else try=maxtim_data;
- msg(DBG_000,"sbp_data: CDi_status loop: try=%d.\n",try);
- for ( ; try!=0;try--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) break;;
- if (!(j&s_not_result_ready)) break;
- if (fam0L_drive) if (j&s_attention) break;
- }
- if (!(j&s_not_data_ready)) goto data_ready;
- if (try==0)
- {
- if (data_retrying == 0) data_waits++;
- data_retrying = 1;
- msg(DBG_000,"sbp_data: CDi_status loop: sleeping.\n");
- sbp_sleep(1);
- try = 1;
- }
- }
- msg(DBG_INF,"sbp_data: CDi_status loop expired.\n");
- data_ready:
- del_timer(&data_timer);
-
- if (timed_out_data)
- {
- msg(DBG_INF,"sbp_data: CDi_status timeout (timed_out_data) (%02X).\n", j);
- error_flag++;
- break;
- }
- if (try==0)
- {
- msg(DBG_INF,"sbp_data: CDi_status timeout (try=0) (%02X).\n", j);
- error_flag++;
- break;
- }
- if (!(j&s_not_result_ready))
- {
- msg(DBG_INF, "sbp_data: RESULT_READY where DATA_READY awaited (%02X).\n", j);
- response_count=20;
- j=ResponseInfo();
- j=inb(CDi_status);
- }
- if (j&s_not_data_ready)
- {
- if ((D_S[d].ored_ctl_adr&0x40)==0)
- msg(DBG_INF, "CD contains no data tracks.\n");
- else msg(DBG_INF, "sbp_data: DATA_READY timeout (%02X).\n", j);
- error_flag++;
- break;
- }
- SBPCD_STI;
- error_flag=0;
- msg(DBG_000, "sbp_data: beginning to read.\n");
- p = D_S[d].sbp_buf + frame * CD_FRAMESIZE;
- if (sbpro_type==1) OUT(CDo_sel_i_d,1);
- if (cmd_type==READ_M2) insb(CDi_data, xa_head_buf, CD_XA_HEAD);
- insb(CDi_data, p, CD_FRAMESIZE);
- if (cmd_type==READ_M2) insb(CDi_data, xa_tail_buf, CD_XA_TAIL);
- if (famT_drive) msg(DBG_TE2, "================frame read=================.\n");
- D_S[d].sbp_current++;
- if (sbpro_type==1) OUT(CDo_sel_i_d,0);
- if (cmd_type==READ_M2)
- {
- for (xa_count=0;xa_count<CD_XA_HEAD;xa_count++)
- sprintf(&msgbuf[xa_count*3], " %02X", xa_head_buf[xa_count]);
- msgbuf[xa_count*3]=0;
- msg(DBG_XA1,"xa head:%s\n", msgbuf);
- }
- data_retrying = 0;
- data_tries++;
- if (data_tries >= 1000)
- {
- msg(DBG_INF,"sbp_data() statistics: %d waits in %d frames.\n", data_waits, data_tries);
- data_waits = data_tries = 0;
- }
- }
- duration=jiffies-duration;
- msg(DBG_TE2,"time to read %d frames: %d jiffies .\n",frame,duration);
- if (famT_drive)
- {
- wait=8;
- do
- {
- sbp_sleep(1);
- OUT(CDo_sel_i_d,0);
- i=inb(CDi_status);
- if (!(i&s_not_data_ready))
- {
- OUT(CDo_sel_i_d,1);
- j=0;
- do
- {
- i=inb(CDi_data);
- j++;
- i=inb(CDi_status);
- }
- while (!(i&s_not_data_ready));
- msg(DBG_TEA, "=============too much data (%d bytes)=================.\n", j);
- }
- if (!(i&s_not_result_ready))
- {
- OUT(CDo_sel_i_d,0);
- l=0;
- do
- {
- infobuf[l++]=inb(CDi_info);
- i=inb(CDi_status);
- }
- while (!(i&s_not_result_ready));
- if (infobuf[0]==0x00) success=1;
-#if 1
- for (j=0;j<l;j++) sprintf(&msgbuf[j*3], " %02X", infobuf[j]);
- msgbuf[j*3]=0;
- msg(DBG_TE2,"sbp_data info response:%s\n", msgbuf);
-#endif
- if (infobuf[0]==0x02)
- {
- error_flag++;
- do
- {
- ++recursion;
- if (recursion>1) msg(DBG_TEA,"cmd_out_T READ_ERR recursion (sbp_data): %d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",recursion);
- else msg(DBG_TEA,"sbp_data: CMDT_READ_ERR necessary.\n");
- clr_cmdbuf();
- drvcmd[0]=CMDT_READ_ERR;
- j=cmd_out_T(); /* !!! recursive here !!! */
- --recursion;
- sbp_sleep(1);
- }
- while (j<0);
- D_S[d].error_state=infobuf[2];
- D_S[d].b3=infobuf[3];
- D_S[d].b4=infobuf[4];
- }
- break;
- }
- else
- {
-#if 0
- msg(DBG_TEA, "============= waiting for result=================.\n");
- sbp_sleep(1);
-#endif
- }
- }
- while (wait--);
- }
-
- if (error_flag) /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
- {
- msg(DBG_TEA, "================error flag: %d=================.\n", error_flag);
- msg(DBG_INF,"sbp_data: read aborted by drive.\n");
-#if 1
- i=cc_DriveReset(); /* ugly fix to prevent a hang */
-#else
- i=cc_ReadError();
-#endif
- return (0);
- }
-
- if (fam0L_drive)
- {
- SBPCD_CLI;
- i=maxtim_data;
- for (timeout=jiffies+100; timeout > jiffies; timeout--)
- {
- for ( ;i!=0;i--)
- {
- j=inb(CDi_status);
- if (!(j&s_not_data_ready)) break;
- if (!(j&s_not_result_ready)) break;
- if (j&s_attention) break;
- }
- if (i != 0 || timeout <= jiffies) break;
- sbp_sleep(0);
- i = 1;
- }
- if (i==0) msg(DBG_INF,"status timeout after READ.\n");
- if (!(j&s_attention))
- {
- msg(DBG_INF,"sbp_data: timeout waiting DRV_ATTN - retrying.\n");
- i=cc_DriveReset(); /* ugly fix to prevent a hang */
- SBPCD_STI;
- return (0);
- }
- SBPCD_STI;
- }
-
-#if 0
- if (!success)
-#endif 0
- do
- {
- if (fam0L_drive) cc_ReadStatus();
-#if 1
- if (famT_drive) msg(DBG_TE2, "================before ResponseStatus=================.\n", i);
-#endif 1
- i=ResponseStatus(); /* builds status_bits, returns orig. status (old) or faked p_success (new) */
-#if 1
- if (famT_drive) msg(DBG_TE2, "================ResponseStatus: %d=================.\n", i);
-#endif 1
- if (i<0)
- {
- msg(DBG_INF,"bad cc_ReadStatus after read: %02X\n", D_S[d].status_bits);
- return (0);
- }
- }
- while ((fam0L_drive)&&(!st_check)&&(!(i&p_success)));
- if (st_check)
- {
- i=cc_ReadError();
- msg(DBG_INF,"cc_ReadError was necessary after read: %d\n",i);
- return (0);
- }
- if (fatal_err)
- {
- fatal_err=0;
- D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1; /* purge buffer */
- D_S[d].sbp_current = 0;
- msg(DBG_INF,"sbp_data: fatal_err - retrying.\n");
- return (0);
- }
-
- D_S[d].sbp_first_frame = CURRENT -> sector / 4;
- D_S[d].sbp_last_frame = D_S[d].sbp_first_frame + D_S[d].sbp_read_frames - 1;
- sbp_transfer();
-#if 1
- if (famT_drive) msg(DBG_TE2, "================sbp_transfer() done=================.\n");
-#endif 1
- return (1);
-}
-/*==========================================================================*/
-/*==========================================================================*/
-/*
- * Open the device special file. Check that a disk is in. Read TOC.
- */
-static int sbpcd_open(struct inode *ip, struct file *fp)
-{
- int i;
-
- i = MINOR(ip->i_rdev);
- if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
- {
- msg(DBG_INF, "open: bad device: %04X\n", ip->i_rdev);
- return (-ENXIO); /* no such drive */
- }
- if (fp->f_mode & 2)
- return -EROFS;
-
- switch_drive(i);
-
- i=cc_ReadError();
- flags_cmd_out |= f_respo2;
- cc_ReadStatus(); /* command: give 1-byte status */
- i=ResponseStatus();
- if (famT_drive&&(i<0))
- {
- cc_DriveReset();
- i=ResponseStatus();
- i=ResponseStatus();
- }
- if (i<0)
- {
- msg(DBG_INF,"sbpcd_open: ResponseStatus timed out (%d).\n",i);
- return (-EIO); /* drive doesn't respond */
- }
- if (famT_drive) msg(DBG_TE2,"sbpcd_open: ResponseStatus=%02X\n", i);
- if (!st_door_closed)
- {
- if (famT_drive) msg(DBG_TE2,"sbpcd_open: !st_door_closed.\n");
- cc_CloseTray();
- flags_cmd_out |= f_respo2;
- cc_ReadStatus();
- i=ResponseStatus();
- }
- if (!(famT_drive))
- if (!st_spinning)
- {
- if (famT_drive) msg(DBG_TE2,"sbpcd_open: !st_spinning.\n");
- cc_SpinUp();
- flags_cmd_out |= f_respo2;
- cc_ReadStatus();
- i=ResponseStatus();
- }
- if (famT_drive) msg(DBG_TE2,"sbpcd_open: status %02X\n", D_S[d].status_bits);
- if (!st_door_closed||!st_caddy_in)
- {
- msg(DBG_INF, "sbpcd_open: no disk in drive.\n");
- D_S[d].open_count=0;
-#if JUKEBOX
- if (!fam0_drive)
- {
- i=UnLockDoor();
- cc_SpinDown(); /* eject tray */
- }
-#endif
- return (-ENXIO);
- }
- /*
- * try to keep an "open" counter here and lock the door if 0->1.
- */
- MOD_INC_USE_COUNT;
- msg(DBG_LCK,"open_count: %d -> %d\n",
- D_S[d].open_count,D_S[d].open_count+1);
- if (++D_S[d].open_count<=1)
- {
- i=LockDoor();
- D_S[d].open_count=1;
- if (famT_drive) msg(DBG_TE2,"sbpcd_open: before i=DiskInfo();.\n");
- i=DiskInfo();
- if (famT_drive) msg(DBG_TE2,"sbpcd_open: after i=DiskInfo();.\n");
- if ((D_S[d].ored_ctl_adr&0x40)==0)
- msg(DBG_INF,"CD contains no data tracks.\n");
- }
- if (!st_spinning) cc_SpinUp();
- return (0);
-}
-/*==========================================================================*/
-/*
- * On close, we flush all sbp blocks from the buffer cache.
- */
-static void sbpcd_release(struct inode * ip, struct file * file)
-{
- int i;
-
- i = MINOR(ip->i_rdev);
- if ((i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1))
- {
- msg(DBG_INF, "release: bad device: %04X\n", ip->i_rdev);
- return;
- }
- switch_drive(i);
-
- D_S[d].sbp_first_frame=D_S[d].sbp_last_frame=-1;
- sync_dev(ip->i_rdev); /* nonsense if read only device? */
- invalidate_buffers(ip->i_rdev);
-
- /*
- * try to keep an "open" counter here and unlock the door if 1->0.
- */
- MOD_DEC_USE_COUNT;
- msg(DBG_LCK,"open_count: %d -> %d\n",
- D_S[d].open_count,D_S[d].open_count-1);
- if (D_S[d].open_count>-2) /* CDROMEJECT may have been done */
- {
- if (--D_S[d].open_count<=0)
- {
- i=UnLockDoor();
- if (D_S[d].audio_state!=audio_playing)
- if (D_S[d].f_eject) cc_SpinDown();
- D_S[d].diskstate_flags &= ~cd_size_bit;
- D_S[d].open_count=0;
- }
- }
-}
-/*==========================================================================*/
-/*
- *
- */
-static struct file_operations sbpcd_fops =
-{
- NULL, /* lseek - default */
- block_read, /* read - general block-dev read */
- block_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- sbpcd_ioctl, /* ioctl */
- NULL, /* mmap */
- sbpcd_open, /* open */
- sbpcd_release, /* release */
- NULL, /* fsync */
- NULL, /* fasync */
- sbpcd_chk_disk_change, /* media_change */
- NULL /* revalidate */
-};
-/*==========================================================================*/
-/*
- * accept "kernel command line" parameters
- * (suggested by Peter MacDonald with SLS 1.03)
- *
- * This is only implemented for the first controller. Should be enough to
- * allow installing with a "strange" distribution kernel.
- *
- * use: tell LILO:
- * sbpcd=0x230,SoundBlaster
- * or
- * sbpcd=0x300,LaserMate
- * or
- * sbpcd=0x330,SPEA
- *
- * (upper/lower case sensitive here!!!).
- *
- * the address value has to be the TRUE CDROM PORT ADDRESS -
- * not the soundcard base address.
- *
- */
-#if (SBPCD_ISSUE-1)
-static
-#endif
-void sbpcd_setup(char *s, int *p)
-{
- setup_done++;
- msg(DBG_INI,"sbpcd_setup called with %04X,%s\n",p[1], s);
- sbpro_type=0;
- if (!strcmp(s,str_sb)) sbpro_type=1;
- else if (!strcmp(s,str_sb_l)) sbpro_type=1;
- else if (!strcmp(s,str_sp)) sbpro_type=2;
- else if (!strcmp(s,str_sp_l)) sbpro_type=2;
- if (p[0]>0) sbpcd_ioaddr=p[1];
-
- CDo_command=sbpcd_ioaddr;
- CDi_info=sbpcd_ioaddr;
- CDi_status=sbpcd_ioaddr+1;
- CDo_sel_i_d=sbpcd_ioaddr+1;
- CDo_reset=sbpcd_ioaddr+2;
- CDo_enable=sbpcd_ioaddr+3;
- if (sbpro_type==1)
- {
- MIXER_addr=sbpcd_ioaddr-0x10+0x04;
- MIXER_data=sbpcd_ioaddr-0x10+0x05;
- CDi_data=sbpcd_ioaddr;
- }
- else CDi_data=sbpcd_ioaddr+2;
-}
-/*==========================================================================*/
-/*
- * Sequoia S-1000 CD-ROM Interface Configuration
- * as used within SPEA Media FX card
- * The SPEA soundcard has to get configured for
- * -> interface type "Matsushita/Panasonic" (not Sony or Mitsumi)
- * -> I/O base address (0x320, 0x330, 0x340, 0x350)
- */
-static int config_spea(void)
-{
- int n_ports=0x10; /* 2:0x00, 8:0x10, 16:0x20, 32:0x30 */
- /* What is n_ports? Number of addresses or base address offset? */
- int irq_number=0; /* 2:0x01, 7:0x03, 12:0x05, 15:0x07, OFF:0x00 */
- int dma_channel=0; /* 0:0x08, 1:0x18, 3:0x38, 5:0x58, 6:0x68, 7:0x78, OFF: 0x00 */
- int dack_polarity=0; /* L:0x00, H:0x80 */
- int drq_polarity=0x40; /* L:0x00, H:0x40 */
-
- int i;
-
-#define SPEA_REG_1 sbpcd_ioaddr+4
-#define SPEA_REG_2 sbpcd_ioaddr+5
-
- OUT(SPEA_REG_1,0xFF);
- i=inb(SPEA_REG_1);
- if (i!=0x0F)
- {
- msg(DBG_SEQ,"no SPEA interface at %04X present.\n", sbpcd_ioaddr);
- return (-1); /* no interface found */
- }
- OUT(SPEA_REG_1,0x04);
- OUT(SPEA_REG_2,0xC0);
-
- OUT(SPEA_REG_1,0x05);
- OUT(SPEA_REG_2,0x10|drq_polarity|dack_polarity);
-
-#if 1
-#define SPEA_PATTERN 0x80
-#else
-#define SPEA_PATTERN 0x00
-#endif
- OUT(SPEA_REG_1,0x06);
- OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
- OUT(SPEA_REG_2,dma_channel|irq_number|SPEA_PATTERN);
-
- OUT(SPEA_REG_1,0x09);
- i=(inb(SPEA_REG_2)&0xCF)|n_ports;
- OUT(SPEA_REG_2,i);
-
- sbpro_type = 0; /* acts like a LaserMate interface now */
- msg(DBG_SEQ,"found SPEA interface at %04X.\n", sbpcd_ioaddr);
- return (0);
-}
-/*==========================================================================*/
-/*
- * Test for presence of drive and initialize it. Called at boot time.
- */
-#ifdef MODULE
-int init_module(void)
-#else
- unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
-#endif MODULE
-{
- int i=0, j=0;
- int addr[2]={1, CDROM_PORT};
- int port_index;
-
- sti();
-
- msg(DBG_INF,"sbpcd.c %s\n", VERSION);
-#ifndef MODULE
-#if DISTRIBUTION
- if (!setup_done)
- {
- msg(DBG_INF,"Looking for Matsushita/Panasonic, CreativeLabs, IBM, Longshine, TEAC CD-ROM drives\n");
- msg(DBG_INF,"\n= = = = = = = = = = W A R N I N G = = = = = = = = = =\n");
- msg(DBG_INF,"Auto-Probing can cause a hang (f.e. touching an ethernet card).\n");
- msg(DBG_INF,"If that happens, you have to reboot and use the\n");
- msg(DBG_INF,"LILO (kernel) command line feature like:\n");
- msg(DBG_INF," LILO boot: ... sbpcd=0x230,SoundBlaster\n");
- msg(DBG_INF,"or like:\n");
- msg(DBG_INF," LILO boot: ... sbpcd=0x300,LaserMate\n");
- msg(DBG_INF,"or like:\n");
- msg(DBG_INF," LILO boot: ... sbpcd=0x330,SPEA\n");
- msg(DBG_INF,"with your REAL address.\n");
- msg(DBG_INF,"= = = = = = = = = = END of WARNING = = = = = = = = = =\n\n");
- }
-#endif DISTRIBUTION
- sbpcd[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
- sbpcd[1]=sbpro_type; /* possibly changed by kernel command line */
-#endif MODULE
-
- for (port_index=0;port_index<NUM_PROBE;port_index+=2)
- {
- addr[1]=sbpcd[port_index];
- if (addr[1]==0) break;
- if (check_region(addr[1],4))
- {
- msg(DBG_INF,"check_region: %03X is not free.\n",addr[1]);
- continue;
- }
- if (sbpcd[port_index+1]==2) type=str_sp;
- else if (sbpcd[port_index+1]==1) type=str_sb;
- else type=str_lm;
- sbpcd_setup(type, addr);
-#if DISTRIBUTION
- msg(DBG_INF,"Scanning 0x%X (%s)...\n", CDo_command, type);
-#endif DISTRIBUTION
- if (sbpcd[port_index+1]==2)
- {
- i=config_spea();
- if (i<0) continue;
- }
-#ifdef PATH_CHECK
- if (check_card(addr[1])) continue;
-#endif PATH_CHECK
- i=check_drives();
- msg(DBG_INI,"check_drives done.\n");
- if (i>=0) break; /* drive found */
- } /* end of cycling through the set of possible I/O port addresses */
-
- if (ndrives==0)
- {
- msg(DBG_INF, "No drive found.\n");
-#ifdef MODULE
- return -EIO;
-#else
- goto init_done;
-#endif MODULE
- }
-
- if (port_index>0)
- msg(DBG_INF, "You should configure sbpcd.h for your hardware.\n");
- check_datarate();
- msg(DBG_INI,"check_datarate done.\n");
-
-#if 0
- if (!famL_drive)
- {
- OUT(CDo_reset,0);
- sbp_sleep(100);
- }
-#endif 0
-
- for (j=0;j<NR_SBPCD;j++)
- {
- if (D_S[j].drv_id==-1) continue;
- switch_drive(j);
-#if 1
- if (!famL_drive) cc_DriveReset();
-#endif 0
- if (!st_spinning) cc_SpinUp();
- D_S[d].sbp_first_frame = -1; /* First frame in buffer */
- D_S[d].sbp_last_frame = -1; /* Last frame in buffer */
- D_S[d].sbp_read_frames = 0; /* Number of frames being read to buffer */
- D_S[d].sbp_current = 0; /* Frame being currently read */
- D_S[d].CD_changed=1;
- D_S[d].frame_size=CD_FRAMESIZE;
-#if EJECT
- if (!fam0_drive) D_S[d].f_eject=1;
- else D_S[d].f_eject=0;
-#else
- D_S[d].f_eject=0;
-#endif
-
- cc_ReadStatus();
- i=ResponseStatus(); /* returns orig. status or p_busy_new */
- if (famT_drive) i=ResponseStatus(); /* returns orig. status or p_busy_new */
- if (i<0)
- if (i!=-402)
- msg(DBG_INF,"init: ResponseStatus returns %d.\n",i);
- else
- {
- if (st_check)
- {
- i=cc_ReadError();
- msg(DBG_INI,"init: cc_ReadError returns %d\n",i);
- }
- }
- msg(DBG_INI,"init: first GetStatus: %d\n",i);
- msg(DBG_LCS,"init: first GetStatus: error_byte=%d\n",
- D_S[d].error_byte);
- if (D_S[d].error_byte==aud_12)
- {
- timeout=jiffies+200;
- do
- {
- i=GetStatus();
- msg(DBG_INI,"init: second GetStatus: %02X\n",i);
- msg(DBG_LCS,
- "init: second GetStatus: error_byte=%d\n",
- D_S[d].error_byte);
- if (i<0) break;
- if (!st_caddy_in) break;
- }
- while ((!st_diskok)||(timeout<jiffies));
- }
- i=SetSpeed();
- if (i>=0) D_S[d].CD_changed=1;
- }
-
- /*
- * Turn on the CD audio channels.
- * For "compatible" soundcards (with "SBPRO 0" or "SBPRO 2"), the addresses
- * are obtained from SOUND_BASE (see sbpcd.h).
- */
- if ((sbpro_type==1) || (SOUND_BASE))
- {
- if (sbpro_type!=1)
- {
- MIXER_addr=SOUND_BASE+0x04; /* sound card's address register */
- MIXER_data=SOUND_BASE+0x05; /* sound card's data register */
- }
- OUT(MIXER_addr,MIXER_CD_Volume); /* select SB Pro mixer register */
- OUT(MIXER_data,0xCC); /* one nibble per channel, max. value: 0xFF */
- }
-
- if (register_blkdev(MAJOR_NR, major_name, &sbpcd_fops) != 0)
- {
- msg(DBG_INF, "Can't get MAJOR %d for Matsushita CDROM\n", MAJOR_NR);
-#ifdef MODULE
- return -EIO;
-#else
- goto init_done;
-#endif MODULE
- }
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- read_ahead[MAJOR_NR] = SBP_BUFFER_FRAMES * (CD_FRAMESIZE / 512);
-
- request_region(CDo_command,4,major_name);
-
- for (j=0;j<NR_SBPCD;j++)
- {
- if (D_S[j].drv_id==-1) continue;
- switch_drive(j);
- /*
- * allocate memory for the frame buffers
- */
- D_S[j].aud_buf=NULL;
- D_S[j].sbp_audsiz=0;
- D_S[j].sbp_bufsiz=SBP_BUFFER_FRAMES;
- if (D_S[j].drv_type&drv_fam1)
- if (READ_AUDIO>0) D_S[j].sbp_audsiz=READ_AUDIO;
-#ifdef MODULE
- D_S[j].sbp_buf=(u_char *) vmalloc(D_S[j].sbp_bufsiz*CD_FRAMESIZE);
- if (D_S[j].sbp_buf==NULL)
- {
- msg(DBG_INF,"data buffer (%d frames) not available.\n",D_S[j].sbp_bufsiz);
- return -EIO;
- }
- msg(DBG_INF,"data buffer size: %d frames.\n",SBP_BUFFER_FRAMES);
- if (D_S[j].sbp_audsiz>0)
- {
- D_S[j].aud_buf=(u_char *) vmalloc(D_S[j].sbp_audsiz*CD_FRAMESIZE_RAW);
- if (D_S[j].aud_buf==NULL) msg(DBG_INF,"audio buffer (%d frames) not available.\n",D_S[j].sbp_audsiz);
- else msg(DBG_INF,"audio buffer size: %d frames.\n",D_S[j].sbp_audsiz);
- }
-#else
- D_S[j].sbp_buf=(u_char *)mem_start;
- mem_start += D_S[j].sbp_bufsiz*CD_FRAMESIZE;
- if (D_S[j].sbp_audsiz>0)
- {
- D_S[j].aud_buf=(u_char *)mem_start;
- mem_start += D_S[j].sbp_audsiz*CD_FRAMESIZE_RAW;
- }
-#endif MODULE
- /*
- * set the block size
- */
- sbpcd_blocksizes[j]=CD_FRAMESIZE;
- }
- blksize_size[MAJOR_NR]=sbpcd_blocksizes;
-
-#ifdef MODULE
- return (0);
-#else
- init_done:
-#if !(SBPCD_ISSUE-1)
-#ifdef CONFIG_SBPCD2
- mem_start=sbpcd2_init(mem_start, mem_end);
-#endif
-#ifdef CONFIG_SBPCD3
- mem_start=sbpcd3_init(mem_start, mem_end);
-#endif
-#ifdef CONFIG_SBPCD4
- mem_start=sbpcd4_init(mem_start, mem_end);
-#endif
-#endif
- return (mem_start);
-#endif MODULE
-}
-/*==========================================================================*/
-#ifdef MODULE
-void cleanup_module(void)
-{
- int j;
-
- if (MOD_IN_USE)
- {
- msg(DBG_INF, "%s module in use - can't remove it.\n", major_name);
- return;
- }
- if ((unregister_blkdev(MAJOR_NR, major_name) == -EINVAL))
- {
- msg(DBG_INF, "What's that: can't unregister %s.\n", major_name);
- return;
- }
- release_region(CDo_command,4);
-
- for (j=0;j<NR_SBPCD;j++)
- {
- if (D_S[j].drv_id==-1) continue;
- vfree(D_S[j].sbp_buf);
- if (D_S[j].sbp_audsiz>0)
- vfree(D_S[j].aud_buf);
- }
- msg(DBG_INF, "%s module released.\n", major_name);
-}
-#endif MODULE
-/*==========================================================================*/
-/*
- * Check if the media has changed in the CD-ROM drive.
- * used externally (isofs/inode.c, fs/buffer.c)
- * Currently disabled (has to get "synchronized").
- */
-static int sbpcd_chk_disk_change(dev_t full_dev)
-{
- int i, st;
-
- msg(DBG_CHK,"media_check (%d) called\n", MINOR(full_dev));
- return (0); /* "busy" test necessary before we really can check */
-
- i=MINOR(full_dev);
- if ( (i<0) || (i>=NR_SBPCD) || (D_S[i].drv_id==-1) )
- {
- msg(DBG_INF, "media_check: invalid device %04X.\n", full_dev);
- return (-1);
- }
-
- switch_drive(i);
-
- cc_ReadStatus(); /* command: give 1-byte status */
- st=ResponseStatus();
- msg(DBG_CHK,"media_check: %02X\n",D_S[d].status_bits);
- if (st<0)
- {
- msg(DBG_INF,"media_check: ResponseStatus error.\n");
- return (1); /* status not obtainable */
- }
- if (D_S[d].CD_changed==0xFF) msg(DBG_CHK,"media_check: \"changed\" assumed.\n");
- if (!st_spinning) msg(DBG_CHK,"media_check: motor off.\n");
- if (!st_door_closed)
- {
- msg(DBG_CHK,"media_check: door open.\n");
- D_S[d].CD_changed=0xFF;
- }
- if (!st_caddy_in)
- {
- msg(DBG_CHK,"media_check: no disk in drive.\n");
- D_S[d].open_count=0;
- D_S[d].CD_changed=0xFF;
- }
- if (!st_diskok) msg(DBG_CHK,"media_check: !st_diskok.\n");
-
-#if 0000
- if (D_S[d].CD_changed==0xFF)
- {
- D_S[d].CD_changed=1;
- return (1); /* driver had a change detected before */
- }
-#endif 0000 /* seems to give additional errors at the moment */
-
- if (!st_diskok) return (1); /* disk not o.k. */
- if (!st_caddy_in) return (1); /* disk removed */
- if (!st_door_closed) return (1); /* door open */
- return (0);
-}
-/*==========================================================================*/
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 8
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
- * c-continued-brace-offset: 0
- * End:
- */
diff --git a/drivers/block/sbpcd2.c b/drivers/block/sbpcd2.c
deleted file mode 100644
index 82e2c68ed..000000000
--- a/drivers/block/sbpcd2.c
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 2
-#include "sbpcd.c"
diff --git a/drivers/block/sbpcd3.c b/drivers/block/sbpcd3.c
deleted file mode 100644
index 0e79c4155..000000000
--- a/drivers/block/sbpcd3.c
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 3
-#include "sbpcd.c"
diff --git a/drivers/block/sbpcd4.c b/drivers/block/sbpcd4.c
deleted file mode 100644
index f4b34cc29..000000000
--- a/drivers/block/sbpcd4.c
+++ /dev/null
@@ -1,5 +0,0 @@
-/*
- * duplication of sbpcd.c for multiple interfaces
- */
-#define SBPCD_ISSUE 4
-#include "sbpcd.c"
diff --git a/drivers/block/sonycd535.c b/drivers/block/sonycd535.c
deleted file mode 100644
index 0ab6f13aa..000000000
--- a/drivers/block/sonycd535.c
+++ /dev/null
@@ -1,1743 +0,0 @@
-/*
- * Sony CDU-535 interface device driver
- *
- * This is a modified version of the CDU-31A device driver (see below).
- * Changes were made using documentation for the CDU-531 (which Sony
- * assures me is very similar to the 535) and partial disassembly of the
- * DOS driver. I used Minyard's driver and replaced the the CDU-31A
- * commands with the CDU-531 commands. This was complicated by a different
- * interface protocol with the drive. The driver is still polled.
- *
- * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec.
- * I tried polling without the sony_sleep during the data transfers but
- * it did not speed things up any.
- *
- * 1993-05-23 (rgj) changed the major number to 21 to get rid of conflict
- * with CDU-31A driver. This is the also the number from the Linux
- * Device Driver Registry for the Sony Drive. Hope nobody else is using it.
- *
- * 1993-08-29 (rgj) remove the configuring of the interface board address
- * from the top level configuration, you have to modify it in this file.
- *
- * 1995-01-26 Made module-capable (Joel Katz <Stimpson@Panix.COM>)
- *
- * 1995-05-20
- * Modified to support CDU-510/515 series
- * (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
- * Fixed to report verify_area() failures
- * (Heiko Eissfeldt <heiko@colossus.escape.de>)
- *
- * 1995-06-01
- * More chages to support CDU-510/515 series
- * (Claudio Porfiri<C.Porfiri@nisms.tei.ericsson.se>)
- *
- * Things to do:
- * - handle errors and status better, put everything into a single word
- * - use interrupts (code mostly there, but a big hole still missing)
- * - handle multi-session CDs?
- * - use DMA?
- *
- * Known Bugs:
- * -
- *
- * Ken Pizzini (ken@halcyon.com)
- *
- * Original by:
- * Ron Jeppesen (ronj.an@site007.saic.com)
- *
- *
- *------------------------------------------------------------------------
- * Sony CDROM interface device driver.
- *
- * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to Ken above)
- *
- * Colossians 3:17
- *
- * The Sony interface device driver handles Sony interface CDROM
- * drives and provides a complete block-level interface as well as an
- * ioctl() interface compatible with the Sun (as specified in
- * include/linux/cdrom.h). With this interface, CDROMs can be
- * accessed and standard audio CDs can be played back normally.
- *
- * This interface is (unfortunately) a polled interface. This is
- * because most Sony interfaces are set up with DMA and interrupts
- * disables. Some (like mine) do not even have the capability to
- * handle interrupts or DMA. For this reason you will see a lot of
- * the following:
- *
- * retry_count = jiffies+ SONY_JIFFIES_TIMEOUT;
- * while ((retry_count > jiffies) && (! <some condition to wait for))
- * {
- * while (handle_sony_cd_attention())
- * ;
- *
- * sony_sleep();
- * }
- * if (the condition not met)
- * {
- * return an error;
- * }
- *
- * This ugly hack waits for something to happen, sleeping a little
- * between every try. it also handles attentions, which are
- * asynchronous events from the drive informing the driver that a disk
- * has been inserted, removed, etc.
- *
- * One thing about these drives: They talk in MSF (Minute Second Frame) format.
- * There are 75 frames a second, 60 seconds a minute, and up to 75 minutes on a
- * disk. The funny thing is that these are sent to the drive in BCD, but the
- * interface wants to see them in decimal. A lot of conversion goes on.
- *
- * Copyright (C) 1993 Corey Minyard
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-
-#include <linux/config.h>
-#if defined(CONFIG_CDU535) || defined(MODULE)
-
-#ifdef MODULE
-# include <linux/module.h>
-# include <linux/malloc.h>
-# include <linux/version.h>
-# ifndef CONFIG_MODVERSIONS
- char kernel_version[]= UTS_RELEASE;
-# endif
-#endif
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/hdreg.h>
-#include <linux/genhd.h>
-#include <linux/mm.h>
-
-#define REALLY_SLOW_IO
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-
-#include <linux/cdrom.h>
-#include <linux/sonycd535.h>
-
-#define MAJOR_NR CDU535_CDROM_MAJOR
-
-#ifdef MODULE
-# include "blk.h"
-#else
-# include "blk.h"
-# define MOD_INC_USE_COUNT
-# define MOD_DEC_USE_COUNT
-#endif
-
-/*
- * this is the base address of the interface card for the Sony CDU-535
- * CDROM drive. If your jumpers are set for an address other than
- * this one (the default), change the following line to the
- * proper address.
- */
-#ifndef CDU535_ADDRESS
-# define CDU535_ADDRESS 0x340
-#endif
-#ifndef CDU535_INTERRUPT
-# define CDU535_INTERRUPT 0
-#endif
-#ifndef CDU535_HANDLE
-# define CDU535_HANDLE "cdu535"
-#endif
-#ifndef CDU535_MESSAGE_NAME
-# define CDU535_MESSAGE_NAME "Sony CDU-535"
-#endif
-
-#ifndef MAX_SPINUP_RETRY
-# define MAX_SPINUP_RETRY 3 /* 1 is sufficient for most drives... */
-#endif
-#ifndef RETRY_FOR_BAD_STATUS
-# define RETRY_FOR_BAD_STATUS 100 /* in 10th of second */
-#endif
-
-#ifndef DEBUG
-# define DEBUG 1
-#endif
-
-/*
- * SONY535_BUFFER_SIZE determines the size of internal buffer used
- * by the drive. It must be at least 2K and the larger the buffer
- * the better the transfer rate. It does however take system memory.
- * On my system I get the following transfer rates using dd to read
- * 10 Mb off /dev/cdrom.
- *
- * 8K buffer 43 Kb/sec
- * 16K buffer 66 Kb/sec
- * 32K buffer 91 Kb/sec
- * 64K buffer 111 Kb/sec
- * 128K buffer 123 Kb/sec
- * 512K buffer 123 Kb/sec
- */
-#define SONY535_BUFFER_SIZE (64*1024)
-
-/*
- * if LOCK_DOORS is defined then the eject button is disabled while
- * the device is open.
- */
-#ifndef NO_LOCK_DOORS
-# define LOCK_DOORS
-#endif
-
-static int read_subcode(void);
-static void sony_get_toc(void);
-static int cdu_open(struct inode *inode, struct file *filp);
-static inline unsigned int int_to_bcd(unsigned int val);
-static unsigned int bcd_to_int(unsigned int bcd);
-static int do_sony_cmd(Byte * cmd, int nCmd, Byte status[2],
- Byte * response, int n_response, int ignoreStatusBit7);
-
-/* The base I/O address of the Sony Interface. This is a variable (not a
- #define) so it can be easily changed via some future ioctl() */
-#ifndef MODULE
-static
-#endif
-unsigned short sony535_cd_base_io = CDU535_ADDRESS;
-
-/*
- * The following are I/O addresses of the various registers for the drive. The
- * comment for the base address also applies here.
- */
-static unsigned short select_unit_reg;
-static unsigned short result_reg;
-static unsigned short command_reg;
-static unsigned short read_status_reg;
-static unsigned short data_reg;
-
-static int initialized = 0; /* Has the drive been initialized? */
-static int sony_disc_changed = 1; /* Has the disk been changed
- since the last check? */
-static int sony_toc_read = 0; /* Has the table of contents been
- read? */
-static unsigned int sony_buffer_size; /* Size in bytes of the read-ahead
- buffer. */
-static unsigned int sony_buffer_sectors; /* Size (in 2048 byte records) of
- the read-ahead buffer. */
-static unsigned int sony_usage = 0; /* How many processes have the
- drive open. */
-
-static int sony_first_block = -1; /* First OS block (512 byte) in
- the read-ahead buffer */
-static int sony_last_block = -1; /* Last OS block (512 byte) in
- the read-ahead buffer */
-
-static struct s535_sony_toc *sony_toc; /* Points to the table of
- contents. */
-static struct s535_sony_subcode *last_sony_subcode; /* Points to the last
- subcode address read */
-#ifndef MODULE
-static Byte *sony_buffer; /* Points to the read-ahead buffer */
-#else
-static Byte **sony_buffer; /* Points to the pointers
- to the sector buffers */
-#endif
-static int sony_inuse = 0; /* is the drive in use? Only one
- open at a time allowed */
-
-/*
- * The audio status uses the values from read subchannel data as specified
- * in include/linux/cdrom.h.
- */
-static int sony_audio_status = CDROM_AUDIO_NO_STATUS;
-
-/*
- * The following are a hack for pausing and resuming audio play. The drive
- * does not work as I would expect it, if you stop it then start it again,
- * the drive seeks back to the beginning and starts over. This holds the
- * position during a pause so a resume can restart it. It uses the
- * audio status variable above to tell if it is paused.
- * I just kept the CDU-31A driver behavior rather than using the PAUSE
- * command on the CDU-535.
- */
-static Byte cur_pos_msf[3] = {0, 0, 0};
-static Byte final_pos_msf[3] = {0, 0, 0};
-
-/* What IRQ is the drive using? 0 if none. */
-#ifndef MODULE
-static
-#endif
-int sony535_irq_used = CDU535_INTERRUPT;
-
-/* The interrupt handler will wake this queue up when it gets an interrupt. */
-static struct wait_queue *cdu535_irq_wait = NULL;
-
-
-/*
- * This routine returns 1 if the disk has been changed since the last
- * check or 0 if it hasn't. Setting flag to 0 resets the changed flag.
- */
-static int
-cdu535_check_media_change(dev_t full_dev)
-{
- int retval;
-
- if (MINOR(full_dev) != 0) {
- printk(CDU535_MESSAGE_NAME " request error: invalid device.\n");
- return 0;
- }
-
- /* if driver is not initialized, always return 0 */
- retval = initialized ? sony_disc_changed : 0;
- sony_disc_changed = 0;
- return retval;
-}
-
-static inline void
-enable_interrupts(void)
-{
-#ifdef USE_IRQ
- /* this code snarfed from cdu31a.c; it will not
- * directly work for the cdu535 as written...
- */
- curr_control_reg |= ( SONY_ATTN_INT_EN_BIT
- | SONY_RES_RDY_INT_EN_BIT
- | SONY_DATA_RDY_INT_EN_BIT);
- outb(curr_control_reg, sony_cd_control_reg);
-#endif
-}
-
-static inline void
-disable_interrupts(void)
-{
-#ifdef USE_IRQ
- /* this code snarfed from cdu31a.c; it will not
- * directly work for the cdu535 as written...
- */
- curr_control_reg &= ~(SONY_ATTN_INT_EN_BIT
- | SONY_RES_RDY_INT_EN_BIT
- | SONY_DATA_RDY_INT_EN_BIT);
- outb(curr_control_reg, sony_cd_control_reg);
-#endif
-}
-
-static void
-cdu535_interrupt(int irq, struct pt_regs *regs)
-{
- disable_interrupts();
- if (cdu535_irq_wait != NULL)
- wake_up(&cdu535_irq_wait);
- else
- printk(CDU535_MESSAGE_NAME
- ": Got an interrupt but nothing was waiting\n");
-}
-
-
-/*
- * Wait a little while (used for polling the drive). If in initialization,
- * setting a timeout doesn't work, so just loop for a while. (We trust
- * that the sony_sleep() call is protected by a test for proper jiffies count.)
- */
-static inline void
-sony_sleep(void)
-{
- if (sony535_irq_used <= 0) { /* poll */
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies;
- schedule();
- } else { /* Interrupt driven */
- cli();
- enable_interrupts();
- interruptible_sleep_on(&cdu535_irq_wait);
- sti();
- }
-}
-
-/*------------------start of SONY CDU535 very specific ---------------------*/
-
-/****************************************************************************
- * void select_unit( int unit_no )
- *
- * Select the specified unit (0-3) so that subsequent commands reference it
- ****************************************************************************/
-static void
-select_unit(int unit_no)
-{
- unsigned int select_mask = ~(1 << unit_no);
- outb(select_mask, select_unit_reg);
-}
-
-/***************************************************************************
- * int read_result_reg( Byte *data_ptr )
- *
- * Read a result byte from the Sony CDU controller, store in location pointed
- * to by data_ptr. Return zero on success, TIME_OUT if we did not receive
- * data.
- ***************************************************************************/
-static int
-read_result_reg(Byte *data_ptr)
-{
- int retry_count;
- int read_status;
-
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while (jiffies < retry_count) {
- if (((read_status = inb(read_status_reg)) & SONY535_RESULT_NOT_READY_BIT) == 0) {
-#if DEBUG > 1
- printk(CDU535_MESSAGE_NAME
- ": read_result_reg(): readStatReg = 0x%x\n", read_status);
-#endif
- *data_ptr = inb(result_reg);
- return 0;
- } else {
- sony_sleep();
- }
- }
- printk(CDU535_MESSAGE_NAME " read_result_reg: TIME OUT!\n");
- return TIME_OUT;
-}
-
-/****************************************************************************
- * int read_exec_status( Byte status[2] )
- *
- * Read the execution status of the last command and put into status.
- * Handles reading second status word if available. Returns 0 on success,
- * TIME_OUT on failure.
- ****************************************************************************/
-static int
-read_exec_status(Byte status[2])
-{
- status[1] = 0;
- if (read_result_reg(&(status[0])) != 0)
- return TIME_OUT;
- if ((status[0] & 0x80) != 0) { /* byte two follows */
- if (read_result_reg(&(status[1])) != 0)
- return TIME_OUT;
- }
-#if DEBUG > 1
- printk(CDU535_MESSAGE_NAME ": read_exec_status: read 0x%x 0x%x\n",
- status[0], status[1]);
-#endif
- return 0;
-}
-
-/****************************************************************************
- * int check_drive_status( void )
- *
- * Check the current drive status. Using this before executing a command
- * takes care of the problem of unsolicited drive status-2 messages.
- * Add a check of the audio status if we think the disk is playing.
- ****************************************************************************/
-static int
-check_drive_status(void)
-{
- Byte status, e_status[2];
- int CDD, ATN;
- Byte cmd;
-
- select_unit(0);
- if (sony_audio_status == CDROM_AUDIO_PLAY) { /* check status */
- outb(SONY535_REQUEST_AUDIO_STATUS, command_reg);
- if (read_result_reg(&status) == 0) {
- switch (status) {
- case 0x0:
- break; /* play in progress */
- case 0x1:
- break; /* paused */
- case 0x3: /* audio play completed */
- case 0x5: /* play not requested */
- sony_audio_status = CDROM_AUDIO_COMPLETED;
- read_subcode();
- break;
- case 0x4: /* error during play */
- sony_audio_status = CDROM_AUDIO_ERROR;
- break;
- }
- }
- }
- /* now check drive status */
- outb(SONY535_REQUEST_DRIVE_STATUS_2, command_reg);
- if (read_result_reg(&status) != 0)
- return TIME_OUT;
-
-#if DEBUG > 1
- printk(CDU535_MESSAGE_NAME ": check_drive_status() got 0x%x\n", status);
-#endif
-
- if (status == 0)
- return 0;
-
- ATN = status & 0xf;
- CDD = (status >> 4) & 0xf;
-
- switch (ATN) {
- case 0x0:
- break; /* go on to CDD stuff */
- case SONY535_ATN_BUSY:
- if (initialized)
- printk(CDU535_MESSAGE_NAME " error: drive busy\n");
- return CD_BUSY;
- case SONY535_ATN_EJECT_IN_PROGRESS:
- printk(CDU535_MESSAGE_NAME " error: eject in progress\n");
- sony_audio_status = CDROM_AUDIO_INVALID;
- return CD_BUSY;
- case SONY535_ATN_RESET_OCCURRED:
- case SONY535_ATN_DISC_CHANGED:
- case SONY535_ATN_RESET_AND_DISC_CHANGED:
-#if DEBUG > 0
- printk(CDU535_MESSAGE_NAME " notice: reset occurred or disc changed\n");
-#endif
- sony_disc_changed = 1;
- sony_toc_read = 0;
- sony_audio_status = CDROM_AUDIO_NO_STATUS;
- sony_first_block = -1;
- sony_last_block = -1;
- if (initialized) {
- cmd = SONY535_SPIN_UP;
- do_sony_cmd(&cmd, 1, e_status, NULL, 0, 0);
- sony_get_toc();
- }
- return 0;
- default:
- printk(CDU535_MESSAGE_NAME " error: drive busy (ATN=0x%x)\n", ATN);
- return CD_BUSY;
- }
- switch (CDD) { /* the 531 docs are not helpful in decoding this */
- case 0x0: /* just use the values from the DOS driver */
- case 0x2:
- case 0xa:
- break; /* no error */
- case 0xc:
- printk(CDU535_MESSAGE_NAME
- ": check_drive_status(): CDD = 0xc! Not properly handled!\n");
- return CD_BUSY; /* ? */
- default:
- return CD_BUSY;
- }
- return 0;
-} /* check_drive_status() */
-
-/*****************************************************************************
- * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2],
- * Byte *response, int n_response, int ignore_status_bit7 )
- *
- * Generic routine for executing commands. The command and its parameters
- * should be placed in the cmd[] array, number of bytes in the command is
- * stored in nCmd. The response from the command will be stored in the
- * response array. The number of bytes you expect back (excluding status)
- * should be passed in n_response. Finally, some
- * commands set bit 7 of the return status even when there is no second
- * status byte, on these commands set ignoreStatusBit7 TRUE.
- * If the command was sent and data received back, then we return 0,
- * else we return TIME_OUT. You still have to check the status yourself.
- * You should call check_drive_status() before calling this routine
- * so that you do not lose notifications of disk changes, etc.
- ****************************************************************************/
-static int
-do_sony_cmd(Byte * cmd, int n_cmd, Byte status[2],
- Byte * response, int n_response, int ignore_status_bit7)
-{
- int i;
-
- /* write out the command */
- for (i = 0; i < n_cmd; i++)
- outb(cmd[i], command_reg);
-
- /* read back the status */
- if (read_result_reg(status) != 0)
- return TIME_OUT;
- if (!ignore_status_bit7 && ((status[0] & 0x80) != 0)) {
- /* get second status byte */
- if (read_result_reg(status + 1) != 0)
- return TIME_OUT;
- } else {
- status[1] = 0;
- }
-#if DEBUG > 2
- printk(CDU535_MESSAGE_NAME ": do_sony_cmd %x: %x %x\n",
- *cmd, status[0], status[1]);
-#endif
-
- /* do not know about when I should read set of data and when not to */
- if ((status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0)
- return 0;
-
- /* else, read in rest of data */
- for (i = 0; 0 < n_response; n_response--, i++)
- if (read_result_reg(response + i) != 0)
- return TIME_OUT;
- return 0;
-} /* do_sony_cmd() */
-
-/**************************************************************************
- * int set_drive_mode( int mode, Byte status[2] )
- *
- * Set the drive mode to the specified value (mode=0 is audio, mode=e0
- * is mode-1 CDROM
- **************************************************************************/
-static int
-set_drive_mode(int mode, Byte status[2])
-{
- Byte cmd_buff[2];
- Byte ret_buff[1];
-
- cmd_buff[0] = SONY535_SET_DRIVE_MODE;
- cmd_buff[1] = mode;
- return do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1);
-}
-
-/***************************************************************************
- * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2],
- * Byte *data_buff, int buff_size )
- *
- * Read n_blocks of data from the CDROM starting at position params[0:2],
- * number of blocks in stored in params[3:5] -- both these are already
- * int bcd format.
- * Transfer the data into the buffer pointed at by data_buff. buff_size
- * gives the number of bytes available in the buffer.
- * The routine returns number of bytes read in if successful, otherwise
- * it returns one of the standard error returns.
- ***************************************************************************/
-static int
-#ifndef MODULE
-seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
- Byte * data_buff, int buf_size)
-#else
-seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
- Byte **buff, int buf_size)
-#endif
-{
- const int block_size = 2048;
- Byte cmd_buff[7];
- int i;
- int read_status;
- int retry_count;
-#ifndef MODULE
- Byte *start_pos = data_buff;
-#else
- Byte *data_buff;
- int sector_count = 0;
-#endif
-
- if (buf_size < ((long)block_size) * n_blocks)
- return NO_ROOM;
-
- set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);
-
- /* send command to read the data */
- cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1;
- for (i = 0; i < 6; i++)
- cmd_buff[i + 1] = params[i];
- for (i = 0; i < 7; i++)
- outb(cmd_buff[i], command_reg);
-
- /* read back the data one block at a time */
- while (0 < n_blocks--) {
- /* wait for data to be ready */
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while (jiffies < retry_count) {
- read_status = inb(read_status_reg);
- if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) {
- read_exec_status(status);
- return BAD_STATUS;
- }
- if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
- /* data is ready, read it */
-#ifdef MODULE
- data_buff = buff[sector_count++];
-#endif
- for (i = 0; i < block_size; i++)
- *data_buff++ = inb(data_reg); /* unrolling this loop does not seem to help */
- break; /* exit the timeout loop */
- }
- sony_sleep(); /* data not ready, sleep a while */
- }
- if (retry_count <= jiffies)
- return TIME_OUT; /* if we reach this stage */
- }
-
- /* read all the data, now read the status */
- if ((i = read_exec_status(status)) != 0)
- return i;
-#ifndef MODULE
- return data_buff - start_pos;
-#else
- return block_size * sector_count;
-#endif
-} /* seek_and_read_N_blocks() */
-
-/****************************************************************************
- * int request_toc_data( Byte status[2], struct s535_sony_toc *toc )
- *
- * Read in the table of contents data. Converts all the bcd data
- * into integers in the toc structure.
- ****************************************************************************/
-static int
-request_toc_data(Byte status[2], struct s535_sony_toc *toc)
-{
- int to_status;
- int i, j, n_tracks, track_no;
- int first_track_num, last_track_num;
- Byte cmd_no = 0xb2;
- Byte track_address_buffer[5];
-
- /* read the fixed portion of the table of contents */
- if ((to_status = do_sony_cmd(&cmd_no, 1, status, (Byte *) toc, 15, 1)) != 0)
- return to_status;
-
- /* convert the data into integers so we can use them */
- first_track_num = bcd_to_int(toc->first_track_num);
- last_track_num = bcd_to_int(toc->last_track_num);
- n_tracks = last_track_num - first_track_num + 1;
-
- /* read each of the track address descriptors */
- for (i = 0; i < n_tracks; i++) {
- /* read the descriptor into a temporary buffer */
- for (j = 0; j < 5; j++) {
- if (read_result_reg(track_address_buffer + j) != 0)
- return TIME_OUT;
- if (j == 1) /* need to convert from bcd */
- track_no = bcd_to_int(track_address_buffer[j]);
- }
- /* copy the descriptor to proper location - sonycd.c just fills */
- memcpy(toc->tracks + i, track_address_buffer, 5);
- }
- return 0;
-} /* request_toc_data() */
-
-/***************************************************************************
- * int spin_up_drive( Byte status[2] )
- *
- * Spin up the drive (unless it is already spinning).
- ***************************************************************************/
-static int
-spin_up_drive(Byte status[2])
-{
- Byte cmd;
-
- /* first see if the drive is already spinning */
- cmd = SONY535_REQUEST_DRIVE_STATUS_1;
- if (do_sony_cmd(&cmd, 1, status, NULL, 0, 0) != 0)
- return TIME_OUT;
- if ((status[0] & SONY535_STATUS1_NOT_SPINNING) == 0)
- return 0; /* it's already spinning */
-
- /* otherwise, give the spin-up command */
- cmd = SONY535_SPIN_UP;
- return do_sony_cmd(&cmd, 1, status, NULL, 0, 0);
-}
-
-/*--------------------end of SONY CDU535 very specific ---------------------*/
-
-/* Convert from an integer 0-99 to BCD */
-static inline unsigned int
-int_to_bcd(unsigned int val)
-{
- int retval;
-
- retval = (val / 10) << 4;
- retval = retval | val % 10;
- return retval;
-}
-
-
-/* Convert from BCD to an integer from 0-99 */
-static unsigned int
-bcd_to_int(unsigned int bcd)
-{
- return (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f);
-}
-
-
-/*
- * Convert a logical sector value (like the OS would want to use for
- * a block device) to an MSF format.
- */
-static void
-log_to_msf(unsigned int log, Byte *msf)
-{
- log = log + LOG_START_OFFSET;
- msf[0] = int_to_bcd(log / 4500);
- log = log % 4500;
- msf[1] = int_to_bcd(log / 75);
- msf[2] = int_to_bcd(log % 75);
-}
-
-
-/*
- * Convert an MSF format to a logical sector.
- */
-static unsigned int
-msf_to_log(Byte *msf)
-{
- unsigned int log;
-
-
- log = bcd_to_int(msf[2]);
- log += bcd_to_int(msf[1]) * 75;
- log += bcd_to_int(msf[0]) * 4500;
- log = log - LOG_START_OFFSET;
-
- return log;
-}
-
-
-/*
- * Take in integer size value and put it into a buffer like
- * the drive would want to see a number-of-sector value.
- */
-static void
-size_to_buf(unsigned int size, Byte *buf)
-{
- buf[0] = size / 65536;
- size = size % 65536;
- buf[1] = size / 256;
- buf[2] = size % 256;
-}
-
-
-/*
- * The OS calls this to perform a read or write operation to the drive.
- * Write obviously fail. Reads to a read ahead of sony_buffer_size
- * bytes to help speed operations. This especially helps since the OS
- * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most
- * data access on a CD is done sequentially, this saves a lot of operations.
- */
-static void
-do_cdu535_request(void)
-{
- unsigned int dev;
- unsigned int read_size;
- int block;
- int nsect;
- int copyoff;
- int spin_up_retry;
- Byte params[10];
- Byte status[2];
- Byte cmd[2];
-
- if (!sony_inuse) {
- cdu_open(NULL, NULL);
- }
- while (1) {
- /*
- * The beginning here is stolen from the hard disk driver. I hope
- * it's right.
- */
- if (!(CURRENT) || CURRENT->dev < 0) {
- return;
- }
- INIT_REQUEST;
- dev = MINOR(CURRENT->dev);
- block = CURRENT->sector;
- nsect = CURRENT->nr_sectors;
- if (dev != 0) {
- end_request(0);
- continue;
- }
- switch (CURRENT->cmd) {
- case READ:
- /*
- * If the block address is invalid or the request goes beyond the end of
- * the media, return an error.
- */
-
- if (sony_toc->lead_out_start_lba <= (block / 4)) {
- end_request(0);
- return;
- }
- if (sony_toc->lead_out_start_lba <= ((block + nsect) / 4)) {
- end_request(0);
- return;
- }
- while (0 < nsect) {
- /*
- * If the requested sector is not currently in the read-ahead buffer,
- * it must be read in.
- */
- if ((block < sony_first_block) || (sony_last_block < block)) {
- sony_first_block = (block / 4) * 4;
- log_to_msf(block / 4, params);
-
- /*
- * If the full read-ahead would go beyond the end of the media, trim
- * it back to read just till the end of the media.
- */
- if (sony_toc->lead_out_start_lba <= ((block / 4) + sony_buffer_sectors)) {
- sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
- read_size = sony_toc->lead_out_start_lba - (block / 4);
- } else {
- sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
- read_size = sony_buffer_sectors;
- }
- size_to_buf(read_size, &params[3]);
-
- /*
- * Read the data. If the drive was not spinning,
- * spin it up and try some more.
- */
- for (spin_up_retry=0 ;; ++spin_up_retry) {
- /* This loop has been modified to support the Sony
- * CDU-510/515 series, thanks to Claudio Porfiri
- * <C.Porfiri@nisms.tei.ericsson.se>.
- */
- /*
- * This part is to deal with very slow hardware. We
- * try at most MAX_SPINUP_RETRY times to read the same
- * block. A check for seek_and_read_N_blocks' result is
- * performed; if the result is wrong, the CDROM's engine
- * is restarted and the operation is tried again.
- */
- /*
- * 1995-06-01: The system got problems when downloading
- * from Slackware CDROM, the problem seems to be:
- * seek_and_read_N_blocks returns BAD_STATUS and we
- * should wait for a while before retrying, so a new
- * part was added to discriminate the return value from
- * seek_and_read_N_blocks for the various cases.
- */
- int readStatus = seek_and_read_N_blocks(params, read_size,
- status, sony_buffer, (read_size * 2048));
- if (0 <= readStatus) /* Good data; common case, placed first */
- break;
- if (readStatus == NO_ROOM || spin_up_retry == MAX_SPINUP_RETRY) {
- /* give up */
- if (readStatus == NO_ROOM)
- printk(CDU535_MESSAGE_NAME " No room to read from CD\n");
- else
- printk(CDU535_MESSAGE_NAME " Read error: 0x%.2x\n",
- status[0]);
- sony_first_block = -1;
- sony_last_block = -1;
- end_request(0);
- return;
- }
- if (readStatus == BAD_STATUS) {
- /* Sleep for a while, then retry */
- current->state = TASK_INTERRUPTIBLE;
- current->timeout = jiffies + RETRY_FOR_BAD_STATUS;
- schedule();
- }
-#if DEBUG > 0
- printk(CDU535_MESSAGE_NAME
- " debug: calling spin up when reading data!\n");
-#endif
- cmd[0] = SONY535_SPIN_UP;
- do_sony_cmd(cmd, 1, status, NULL, 0, 0);
- }
- }
- /*
- * The data is in memory now, copy it to the buffer and advance to the
- * next block to read.
- */
-#ifndef MODULE
- copyoff = (block - sony_first_block) * 512;
- memcpy(CURRENT->buffer, sony_buffer + copyoff, 512);
-#else
- copyoff = block - sony_first_block;
- memcpy(CURRENT->buffer,
- sony_buffer[copyoff / 4] + 512 * (copyoff % 4), 512);
-#endif
-
- block += 1;
- nsect -= 1;
- CURRENT->buffer += 512;
- }
-
- end_request(1);
- break;
-
- case WRITE:
- end_request(0);
- break;
-
- default:
- panic("Unknown SONY CD cmd");
- }
- }
-}
-
-
-/*
- * Read the table of contents from the drive and set sony_toc_read if
- * successful.
- */
-static void
-sony_get_toc(void)
-{
- Byte status[2];
- if (!sony_toc_read) {
- /* do not call check_drive_status() from here since it can call this routine */
- if (request_toc_data(status, sony_toc) < 0)
- return;
- sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf);
- sony_toc_read = 1;
- }
-}
-
-
-/*
- * Search for a specific track in the table of contents. track is
- * passed in bcd format
- */
-static int
-find_track(int track)
-{
- int i;
- int num_tracks;
-
-
- num_tracks = bcd_to_int(sony_toc->last_track_num) -
- bcd_to_int(sony_toc->first_track_num) + 1;
- for (i = 0; i < num_tracks; i++) {
- if (sony_toc->tracks[i].track == track) {
- return i;
- }
- }
-
- return -1;
-}
-
-/*
- * Read the subcode and put it int last_sony_subcode for future use.
- */
-static int
-read_subcode(void)
-{
- Byte cmd = SONY535_REQUEST_SUB_Q_DATA;
- Byte status[2];
- int dsc_status;
-
- if (check_drive_status() != 0)
- return -EIO;
-
- if ((dsc_status = do_sony_cmd(&cmd, 1, status, (Byte *) last_sony_subcode,
- sizeof(struct s535_sony_subcode), 1)) != 0) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x, %d (read_subcode)\n",
- status[0], dsc_status);
- return -EIO;
- }
- return 0;
-}
-
-
-/*
- * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If
- * the drive is playing, the subchannel needs to be read (since it would be
- * changing). If the drive is paused or completed, the subcode information has
- * already been stored, just use that. The ioctl call wants things in decimal
- * (not BCD), so all the conversions are done.
- */
-static int
-sony_get_subchnl_info(long arg)
-{
- struct cdrom_subchnl schi;
- int err;
-
- /* Get attention stuff */
- if (check_drive_status() != 0)
- return -EIO;
-
- sony_get_toc();
- if (!sony_toc_read) {
- return -EIO;
- }
- err = verify_area(VERIFY_WRITE /* and read */ , (char *)arg, sizeof schi);
- if (err)
- return err;
-
- memcpy_fromfs(&schi, (char *)arg, sizeof schi);
-
- switch (sony_audio_status) {
- case CDROM_AUDIO_PLAY:
- if (read_subcode() < 0) {
- return -EIO;
- }
- break;
-
- case CDROM_AUDIO_PAUSED:
- case CDROM_AUDIO_COMPLETED:
- break;
-
- case CDROM_AUDIO_NO_STATUS:
- schi.cdsc_audiostatus = sony_audio_status;
- memcpy_tofs((char *)arg, &schi, sizeof schi);
- return 0;
- break;
-
- case CDROM_AUDIO_INVALID:
- case CDROM_AUDIO_ERROR:
- default:
- return -EIO;
- }
-
- schi.cdsc_audiostatus = sony_audio_status;
- schi.cdsc_adr = last_sony_subcode->address;
- schi.cdsc_ctrl = last_sony_subcode->control;
- schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num);
- schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num);
- if (schi.cdsc_format == CDROM_MSF) {
- schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]);
- schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]);
- schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]);
-
- schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]);
- schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]);
- schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]);
- } else if (schi.cdsc_format == CDROM_LBA) {
- schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf);
- schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf);
- }
- memcpy_tofs((char *)arg, &schi, sizeof schi);
- return 0;
-}
-
-
-/*
- * The big ugly ioctl handler.
- */
-static int
-cdu_ioctl(struct inode *inode,
- struct file *file,
- unsigned int cmd,
- unsigned long arg)
-{
- unsigned int dev;
- Byte status[2];
- Byte cmd_buff[10], params[10];
- int i;
- int dsc_status;
- int err;
-
- if (!inode) {
- return -EINVAL;
- }
- dev = MINOR(inode->i_rdev) >> 6;
- if (dev != 0) {
- return -EINVAL;
- }
- if (check_drive_status() != 0)
- return -EIO;
-
- switch (cmd) {
- case CDROMSTART: /* Spin up the drive */
- if (spin_up_drive(status) < 0) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTART)\n",
- status[0]);
- return -EIO;
- }
- return 0;
- break;
-
- case CDROMSTOP: /* Spin down the drive */
- cmd_buff[0] = SONY535_HOLD;
- do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
-
- /*
- * Spin the drive down, ignoring the error if the disk was
- * already not spinning.
- */
- sony_audio_status = CDROM_AUDIO_NO_STATUS;
- cmd_buff[0] = SONY535_SPIN_DOWN;
- dsc_status = do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
- if (((dsc_status < 0) && (dsc_status != BAD_STATUS)) ||
- ((status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0)) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMSTOP)\n",
- status[0]);
- return -EIO;
- }
- return 0;
- break;
-
- case CDROMPAUSE: /* Pause the drive */
- cmd_buff[0] = SONY535_HOLD; /* CDU-31 driver uses AUDIO_STOP, not pause */
- if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPAUSE)\n",
- status[0]);
- return -EIO;
- }
- /* Get the current position and save it for resuming */
- if (read_subcode() < 0) {
- return -EIO;
- }
- cur_pos_msf[0] = last_sony_subcode->abs_msf[0];
- cur_pos_msf[1] = last_sony_subcode->abs_msf[1];
- cur_pos_msf[2] = last_sony_subcode->abs_msf[2];
- sony_audio_status = CDROM_AUDIO_PAUSED;
- return 0;
- break;
-
- case CDROMRESUME: /* Start the drive after being paused */
- set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
-
- if (sony_audio_status != CDROM_AUDIO_PAUSED) {
- return -EINVAL;
- }
- spin_up_drive(status);
-
- /* Start the drive at the saved position. */
- cmd_buff[0] = SONY535_PLAY_AUDIO;
- cmd_buff[1] = 0; /* play back starting at this address */
- cmd_buff[2] = cur_pos_msf[0];
- cmd_buff[3] = cur_pos_msf[1];
- cmd_buff[4] = cur_pos_msf[2];
- cmd_buff[5] = SONY535_PLAY_AUDIO;
- cmd_buff[6] = 2; /* set ending address */
- cmd_buff[7] = final_pos_msf[0];
- cmd_buff[8] = final_pos_msf[1];
- cmd_buff[9] = final_pos_msf[2];
- if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
- (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMRESUME)\n",
- status[0]);
- return -EIO;
- }
- sony_audio_status = CDROM_AUDIO_PLAY;
- return 0;
- break;
-
- case CDROMPLAYMSF: /* Play starting at the given MSF address. */
- err = verify_area(VERIFY_READ, (char *)arg, 6);
- if (err)
- return err;
- spin_up_drive(status);
- set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
- memcpy_fromfs(params, (void *)arg, 6);
-
- /* The parameters are given in int, must be converted */
- for (i = 0; i < 3; i++) {
- cmd_buff[2 + i] = int_to_bcd(params[i]);
- cmd_buff[7 + i] = int_to_bcd(params[i + 3]);
- }
- cmd_buff[0] = SONY535_PLAY_AUDIO;
- cmd_buff[1] = 0; /* play back starting at this address */
- /* cmd_buff[2-4] are filled in for loop above */
- cmd_buff[5] = SONY535_PLAY_AUDIO;
- cmd_buff[6] = 2; /* set ending address */
- /* cmd_buff[7-9] are filled in for loop above */
- if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
- (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYMSF)\n",
- status[0]);
- return -EIO;
- }
- /* Save the final position for pauses and resumes */
- final_pos_msf[0] = cmd_buff[7];
- final_pos_msf[1] = cmd_buff[8];
- final_pos_msf[2] = cmd_buff[9];
- sony_audio_status = CDROM_AUDIO_PLAY;
- return 0;
- break;
-
- case CDROMREADTOCHDR: /* Read the table of contents header */
- {
- struct cdrom_tochdr *hdr;
- struct cdrom_tochdr loc_hdr;
-
- sony_get_toc();
- if (!sony_toc_read)
- return -EIO;
- hdr = (struct cdrom_tochdr *)arg;
- err = verify_area(VERIFY_WRITE, hdr, sizeof *hdr);
- if (err)
- return err;
- loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num);
- loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num);
- memcpy_tofs(hdr, &loc_hdr, sizeof *hdr);
- }
- return 0;
- break;
-
- case CDROMREADTOCENTRY: /* Read a given table of contents entry */
- {
- struct cdrom_tocentry *entry;
- struct cdrom_tocentry loc_entry;
- int track_idx;
- Byte *msf_val = NULL;
-
- sony_get_toc();
- if (!sony_toc_read) {
- return -EIO;
- }
- entry = (struct cdrom_tocentry *)arg;
- err = verify_area(VERIFY_WRITE /* and read */ , entry, sizeof *entry);
- if (err)
- return err;
-
- memcpy_fromfs(&loc_entry, entry, sizeof loc_entry);
-
- /* Lead out is handled separately since it is special. */
- if (loc_entry.cdte_track == CDROM_LEADOUT) {
- loc_entry.cdte_adr = 0 /*sony_toc->address2 */ ;
- loc_entry.cdte_ctrl = sony_toc->control2;
- msf_val = sony_toc->lead_out_start_msf;
- } else {
- track_idx = find_track(int_to_bcd(loc_entry.cdte_track));
- if (track_idx < 0)
- return -EINVAL;
- loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address */ ;
- loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control;
- msf_val = sony_toc->tracks[track_idx].track_start_msf;
- }
-
- /* Logical buffer address or MSF format requested? */
- if (loc_entry.cdte_format == CDROM_LBA) {
- loc_entry.cdte_addr.lba = msf_to_log(msf_val);
- } else if (loc_entry.cdte_format == CDROM_MSF) {
- loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val);
- loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val + 1));
- loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val + 2));
- }
- memcpy_tofs(entry, &loc_entry, sizeof *entry);
- }
- return 0;
- break;
-
- case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */
- {
- struct cdrom_ti ti;
- int track_idx;
-
- sony_get_toc();
- if (!sony_toc_read)
- return -EIO;
- err = verify_area(VERIFY_READ, (char *)arg, sizeof ti);
- if (err)
- return err;
-
- memcpy_fromfs(&ti, (char *)arg, sizeof ti);
- if ((ti.cdti_trk0 < sony_toc->first_track_num)
- || (sony_toc->last_track_num < ti.cdti_trk0)
- || (ti.cdti_trk1 < ti.cdti_trk0)) {
- return -EINVAL;
- }
- track_idx = find_track(int_to_bcd(ti.cdti_trk0));
- if (track_idx < 0)
- return -EINVAL;
- params[1] = sony_toc->tracks[track_idx].track_start_msf[0];
- params[2] = sony_toc->tracks[track_idx].track_start_msf[1];
- params[3] = sony_toc->tracks[track_idx].track_start_msf[2];
- /*
- * If we want to stop after the last track, use the lead-out
- * MSF to do that.
- */
- if (bcd_to_int(sony_toc->last_track_num) <= ti.cdti_trk1) {
- log_to_msf(msf_to_log(sony_toc->lead_out_start_msf) - 1,
- &(params[4]));
- } else {
- track_idx = find_track(int_to_bcd(ti.cdti_trk1 + 1));
- if (track_idx < 0)
- return -EINVAL;
- log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf) - 1,
- &(params[4]));
- }
- params[0] = 0x03;
-
- spin_up_drive(status);
-
- set_drive_mode(SONY535_AUDIO_DRIVE_MODE, status);
-
- /* Start the drive at the saved position. */
- cmd_buff[0] = SONY535_PLAY_AUDIO;
- cmd_buff[1] = 0; /* play back starting at this address */
- cmd_buff[2] = params[1];
- cmd_buff[3] = params[2];
- cmd_buff[4] = params[3];
- cmd_buff[5] = SONY535_PLAY_AUDIO;
- cmd_buff[6] = 2; /* set ending address */
- cmd_buff[7] = params[4];
- cmd_buff[8] = params[5];
- cmd_buff[9] = params[6];
- if ((do_sony_cmd(cmd_buff, 5, status, NULL, 0, 0) != 0) ||
- (do_sony_cmd(cmd_buff + 5, 5, status, NULL, 0, 0) != 0)) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMPLAYTRKIND)\n",
- status[0]);
- printk("... Params: %x %x %x %x %x %x %x\n",
- params[0], params[1], params[2],
- params[3], params[4], params[5], params[6]);
- return -EIO;
- }
- /* Save the final position for pauses and resumes */
- final_pos_msf[0] = params[4];
- final_pos_msf[1] = params[5];
- final_pos_msf[2] = params[6];
- sony_audio_status = CDROM_AUDIO_PLAY;
- return 0;
- }
-
- case CDROMSUBCHNL: /* Get subchannel info */
- return sony_get_subchnl_info(arg);
-
- case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */
- {
- struct cdrom_volctrl volctrl;
-
- err = verify_area(VERIFY_READ, (char *)arg, sizeof volctrl);
- if (err)
- return err;
-
- memcpy_fromfs(&volctrl, (char *)arg, sizeof volctrl);
- cmd_buff[0] = SONY535_SET_VOLUME;
- cmd_buff[1] = volctrl.channel0;
- cmd_buff[2] = volctrl.channel1;
- if (do_sony_cmd(cmd_buff, 3, status, NULL, 0, 0) != 0) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMVOLCTRL)\n",
- status[0]);
- return -EIO;
- }
- }
- return 0;
-
- case CDROMEJECT: /* Eject the drive */
- cmd_buff[0] = SONY535_STOP;
- do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
- cmd_buff[0] = SONY535_SPIN_DOWN;
- do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
-
- sony_audio_status = CDROM_AUDIO_INVALID;
- cmd_buff[0] = SONY535_EJECT_CADDY;
- if (do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0) != 0) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x (CDROMEJECT)\n",
- status[0]);
- return -EIO;
- }
- return 0;
- break;
-
- default:
- return -EINVAL;
- }
-}
-
-
-/*
- * Open the drive for operations. Spin the drive up and read the table of
- * contents if these have not already been done.
- */
-static int
-cdu_open(struct inode *inode,
- struct file *filp)
-{
- Byte status[2], cmd_buff[2];
-
-
- if (sony_inuse)
- return -EBUSY;
- if (check_drive_status() != 0)
- return -EIO;
- sony_inuse = 1;
- MOD_INC_USE_COUNT;
-
- if (spin_up_drive(status) != 0) {
- printk(CDU535_MESSAGE_NAME " error 0x%.2x (cdu_open, spin up)\n",
- status[0]);
- sony_inuse = 0;
- MOD_DEC_USE_COUNT;
- return -EIO;
- }
- sony_get_toc();
- if (!sony_toc_read) {
- cmd_buff[0] = SONY535_SPIN_DOWN;
- do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
- sony_inuse = 0;
- MOD_DEC_USE_COUNT;
- return -EIO;
- }
- if (inode) {
- check_disk_change(inode->i_rdev);
- }
- sony_usage++;
-
-#ifdef LOCK_DOORS
- /* disable the eject button while mounted */
- cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON;
- do_sony_cmd(cmd_buff, 1, status, NULL, 0, 0);
-#endif
-
- return 0;
-}
-
-
-/*
- * Close the drive. Spin it down if no task is using it. The spin
- * down will fail if playing audio, so audio play is OK.
- */
-static void
-cdu_release(struct inode *inode,
- struct file *filp)
-{
- Byte status[2], cmd_no;
-
- sony_inuse = 0;
- MOD_DEC_USE_COUNT;
-
- if (0 < sony_usage) {
- sony_usage--;
- }
- if (sony_usage == 0) {
- sync_dev(inode->i_rdev);
- check_drive_status();
-
- if (sony_audio_status != CDROM_AUDIO_PLAY) {
- cmd_no = SONY535_SPIN_DOWN;
- do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
- }
-#ifdef LOCK_DOORS
- /* enable the eject button after umount */
- cmd_no = SONY535_ENABLE_EJECT_BUTTON;
- do_sony_cmd(&cmd_no, 1, status, NULL, 0, 0);
-#endif
- }
-}
-
-
-static struct file_operations cdu_fops =
-{
- NULL, /* lseek - default */
- block_read, /* read - general block-dev read */
- block_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- cdu_ioctl, /* ioctl */
- NULL, /* mmap */
- cdu_open, /* open */
- cdu_release, /* release */
- NULL, /* fsync */
- NULL, /* fasync */
- cdu535_check_media_change, /* check media change */
- NULL /* revalidate */
-};
-
-/*
- * Initialize the driver.
- */
-#ifndef MODULE
-unsigned long
-sony535_init(unsigned long mem_start, unsigned long mem_end)
-#else
-int
-init_module(void)
-#endif
-{
- struct s535_sony_drive_config drive_config;
- Byte cmd_buff[3];
- Byte ret_buff[2];
- Byte status[2];
- int retry_count;
- int tmp_irq;
-#ifdef MODULE
- int i;
-#endif
-
- /* Setting the base I/O address to 0xffff will disable it. */
- if (sony535_cd_base_io == 0xffff)
- goto bail;
-
- /* Set up all the register locations */
- result_reg = sony535_cd_base_io;
- command_reg = sony535_cd_base_io;
- data_reg = sony535_cd_base_io + 1;
- read_status_reg = sony535_cd_base_io + 2;
- select_unit_reg = sony535_cd_base_io + 3;
-
-#ifndef USE_IRQ
- sony535_irq_used = 0; /* polling only until this is ready... */
-#endif
- /* we need to poll until things get initialized */
- tmp_irq = sony535_irq_used;
- sony535_irq_used = 0;
-
-#if DEBUG > 0
- printk(CDU535_MESSAGE_NAME ": probing base address %03X\n",
- sony535_cd_base_io);
-#endif
- if (check_region(sony535_cd_base_io,4)) {
- printk(CDU535_MESSAGE_NAME ": my base address is not free!\n");
-#ifndef MODULE
- return mem_start;
-#else
- return -EIO;
-#endif
- }
- /* look for the CD-ROM, follows the procedure in the DOS driver */
- inb(select_unit_reg);
- retry_count = jiffies + 2 * HZ;
- while (jiffies < retry_count)
- sony_sleep(); /* wait for 40 18 Hz ticks (from DOS driver) */
- inb(result_reg);
-
- outb(0, read_status_reg); /* does a reset? */
- retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
- while (jiffies < retry_count) {
- select_unit(0);
- if (inb(result_reg) != 0xff)
- break;
- sony_sleep();
- }
-
- if ((jiffies < retry_count) && (check_drive_status() != TIME_OUT)) {
- /* CD-ROM drive responded -- get the drive configuration */
- cmd_buff[0] = SONY535_INQUIRY;
- if (do_sony_cmd(cmd_buff, 1, status,
- (Byte *)&drive_config, 28, 1) == 0) {
- /* was able to get the configuration,
- * set drive mode as rest of init
- */
-#if DEBUG > 0
- /* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */
- if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 )
- printk(CDU535_MESSAGE_NAME
- "Inquiry command returned status = 0x%x\n", status[0]);
-#endif
- /* now ready to use interrupts, if available */
- sony535_irq_used = tmp_irq;
-#ifndef MODULE
-/* This code is not in MODULEs by default, since the autoirq stuff might
- * not be in the module-accessible symbol table.
- */
- /* A negative sony535_irq_used will attempt an autoirq. */
- if (sony535_irq_used < 0) {
- autoirq_setup(0);
- enable_interrupts();
- outb(0, read_status_reg); /* does a reset? */
- sony535_irq_used = autoirq_report(10);
- disable_interrupts();
- }
-#endif
- if (sony535_irq_used > 0) {
- if (request_irq(sony535_irq_used, cdu535_interrupt,
- SA_INTERRUPT, CDU535_HANDLE)) {
- printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME
- " driver; polling instead.\n", sony535_irq_used);
- sony535_irq_used = 0;
- }
- }
- cmd_buff[0] = SONY535_SET_DRIVE_MODE;
- cmd_buff[1] = 0x0; /* default audio */
- if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) {
- /* set the drive mode successful, we are set! */
- sony_buffer_size = SONY535_BUFFER_SIZE;
- sony_buffer_sectors = sony_buffer_size / 2048;
-
- printk(CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
- drive_config.vendor_id,
- drive_config.product_id,
- drive_config.product_rev_level);
- printk(" base address %03X, ", sony535_cd_base_io);
- if (tmp_irq > 0)
- printk("IRQ%d, ", tmp_irq);
- printk("using %d byte buffer\n", sony_buffer_size);
-
- if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) {
- printk("Unable to get major %d for %s\n",
- MAJOR_NR, CDU535_MESSAGE_NAME);
-#ifndef MODULE
- return mem_start;
-#else
- return -EIO;
-#endif
- }
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
-
-#ifndef MODULE
- sony_toc = (struct s535_sony_toc *)mem_start;
- mem_start += sizeof *sony_toc;
- last_sony_subcode = (struct s535_sony_subcode *)mem_start;
- mem_start += sizeof *last_sony_subcode;
- sony_buffer = (Byte *)mem_start;
- mem_start += sony_buffer_size;
-
-#else /* MODULE */
- sony_toc = (struct s535_sony_toc *)
- kmalloc(sizeof *sony_toc, GFP_KERNEL);
- last_sony_subcode = (struct s535_sony_subcode *)
- kmalloc(sizeof *last_sony_subcode, GFP_KERNEL);
- sony_buffer = (Byte **)
- kmalloc(4 * sony_buffer_sectors, GFP_KERNEL);
- for (i = 0; i < sony_buffer_sectors; i++)
- sony_buffer[i] = (Byte *)kmalloc(2048, GFP_KERNEL);
-#endif /* MODULE */
- initialized = 1;
- }
- }
- }
-
- if (!initialized) {
- printk("Did not find a " CDU535_MESSAGE_NAME " drive\n");
-#ifdef MODULE
- return -EIO;
-#endif
- } else {
- request_region(sony535_cd_base_io, 4, CDU535_HANDLE);
- }
-bail:
-#ifndef MODULE
- return mem_start;
-#else
- return 0;
-#endif
-}
-
-#ifndef MODULE
-/*
- * accept "kernel command line" parameters
- * (added by emoenke@gwdg.de)
- *
- * use: tell LILO:
- * sonycd535=0x320
- *
- * the address value has to be the existing CDROM port address.
- */
-void
-sonycd535_setup(char *strings, int *ints)
-{
- /* if IRQ change and default io base desired,
- * then call with io base of 0
- */
- if (ints[0] > 0)
- if (ints[0] != 0)
- sony535_cd_base_io = ints[1];
- if (ints[0] > 1)
- sony535_irq_used = ints[2];
- if ((strings != NULL) && (*strings != '\0'))
- printk(CDU535_MESSAGE_NAME
- ": Warning: Unknown interface type: %s\n", strings);
-}
-
-#else /* MODULE */
-
-void
-cleanup_module(void)
-{
- int i;
- if (MOD_IN_USE) {
- printk(CDU535_HANDLE " module in use, cannot remove\n");
- return;
- }
- release_region(sony535_cd_base_io, 4);
- for (i = 0; i < sony_buffer_sectors; i++)
- kfree_s(sony_buffer[i], 2048);
- kfree_s(sony_buffer, 4 * sony_buffer_sectors);
- kfree_s(last_sony_subcode, sizeof *last_sony_subcode);
- kfree_s(sony_toc, sizeof *sony_toc);
- if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL)
- printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n");
- else
- printk(CDU535_HANDLE " module released\n");
-}
-#endif /* MODULE */
-
-#endif /* CONFIG_CDU535 */
diff --git a/drivers/block/triton.c b/drivers/block/triton.c
new file mode 100644
index 000000000..4faeae808
--- /dev/null
+++ b/drivers/block/triton.c
@@ -0,0 +1,485 @@
+/*
+ * linux/drivers/block/triton.c Version 1.13 Aug 12, 1996
+ *
+ * Copyright (c) 1995-1996 Mark Lord
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This module provides support for the Bus Master IDE DMA function
+ * of the Intel PCI Triton I/II chipsets (i82371FB or i82371SB).
+ *
+ * Pretty much the same code will work for the OPTi "Viper" chipset.
+ * Look for DMA support for this in linux kernel 2.1.xx, when it appears.
+ *
+ * Up to four drives may be enabled for DMA, and the Triton chipset will
+ * (hopefully) arbitrate the PCI bus among them. Note that the i82371 chip
+ * provides a single "line buffer" for the BM IDE function, so performance of
+ * multiple (two) drives doing DMA simultaneously will suffer somewhat,
+ * as they contest for that resource bottleneck. This is handled transparently
+ * inside the i82371 chip.
+ *
+ * By default, DMA support is prepared for use, but is currently enabled only
+ * for drives which support multi-word DMA mode2 (mword2), or which are
+ * recognized as "good" (see table below). Drives with only mode0 or mode1
+ * (single or multi) DMA should also work with this chipset/driver (eg. MC2112A)
+ * but are not enabled by default. Use "hdparm -i" to view modes supported
+ * by a given drive.
+ *
+ * The hdparm-2.4 (or later) utility can be used for manually enabling/disabling
+ * DMA support, but must be (re-)compiled against this kernel version or later.
+ *
+ * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
+ * If problems arise, ide.c will disable DMA operation after a few retries.
+ * This error recovery mechanism works and has been extremely well exercised.
+ *
+ * IDE drives, depending on their vintage, may support several different modes
+ * of DMA operation. The boot-time modes are indicated with a "*" in
+ * the "hdparm -i" listing, and can be changed with *knowledgeable* use of
+ * the "hdparm -X" feature. There is seldom a need to do this, as drives
+ * normally power-up with their "best" PIO/DMA modes enabled.
+ *
+ * Testing was done with an ASUS P55TP4XE/100 system and the following drives:
+ *
+ * Quantum Fireball 1080A (1Gig w/83kB buffer), DMA mode2, PIO mode4.
+ * - DMA mode2 works well (7.4MB/sec), despite the tiny on-drive buffer.
+ * - This drive also does PIO mode4, at about the same speed as DMA mode2.
+ * An awesome drive for the price!
+ *
+ * Fujitsu M1606TA (1Gig w/256kB buffer), DMA mode2, PIO mode4.
+ * - DMA mode2 gives horrible performance (1.6MB/sec), despite the good
+ * size of the on-drive buffer and a boasted 10ms average access time.
+ * - PIO mode4 was better, but peaked at a mere 4.5MB/sec.
+ *
+ * Micropolis MC2112A (1Gig w/508kB buffer), drive pre-dates EIDE and ATA2.
+ * - DMA works fine (2.2MB/sec), probably due to the large on-drive buffer.
+ * - This older drive can also be tweaked for fastPIO (3.7MB/sec) by using
+ * maximum clock settings (5,4) and setting all flags except prefetch.
+ *
+ * Western Digital AC31000H (1Gig w/128kB buffer), DMA mode1, PIO mode3.
+ * - DMA does not work reliably. The drive appears to be somewhat tardy
+ * in deasserting DMARQ at the end of a sector. This is evident in
+ * the observation that WRITEs work most of the time, depending on
+ * cache-buffer occupancy, but multi-sector reads seldom work.
+ *
+ * Testing was done with a Gigabyte GA-586 ATE system and the following drive:
+ * (Uwe Bonnes - bon@elektron.ikp.physik.th-darmstadt.de)
+ *
+ * Western Digital AC31600H (1.6Gig w/128kB buffer), DMA mode2, PIO mode4.
+ * - much better than its 1Gig cousin, this drive is reported to work
+ * very well with DMA (7.3MB/sec).
+ *
+ * Other drives:
+ *
+ * Maxtor 7540AV (515Meg w/32kB buffer), DMA modes mword0/sword2, PIO mode3.
+ * - a budget drive, with budget performance, around 3MB/sec.
+ *
+ * Western Digital AC2850F (814Meg w/64kB buffer), DMA mode1, PIO mode3.
+ * - another "caviar" drive, similar to the AC31000, except that this one
+ * worked with DMA in at least one system. Throughput is about 3.8MB/sec
+ * for both DMA and PIO.
+ *
+ * Conner CFS850A (812Meg w/64kB buffer), DMA mode2, PIO mode4.
+ * - like most Conner models, this drive proves that even a fast interface
+ * cannot improve slow media. Both DMA and PIO peak around 3.5MB/sec.
+ *
+ * Maxtor 71260AT (1204Meg w/256kB buffer), DMA mword0/sword2, PIO mode3.
+ * - works with DMA, on some systems (but not always on others, eg. Dell),
+ * giving 3-4MB/sec performance, about the same as mode3.
+ *
+ * If you have any drive models to add, email your results to: mlord@pobox.com
+ * Keep an eye on /var/adm/messages for "DMA disabled" messages.
+ *
+ * Some people have reported trouble with Intel Zappa motherboards.
+ * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
+ * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
+ * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
+ *
+ * And, yes, Intel Zappa boards really *do* use the Triton IDE ports.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "ide.h"
+
+#undef DISPLAY_TRITON_TIMINGS /* define this to display timings */
+
+/*
+ * good_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which do not support mword2 DMA but which are
+ * known to work fine with this interface under Linux.
+ */
+const char *good_dma_drives[] = {"Micropolis 2112A",
+ "CONNER CTMA 4000",
+ "CONNER CTT8000-A",
+ NULL};
+
+/*
+ * Our Physical Region Descriptor (PRD) table should be large enough
+ * to handle the biggest I/O request we are likely to see. Since requests
+ * can have no more than 256 sectors, and since the typical blocksize is
+ * two sectors, we could get by with a limit of 128 entries here for the
+ * usual worst case. Most requests seem to include some contiguous blocks,
+ * further reducing the number of table entries required.
+ *
+ * The driver reverts to PIO mode for individual requests that exceed
+ * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling
+ * 100% of all crazy scenarios here is not necessary.
+ *
+ * As it turns out though, we must allocate a full 4KB page for this,
+ * so the two PRD tables (ide0 & ide1) will each get half of that,
+ * allowing each to have about 256 entries (8 bytes each) from this.
+ */
+#define PRD_BYTES 8
+#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES))
+#define DEFAULT_BMIBA 0xe800 /* in case BIOS did not init it */
+
+/*
+ * dma_intr() is the handler for disk read/write DMA interrupts
+ */
+static void dma_intr (ide_drive_t *drive)
+{
+ byte stat, dma_stat;
+ int i;
+ struct request *rq = HWGROUP(drive)->rq;
+ unsigned short dma_base = HWIF(drive)->dma_base;
+
+ dma_stat = inb(dma_base+2); /* get DMA status */
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA operation */
+ stat = GET_STAT(); /* get drive status */
+ if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+ if ((dma_stat & 7) == 4) { /* verify good DMA status */
+ rq = HWGROUP(drive)->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, HWGROUP(drive));
+ }
+ return;
+ }
+ printk("%s: bad DMA status: 0x%02x\n", drive->name, dma_stat);
+ }
+ sti();
+ ide_error(drive, "dma_intr", stat);
+}
+
+/*
+ * build_dmatable() prepares a dma request.
+ * Returns 0 if all went okay, returns 1 otherwise.
+ */
+static int build_dmatable (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct buffer_head *bh = rq->bh;
+ unsigned long size, addr, *table = HWIF(drive)->dmatable;
+ unsigned int count = 0;
+
+ do {
+ /*
+ * Determine addr and size of next buffer area. We assume that
+ * individual virtual buffers are always composed linearly in
+ * physical memory. For example, we assume that any 8kB buffer
+ * is always composed of two adjacent physical 4kB pages rather
+ * than two possibly non-adjacent physical 4kB pages.
+ */
+ if (bh == NULL) { /* paging requests have (rq->bh == NULL) */
+ addr = virt_to_bus (rq->buffer);
+ size = rq->nr_sectors << 9;
+ } else {
+ /* group sequential buffers into one large buffer */
+ addr = virt_to_bus (bh->b_data);
+ size = bh->b_size;
+ while ((bh = bh->b_reqnext) != NULL) {
+ if ((addr + size) != virt_to_bus (bh->b_data))
+ break;
+ size += bh->b_size;
+ }
+ }
+
+ /*
+ * Fill in the dma table, without crossing any 64kB boundaries.
+ * We assume 16-bit alignment of all blocks.
+ */
+ while (size) {
+ if (++count >= PRD_ENTRIES) {
+ printk("%s: DMA table too small\n", drive->name);
+ return 1; /* revert to PIO for this request */
+ } else {
+ unsigned long bcount = 0x10000 - (addr & 0xffff);
+ if (bcount > size)
+ bcount = size;
+ *table++ = addr;
+ *table++ = bcount & 0xffff;
+ addr += bcount;
+ size -= bcount;
+ }
+ }
+ } while (bh != NULL);
+ if (count) {
+ *--table |= 0x80000000; /* set End-Of-Table (EOT) bit */
+ return 0;
+ }
+ printk("%s: empty DMA table?\n", drive->name);
+ return 1; /* let the PIO routines handle this weirdness */
+}
+
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+ const char **list;
+
+ struct hd_driveid *id = drive->id;
+ if (id && (id->capability & 1)) {
+ /* Enable DMA on any drive that supports mword2 DMA */
+ if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) {
+ drive->using_dma = 1;
+ return 0; /* DMA enabled */
+ }
+ /* Consult the list of known "good" drives */
+ list = good_dma_drives;
+ while (*list) {
+ if (!strcmp(*list++,id->model)) {
+ drive->using_dma = 1;
+ return 0; /* DMA enabled */
+ }
+ }
+ }
+ return 1; /* DMA not enabled */
+}
+
+/*
+ * triton_dmaproc() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA. All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * For ATAPI devices, we just prepare for DMA and return. The caller should
+ * then issue the packet command to the drive and call us again with
+ * ide_dma_begin afterwards.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case
+ * the caller should revert to PIO for the current request.
+ */
+static int triton_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ unsigned long dma_base = HWIF(drive)->dma_base;
+ unsigned int reading = (1 << 3);
+
+ switch (func) {
+ case ide_dma_abort:
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA */
+ return 0;
+ case ide_dma_check:
+ return config_drive_for_dma (drive);
+ case ide_dma_write:
+ reading = 0;
+ case ide_dma_read:
+ break;
+ case ide_dma_status_bad:
+ return ((inb(dma_base+2) & 7) != 4); /* verify good DMA status */
+ case ide_dma_transferred:
+#if 0
+ return (number of bytes actually transferred);
+#else
+ return (0);
+#endif
+ case ide_dma_begin:
+ outb(inb(dma_base)|1, dma_base); /* begin DMA */
+ return 0;
+ default:
+ printk("triton_dmaproc: unsupported func: %d\n", func);
+ return 1;
+ }
+ if (build_dmatable (drive))
+ return 1;
+ outl(virt_to_bus (HWIF(drive)->dmatable), dma_base + 4); /* PRD table */
+ outb(reading, dma_base); /* specify r/w */
+ outb(inb(dma_base+2)|0x06, dma_base+2); /* clear status bits */
+ if (drive->media != ide_disk)
+ return 0;
+ ide_set_handler(drive, &dma_intr, WAIT_CMD); /* issue cmd to drive */
+ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+ outb(inb(dma_base)|1, dma_base); /* begin DMA */
+ return 0;
+}
+
+#ifdef DISPLAY_TRITON_TIMINGS
+/*
+ * print_triton_drive_flags() displays the currently programmed options
+ * in the i82371 (Triton) for a given drive.
+ *
+ * If fastDMA is "no", then slow ISA timings are used for DMA data xfers.
+ * If fastPIO is "no", then slow ISA timings are used for PIO data xfers.
+ * If IORDY is "no", then IORDY is assumed to always be asserted.
+ * If PreFetch is "no", then data pre-fetch/post are not used.
+ *
+ * When "fastPIO" and/or "fastDMA" are "yes", then faster PCI timings and
+ * back-to-back 16-bit data transfers are enabled, using the sample_CLKs
+ * and recovery_CLKs (PCI clock cycles) timing parameters for that interface.
+ */
+static void print_triton_drive_flags (unsigned int unit, byte flags)
+{
+ printk(" %s ", unit ? "slave :" : "master:");
+ printk( "fastDMA=%s", (flags&9) ? "on " : "off");
+ printk(" PreFetch=%s", (flags&4) ? "on " : "off");
+ printk(" IORDY=%s", (flags&2) ? "on " : "off");
+ printk(" fastPIO=%s\n", ((flags&9)==1) ? "on " : "off");
+}
+#endif /* DISPLAY_TRITON_TIMINGS */
+
+static void init_triton_dma (ide_hwif_t *hwif, unsigned short base)
+{
+ static unsigned long dmatable = 0;
+
+ printk(" %s: BM-DMA at 0x%04x-0x%04x", hwif->name, base, base+7);
+ if (check_region(base, 8)) {
+ printk(" -- ERROR, PORTS ALREADY IN USE");
+ } else {
+ request_region(base, 8, "IDE DMA");
+ hwif->dma_base = base;
+ if (!dmatable) {
+ /*
+ * The BM-DMA uses a full 32-bits, so we can
+ * safely use __get_free_page() here instead
+ * of __get_dma_pages() -- no ISA limitations.
+ */
+ dmatable = __get_free_page(GFP_KERNEL);
+ }
+ if (dmatable) {
+ hwif->dmatable = (unsigned long *) dmatable;
+ dmatable += (PRD_ENTRIES * PRD_BYTES);
+ outl(virt_to_bus(hwif->dmatable), base + 4);
+ hwif->dmaproc = &triton_dmaproc;
+ }
+ }
+ printk("\n");
+}
+
+/*
+ * ide_init_triton() prepares the IDE driver for DMA operation.
+ * This routine is called once, from ide.c during driver initialization,
+ * for each triton chipset which is found (unlikely to be more than one).
+ */
+void ide_init_triton (byte bus, byte fn)
+{
+ int rc = 0, h;
+ int dma_enabled = 0;
+ unsigned short pcicmd;
+ unsigned int bmiba, timings;
+
+ printk("ide: i82371 PIIX (Triton) on PCI bus %d function %d\n", bus, fn);
+ /*
+ * See if IDE and BM-DMA features are enabled:
+ */
+ if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd)))
+ goto quit;
+ if ((pcicmd & 1) == 0) {
+ printk("ide: ports are not enabled (BIOS)\n");
+ goto quit;
+ }
+ if ((pcicmd & 4) == 0) {
+ printk("ide: BM-DMA feature is not enabled (BIOS)\n");
+ } else {
+ /*
+ * Get the bmiba base address
+ */
+ int try_again = 1;
+ do {
+ if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba)))
+ goto quit;
+ bmiba &= 0xfff0; /* extract port base address */
+ if (bmiba) {
+ dma_enabled = 1;
+ break;
+ } else {
+ printk("ide: BM-DMA base register is invalid (0x%04x, PnP BIOS problem)\n", bmiba);
+ if (inb(DEFAULT_BMIBA) != 0xff || !try_again)
+ break;
+ printk("ide: setting BM-DMA base register to 0x%04x\n", DEFAULT_BMIBA);
+ if ((rc = pcibios_write_config_word(bus, fn, 0x04, pcicmd&~1)))
+ goto quit;
+ rc = pcibios_write_config_dword(bus, fn, 0x20, DEFAULT_BMIBA|1);
+ if (pcibios_write_config_word(bus, fn, 0x04, pcicmd|5) || rc)
+ goto quit;
+ }
+ } while (try_again--);
+ }
+
+ /*
+ * See if ide port(s) are enabled
+ */
+ if ((rc = pcibios_read_config_dword(bus, fn, 0x40, &timings)))
+ goto quit;
+ if (!(timings & 0x80008000)) {
+ printk("ide: neither port is enabled\n");
+ goto quit;
+ }
+
+ /*
+ * Save the dma_base port addr for each interface
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+#ifdef DISPLAY_TRITON_TIMINGS
+ byte s_clks, r_clks;
+ unsigned short devid;
+#endif /* DISPLAY_TRITON_TIMINGS */
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ unsigned short time;
+ if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0) {
+ time = timings & 0xffff;
+ if ((time & 0x8000) == 0) /* interface enabled? */
+ continue;
+ hwif->chipset = ide_triton;
+ if (dma_enabled)
+ init_triton_dma(hwif, bmiba);
+ } else if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170) {
+ time = timings >> 16;
+ if ((time & 0x8000) == 0) /* interface enabled? */
+ continue;
+ hwif->chipset = ide_triton;
+ if (dma_enabled)
+ init_triton_dma(hwif, bmiba + 8);
+ } else
+ continue;
+#ifdef DISPLAY_TRITON_TIMINGS
+ s_clks = ((~time >> 12) & 3) + 2;
+ r_clks = ((~time >> 8) & 3) + 1;
+ printk(" %s timing: (0x%04x) sample_CLKs=%d, recovery_CLKs=%d\n",
+ hwif->name, time, s_clks, r_clks);
+ if ((time & 0x40) && !pcibios_read_config_word(bus, fn, 0x02, &devid)
+ && devid == PCI_DEVICE_ID_INTEL_82371SB_1)
+ {
+ byte stime;
+ if (pcibios_read_config_byte(bus, fn, 0x44, &stime)) {
+ if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0) {
+ s_clks = ~stime >> 6;
+ r_clks = ~stime >> 4;
+ } else {
+ s_clks = ~stime >> 2;
+ r_clks = ~stime;
+ }
+ s_clks = (s_clks & 3) + 2;
+ r_clks = (r_clks & 3) + 1;
+ printk(" slave: sample_CLKs=%d, recovery_CLKs=%d\n",
+ s_clks, r_clks);
+ }
+ }
+ print_triton_drive_flags (0, time & 0xf);
+ print_triton_drive_flags (1, (time >> 4) & 0xf);
+#endif /* DISPLAY_TRITON_TIMINGS */
+ }
+
+quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
+}
+
diff --git a/drivers/block/umc8672.c b/drivers/block/umc8672.c
new file mode 100644
index 000000000..3427f7077
--- /dev/null
+++ b/drivers/block/umc8672.c
@@ -0,0 +1,155 @@
+/*
+ * linux/drivers/block/umc8672.c Version 0.05 Jul 31, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: PODIEN@hml2.atlas.de (Wolfram Podien)
+ *
+ * This file provides support for the advanced features
+ * of the UMC 8672 IDE interface.
+ *
+ * Version 0.01 Initial version, hacked out of ide.c,
+ * and #include'd rather than compiled separately.
+ * This will get cleaned up in a subsequent release.
+ *
+ * Version 0.02 now configs/compiles separate from ide.c -ml
+ * Version 0.03 enhanced auto-tune, fix display bug
+ * Version 0.05 replace sti() with restore_flags() -ml
+ * add detection of possible race condition -ml
+ */
+
+/*
+ * VLB Controller Support from
+ * Wolfram Podien
+ * Rohoefe 3
+ * D28832 Achim
+ * Germany
+ *
+ * To enable UMC8672 support there must a lilo line like
+ * append="ide0=umc8672"...
+ * To set the speed according to the abilities of the hardware there must be a
+ * line like
+ * #define UMC_DRIVE0 11
+ * in the beginning of the driver, which sets the speed of drive 0 to 11 (there
+ * are some lines present). 0 - 11 are allowed speed values. These values are
+ * the results from the DOS speed test program supplied from UMC. 11 is the
+ * highest speed (about PIO mode 3)
+ */
+#define REALLY_SLOW_IO /* some systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "ide_modes.h"
+
+/*
+ * Default speeds. These can be changed with "auto-tune" and/or hdparm.
+ */
+#define UMC_DRIVE0 1 /* DOS measured drive speeds */
+#define UMC_DRIVE1 1 /* 0 to 11 allowed */
+#define UMC_DRIVE2 1 /* 11 = Fastest Speed */
+#define UMC_DRIVE3 1 /* In case of crash reduce speed */
+
+static byte current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3};
+static const byte pio_to_umc [5] = {0,3,7,10,11}; /* rough guesses */
+
+/* 0 1 2 3 4 5 6 7 8 9 10 11 */
+static const byte speedtab [3][12] = {
+ {0xf, 0xb, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+ {0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 },
+ {0xff,0xcb,0xc0,0x58,0x36,0x33,0x23,0x22,0x21,0x11,0x10,0x0}};
+
+static void out_umc (char port,char wert)
+{
+ outb_p (port,0x108);
+ outb_p (wert,0x109);
+}
+
+static byte in_umc (char port)
+{
+ outb_p (port,0x108);
+ return inb_p (0x109);
+}
+
+static void umc_set_speeds (byte speeds[])
+{
+ int i, tmp;
+
+ outb_p (0x5A,0x108); /* enable umc */
+
+ out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
+ out_umc (0xd6,(speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
+ tmp = 0;
+ for (i = 3; i >= 0; i--)
+ {
+ tmp = (tmp << 2) | speedtab[1][speeds[i]];
+ }
+ out_umc (0xdc,tmp);
+ for (i = 0;i < 4; i++)
+ {
+ out_umc (0xd0+i,speedtab[2][speeds[i]]);
+ out_umc (0xd8+i,speedtab[2][speeds[i]]);
+ }
+ outb_p (0xa5,0x108); /* disable umc */
+
+ printk ("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
+ speeds[0], speeds[1], speeds[2], speeds[3]);
+}
+
+static void tune_umc (ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+ ide_hwgroup_t *hwgroup = ide_hwifs[HWIF(drive)->index^1].hwgroup;
+
+ pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+ printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", drive->name, pio, pio_to_umc[pio]);
+ save_flags(flags);
+ cli();
+ if (hwgroup && hwgroup->handler != NULL) {
+ printk("umc8672: other interface is busy: exiting tune_umc()\n");
+ } else {
+ current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
+ umc_set_speeds (current_speeds);
+ }
+ restore_flags(flags);
+}
+
+void init_umc8672 (void) /* called from ide.c */
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli ();
+ if (check_region(0x108, 2)) {
+ restore_flags(flags);
+ printk("\numc8672: PORTS 0x108-0x109 ALREADY IN USE\n");
+ return;
+ }
+ outb_p (0x5A,0x108); /* enable umc */
+ if (in_umc (0xd5) != 0xa0)
+ {
+ restore_flags(flags);
+ printk ("umc8672: not found\n");
+ return;
+ }
+ outb_p (0xa5,0x108); /* disable umc */
+
+ umc_set_speeds (current_speeds);
+ restore_flags(flags);
+
+ request_region(0x108, 2, "umc8672");
+ ide_hwifs[0].chipset = ide_umc8672;
+ ide_hwifs[1].chipset = ide_umc8672;
+ ide_hwifs[0].tuneproc = &tune_umc;
+ ide_hwifs[1].tuneproc = &tune_umc;
+
+}
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index 46cb09957..c85ca6b58 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -17,24 +17,28 @@
* interrupts enabled and Linus didn't want to enable them in that first
* phase. xd_geninit() is the place to do these kinds of things anyway,
* he says.
+ *
+ * Modularized: 04/10/96 by Todd Fries, tfries@umr.edu
+ *
*/
-
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/genhd.h>
-#include <linux/xd.h>
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/dma.h>
#define MAJOR_NR XT_DISK_MAJOR
-#include "blk.h"
+#include <linux/blk.h>
+
+#include "xd.h"
XD_INFO xd_info[XD_MAXDRIVES];
@@ -64,20 +68,22 @@ XD_INFO xd_info[XD_MAXDRIVES];
static XD_SIGNATURE xd_sigs[] = {
{ 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, pat@it.com.au */
+ { 0x000B,"CRD18A Not an IBM rom. (C) Copyright Data Technology Corp. 05/31/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Todd Fries, tfries@umr.edu */
{ 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, pat@it.com.au */
{ 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */
{ 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */
{ 0x0008,"06/24/88(C) Copyright 1988 Western Digital Corp.",xd_wd_init_controller,xd_wd_init_drive," Western Digital WDXT-GEN2" }, /* Dan Newcombe, newcombe@aa.csc.peachnet.edu */
{ 0x0015,"SEAGATE ST11 BIOS REVISION",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Salvador Abreu, spa@fct.unl.pt */
{ 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */
- { 0x0010,"ST11 BIOS V1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */
+ { 0x0010,"ST11 BIOS v1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */
{ 0x1000,"(c)Copyright 1987 SMS",xd_omti_init_controller,xd_omti_init_drive,"n OMTI 5520" }, /* Dirk Melchers, dirk@merlin.nbg.sub.org */
};
-static u_char *xd_bases[] =
+
+static unsigned int xd_bases[] =
{
- (u_char *) 0xC8000,(u_char *) 0xCA000,(u_char *) 0xCC000,
- (u_char *) 0xCE000,(u_char *) 0xD0000,(u_char *) 0xD8000,
- (u_char *) 0xE0000
+ 0xC8000, 0xCA000, 0xCC000,
+ 0xCE000, 0xD0000, 0xD8000,
+ 0xE0000
};
static struct hd_struct xd[XD_MAXDRIVES << 6];
@@ -89,7 +95,11 @@ static struct gendisk xd_gendisk = {
6, /* Bits to shift to get real from partition */
1 << 6, /* Number of partitions per real */
XD_MAXDRIVES, /* maximum number of real */
- xd_geninit, /* init function */
+#ifdef MODULE
+ NULL, /* called from init_module */
+#else
+ xd_geninit, /* init function */
+#endif
xd, /* hd struct */
xd_sizes, /* block sizes */
0, /* number */
@@ -115,35 +125,35 @@ static u_char xd_override = 0, xd_type = 0;
static u_short xd_iobase = 0;
/* xd_init: register the block device number and set up pointer tables */
-u_long xd_init (u_long mem_start,u_long mem_end)
+int xd_init (void)
{
if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) {
printk("xd_init: unable to get major number %d\n",MAJOR_NR);
- return (mem_start);
+ return -1;
}
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
xd_gendisk.next = gendisk_head;
gendisk_head = &xd_gendisk;
- return mem_start;
+ return 0;
}
/* xd_detect: scan the possible BIOS ROM locations for the signature strings */
-static u_char xd_detect (u_char *controller,u_char **address)
+static u_char xd_detect (u_char *controller, unsigned int *address)
{
u_char i,j,found = 0;
if (xd_override)
{
*controller = xd_type;
- *address = NULL;
+ *address = 0;
return(1);
}
for (i = 0; i < (sizeof(xd_bases) / sizeof(xd_bases[0])) && !found; i++)
for (j = 1; j < (sizeof(xd_sigs) / sizeof(xd_sigs[0])) && !found; j++)
- if (!memcmp(xd_bases[i] + xd_sigs[j].offset,xd_sigs[j].string,strlen(xd_sigs[j].string))) {
+ if (check_signature(xd_bases[i] + xd_sigs[j].offset,xd_sigs[j].string,strlen(xd_sigs[j].string))) {
*controller = j;
*address = xd_bases[i];
found++;
@@ -153,13 +163,14 @@ static u_char xd_detect (u_char *controller,u_char **address)
/* xd_geninit: grab the IRQ and DMA channel, initialise the drives */
/* and set up the "raw" device entries in the table */
-static void xd_geninit (void)
+static void xd_geninit (struct gendisk *ignored)
{
- u_char i,controller,*address;
+ u_char i,controller;
+ unsigned int address;
if (xd_detect(&controller,&address)) {
- printk("xd_geninit: detected a%s controller (type %d) at address %p\n",xd_sigs[controller].name,controller,address);
+ printk("xd_geninit: detected a%s controller (type %d) at address %06x\n",xd_sigs[controller].name,controller,address);
if (controller)
xd_sigs[controller].init_controller(address);
xd_drives = xd_initdrives(xd_sigs[controller].init_drive);
@@ -168,10 +179,10 @@ static void xd_geninit (void)
for (i = 0; i < xd_drives; i++)
printk("xd_geninit: drive %d geometry - heads = %d, cylinders = %d, sectors = %d\n",i,xd_info[i].heads,xd_info[i].cylinders,xd_info[i].sectors);
- if (!request_irq(xd_irq,xd_interrupt_handler, 0, "XT harddisk")) {
+ if (!request_irq(xd_irq,xd_interrupt_handler, 0, "XT harddisk", NULL)) {
if (request_dma(xd_dma,"xd")) {
printk("xd_geninit: unable to get DMA%d\n",xd_dma);
- free_irq(xd_irq);
+ free_irq(xd_irq, NULL);
}
}
else
@@ -192,7 +203,7 @@ static void xd_geninit (void)
/* xd_open: open a device */
static int xd_open (struct inode *inode,struct file *file)
{
- int dev = DEVICE_NR(MINOR(inode->i_rdev));
+ int dev = DEVICE_NR(inode->i_rdev);
if (dev < xd_drives) {
while (!xd_valid[dev])
@@ -203,7 +214,7 @@ static int xd_open (struct inode *inode,struct file *file)
return (0);
}
else
- return (-ENODEV);
+ return -ENXIO;
}
/* do_xd_request: handle an incoming request */
@@ -216,8 +227,10 @@ static void do_xd_request (void)
while (code = 0, CURRENT) {
INIT_REQUEST; /* do some checking on the request structure */
- if (CURRENT_DEV < xd_drives && CURRENT->sector + CURRENT->nr_sectors <= xd[MINOR(CURRENT->dev)].nr_sects) {
- block = CURRENT->sector + xd[MINOR(CURRENT->dev)].start_sect;
+ if (CURRENT_DEV < xd_drives
+ && CURRENT->sector + CURRENT->nr_sectors
+ <= xd[MINOR(CURRENT->rq_dev)].nr_sects) {
+ block = CURRENT->sector + xd[MINOR(CURRENT->rq_dev)].start_sect;
count = CURRENT->nr_sectors;
switch (CURRENT->cmd) {
@@ -238,7 +251,7 @@ static void do_xd_request (void)
static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
{
XD_GEOMETRY *geometry = (XD_GEOMETRY *) arg;
- int dev = DEVICE_NR(MINOR(inode->i_rdev)),err;
+ int dev = DEVICE_NR(inode->i_rdev),err;
if (inode && (dev < xd_drives))
switch (cmd) {
@@ -246,32 +259,36 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
if (arg) {
if ((err = verify_area(VERIFY_WRITE,geometry,sizeof(*geometry))))
return (err);
- put_fs_byte(xd_info[dev].heads,(char *) &geometry->heads);
- put_fs_byte(xd_info[dev].sectors,(char *) &geometry->sectors);
- put_fs_word(xd_info[dev].cylinders,(short *) &geometry->cylinders);
- put_fs_long(xd[MINOR(inode->i_rdev)].start_sect,(long *) &geometry->start);
+ put_user(xd_info[dev].heads, &geometry->heads);
+ put_user(xd_info[dev].sectors, &geometry->sectors);
+ put_user(xd_info[dev].cylinders, &geometry->cylinders);
+ put_user(xd[MINOR(inode->i_rdev)].start_sect,&geometry->start);
return (0);
}
break;
case BLKRASET:
- if(!suser()) return -EACCES;
- if(!inode->i_rdev) return -EINVAL;
- if(arg > 0xff) return -EINVAL;
- read_ahead[MAJOR(inode->i_rdev)] = arg;
- return 0;
+ if(!suser())
+ return -EACCES;
+ if(!(inode->i_rdev))
+ return -EINVAL;
+ if(arg > 0xff)
+ return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
case BLKGETSIZE:
if (arg) {
if ((err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long))))
return (err);
- put_fs_long(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
+ put_user(xd[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
return (0);
}
break;
case BLKFLSBUF:
if(!suser()) return -EACCES;
- if(!inode->i_rdev) return -EINVAL;
+ if(!(inode->i_rdev))
+ return -EINVAL;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
@@ -286,29 +303,33 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
/* xd_release: release the device */
static void xd_release (struct inode *inode, struct file *file)
{
- int dev = DEVICE_NR(MINOR(inode->i_rdev));
+ int dev = DEVICE_NR(inode->i_rdev);
if (dev < xd_drives) {
- sync_dev(dev);
+ sync_dev(inode->i_rdev);
xd_access[dev]--;
}
}
/* xd_reread_partitions: rereads the partition table from a drive */
-static int xd_reread_partitions(int dev)
+static int xd_reread_partitions(kdev_t dev)
{
- int target = DEVICE_NR(MINOR(dev)),start = target << xd_gendisk.minor_shift,partition;
+ int target = DEVICE_NR(dev);
+ int start = target << xd_gendisk.minor_shift;
+ int partition;
cli(); xd_valid[target] = (xd_access[target] != 1); sti();
if (xd_valid[target])
return (-EBUSY);
for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) {
- sync_dev(MAJOR_NR << 8 | start | partition);
- invalidate_inodes(MAJOR_NR << 8 | start | partition);
- invalidate_buffers(MAJOR_NR << 8 | start | partition);
- xd_gendisk.part[start + partition].start_sect = 0;
- xd_gendisk.part[start + partition].nr_sects = 0;
+ int minor = (start | partition);
+ kdev_t devp = MKDEV(MAJOR_NR, minor);
+ sync_dev(devp);
+ invalidate_inodes(devp);
+ invalidate_buffers(devp);
+ xd_gendisk.part[minor].start_sect = 0;
+ xd_gendisk.part[minor].nr_sects = 0;
};
xd_gendisk.part[start].nr_sects = xd_info[target].heads * xd_info[target].cylinders * xd_info[target].sectors;
@@ -381,7 +402,7 @@ static void xd_recalibrate (u_char drive)
}
/* xd_interrupt_handler: interrupt service routine */
-static void xd_interrupt_handler(int irq, struct pt_regs * regs)
+static void xd_interrupt_handler(int irq, void *dev_id, struct pt_regs * regs)
{
if (inb(XD_STATUS) & STAT_INTERRUPT) { /* check if it was our device */
#ifdef DEBUG_OTHER
@@ -398,7 +419,7 @@ static void xd_interrupt_handler(int irq, struct pt_regs * regs)
static u_char xd_setup_dma (u_char mode,u_char *buffer,u_int count)
{
if (buffer < ((u_char *) 0x1000000 - count)) { /* transfer to address < 16M? */
- if (((u_int) buffer & 0xFFFF0000) != ((u_int) buffer + count) & 0xFFFF0000) {
+ if (((u_int) buffer & 0xFFFF0000) != (((u_int) buffer + count) & 0xFFFF0000)) {
#ifdef DEBUG_OTHER
printk("xd_setup_dma: using PIO, transfer overlaps 64k boundary\n");
#endif /* DEBUG_OTHER */
@@ -520,12 +541,12 @@ static u_char xd_initdrives (void (*init_drive)(u_char drive))
return (count);
}
-static void xd_dtc_init_controller (u_char *address)
+static void xd_dtc_init_controller (unsigned int address)
{
- switch ((u_long) address) {
+ switch (address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xCA000: xd_iobase = 0x324; break;
- default: printk("xd_dtc_init_controller: unsupported BIOS address %p\n",address);
+ default: printk("xd_dtc_init_controller: unsupported BIOS address %06x\n",address);
xd_iobase = 0x320; break;
}
xd_irq = 5; /* the IRQ _can_ be changed on this card, but requires a hardware mod */
@@ -560,16 +581,16 @@ static void xd_dtc_init_drive (u_char drive)
printk("xd_dtc_init_drive: error reading geometry for drive %d\n",drive);
}
-static void xd_wd_init_controller (u_char *address)
+static void xd_wd_init_controller (unsigned int address)
{
- switch ((u_long) address) {
+ switch (address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xCA000: xd_iobase = 0x324; break;
case 0xCC000: xd_iobase = 0x328; break;
case 0xCE000: xd_iobase = 0x32C; break;
case 0xD0000: xd_iobase = 0x328; break;
case 0xD8000: xd_iobase = 0x32C; break;
- default: printk("xd_wd_init_controller: unsupported BIOS address %p\n",address);
+ default: printk("xd_wd_init_controller: unsupported BIOS address %06x\n",address);
xd_iobase = 0x320; break;
}
xd_irq = 5; /* don't know how to auto-detect this yet */
@@ -601,14 +622,14 @@ static void xd_wd_init_drive (u_char drive)
printk("xd_wd_init_drive: error reading geometry for drive %d\n",drive);
}
-static void xd_seagate_init_controller (u_char *address)
+static void xd_seagate_init_controller (unsigned int address)
{
- switch ((u_long) address) {
+ switch (address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xD0000: xd_iobase = 0x324; break;
case 0xD8000: xd_iobase = 0x328; break;
case 0xE0000: xd_iobase = 0x32C; break;
- default: printk("xd_seagate_init_controller: unsupported BIOS address %p\n",address);
+ default: printk("xd_seagate_init_controller: unsupported BIOS address %06x\n",address);
xd_iobase = 0x320; break;
}
xd_irq = 5; /* the IRQ and DMA channel are fixed on the Seagate controllers */
@@ -634,14 +655,14 @@ static void xd_seagate_init_drive (u_char drive)
}
/* Omti support courtesy Dirk Melchers */
-static void xd_omti_init_controller (u_char *address)
+static void xd_omti_init_controller (unsigned int address)
{
- switch ((u_long) address) {
+ switch (address) {
case 0xC8000: xd_iobase = 0x320; break;
case 0xD0000: xd_iobase = 0x324; break;
case 0xD8000: xd_iobase = 0x328; break;
case 0xE0000: xd_iobase = 0x32C; break;
- default: printk("xd_omti_init_controller: unsupported BIOS address %p\n",address);
+ default: printk("xd_omti_init_controller: unsupported BIOS address %06x\n",address);
xd_iobase = 0x320; break;
}
@@ -717,3 +738,25 @@ static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylind
printk("xd_setparam: error setting characteristics for drive %d\n",drive);
}
+
+#ifdef MODULE
+int init_module(void)
+{
+ int error = xd_init();
+ if (!error)
+ {
+ printk(KERN_INFO "XD: Loaded as a module.\n");
+ xd_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 });
+ }
+
+ return error;
+}
+
+void cleanup_module(void)
+{
+ unregister_blkdev(MAJOR_NR, "xd");
+ free_irq(xd_irq, NULL);
+ free_dma(xd_dma);
+}
+#endif /* MODULE */
+
diff --git a/drivers/block/xd.h b/drivers/block/xd.h
new file mode 100644
index 000000000..02ee8ee0f
--- /dev/null
+++ b/drivers/block/xd.h
@@ -0,0 +1,138 @@
+#ifndef _LINUX_XD_H
+#define _LINUX_XD_H
+
+/*
+ * This file contains the definitions for the IO ports and errors etc. for XT hard disk controllers (at least the DTC 5150X).
+ *
+ * Author: Pat Mackinlay, pat@it.com.au
+ * Date: 29/09/92
+ *
+ * Revised: 01/01/93, ...
+ *
+ * Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, kevinf@agora.rain.com)
+ * Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and Wim Van Dorst.
+ */
+
+/* XT hard disk controller registers */
+#define XD_DATA (xd_iobase + 0x00) /* data RW register */
+#define XD_RESET (xd_iobase + 0x01) /* reset WO register */
+#define XD_STATUS (xd_iobase + 0x01) /* status RO register */
+#define XD_SELECT (xd_iobase + 0x02) /* select WO register */
+#define XD_JUMPER (xd_iobase + 0x02) /* jumper RO register */
+#define XD_CONTROL (xd_iobase + 0x03) /* DMAE/INTE WO register */
+#define XD_RESERVED (xd_iobase + 0x03) /* reserved */
+
+/* XT hard disk controller commands (incomplete list) */
+#define CMD_TESTREADY 0x00 /* test drive ready */
+#define CMD_RECALIBRATE 0x01 /* recalibrate drive */
+#define CMD_SENSE 0x03 /* request sense */
+#define CMD_FORMATDRV 0x04 /* format drive */
+#define CMD_VERIFY 0x05 /* read verify */
+#define CMD_FORMATTRK 0x06 /* format track */
+#define CMD_FORMATBAD 0x07 /* format bad track */
+#define CMD_READ 0x08 /* read */
+#define CMD_WRITE 0x0A /* write */
+#define CMD_SEEK 0x0B /* seek */
+
+/* Controller specific commands */
+#define CMD_DTCSETPARAM 0x0C /* set drive parameters (DTC 5150X only?) */
+#define CMD_DTCGETECC 0x0D /* get ecc error length (DTC 5150X only?) */
+#define CMD_DTCREADBUF 0x0E /* read sector buffer (DTC 5150X only?) */
+#define CMD_DTCWRITEBUF 0x0F /* write sector buffer (DTC 5150X only?) */
+#define CMD_DTCREMAPTRK 0x11 /* assign alternate track (DTC 5150X only?) */
+#define CMD_DTCGETPARAM 0xFB /* get drive parameters (DTC 5150X only?) */
+#define CMD_DTCSETSTEP 0xFC /* set step rate (DTC 5150X only?) */
+#define CMD_DTCSETGEOM 0xFE /* set geometry data (DTC 5150X only?) */
+#define CMD_DTCGETGEOM 0xFF /* get geometry data (DTC 5150X only?) */
+#define CMD_ST11GETGEOM 0xF8 /* get geometry data (Seagate ST11R/M only?) */
+#define CMD_WDSETPARAM 0x0C /* set drive parameters (WD 1004A27X only?) */
+
+/* Bits for command status byte */
+#define CSB_ERROR 0x02 /* error */
+#define CSB_LUN 0x20 /* logical Unit Number */
+
+/* XT hard disk controller status bits */
+#define STAT_READY 0x01 /* controller is ready */
+#define STAT_INPUT 0x02 /* data flowing from controller to host */
+#define STAT_COMMAND 0x04 /* controller in command phase */
+#define STAT_SELECT 0x08 /* controller is selected */
+#define STAT_REQUEST 0x10 /* controller requesting data */
+#define STAT_INTERRUPT 0x20 /* controller requesting interrupt */
+
+/* XT hard disk controller control bits */
+#define PIO_MODE 0x00 /* control bits to set for PIO */
+#define DMA_MODE 0x03 /* control bits to set for DMA & interrupt */
+
+#define XD_MAXDRIVES 2 /* maximum 2 drives */
+#define XD_TIMEOUT HZ /* 1 second timeout */
+#define XD_RETRIES 4 /* maximum 4 retries */
+
+#undef DEBUG /* define for debugging output */
+
+#ifdef DEBUG
+ #define DEBUG_STARTUP /* debug driver initialisation */
+ #define DEBUG_OVERRIDE /* debug override geometry detection */
+ #define DEBUG_READWRITE /* debug each read/write command */
+ #define DEBUG_OTHER /* debug misc. interrupt/DMA stuff */
+ #define DEBUG_COMMAND /* debug each controller command */
+#endif /* DEBUG */
+
+/* this structure defines the XT drives and their types */
+typedef struct {
+ u_char heads;
+ u_short cylinders;
+ u_char sectors;
+ u_char control;
+} XD_INFO;
+
+#define HDIO_GETGEO 0x0301 /* get drive geometry */
+
+/* this structure is returned to the HDIO_GETGEO ioctl */
+typedef struct {
+ __u8 heads;
+ __u8 sectors;
+ __u8 cylinders;
+ __u32 start;
+} XD_GEOMETRY;
+
+/* this structure defines a ROM BIOS signature */
+typedef struct {
+ unsigned int offset;
+ const char *string;
+ void (*init_controller)(unsigned int address);
+ void (*init_drive)(u_char drive);
+ const char *name;
+} XD_SIGNATURE;
+
+void xd_setup (char *command,int *integers);
+static u_char xd_detect (u_char *controller, unsigned int *address);
+static u_char xd_initdrives (void (*init_drive)(u_char drive));
+static void xd_geninit (struct gendisk *);
+
+static int xd_open (struct inode *inode,struct file *file);
+static void do_xd_request (void);
+static int xd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg);
+static void xd_release (struct inode *inode,struct file *file);
+static int xd_reread_partitions (kdev_t dev);
+static int xd_readwrite (u_char operation,u_char drive,char *buffer,u_int block,u_int count);
+static void xd_recalibrate (u_char drive);
+
+static void xd_interrupt_handler (int irq, void *dev_id, struct pt_regs *regs);
+static u_char xd_setup_dma (u_char opcode,u_char *buffer,u_int count);
+static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control);
+static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout);
+static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout);
+
+/* card specific setup and geometry gathering code */
+static void xd_dtc_init_controller (unsigned int address);
+static void xd_dtc_init_drive (u_char drive);
+static void xd_wd_init_controller (unsigned int address);
+static void xd_wd_init_drive (u_char drive);
+static void xd_seagate_init_controller (unsigned int address);
+static void xd_seagate_init_drive (u_char drive);
+static void xd_omti_init_controller (unsigned int address);
+static void xd_omti_init_drive (u_char drive);
+static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc);
+static void xd_override_init_drive (u_char drive);
+
+#endif /* _LINUX_XD_H */