Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
7204
drivers/block/DAC960.c
Normal file
7204
drivers/block/DAC960.c
Normal file
File diff suppressed because it is too large
Load Diff
4422
drivers/block/DAC960.h
Normal file
4422
drivers/block/DAC960.h
Normal file
File diff suppressed because it is too large
Load Diff
458
drivers/block/Kconfig
Normal file
458
drivers/block/Kconfig
Normal file
@@ -0,0 +1,458 @@
|
||||
#
|
||||
# Block device driver configuration
|
||||
#
|
||||
|
||||
if BLOCK
|
||||
|
||||
menu "Block devices"
|
||||
|
||||
config BLK_DEV_FD
|
||||
tristate "Normal floppy disk support"
|
||||
depends on ARCH_MAY_HAVE_PC_FDC
|
||||
---help---
|
||||
If you want to use the floppy disk drive(s) of your PC under Linux,
|
||||
say Y. Information about this driver, especially important for IBM
|
||||
Thinkpad users, is contained in <file:Documentation/floppy.txt>.
|
||||
That file also contains the location of the Floppy driver FAQ as
|
||||
well as location of the fdutils package used to configure additional
|
||||
parameters of the driver at run time.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called floppy.
|
||||
|
||||
config AMIGA_FLOPPY
|
||||
tristate "Amiga floppy support"
|
||||
depends on AMIGA
|
||||
|
||||
config ATARI_FLOPPY
|
||||
tristate "Atari floppy support"
|
||||
depends on ATARI
|
||||
|
||||
config MAC_FLOPPY
|
||||
tristate "Support for PowerMac floppy"
|
||||
depends on PPC_PMAC && !PPC_PMAC64
|
||||
help
|
||||
If you have a SWIM-3 (Super Woz Integrated Machine 3; from Apple)
|
||||
floppy controller, say Y here. Most commonly found in PowerMacs.
|
||||
|
||||
config BLK_DEV_PS2
|
||||
tristate "PS/2 ESDI hard disk support"
|
||||
depends on MCA && MCA_LEGACY && BROKEN
|
||||
help
|
||||
Say Y here if you have a PS/2 machine with a MCA bus and an ESDI
|
||||
hard disk.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ps2esdi.
|
||||
|
||||
config AMIGA_Z2RAM
|
||||
tristate "Amiga Zorro II ramdisk support"
|
||||
depends on ZORRO
|
||||
help
|
||||
This enables support for using Chip RAM and Zorro II RAM as a
|
||||
ramdisk or as a swap partition. Say Y if you want to include this
|
||||
driver in the kernel.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called z2ram.
|
||||
|
||||
config ATARI_ACSI
|
||||
tristate "Atari ACSI support"
|
||||
depends on ATARI && BROKEN
|
||||
---help---
|
||||
This enables support for the Atari ACSI interface. The driver
|
||||
supports hard disks and CD-ROMs, which have 512-byte sectors, or can
|
||||
be switched to that mode. Due to the ACSI command format, only disks
|
||||
up to 1 GB are supported. Special support for certain ACSI to SCSI
|
||||
adapters, which could relax that, isn't included yet. The ACSI
|
||||
driver is also the basis for certain other drivers for devices
|
||||
attached to the ACSI bus: Atari SLM laser printer, BioNet-100
|
||||
Ethernet, and PAMsNet Ethernet. If you want to use one of these
|
||||
devices, you need ACSI support, too.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called acsi.
|
||||
|
||||
comment "Some devices (e.g. CD jukebox) support multiple LUNs"
|
||||
depends on ATARI && ATARI_ACSI
|
||||
|
||||
config ACSI_MULTI_LUN
|
||||
bool "Probe all LUNs on each ACSI device"
|
||||
depends on ATARI_ACSI
|
||||
help
|
||||
If you have a ACSI device that supports more than one LUN (Logical
|
||||
Unit Number), e.g. a CD jukebox, you should say Y here so that all
|
||||
will be found by the ACSI driver. An ACSI device with multiple LUNs
|
||||
acts logically like multiple ACSI devices. The vast majority of ACSI
|
||||
devices have only one LUN, and so most people can say N here and
|
||||
should in fact do so, because it is safer.
|
||||
|
||||
config ATARI_SLM
|
||||
tristate "Atari SLM laser printer support"
|
||||
depends on ATARI && ATARI_ACSI!=n
|
||||
help
|
||||
If you have an Atari SLM laser printer, say Y to include support for
|
||||
it in the kernel. Otherwise, say N. This driver is also available as
|
||||
a module ( = code which can be inserted in and removed from the
|
||||
running kernel whenever you want). The module will be called
|
||||
acsi_slm. Be warned: the driver needs much ST-RAM and can cause
|
||||
problems due to that fact!
|
||||
|
||||
config BLK_DEV_XD
|
||||
tristate "XT hard disk support"
|
||||
depends on ISA && ISA_DMA_API
|
||||
help
|
||||
Very old 8 bit hard disk controllers used in the IBM XT computer
|
||||
will be supported if you say Y here.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called xd.
|
||||
|
||||
It's pretty unlikely that you have one of these: say N.
|
||||
|
||||
config PARIDE
|
||||
tristate "Parallel port IDE device support"
|
||||
depends on PARPORT_PC
|
||||
---help---
|
||||
There are many external CD-ROM and disk devices that connect through
|
||||
your computer's parallel port. Most of them are actually IDE devices
|
||||
using a parallel port IDE adapter. This option enables the PARIDE
|
||||
subsystem which contains drivers for many of these external drives.
|
||||
Read <file:Documentation/paride.txt> for more information.
|
||||
|
||||
If you have said Y to the "Parallel-port support" configuration
|
||||
option, you may share a single port between your printer and other
|
||||
parallel port devices. Answer Y to build PARIDE support into your
|
||||
kernel, or M if you would like to build it as a loadable module. If
|
||||
your parallel port support is in a loadable module, you must build
|
||||
PARIDE as a module. If you built PARIDE support into your kernel,
|
||||
you may still build the individual protocol modules and high-level
|
||||
drivers as loadable modules. If you build this support as a module,
|
||||
it will be called paride.
|
||||
|
||||
To use the PARIDE support, you must say Y or M here and also to at
|
||||
least one high-level driver (e.g. "Parallel port IDE disks",
|
||||
"Parallel port ATAPI CD-ROMs", "Parallel port ATAPI disks" etc.) and
|
||||
to at least one protocol driver (e.g. "ATEN EH-100 protocol",
|
||||
"MicroSolutions backpack protocol", "DataStor Commuter protocol"
|
||||
etc.).
|
||||
|
||||
source "drivers/block/paride/Kconfig"
|
||||
|
||||
config BLK_CPQ_DA
|
||||
tristate "Compaq SMART2 support"
|
||||
depends on PCI
|
||||
help
|
||||
This is the driver for Compaq Smart Array controllers. Everyone
|
||||
using these boards should say Y here. See the file
|
||||
<file:Documentation/cpqarray.txt> for the current list of boards
|
||||
supported by this driver, and for further information on the use of
|
||||
this driver.
|
||||
|
||||
config BLK_CPQ_CISS_DA
|
||||
tristate "Compaq Smart Array 5xxx support"
|
||||
depends on PCI
|
||||
help
|
||||
This is the driver for Compaq Smart Array 5xxx controllers.
|
||||
Everyone using these boards should say Y here.
|
||||
See <file:Documentation/cciss.txt> for the current list of
|
||||
boards supported by this driver, and for further information
|
||||
on the use of this driver.
|
||||
|
||||
config CISS_SCSI_TAPE
|
||||
bool "SCSI tape drive support for Smart Array 5xxx"
|
||||
depends on BLK_CPQ_CISS_DA && PROC_FS
|
||||
depends on SCSI=y || SCSI=BLK_CPQ_CISS_DA
|
||||
help
|
||||
When enabled (Y), this option allows SCSI tape drives and SCSI medium
|
||||
changers (tape robots) to be accessed via a Compaq 5xxx array
|
||||
controller. (See <file:Documentation/cciss.txt> for more details.)
|
||||
|
||||
"SCSI support" and "SCSI tape support" must also be enabled for this
|
||||
option to work.
|
||||
|
||||
When this option is disabled (N), the SCSI portion of the driver
|
||||
is not compiled.
|
||||
|
||||
config BLK_DEV_DAC960
|
||||
tristate "Mylex DAC960/DAC1100 PCI RAID Controller support"
|
||||
depends on PCI
|
||||
help
|
||||
This driver adds support for the Mylex DAC960, AcceleRAID, and
|
||||
eXtremeRAID PCI RAID controllers. See the file
|
||||
<file:Documentation/README.DAC960> for further information about
|
||||
this driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called DAC960.
|
||||
|
||||
config BLK_DEV_UMEM
|
||||
tristate "Micro Memory MM5415 Battery Backed RAM support (EXPERIMENTAL)"
|
||||
depends on PCI && EXPERIMENTAL
|
||||
---help---
|
||||
Saying Y here will include support for the MM5415 family of
|
||||
battery backed (Non-volatile) RAM cards.
|
||||
<http://www.umem.com/>
|
||||
|
||||
The cards appear as block devices that can be partitioned into
|
||||
as many as 15 partitions.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called umem.
|
||||
|
||||
The umem driver has not yet been allocated a MAJOR number, so
|
||||
one is chosen dynamically.
|
||||
|
||||
config BLK_DEV_UBD
|
||||
bool "Virtual block device"
|
||||
depends on UML
|
||||
---help---
|
||||
The User-Mode Linux port includes a driver called UBD which will let
|
||||
you access arbitrary files on the host computer as block devices.
|
||||
Unless you know that you do not need such virtual block devices say
|
||||
Y here.
|
||||
|
||||
config BLK_DEV_UBD_SYNC
|
||||
bool "Always do synchronous disk IO for UBD"
|
||||
depends on BLK_DEV_UBD
|
||||
---help---
|
||||
Writes to the virtual block device are not immediately written to the
|
||||
host's disk; this may cause problems if, for example, the User-Mode
|
||||
Linux 'Virtual Machine' uses a journalling filesystem and the host
|
||||
computer crashes.
|
||||
|
||||
Synchronous operation (i.e. always writing data to the host's disk
|
||||
immediately) is configurable on a per-UBD basis by using a special
|
||||
kernel command line option. Alternatively, you can say Y here to
|
||||
turn on synchronous operation by default for all block devices.
|
||||
|
||||
If you're running a journalling file system (like reiserfs, for
|
||||
example) in your virtual machine, you will want to say Y here. If
|
||||
you care for the safety of the data in your virtual machine, Y is a
|
||||
wise choice too. In all other cases (for example, if you're just
|
||||
playing around with User-Mode Linux) you can choose N.
|
||||
|
||||
config BLK_DEV_COW_COMMON
|
||||
bool
|
||||
default BLK_DEV_UBD
|
||||
|
||||
config MMAPPER
|
||||
tristate "Example IO memory driver (BROKEN)"
|
||||
depends on UML && BROKEN
|
||||
---help---
|
||||
The User-Mode Linux port can provide support for IO Memory
|
||||
emulation with this option. This allows a host file to be
|
||||
specified as an I/O region on the kernel command line. That file
|
||||
will be mapped into UML's kernel address space where a driver can
|
||||
locate it and do whatever it wants with the memory, including
|
||||
providing an interface to it for UML processes to use.
|
||||
|
||||
For more information, see
|
||||
<http://user-mode-linux.sourceforge.net/iomem.html>.
|
||||
|
||||
If you'd like to be able to provide a simulated IO port space for
|
||||
User-Mode Linux processes, say Y. If unsure, say N.
|
||||
|
||||
config BLK_DEV_LOOP
|
||||
tristate "Loopback device support"
|
||||
---help---
|
||||
Saying Y here will allow you to use a regular file as a block
|
||||
device; you can then create a file system on that block device and
|
||||
mount it just as you would mount other block devices such as hard
|
||||
drive partitions, CD-ROM drives or floppy drives. The loop devices
|
||||
are block special device files with major number 7 and typically
|
||||
called /dev/loop0, /dev/loop1 etc.
|
||||
|
||||
This is useful if you want to check an ISO 9660 file system before
|
||||
burning the CD, or if you want to use floppy images without first
|
||||
writing them to floppy. Furthermore, some Linux distributions avoid
|
||||
the need for a dedicated Linux partition by keeping their complete
|
||||
root file system inside a DOS FAT file using this loop device
|
||||
driver.
|
||||
|
||||
To use the loop device, you need the losetup utility, found in the
|
||||
util-linux package, see
|
||||
<ftp://ftp.kernel.org/pub/linux/utils/util-linux/>.
|
||||
|
||||
The loop device driver can also be used to "hide" a file system in
|
||||
a disk partition, floppy, or regular file, either using encryption
|
||||
(scrambling the data) or steganography (hiding the data in the low
|
||||
bits of, say, a sound file). This is also safe if the file resides
|
||||
on a remote file server.
|
||||
|
||||
There are several ways of encrypting disks. Some of these require
|
||||
kernel patches. The vanilla kernel offers the cryptoloop option
|
||||
and a Device Mapper target (which is superior, as it supports all
|
||||
file systems). If you want to use the cryptoloop, say Y to both
|
||||
LOOP and CRYPTOLOOP, and make sure you have a recent (version 2.12
|
||||
or later) version of util-linux. Additionally, be aware that
|
||||
the cryptoloop is not safe for storing journaled filesystems.
|
||||
|
||||
Note that this loop device has nothing to do with the loopback
|
||||
device used for network connections from the machine to itself.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called loop.
|
||||
|
||||
Most users will answer N here.
|
||||
|
||||
config BLK_DEV_CRYPTOLOOP
|
||||
tristate "Cryptoloop Support"
|
||||
select CRYPTO
|
||||
select CRYPTO_CBC
|
||||
depends on BLK_DEV_LOOP
|
||||
---help---
|
||||
Say Y here if you want to be able to use the ciphers that are
|
||||
provided by the CryptoAPI as loop transformation. This might be
|
||||
used as hard disk encryption.
|
||||
|
||||
WARNING: This device is not safe for journaled file systems like
|
||||
ext3 or Reiserfs. Please use the Device Mapper crypto module
|
||||
instead, which can be configured to be on-disk compatible with the
|
||||
cryptoloop device.
|
||||
|
||||
config BLK_DEV_NBD
|
||||
tristate "Network block device support"
|
||||
depends on NET
|
||||
---help---
|
||||
Saying Y here will allow your computer to be a client for network
|
||||
block devices, i.e. it will be able to use block devices exported by
|
||||
servers (mount file systems on them etc.). Communication between
|
||||
client and server works over TCP/IP networking, but to the client
|
||||
program this is hidden: it looks like a regular local file access to
|
||||
a block device special file such as /dev/nd0.
|
||||
|
||||
Network block devices also allows you to run a block-device in
|
||||
userland (making server and client physically the same computer,
|
||||
communicating using the loopback network device).
|
||||
|
||||
Read <file:Documentation/nbd.txt> for more information, especially
|
||||
about where to find the server code, which runs in user space and
|
||||
does not need special kernel support.
|
||||
|
||||
Note that this has nothing to do with the network file systems NFS
|
||||
or Coda; you can say N here even if you intend to use NFS or Coda.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nbd.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config BLK_DEV_SX8
|
||||
tristate "Promise SATA SX8 support"
|
||||
depends on PCI
|
||||
---help---
|
||||
Saying Y or M here will enable support for the
|
||||
Promise SATA SX8 controllers.
|
||||
|
||||
Use devices /dev/sx8/$N and /dev/sx8/$Np$M.
|
||||
|
||||
config BLK_DEV_UB
|
||||
tristate "Low Performance USB Block driver"
|
||||
depends on USB
|
||||
help
|
||||
This driver supports certain USB attached storage devices
|
||||
such as flash keys.
|
||||
|
||||
If you enable this driver, it is recommended to avoid conflicts
|
||||
with usb-storage by enabling USB_LIBUSUAL.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config BLK_DEV_RAM
|
||||
tristate "RAM disk support"
|
||||
---help---
|
||||
Saying Y here will allow you to use a portion of your RAM memory as
|
||||
a block device, so that you can make file systems on it, read and
|
||||
write to it and do all the other things that you can do with normal
|
||||
block devices (such as hard drives). It is usually used to load and
|
||||
store a copy of a minimal root file system off of a floppy into RAM
|
||||
during the initial install of Linux.
|
||||
|
||||
Note that the kernel command line option "ramdisk=XX" is now
|
||||
obsolete. For details, read <file:Documentation/ramdisk.txt>.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rd.
|
||||
|
||||
Most normal users won't need the RAM disk functionality, and can
|
||||
thus say N here.
|
||||
|
||||
config BLK_DEV_RAM_COUNT
|
||||
int "Default number of RAM disks"
|
||||
default "16"
|
||||
depends on BLK_DEV_RAM
|
||||
help
|
||||
The default value is 16 RAM disks. Change this if you know what
|
||||
are doing. If you boot from a filesystem that needs to be extracted
|
||||
in memory, you will need at least one RAM disk (e.g. root on cramfs).
|
||||
|
||||
config BLK_DEV_RAM_SIZE
|
||||
int "Default RAM disk size (kbytes)"
|
||||
depends on BLK_DEV_RAM
|
||||
default "4096"
|
||||
help
|
||||
The default value is 4096 kilobytes. Only change this if you know
|
||||
what are you doing. If you are using IBM S/390, then set this to
|
||||
8192.
|
||||
|
||||
config BLK_DEV_RAM_BLOCKSIZE
|
||||
int "Default RAM disk block size (bytes)"
|
||||
depends on BLK_DEV_RAM
|
||||
default "1024"
|
||||
help
|
||||
The default value is 1024 bytes. PAGE_SIZE is a much more
|
||||
efficient choice however. The default is kept to ensure initrd
|
||||
setups function - apparently needed by the rd_load_image routine
|
||||
that supposes the filesystem in the image uses a 1024 blocksize.
|
||||
|
||||
config CDROM_PKTCDVD
|
||||
tristate "Packet writing on CD/DVD media"
|
||||
depends on !UML
|
||||
help
|
||||
If you have a CDROM/DVD drive that supports packet writing, say
|
||||
Y to include support. It should work with any MMC/Mt Fuji
|
||||
compliant ATAPI or SCSI drive, which is just about any newer
|
||||
DVD/CD writer.
|
||||
|
||||
Currently only writing to CD-RW, DVD-RW, DVD+RW and DVDRAM discs
|
||||
is possible.
|
||||
DVD-RW disks must be in restricted overwrite mode.
|
||||
|
||||
See the file <file:Documentation/cdrom/packet-writing.txt>
|
||||
for further information on the use of this driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called pktcdvd.
|
||||
|
||||
config CDROM_PKTCDVD_BUFFERS
|
||||
int "Free buffers for data gathering"
|
||||
depends on CDROM_PKTCDVD
|
||||
default "8"
|
||||
help
|
||||
This controls the maximum number of active concurrent packets. More
|
||||
concurrent packets can increase write performance, but also require
|
||||
more memory. Each concurrent packet will require approximately 64Kb
|
||||
of non-swappable kernel memory, memory which will be allocated when
|
||||
a disc is opened for writing.
|
||||
|
||||
config CDROM_PKTCDVD_WCACHE
|
||||
bool "Enable write caching (EXPERIMENTAL)"
|
||||
depends on CDROM_PKTCDVD && EXPERIMENTAL
|
||||
help
|
||||
If enabled, write caching will be set for the CD-R/W device. For now
|
||||
this option is dangerous unless the CD-RW media is known good, as we
|
||||
don't do deferred write error handling yet.
|
||||
|
||||
source "drivers/s390/block/Kconfig"
|
||||
|
||||
config ATA_OVER_ETH
|
||||
tristate "ATA over Ethernet support"
|
||||
depends on NET
|
||||
help
|
||||
This driver provides Support for ATA over Ethernet block
|
||||
devices like the Coraid EtherDrive (R) Storage Blade.
|
||||
|
||||
endmenu
|
||||
|
||||
endif
|
||||
31
drivers/block/Makefile
Normal file
31
drivers/block/Makefile
Normal file
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# Makefile for the kernel block device drivers.
|
||||
#
|
||||
# 12 June 2000, Christoph Hellwig <hch@infradead.org>
|
||||
# Rewritten to use lists instead of if-statements.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MAC_FLOPPY) += swim3.o
|
||||
obj-$(CONFIG_BLK_DEV_FD) += floppy.o
|
||||
obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o
|
||||
obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
|
||||
obj-$(CONFIG_ATARI_ACSI) += acsi.o
|
||||
obj-$(CONFIG_ATARI_SLM) += acsi_slm.o
|
||||
obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
|
||||
obj-$(CONFIG_BLK_DEV_RAM) += rd.o
|
||||
obj-$(CONFIG_BLK_DEV_LOOP) += loop.o
|
||||
obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o
|
||||
obj-$(CONFIG_BLK_DEV_XD) += xd.o
|
||||
obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o
|
||||
obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
|
||||
obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o
|
||||
obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o
|
||||
|
||||
obj-$(CONFIG_BLK_DEV_UMEM) += umem.o
|
||||
obj-$(CONFIG_BLK_DEV_NBD) += nbd.o
|
||||
obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) += cryptoloop.o
|
||||
|
||||
obj-$(CONFIG_VIODASD) += viodasd.o
|
||||
obj-$(CONFIG_BLK_DEV_SX8) += sx8.o
|
||||
obj-$(CONFIG_BLK_DEV_UB) += ub.o
|
||||
|
||||
1825
drivers/block/acsi.c
Normal file
1825
drivers/block/acsi.c
Normal file
File diff suppressed because it is too large
Load Diff
1033
drivers/block/acsi_slm.c
Normal file
1033
drivers/block/acsi_slm.c
Normal file
File diff suppressed because it is too large
Load Diff
1855
drivers/block/amiflop.c
Normal file
1855
drivers/block/amiflop.c
Normal file
File diff suppressed because it is too large
Load Diff
6
drivers/block/aoe/Makefile
Normal file
6
drivers/block/aoe/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile for ATA over Ethernet
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ATA_OVER_ETH) += aoe.o
|
||||
aoe-objs := aoeblk.o aoechr.o aoecmd.o aoedev.o aoemain.o aoenet.o
|
||||
178
drivers/block/aoe/aoe.h
Normal file
178
drivers/block/aoe/aoe.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
|
||||
#define VERSION "32"
|
||||
#define AOE_MAJOR 152
|
||||
#define DEVICE_NAME "aoe"
|
||||
|
||||
/* set AOE_PARTITIONS to 1 to use whole-disks only
|
||||
* default is 16, which is 15 partitions plus the whole disk
|
||||
*/
|
||||
#ifndef AOE_PARTITIONS
|
||||
#define AOE_PARTITIONS (16)
|
||||
#endif
|
||||
|
||||
#define SYSMINOR(aoemajor, aoeminor) ((aoemajor) * NPERSHELF + (aoeminor))
|
||||
#define AOEMAJOR(sysminor) ((sysminor) / NPERSHELF)
|
||||
#define AOEMINOR(sysminor) ((sysminor) % NPERSHELF)
|
||||
#define WHITESPACE " \t\v\f\n"
|
||||
|
||||
enum {
|
||||
AOECMD_ATA,
|
||||
AOECMD_CFG,
|
||||
|
||||
AOEFL_RSP = (1<<3),
|
||||
AOEFL_ERR = (1<<2),
|
||||
|
||||
AOEAFL_EXT = (1<<6),
|
||||
AOEAFL_DEV = (1<<4),
|
||||
AOEAFL_ASYNC = (1<<1),
|
||||
AOEAFL_WRITE = (1<<0),
|
||||
|
||||
AOECCMD_READ = 0,
|
||||
AOECCMD_TEST,
|
||||
AOECCMD_PTEST,
|
||||
AOECCMD_SET,
|
||||
AOECCMD_FSET,
|
||||
|
||||
AOE_HVER = 0x10,
|
||||
};
|
||||
|
||||
struct aoe_hdr {
|
||||
unsigned char dst[6];
|
||||
unsigned char src[6];
|
||||
__be16 type;
|
||||
unsigned char verfl;
|
||||
unsigned char err;
|
||||
__be16 major;
|
||||
unsigned char minor;
|
||||
unsigned char cmd;
|
||||
__be32 tag;
|
||||
};
|
||||
|
||||
struct aoe_atahdr {
|
||||
unsigned char aflags;
|
||||
unsigned char errfeat;
|
||||
unsigned char scnt;
|
||||
unsigned char cmdstat;
|
||||
unsigned char lba0;
|
||||
unsigned char lba1;
|
||||
unsigned char lba2;
|
||||
unsigned char lba3;
|
||||
unsigned char lba4;
|
||||
unsigned char lba5;
|
||||
unsigned char res[2];
|
||||
};
|
||||
|
||||
struct aoe_cfghdr {
|
||||
__be16 bufcnt;
|
||||
__be16 fwver;
|
||||
unsigned char scnt;
|
||||
unsigned char aoeccmd;
|
||||
unsigned char cslen[2];
|
||||
};
|
||||
|
||||
enum {
|
||||
DEVFL_UP = 1, /* device is installed in system and ready for AoE->ATA commands */
|
||||
DEVFL_TKILL = (1<<1), /* flag for timer to know when to kill self */
|
||||
DEVFL_EXT = (1<<2), /* device accepts lba48 commands */
|
||||
DEVFL_CLOSEWAIT = (1<<3), /* device is waiting for all closes to revalidate */
|
||||
DEVFL_GDALLOC = (1<<4), /* need to alloc gendisk */
|
||||
DEVFL_PAUSE = (1<<5),
|
||||
DEVFL_NEWSIZE = (1<<6), /* need to update dev size in block layer */
|
||||
DEVFL_MAXBCNT = (1<<7), /* d->maxbcnt is not changeable */
|
||||
DEVFL_KICKME = (1<<8),
|
||||
|
||||
BUFFL_FAIL = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
DEFAULTBCNT = 2 * 512, /* 2 sectors */
|
||||
NPERSHELF = 16, /* number of slots per shelf address */
|
||||
FREETAG = -1,
|
||||
MIN_BUFS = 8,
|
||||
};
|
||||
|
||||
struct buf {
|
||||
struct list_head bufs;
|
||||
ulong start_time; /* for disk stats */
|
||||
ulong flags;
|
||||
ulong nframesout;
|
||||
char *bufaddr;
|
||||
ulong resid;
|
||||
ulong bv_resid;
|
||||
sector_t sector;
|
||||
struct bio *bio;
|
||||
struct bio_vec *bv;
|
||||
};
|
||||
|
||||
struct frame {
|
||||
int tag;
|
||||
ulong waited;
|
||||
struct buf *buf;
|
||||
char *bufaddr;
|
||||
ulong bcnt;
|
||||
sector_t lba;
|
||||
struct sk_buff *skb;
|
||||
};
|
||||
|
||||
struct aoedev {
|
||||
struct aoedev *next;
|
||||
unsigned char addr[6]; /* remote mac addr */
|
||||
ushort flags;
|
||||
ulong sysminor;
|
||||
ulong aoemajor;
|
||||
ulong aoeminor;
|
||||
u16 nopen; /* (bd_openers isn't available without sleeping) */
|
||||
u16 lasttag; /* last tag sent */
|
||||
u16 rttavg; /* round trip average of requests/responses */
|
||||
u16 mintimer;
|
||||
u16 fw_ver; /* version of blade's firmware */
|
||||
u16 maxbcnt;
|
||||
struct work_struct work;/* disk create work struct */
|
||||
struct gendisk *gd;
|
||||
request_queue_t blkq;
|
||||
struct hd_geometry geo;
|
||||
sector_t ssize;
|
||||
struct timer_list timer;
|
||||
spinlock_t lock;
|
||||
struct net_device *ifp; /* interface ed is attached to */
|
||||
struct sk_buff *sendq_hd; /* packets needing to be sent, list head */
|
||||
struct sk_buff *sendq_tl;
|
||||
mempool_t *bufpool; /* for deadlock-free Buf allocation */
|
||||
struct list_head bufq; /* queue of bios to work on */
|
||||
struct buf *inprocess; /* the one we're currently working on */
|
||||
ushort lostjumbo;
|
||||
ushort nframes; /* number of frames below */
|
||||
struct frame *frames;
|
||||
};
|
||||
|
||||
|
||||
int aoeblk_init(void);
|
||||
void aoeblk_exit(void);
|
||||
void aoeblk_gdalloc(void *);
|
||||
void aoedisk_rm_sysfs(struct aoedev *d);
|
||||
|
||||
int aoechr_init(void);
|
||||
void aoechr_exit(void);
|
||||
void aoechr_error(char *);
|
||||
|
||||
void aoecmd_work(struct aoedev *d);
|
||||
void aoecmd_cfg(ushort aoemajor, unsigned char aoeminor);
|
||||
void aoecmd_ata_rsp(struct sk_buff *);
|
||||
void aoecmd_cfg_rsp(struct sk_buff *);
|
||||
void aoecmd_sleepwork(struct work_struct *);
|
||||
struct sk_buff *new_skb(ulong);
|
||||
|
||||
int aoedev_init(void);
|
||||
void aoedev_exit(void);
|
||||
struct aoedev *aoedev_by_aoeaddr(int maj, int min);
|
||||
struct aoedev *aoedev_by_sysminor_m(ulong sysminor, ulong bufcnt);
|
||||
void aoedev_downdev(struct aoedev *d);
|
||||
int aoedev_isbusy(struct aoedev *d);
|
||||
|
||||
int aoenet_init(void);
|
||||
void aoenet_exit(void);
|
||||
void aoenet_xmit(struct sk_buff *);
|
||||
int is_aoe_netif(struct net_device *ifp);
|
||||
int set_aoe_iflist(const char __user *str, size_t size);
|
||||
|
||||
u64 mac_addr(char addr[6]);
|
||||
268
drivers/block/aoe/aoeblk.c
Normal file
268
drivers/block/aoe/aoeblk.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
|
||||
/*
|
||||
* aoeblk.c
|
||||
* block device routines
|
||||
*/
|
||||
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include "aoe.h"
|
||||
|
||||
static struct kmem_cache *buf_pool_cache;
|
||||
|
||||
static ssize_t aoedisk_show_state(struct gendisk * disk, char *page)
|
||||
{
|
||||
struct aoedev *d = disk->private_data;
|
||||
|
||||
return snprintf(page, PAGE_SIZE,
|
||||
"%s%s\n",
|
||||
(d->flags & DEVFL_UP) ? "up" : "down",
|
||||
(d->flags & DEVFL_PAUSE) ? ",paused" :
|
||||
(d->nopen && !(d->flags & DEVFL_UP)) ? ",closewait" : "");
|
||||
/* I'd rather see nopen exported so we can ditch closewait */
|
||||
}
|
||||
static ssize_t aoedisk_show_mac(struct gendisk * disk, char *page)
|
||||
{
|
||||
struct aoedev *d = disk->private_data;
|
||||
|
||||
return snprintf(page, PAGE_SIZE, "%012llx\n",
|
||||
(unsigned long long)mac_addr(d->addr));
|
||||
}
|
||||
static ssize_t aoedisk_show_netif(struct gendisk * disk, char *page)
|
||||
{
|
||||
struct aoedev *d = disk->private_data;
|
||||
|
||||
return snprintf(page, PAGE_SIZE, "%s\n", d->ifp->name);
|
||||
}
|
||||
/* firmware version */
|
||||
static ssize_t aoedisk_show_fwver(struct gendisk * disk, char *page)
|
||||
{
|
||||
struct aoedev *d = disk->private_data;
|
||||
|
||||
return snprintf(page, PAGE_SIZE, "0x%04x\n", (unsigned int) d->fw_ver);
|
||||
}
|
||||
|
||||
static struct disk_attribute disk_attr_state = {
|
||||
.attr = {.name = "state", .mode = S_IRUGO },
|
||||
.show = aoedisk_show_state
|
||||
};
|
||||
static struct disk_attribute disk_attr_mac = {
|
||||
.attr = {.name = "mac", .mode = S_IRUGO },
|
||||
.show = aoedisk_show_mac
|
||||
};
|
||||
static struct disk_attribute disk_attr_netif = {
|
||||
.attr = {.name = "netif", .mode = S_IRUGO },
|
||||
.show = aoedisk_show_netif
|
||||
};
|
||||
static struct disk_attribute disk_attr_fwver = {
|
||||
.attr = {.name = "firmware-version", .mode = S_IRUGO },
|
||||
.show = aoedisk_show_fwver
|
||||
};
|
||||
|
||||
static struct attribute *aoe_attrs[] = {
|
||||
&disk_attr_state.attr,
|
||||
&disk_attr_mac.attr,
|
||||
&disk_attr_netif.attr,
|
||||
&disk_attr_fwver.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group attr_group = {
|
||||
.attrs = aoe_attrs,
|
||||
};
|
||||
|
||||
static int
|
||||
aoedisk_add_sysfs(struct aoedev *d)
|
||||
{
|
||||
return sysfs_create_group(&d->gd->kobj, &attr_group);
|
||||
}
|
||||
void
|
||||
aoedisk_rm_sysfs(struct aoedev *d)
|
||||
{
|
||||
sysfs_remove_group(&d->gd->kobj, &attr_group);
|
||||
}
|
||||
|
||||
static int
|
||||
aoeblk_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct aoedev *d;
|
||||
ulong flags;
|
||||
|
||||
d = inode->i_bdev->bd_disk->private_data;
|
||||
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
if (d->flags & DEVFL_UP) {
|
||||
d->nopen++;
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int
|
||||
aoeblk_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct aoedev *d;
|
||||
ulong flags;
|
||||
|
||||
d = inode->i_bdev->bd_disk->private_data;
|
||||
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
|
||||
if (--d->nopen == 0) {
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
aoecmd_cfg(d->aoemajor, d->aoeminor);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aoeblk_make_request(request_queue_t *q, struct bio *bio)
|
||||
{
|
||||
struct aoedev *d;
|
||||
struct buf *buf;
|
||||
struct sk_buff *sl;
|
||||
ulong flags;
|
||||
|
||||
blk_queue_bounce(q, &bio);
|
||||
|
||||
d = bio->bi_bdev->bd_disk->private_data;
|
||||
buf = mempool_alloc(d->bufpool, GFP_NOIO);
|
||||
if (buf == NULL) {
|
||||
printk(KERN_INFO "aoe: buf allocation failure\n");
|
||||
bio_endio(bio, bio->bi_size, -ENOMEM);
|
||||
return 0;
|
||||
}
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
INIT_LIST_HEAD(&buf->bufs);
|
||||
buf->start_time = jiffies;
|
||||
buf->bio = bio;
|
||||
buf->resid = bio->bi_size;
|
||||
buf->sector = bio->bi_sector;
|
||||
buf->bv = &bio->bi_io_vec[bio->bi_idx];
|
||||
WARN_ON(buf->bv->bv_len == 0);
|
||||
buf->bv_resid = buf->bv->bv_len;
|
||||
buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
|
||||
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
|
||||
if ((d->flags & DEVFL_UP) == 0) {
|
||||
printk(KERN_INFO "aoe: device %ld.%ld is not up\n",
|
||||
d->aoemajor, d->aoeminor);
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
mempool_free(buf, d->bufpool);
|
||||
bio_endio(bio, bio->bi_size, -ENXIO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_add_tail(&buf->bufs, &d->bufq);
|
||||
|
||||
aoecmd_work(d);
|
||||
sl = d->sendq_hd;
|
||||
d->sendq_hd = d->sendq_tl = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
aoenet_xmit(sl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aoeblk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
||||
{
|
||||
struct aoedev *d = bdev->bd_disk->private_data;
|
||||
|
||||
if ((d->flags & DEVFL_UP) == 0) {
|
||||
printk(KERN_ERR "aoe: disk not up\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
geo->cylinders = d->geo.cylinders;
|
||||
geo->heads = d->geo.heads;
|
||||
geo->sectors = d->geo.sectors;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct block_device_operations aoe_bdops = {
|
||||
.open = aoeblk_open,
|
||||
.release = aoeblk_release,
|
||||
.getgeo = aoeblk_getgeo,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* alloc_disk and add_disk can sleep */
|
||||
void
|
||||
aoeblk_gdalloc(void *vp)
|
||||
{
|
||||
struct aoedev *d = vp;
|
||||
struct gendisk *gd;
|
||||
ulong flags;
|
||||
|
||||
gd = alloc_disk(AOE_PARTITIONS);
|
||||
if (gd == NULL) {
|
||||
printk(KERN_ERR "aoe: cannot allocate disk structure for %ld.%ld\n",
|
||||
d->aoemajor, d->aoeminor);
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
d->flags &= ~DEVFL_GDALLOC;
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
d->bufpool = mempool_create_slab_pool(MIN_BUFS, buf_pool_cache);
|
||||
if (d->bufpool == NULL) {
|
||||
printk(KERN_ERR "aoe: cannot allocate bufpool for %ld.%ld\n",
|
||||
d->aoemajor, d->aoeminor);
|
||||
put_disk(gd);
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
d->flags &= ~DEVFL_GDALLOC;
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
blk_queue_make_request(&d->blkq, aoeblk_make_request);
|
||||
gd->major = AOE_MAJOR;
|
||||
gd->first_minor = d->sysminor * AOE_PARTITIONS;
|
||||
gd->fops = &aoe_bdops;
|
||||
gd->private_data = d;
|
||||
gd->capacity = d->ssize;
|
||||
snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%ld",
|
||||
d->aoemajor, d->aoeminor);
|
||||
|
||||
gd->queue = &d->blkq;
|
||||
d->gd = gd;
|
||||
d->flags &= ~DEVFL_GDALLOC;
|
||||
d->flags |= DEVFL_UP;
|
||||
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
|
||||
add_disk(gd);
|
||||
aoedisk_add_sysfs(d);
|
||||
}
|
||||
|
||||
void
|
||||
aoeblk_exit(void)
|
||||
{
|
||||
kmem_cache_destroy(buf_pool_cache);
|
||||
}
|
||||
|
||||
int __init
|
||||
aoeblk_init(void)
|
||||
{
|
||||
buf_pool_cache = kmem_cache_create("aoe_bufs",
|
||||
sizeof(struct buf),
|
||||
0, 0, NULL, NULL);
|
||||
if (buf_pool_cache == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
279
drivers/block/aoe/aoechr.c
Normal file
279
drivers/block/aoe/aoechr.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
|
||||
/*
|
||||
* aoechr.c
|
||||
* AoE character device driver
|
||||
*/
|
||||
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include "aoe.h"
|
||||
|
||||
enum {
|
||||
//MINOR_STAT = 1, (moved to sysfs)
|
||||
MINOR_ERR = 2,
|
||||
MINOR_DISCOVER,
|
||||
MINOR_INTERFACES,
|
||||
MINOR_REVALIDATE,
|
||||
MSGSZ = 2048,
|
||||
NMSG = 100, /* message backlog to retain */
|
||||
};
|
||||
|
||||
struct aoe_chardev {
|
||||
ulong minor;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
enum { EMFL_VALID = 1 };
|
||||
|
||||
struct ErrMsg {
|
||||
short flags;
|
||||
short len;
|
||||
char *msg;
|
||||
};
|
||||
|
||||
static struct ErrMsg emsgs[NMSG];
|
||||
static int emsgs_head_idx, emsgs_tail_idx;
|
||||
static struct semaphore emsgs_sema;
|
||||
static spinlock_t emsgs_lock;
|
||||
static int nblocked_emsgs_readers;
|
||||
static struct class *aoe_class;
|
||||
static struct aoe_chardev chardevs[] = {
|
||||
{ MINOR_ERR, "err" },
|
||||
{ MINOR_DISCOVER, "discover" },
|
||||
{ MINOR_INTERFACES, "interfaces" },
|
||||
{ MINOR_REVALIDATE, "revalidate" },
|
||||
};
|
||||
|
||||
static int
|
||||
discover(void)
|
||||
{
|
||||
aoecmd_cfg(0xffff, 0xff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
interfaces(const char __user *str, size_t size)
|
||||
{
|
||||
if (set_aoe_iflist(str, size)) {
|
||||
printk(KERN_ERR
|
||||
"aoe: could not set interface list: too many interfaces\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
revalidate(const char __user *str, size_t size)
|
||||
{
|
||||
int major, minor, n;
|
||||
ulong flags;
|
||||
struct aoedev *d;
|
||||
char buf[16];
|
||||
|
||||
if (size >= sizeof buf)
|
||||
return -EINVAL;
|
||||
buf[sizeof buf - 1] = '\0';
|
||||
if (copy_from_user(buf, str, size))
|
||||
return -EFAULT;
|
||||
|
||||
/* should be e%d.%d format */
|
||||
n = sscanf(buf, "e%d.%d", &major, &minor);
|
||||
if (n != 2) {
|
||||
printk(KERN_ERR "aoe: invalid device specification\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
d = aoedev_by_aoeaddr(major, minor);
|
||||
if (!d)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
d->flags &= ~DEVFL_MAXBCNT;
|
||||
d->flags |= DEVFL_PAUSE;
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
aoecmd_cfg(major, minor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
aoechr_error(char *msg)
|
||||
{
|
||||
struct ErrMsg *em;
|
||||
char *mp;
|
||||
ulong flags, n;
|
||||
|
||||
n = strlen(msg);
|
||||
|
||||
spin_lock_irqsave(&emsgs_lock, flags);
|
||||
|
||||
em = emsgs + emsgs_tail_idx;
|
||||
if ((em->flags & EMFL_VALID)) {
|
||||
bail: spin_unlock_irqrestore(&emsgs_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
mp = kmalloc(n, GFP_ATOMIC);
|
||||
if (mp == NULL) {
|
||||
printk(KERN_ERR "aoe: allocation failure, len=%ld\n", n);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
memcpy(mp, msg, n);
|
||||
em->msg = mp;
|
||||
em->flags |= EMFL_VALID;
|
||||
em->len = n;
|
||||
|
||||
emsgs_tail_idx++;
|
||||
emsgs_tail_idx %= ARRAY_SIZE(emsgs);
|
||||
|
||||
spin_unlock_irqrestore(&emsgs_lock, flags);
|
||||
|
||||
if (nblocked_emsgs_readers)
|
||||
up(&emsgs_sema);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch ((unsigned long) filp->private_data) {
|
||||
default:
|
||||
printk(KERN_INFO "aoe: can't write to that file.\n");
|
||||
break;
|
||||
case MINOR_DISCOVER:
|
||||
ret = discover();
|
||||
break;
|
||||
case MINOR_INTERFACES:
|
||||
ret = interfaces(buf, cnt);
|
||||
break;
|
||||
case MINOR_REVALIDATE:
|
||||
ret = revalidate(buf, cnt);
|
||||
}
|
||||
if (ret == 0)
|
||||
ret = cnt;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
aoechr_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int n, i;
|
||||
|
||||
n = iminor(inode);
|
||||
filp->private_data = (void *) (unsigned long) n;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
|
||||
if (chardevs[i].minor == n)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
aoechr_rel(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
|
||||
{
|
||||
unsigned long n;
|
||||
char *mp;
|
||||
struct ErrMsg *em;
|
||||
ssize_t len;
|
||||
ulong flags;
|
||||
|
||||
n = (unsigned long) filp->private_data;
|
||||
switch (n) {
|
||||
case MINOR_ERR:
|
||||
spin_lock_irqsave(&emsgs_lock, flags);
|
||||
loop:
|
||||
em = emsgs + emsgs_head_idx;
|
||||
if ((em->flags & EMFL_VALID) == 0) {
|
||||
if (filp->f_flags & O_NDELAY) {
|
||||
spin_unlock_irqrestore(&emsgs_lock, flags);
|
||||
return -EAGAIN;
|
||||
}
|
||||
nblocked_emsgs_readers++;
|
||||
|
||||
spin_unlock_irqrestore(&emsgs_lock, flags);
|
||||
|
||||
n = down_interruptible(&emsgs_sema);
|
||||
|
||||
spin_lock_irqsave(&emsgs_lock, flags);
|
||||
|
||||
nblocked_emsgs_readers--;
|
||||
|
||||
if (n) {
|
||||
spin_unlock_irqrestore(&emsgs_lock, flags);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
goto loop;
|
||||
}
|
||||
if (em->len > cnt) {
|
||||
spin_unlock_irqrestore(&emsgs_lock, flags);
|
||||
return -EAGAIN;
|
||||
}
|
||||
mp = em->msg;
|
||||
len = em->len;
|
||||
em->msg = NULL;
|
||||
em->flags &= ~EMFL_VALID;
|
||||
|
||||
emsgs_head_idx++;
|
||||
emsgs_head_idx %= ARRAY_SIZE(emsgs);
|
||||
|
||||
spin_unlock_irqrestore(&emsgs_lock, flags);
|
||||
|
||||
n = copy_to_user(buf, mp, len);
|
||||
kfree(mp);
|
||||
return n == 0 ? len : -EFAULT;
|
||||
default:
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct file_operations aoe_fops = {
|
||||
.write = aoechr_write,
|
||||
.read = aoechr_read,
|
||||
.open = aoechr_open,
|
||||
.release = aoechr_rel,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
int __init
|
||||
aoechr_init(void)
|
||||
{
|
||||
int n, i;
|
||||
|
||||
n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
|
||||
if (n < 0) {
|
||||
printk(KERN_ERR "aoe: can't register char device\n");
|
||||
return n;
|
||||
}
|
||||
sema_init(&emsgs_sema, 0);
|
||||
spin_lock_init(&emsgs_lock);
|
||||
aoe_class = class_create(THIS_MODULE, "aoe");
|
||||
if (IS_ERR(aoe_class)) {
|
||||
unregister_chrdev(AOE_MAJOR, "aoechr");
|
||||
return PTR_ERR(aoe_class);
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
|
||||
class_device_create(aoe_class, NULL,
|
||||
MKDEV(AOE_MAJOR, chardevs[i].minor),
|
||||
NULL, chardevs[i].name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
aoechr_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
|
||||
class_device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
|
||||
class_destroy(aoe_class);
|
||||
unregister_chrdev(AOE_MAJOR, "aoechr");
|
||||
}
|
||||
|
||||
796
drivers/block/aoe/aoecmd.c
Normal file
796
drivers/block/aoe/aoecmd.c
Normal file
@@ -0,0 +1,796 @@
|
||||
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
|
||||
/*
|
||||
* aoecmd.c
|
||||
* Filesystem request handling methods
|
||||
*/
|
||||
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "aoe.h"
|
||||
|
||||
#define TIMERTICK (HZ / 10)
|
||||
#define MINTIMER (2 * TIMERTICK)
|
||||
#define MAXTIMER (HZ << 1)
|
||||
|
||||
static int aoe_deadsecs = 60 * 3;
|
||||
module_param(aoe_deadsecs, int, 0644);
|
||||
MODULE_PARM_DESC(aoe_deadsecs, "After aoe_deadsecs seconds, give up and fail dev.");
|
||||
|
||||
struct sk_buff *
|
||||
new_skb(ulong len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(len, GFP_ATOMIC);
|
||||
if (skb) {
|
||||
skb->nh.raw = skb->mac.raw = skb->data;
|
||||
skb->protocol = __constant_htons(ETH_P_AOE);
|
||||
skb->priority = 0;
|
||||
skb->next = skb->prev = NULL;
|
||||
|
||||
/* tell the network layer not to perform IP checksums
|
||||
* or to get the NIC to do it
|
||||
*/
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
}
|
||||
return skb;
|
||||
}
|
||||
|
||||
static struct frame *
|
||||
getframe(struct aoedev *d, int tag)
|
||||
{
|
||||
struct frame *f, *e;
|
||||
|
||||
f = d->frames;
|
||||
e = f + d->nframes;
|
||||
for (; f<e; f++)
|
||||
if (f->tag == tag)
|
||||
return f;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Leave the top bit clear so we have tagspace for userland.
|
||||
* The bottom 16 bits are the xmit tick for rexmit/rttavg processing.
|
||||
* This driver reserves tag -1 to mean "unused frame."
|
||||
*/
|
||||
static int
|
||||
newtag(struct aoedev *d)
|
||||
{
|
||||
register ulong n;
|
||||
|
||||
n = jiffies & 0xffff;
|
||||
return n |= (++d->lasttag & 0x7fff) << 16;
|
||||
}
|
||||
|
||||
static int
|
||||
aoehdr_atainit(struct aoedev *d, struct aoe_hdr *h)
|
||||
{
|
||||
u32 host_tag = newtag(d);
|
||||
|
||||
memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
|
||||
memcpy(h->dst, d->addr, sizeof h->dst);
|
||||
h->type = __constant_cpu_to_be16(ETH_P_AOE);
|
||||
h->verfl = AOE_HVER;
|
||||
h->major = cpu_to_be16(d->aoemajor);
|
||||
h->minor = d->aoeminor;
|
||||
h->cmd = AOECMD_ATA;
|
||||
h->tag = cpu_to_be32(host_tag);
|
||||
|
||||
return host_tag;
|
||||
}
|
||||
|
||||
static inline void
|
||||
put_lba(struct aoe_atahdr *ah, sector_t lba)
|
||||
{
|
||||
ah->lba0 = lba;
|
||||
ah->lba1 = lba >>= 8;
|
||||
ah->lba2 = lba >>= 8;
|
||||
ah->lba3 = lba >>= 8;
|
||||
ah->lba4 = lba >>= 8;
|
||||
ah->lba5 = lba >>= 8;
|
||||
}
|
||||
|
||||
static void
|
||||
aoecmd_ata_rw(struct aoedev *d, struct frame *f)
|
||||
{
|
||||
struct aoe_hdr *h;
|
||||
struct aoe_atahdr *ah;
|
||||
struct buf *buf;
|
||||
struct sk_buff *skb;
|
||||
ulong bcnt;
|
||||
register sector_t sector;
|
||||
char writebit, extbit;
|
||||
|
||||
writebit = 0x10;
|
||||
extbit = 0x4;
|
||||
|
||||
buf = d->inprocess;
|
||||
|
||||
sector = buf->sector;
|
||||
bcnt = buf->bv_resid;
|
||||
if (bcnt > d->maxbcnt)
|
||||
bcnt = d->maxbcnt;
|
||||
|
||||
/* initialize the headers & frame */
|
||||
skb = f->skb;
|
||||
h = (struct aoe_hdr *) skb->mac.raw;
|
||||
ah = (struct aoe_atahdr *) (h+1);
|
||||
skb_put(skb, sizeof *h + sizeof *ah);
|
||||
memset(h, 0, skb->len);
|
||||
f->tag = aoehdr_atainit(d, h);
|
||||
f->waited = 0;
|
||||
f->buf = buf;
|
||||
f->bufaddr = buf->bufaddr;
|
||||
f->bcnt = bcnt;
|
||||
f->lba = sector;
|
||||
|
||||
/* set up ata header */
|
||||
ah->scnt = bcnt >> 9;
|
||||
put_lba(ah, sector);
|
||||
if (d->flags & DEVFL_EXT) {
|
||||
ah->aflags |= AOEAFL_EXT;
|
||||
} else {
|
||||
extbit = 0;
|
||||
ah->lba3 &= 0x0f;
|
||||
ah->lba3 |= 0xe0; /* LBA bit + obsolete 0xa0 */
|
||||
}
|
||||
|
||||
if (bio_data_dir(buf->bio) == WRITE) {
|
||||
skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr),
|
||||
offset_in_page(f->bufaddr), bcnt);
|
||||
ah->aflags |= AOEAFL_WRITE;
|
||||
skb->len += bcnt;
|
||||
skb->data_len = bcnt;
|
||||
} else {
|
||||
writebit = 0;
|
||||
}
|
||||
|
||||
ah->cmdstat = WIN_READ | writebit | extbit;
|
||||
|
||||
/* mark all tracking fields and load out */
|
||||
buf->nframesout += 1;
|
||||
buf->bufaddr += bcnt;
|
||||
buf->bv_resid -= bcnt;
|
||||
/* printk(KERN_DEBUG "aoe: bv_resid=%ld\n", buf->bv_resid); */
|
||||
buf->resid -= bcnt;
|
||||
buf->sector += bcnt >> 9;
|
||||
if (buf->resid == 0) {
|
||||
d->inprocess = NULL;
|
||||
} else if (buf->bv_resid == 0) {
|
||||
buf->bv++;
|
||||
WARN_ON(buf->bv->bv_len == 0);
|
||||
buf->bv_resid = buf->bv->bv_len;
|
||||
buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
|
||||
}
|
||||
|
||||
skb->dev = d->ifp;
|
||||
skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return;
|
||||
if (d->sendq_hd)
|
||||
d->sendq_tl->next = skb;
|
||||
else
|
||||
d->sendq_hd = skb;
|
||||
d->sendq_tl = skb;
|
||||
}
|
||||
|
||||
/* some callers cannot sleep, and they can call this function,
|
||||
* transmitting the packets later, when interrupts are on
|
||||
*/
|
||||
static struct sk_buff *
|
||||
aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff **tail)
|
||||
{
|
||||
struct aoe_hdr *h;
|
||||
struct aoe_cfghdr *ch;
|
||||
struct sk_buff *skb, *sl, *sl_tail;
|
||||
struct net_device *ifp;
|
||||
|
||||
sl = sl_tail = NULL;
|
||||
|
||||
read_lock(&dev_base_lock);
|
||||
for (ifp = dev_base; ifp; dev_put(ifp), ifp = ifp->next) {
|
||||
dev_hold(ifp);
|
||||
if (!is_aoe_netif(ifp))
|
||||
continue;
|
||||
|
||||
skb = new_skb(sizeof *h + sizeof *ch);
|
||||
if (skb == NULL) {
|
||||
printk(KERN_INFO "aoe: skb alloc failure\n");
|
||||
continue;
|
||||
}
|
||||
skb_put(skb, sizeof *h + sizeof *ch);
|
||||
skb->dev = ifp;
|
||||
if (sl_tail == NULL)
|
||||
sl_tail = skb;
|
||||
h = (struct aoe_hdr *) skb->mac.raw;
|
||||
memset(h, 0, sizeof *h + sizeof *ch);
|
||||
|
||||
memset(h->dst, 0xff, sizeof h->dst);
|
||||
memcpy(h->src, ifp->dev_addr, sizeof h->src);
|
||||
h->type = __constant_cpu_to_be16(ETH_P_AOE);
|
||||
h->verfl = AOE_HVER;
|
||||
h->major = cpu_to_be16(aoemajor);
|
||||
h->minor = aoeminor;
|
||||
h->cmd = AOECMD_CFG;
|
||||
|
||||
skb->next = sl;
|
||||
sl = skb;
|
||||
}
|
||||
read_unlock(&dev_base_lock);
|
||||
|
||||
if (tail != NULL)
|
||||
*tail = sl_tail;
|
||||
return sl;
|
||||
}
|
||||
|
||||
static struct frame *
|
||||
freeframe(struct aoedev *d)
|
||||
{
|
||||
struct frame *f, *e;
|
||||
int n = 0;
|
||||
|
||||
f = d->frames;
|
||||
e = f + d->nframes;
|
||||
for (; f<e; f++) {
|
||||
if (f->tag != FREETAG)
|
||||
continue;
|
||||
if (atomic_read(&skb_shinfo(f->skb)->dataref) == 1) {
|
||||
skb_shinfo(f->skb)->nr_frags = f->skb->data_len = 0;
|
||||
skb_trim(f->skb, 0);
|
||||
return f;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
if (n == d->nframes) /* wait for network layer */
|
||||
d->flags |= DEVFL_KICKME;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* enters with d->lock held */
|
||||
void
|
||||
aoecmd_work(struct aoedev *d)
|
||||
{
|
||||
struct frame *f;
|
||||
struct buf *buf;
|
||||
|
||||
if (d->flags & DEVFL_PAUSE) {
|
||||
if (!aoedev_isbusy(d))
|
||||
d->sendq_hd = aoecmd_cfg_pkts(d->aoemajor,
|
||||
d->aoeminor, &d->sendq_tl);
|
||||
return;
|
||||
}
|
||||
|
||||
loop:
|
||||
f = freeframe(d);
|
||||
if (f == NULL)
|
||||
return;
|
||||
if (d->inprocess == NULL) {
|
||||
if (list_empty(&d->bufq))
|
||||
return;
|
||||
buf = container_of(d->bufq.next, struct buf, bufs);
|
||||
list_del(d->bufq.next);
|
||||
/*printk(KERN_DEBUG "aoe: bi_size=%ld\n", buf->bio->bi_size); */
|
||||
d->inprocess = buf;
|
||||
}
|
||||
aoecmd_ata_rw(d, f);
|
||||
goto loop;
|
||||
}
|
||||
|
||||
static void
|
||||
rexmit(struct aoedev *d, struct frame *f)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct aoe_hdr *h;
|
||||
struct aoe_atahdr *ah;
|
||||
char buf[128];
|
||||
u32 n;
|
||||
|
||||
n = newtag(d);
|
||||
|
||||
snprintf(buf, sizeof buf,
|
||||
"%15s e%ld.%ld oldtag=%08x@%08lx newtag=%08x\n",
|
||||
"retransmit",
|
||||
d->aoemajor, d->aoeminor, f->tag, jiffies, n);
|
||||
aoechr_error(buf);
|
||||
|
||||
skb = f->skb;
|
||||
h = (struct aoe_hdr *) skb->mac.raw;
|
||||
ah = (struct aoe_atahdr *) (h+1);
|
||||
f->tag = n;
|
||||
h->tag = cpu_to_be32(n);
|
||||
memcpy(h->dst, d->addr, sizeof h->dst);
|
||||
memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
|
||||
|
||||
n = DEFAULTBCNT / 512;
|
||||
if (ah->scnt > n) {
|
||||
ah->scnt = n;
|
||||
if (ah->aflags & AOEAFL_WRITE) {
|
||||
skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr),
|
||||
offset_in_page(f->bufaddr), DEFAULTBCNT);
|
||||
skb->len = sizeof *h + sizeof *ah + DEFAULTBCNT;
|
||||
skb->data_len = DEFAULTBCNT;
|
||||
}
|
||||
if (++d->lostjumbo > (d->nframes << 1))
|
||||
if (d->maxbcnt != DEFAULTBCNT) {
|
||||
printk(KERN_INFO "aoe: e%ld.%ld: too many lost jumbo on %s - using 1KB frames.\n",
|
||||
d->aoemajor, d->aoeminor, d->ifp->name);
|
||||
d->maxbcnt = DEFAULTBCNT;
|
||||
d->flags |= DEVFL_MAXBCNT;
|
||||
}
|
||||
}
|
||||
|
||||
skb->dev = d->ifp;
|
||||
skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return;
|
||||
if (d->sendq_hd)
|
||||
d->sendq_tl->next = skb;
|
||||
else
|
||||
d->sendq_hd = skb;
|
||||
d->sendq_tl = skb;
|
||||
}
|
||||
|
||||
static int
|
||||
tsince(int tag)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = jiffies & 0xffff;
|
||||
n -= tag & 0xffff;
|
||||
if (n < 0)
|
||||
n += 1<<16;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void
|
||||
rexmit_timer(ulong vp)
|
||||
{
|
||||
struct aoedev *d;
|
||||
struct frame *f, *e;
|
||||
struct sk_buff *sl;
|
||||
register long timeout;
|
||||
ulong flags, n;
|
||||
|
||||
d = (struct aoedev *) vp;
|
||||
sl = NULL;
|
||||
|
||||
/* timeout is always ~150% of the moving average */
|
||||
timeout = d->rttavg;
|
||||
timeout += timeout >> 1;
|
||||
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
|
||||
if (d->flags & DEVFL_TKILL) {
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
return;
|
||||
}
|
||||
f = d->frames;
|
||||
e = f + d->nframes;
|
||||
for (; f<e; f++) {
|
||||
if (f->tag != FREETAG && tsince(f->tag) >= timeout) {
|
||||
n = f->waited += timeout;
|
||||
n /= HZ;
|
||||
if (n > aoe_deadsecs) { /* waited too long for response */
|
||||
aoedev_downdev(d);
|
||||
break;
|
||||
}
|
||||
rexmit(d, f);
|
||||
}
|
||||
}
|
||||
if (d->flags & DEVFL_KICKME) {
|
||||
d->flags &= ~DEVFL_KICKME;
|
||||
aoecmd_work(d);
|
||||
}
|
||||
|
||||
sl = d->sendq_hd;
|
||||
d->sendq_hd = d->sendq_tl = NULL;
|
||||
if (sl) {
|
||||
n = d->rttavg <<= 1;
|
||||
if (n > MAXTIMER)
|
||||
d->rttavg = MAXTIMER;
|
||||
}
|
||||
|
||||
d->timer.expires = jiffies + TIMERTICK;
|
||||
add_timer(&d->timer);
|
||||
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
|
||||
aoenet_xmit(sl);
|
||||
}
|
||||
|
||||
/* this function performs work that has been deferred until sleeping is OK
|
||||
*/
|
||||
void
|
||||
aoecmd_sleepwork(struct work_struct *work)
|
||||
{
|
||||
struct aoedev *d = container_of(work, struct aoedev, work);
|
||||
|
||||
if (d->flags & DEVFL_GDALLOC)
|
||||
aoeblk_gdalloc(d);
|
||||
|
||||
if (d->flags & DEVFL_NEWSIZE) {
|
||||
struct block_device *bd;
|
||||
unsigned long flags;
|
||||
u64 ssize;
|
||||
|
||||
ssize = d->gd->capacity;
|
||||
bd = bdget_disk(d->gd, 0);
|
||||
|
||||
if (bd) {
|
||||
mutex_lock(&bd->bd_inode->i_mutex);
|
||||
i_size_write(bd->bd_inode, (loff_t)ssize<<9);
|
||||
mutex_unlock(&bd->bd_inode->i_mutex);
|
||||
bdput(bd);
|
||||
}
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
d->flags |= DEVFL_UP;
|
||||
d->flags &= ~DEVFL_NEWSIZE;
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ataid_complete(struct aoedev *d, unsigned char *id)
|
||||
{
|
||||
u64 ssize;
|
||||
u16 n;
|
||||
|
||||
/* word 83: command set supported */
|
||||
n = le16_to_cpu(get_unaligned((__le16 *) &id[83<<1]));
|
||||
|
||||
/* word 86: command set/feature enabled */
|
||||
n |= le16_to_cpu(get_unaligned((__le16 *) &id[86<<1]));
|
||||
|
||||
if (n & (1<<10)) { /* bit 10: LBA 48 */
|
||||
d->flags |= DEVFL_EXT;
|
||||
|
||||
/* word 100: number lba48 sectors */
|
||||
ssize = le64_to_cpu(get_unaligned((__le64 *) &id[100<<1]));
|
||||
|
||||
/* set as in ide-disk.c:init_idedisk_capacity */
|
||||
d->geo.cylinders = ssize;
|
||||
d->geo.cylinders /= (255 * 63);
|
||||
d->geo.heads = 255;
|
||||
d->geo.sectors = 63;
|
||||
} else {
|
||||
d->flags &= ~DEVFL_EXT;
|
||||
|
||||
/* number lba28 sectors */
|
||||
ssize = le32_to_cpu(get_unaligned((__le32 *) &id[60<<1]));
|
||||
|
||||
/* NOTE: obsolete in ATA 6 */
|
||||
d->geo.cylinders = le16_to_cpu(get_unaligned((__le16 *) &id[54<<1]));
|
||||
d->geo.heads = le16_to_cpu(get_unaligned((__le16 *) &id[55<<1]));
|
||||
d->geo.sectors = le16_to_cpu(get_unaligned((__le16 *) &id[56<<1]));
|
||||
}
|
||||
|
||||
if (d->ssize != ssize)
|
||||
printk(KERN_INFO "aoe: %012llx e%lu.%lu v%04x has %llu sectors\n",
|
||||
(unsigned long long)mac_addr(d->addr),
|
||||
d->aoemajor, d->aoeminor,
|
||||
d->fw_ver, (long long)ssize);
|
||||
d->ssize = ssize;
|
||||
d->geo.start = 0;
|
||||
if (d->gd != NULL) {
|
||||
d->gd->capacity = ssize;
|
||||
d->flags |= DEVFL_NEWSIZE;
|
||||
} else {
|
||||
if (d->flags & DEVFL_GDALLOC) {
|
||||
printk(KERN_ERR "aoe: can't schedule work for e%lu.%lu, %s\n",
|
||||
d->aoemajor, d->aoeminor,
|
||||
"it's already on! This shouldn't happen.\n");
|
||||
return;
|
||||
}
|
||||
d->flags |= DEVFL_GDALLOC;
|
||||
}
|
||||
schedule_work(&d->work);
|
||||
}
|
||||
|
||||
static void
|
||||
calc_rttavg(struct aoedev *d, int rtt)
|
||||
{
|
||||
register long n;
|
||||
|
||||
n = rtt;
|
||||
if (n < 0) {
|
||||
n = -rtt;
|
||||
if (n < MINTIMER)
|
||||
n = MINTIMER;
|
||||
else if (n > MAXTIMER)
|
||||
n = MAXTIMER;
|
||||
d->mintimer += (n - d->mintimer) >> 1;
|
||||
} else if (n < d->mintimer)
|
||||
n = d->mintimer;
|
||||
else if (n > MAXTIMER)
|
||||
n = MAXTIMER;
|
||||
|
||||
/* g == .25; cf. Congestion Avoidance and Control, Jacobson & Karels; 1988 */
|
||||
n -= d->rttavg;
|
||||
d->rttavg += n >> 2;
|
||||
}
|
||||
|
||||
void
|
||||
aoecmd_ata_rsp(struct sk_buff *skb)
|
||||
{
|
||||
struct aoedev *d;
|
||||
struct aoe_hdr *hin, *hout;
|
||||
struct aoe_atahdr *ahin, *ahout;
|
||||
struct frame *f;
|
||||
struct buf *buf;
|
||||
struct sk_buff *sl;
|
||||
register long n;
|
||||
ulong flags;
|
||||
char ebuf[128];
|
||||
u16 aoemajor;
|
||||
|
||||
hin = (struct aoe_hdr *) skb->mac.raw;
|
||||
aoemajor = be16_to_cpu(get_unaligned(&hin->major));
|
||||
d = aoedev_by_aoeaddr(aoemajor, hin->minor);
|
||||
if (d == NULL) {
|
||||
snprintf(ebuf, sizeof ebuf, "aoecmd_ata_rsp: ata response "
|
||||
"for unknown device %d.%d\n",
|
||||
aoemajor, hin->minor);
|
||||
aoechr_error(ebuf);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
|
||||
n = be32_to_cpu(get_unaligned(&hin->tag));
|
||||
f = getframe(d, n);
|
||||
if (f == NULL) {
|
||||
calc_rttavg(d, -tsince(n));
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
snprintf(ebuf, sizeof ebuf,
|
||||
"%15s e%d.%d tag=%08x@%08lx\n",
|
||||
"unexpected rsp",
|
||||
be16_to_cpu(get_unaligned(&hin->major)),
|
||||
hin->minor,
|
||||
be32_to_cpu(get_unaligned(&hin->tag)),
|
||||
jiffies);
|
||||
aoechr_error(ebuf);
|
||||
return;
|
||||
}
|
||||
|
||||
calc_rttavg(d, tsince(f->tag));
|
||||
|
||||
ahin = (struct aoe_atahdr *) (hin+1);
|
||||
hout = (struct aoe_hdr *) f->skb->mac.raw;
|
||||
ahout = (struct aoe_atahdr *) (hout+1);
|
||||
buf = f->buf;
|
||||
|
||||
if (ahout->cmdstat == WIN_IDENTIFY)
|
||||
d->flags &= ~DEVFL_PAUSE;
|
||||
if (ahin->cmdstat & 0xa9) { /* these bits cleared on success */
|
||||
printk(KERN_ERR
|
||||
"aoe: ata error cmd=%2.2Xh stat=%2.2Xh from e%ld.%ld\n",
|
||||
ahout->cmdstat, ahin->cmdstat,
|
||||
d->aoemajor, d->aoeminor);
|
||||
if (buf)
|
||||
buf->flags |= BUFFL_FAIL;
|
||||
} else {
|
||||
n = ahout->scnt << 9;
|
||||
switch (ahout->cmdstat) {
|
||||
case WIN_READ:
|
||||
case WIN_READ_EXT:
|
||||
if (skb->len - sizeof *hin - sizeof *ahin < n) {
|
||||
printk(KERN_ERR
|
||||
"aoe: runt data size in read. skb->len=%d\n",
|
||||
skb->len);
|
||||
/* fail frame f? just returning will rexmit. */
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
return;
|
||||
}
|
||||
memcpy(f->bufaddr, ahin+1, n);
|
||||
case WIN_WRITE:
|
||||
case WIN_WRITE_EXT:
|
||||
if (f->bcnt -= n) {
|
||||
skb = f->skb;
|
||||
f->bufaddr += n;
|
||||
put_lba(ahout, f->lba += ahout->scnt);
|
||||
n = f->bcnt;
|
||||
if (n > DEFAULTBCNT)
|
||||
n = DEFAULTBCNT;
|
||||
ahout->scnt = n >> 9;
|
||||
if (ahout->aflags & AOEAFL_WRITE) {
|
||||
skb_fill_page_desc(skb, 0,
|
||||
virt_to_page(f->bufaddr),
|
||||
offset_in_page(f->bufaddr), n);
|
||||
skb->len = sizeof *hout + sizeof *ahout + n;
|
||||
skb->data_len = n;
|
||||
}
|
||||
f->tag = newtag(d);
|
||||
hout->tag = cpu_to_be32(f->tag);
|
||||
skb->dev = d->ifp;
|
||||
skb = skb_clone(skb, GFP_ATOMIC);
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
if (skb)
|
||||
aoenet_xmit(skb);
|
||||
return;
|
||||
}
|
||||
if (n > DEFAULTBCNT)
|
||||
d->lostjumbo = 0;
|
||||
break;
|
||||
case WIN_IDENTIFY:
|
||||
if (skb->len - sizeof *hin - sizeof *ahin < 512) {
|
||||
printk(KERN_INFO
|
||||
"aoe: runt data size in ataid. skb->len=%d\n",
|
||||
skb->len);
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
return;
|
||||
}
|
||||
ataid_complete(d, (char *) (ahin+1));
|
||||
break;
|
||||
default:
|
||||
printk(KERN_INFO
|
||||
"aoe: unrecognized ata command %2.2Xh for %d.%d\n",
|
||||
ahout->cmdstat,
|
||||
be16_to_cpu(get_unaligned(&hin->major)),
|
||||
hin->minor);
|
||||
}
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
buf->nframesout -= 1;
|
||||
if (buf->nframesout == 0 && buf->resid == 0) {
|
||||
unsigned long duration = jiffies - buf->start_time;
|
||||
unsigned long n_sect = buf->bio->bi_size >> 9;
|
||||
struct gendisk *disk = d->gd;
|
||||
const int rw = bio_data_dir(buf->bio);
|
||||
|
||||
disk_stat_inc(disk, ios[rw]);
|
||||
disk_stat_add(disk, ticks[rw], duration);
|
||||
disk_stat_add(disk, sectors[rw], n_sect);
|
||||
disk_stat_add(disk, io_ticks, duration);
|
||||
n = (buf->flags & BUFFL_FAIL) ? -EIO : 0;
|
||||
bio_endio(buf->bio, buf->bio->bi_size, n);
|
||||
mempool_free(buf, d->bufpool);
|
||||
}
|
||||
}
|
||||
|
||||
f->buf = NULL;
|
||||
f->tag = FREETAG;
|
||||
|
||||
aoecmd_work(d);
|
||||
sl = d->sendq_hd;
|
||||
d->sendq_hd = d->sendq_tl = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
aoenet_xmit(sl);
|
||||
}
|
||||
|
||||
void
|
||||
aoecmd_cfg(ushort aoemajor, unsigned char aoeminor)
|
||||
{
|
||||
struct sk_buff *sl;
|
||||
|
||||
sl = aoecmd_cfg_pkts(aoemajor, aoeminor, NULL);
|
||||
|
||||
aoenet_xmit(sl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we only call this in one place (and it only prepares one frame)
|
||||
* we just return the skb. Usually we'd chain it up to the aoedev sendq.
|
||||
*/
|
||||
static struct sk_buff *
|
||||
aoecmd_ata_id(struct aoedev *d)
|
||||
{
|
||||
struct aoe_hdr *h;
|
||||
struct aoe_atahdr *ah;
|
||||
struct frame *f;
|
||||
struct sk_buff *skb;
|
||||
|
||||
f = freeframe(d);
|
||||
if (f == NULL) {
|
||||
printk(KERN_ERR "aoe: can't get a frame. This shouldn't happen.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* initialize the headers & frame */
|
||||
skb = f->skb;
|
||||
h = (struct aoe_hdr *) skb->mac.raw;
|
||||
ah = (struct aoe_atahdr *) (h+1);
|
||||
skb_put(skb, sizeof *h + sizeof *ah);
|
||||
memset(h, 0, skb->len);
|
||||
f->tag = aoehdr_atainit(d, h);
|
||||
f->waited = 0;
|
||||
|
||||
/* set up ata header */
|
||||
ah->scnt = 1;
|
||||
ah->cmdstat = WIN_IDENTIFY;
|
||||
ah->lba3 = 0xa0;
|
||||
|
||||
skb->dev = d->ifp;
|
||||
|
||||
d->rttavg = MAXTIMER;
|
||||
d->timer.function = rexmit_timer;
|
||||
|
||||
return skb_clone(skb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
void
|
||||
aoecmd_cfg_rsp(struct sk_buff *skb)
|
||||
{
|
||||
struct aoedev *d;
|
||||
struct aoe_hdr *h;
|
||||
struct aoe_cfghdr *ch;
|
||||
ulong flags, sysminor, aoemajor;
|
||||
struct sk_buff *sl;
|
||||
enum { MAXFRAMES = 16 };
|
||||
u16 n;
|
||||
|
||||
h = (struct aoe_hdr *) skb->mac.raw;
|
||||
ch = (struct aoe_cfghdr *) (h+1);
|
||||
|
||||
/*
|
||||
* Enough people have their dip switches set backwards to
|
||||
* warrant a loud message for this special case.
|
||||
*/
|
||||
aoemajor = be16_to_cpu(get_unaligned(&h->major));
|
||||
if (aoemajor == 0xfff) {
|
||||
printk(KERN_ERR "aoe: Warning: shelf address is all ones. "
|
||||
"Check shelf dip switches.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
sysminor = SYSMINOR(aoemajor, h->minor);
|
||||
if (sysminor * AOE_PARTITIONS + AOE_PARTITIONS > MINORMASK) {
|
||||
printk(KERN_INFO "aoe: e%ld.%d: minor number too large\n",
|
||||
aoemajor, (int) h->minor);
|
||||
return;
|
||||
}
|
||||
|
||||
n = be16_to_cpu(ch->bufcnt);
|
||||
if (n > MAXFRAMES) /* keep it reasonable */
|
||||
n = MAXFRAMES;
|
||||
|
||||
d = aoedev_by_sysminor_m(sysminor, n);
|
||||
if (d == NULL) {
|
||||
printk(KERN_INFO "aoe: device sysminor_m failure\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
|
||||
/* permit device to migrate mac and network interface */
|
||||
d->ifp = skb->dev;
|
||||
memcpy(d->addr, h->src, sizeof d->addr);
|
||||
if (!(d->flags & DEVFL_MAXBCNT)) {
|
||||
n = d->ifp->mtu;
|
||||
n -= sizeof (struct aoe_hdr) + sizeof (struct aoe_atahdr);
|
||||
n /= 512;
|
||||
if (n > ch->scnt)
|
||||
n = ch->scnt;
|
||||
n = n ? n * 512 : DEFAULTBCNT;
|
||||
if (n != d->maxbcnt) {
|
||||
printk(KERN_INFO
|
||||
"aoe: e%ld.%ld: setting %d byte data frames on %s\n",
|
||||
d->aoemajor, d->aoeminor, n, d->ifp->name);
|
||||
d->maxbcnt = n;
|
||||
}
|
||||
}
|
||||
|
||||
/* don't change users' perspective */
|
||||
if (d->nopen && !(d->flags & DEVFL_PAUSE)) {
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
return;
|
||||
}
|
||||
d->flags |= DEVFL_PAUSE; /* force pause */
|
||||
d->mintimer = MINTIMER;
|
||||
d->fw_ver = be16_to_cpu(ch->fwver);
|
||||
|
||||
/* check for already outstanding ataid */
|
||||
sl = aoedev_isbusy(d) == 0 ? aoecmd_ata_id(d) : NULL;
|
||||
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
|
||||
aoenet_xmit(sl);
|
||||
}
|
||||
|
||||
220
drivers/block/aoe/aoedev.c
Normal file
220
drivers/block/aoe/aoedev.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
|
||||
/*
|
||||
* aoedev.c
|
||||
* AoE device utility functions; maintains device list.
|
||||
*/
|
||||
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include "aoe.h"
|
||||
|
||||
static struct aoedev *devlist;
|
||||
static spinlock_t devlist_lock;
|
||||
|
||||
int
|
||||
aoedev_isbusy(struct aoedev *d)
|
||||
{
|
||||
struct frame *f, *e;
|
||||
|
||||
f = d->frames;
|
||||
e = f + d->nframes;
|
||||
do {
|
||||
if (f->tag != FREETAG)
|
||||
return 1;
|
||||
} while (++f < e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct aoedev *
|
||||
aoedev_by_aoeaddr(int maj, int min)
|
||||
{
|
||||
struct aoedev *d;
|
||||
ulong flags;
|
||||
|
||||
spin_lock_irqsave(&devlist_lock, flags);
|
||||
|
||||
for (d=devlist; d; d=d->next)
|
||||
if (d->aoemajor == maj && d->aoeminor == min)
|
||||
break;
|
||||
|
||||
spin_unlock_irqrestore(&devlist_lock, flags);
|
||||
return d;
|
||||
}
|
||||
|
||||
static void
|
||||
dummy_timer(ulong vp)
|
||||
{
|
||||
struct aoedev *d;
|
||||
|
||||
d = (struct aoedev *)vp;
|
||||
if (d->flags & DEVFL_TKILL)
|
||||
return;
|
||||
d->timer.expires = jiffies + HZ;
|
||||
add_timer(&d->timer);
|
||||
}
|
||||
|
||||
/* called with devlist lock held */
|
||||
static struct aoedev *
|
||||
aoedev_newdev(ulong nframes)
|
||||
{
|
||||
struct aoedev *d;
|
||||
struct frame *f, *e;
|
||||
|
||||
d = kzalloc(sizeof *d, GFP_ATOMIC);
|
||||
f = kcalloc(nframes, sizeof *f, GFP_ATOMIC);
|
||||
switch (!d || !f) {
|
||||
case 0:
|
||||
d->nframes = nframes;
|
||||
d->frames = f;
|
||||
e = f + nframes;
|
||||
for (; f<e; f++) {
|
||||
f->tag = FREETAG;
|
||||
f->skb = new_skb(ETH_ZLEN);
|
||||
if (!f->skb)
|
||||
break;
|
||||
}
|
||||
if (f == e)
|
||||
break;
|
||||
while (f > d->frames) {
|
||||
f--;
|
||||
dev_kfree_skb(f->skb);
|
||||
}
|
||||
default:
|
||||
if (f)
|
||||
kfree(f);
|
||||
if (d)
|
||||
kfree(d);
|
||||
return NULL;
|
||||
}
|
||||
INIT_WORK(&d->work, aoecmd_sleepwork);
|
||||
spin_lock_init(&d->lock);
|
||||
init_timer(&d->timer);
|
||||
d->timer.data = (ulong) d;
|
||||
d->timer.function = dummy_timer;
|
||||
d->timer.expires = jiffies + HZ;
|
||||
add_timer(&d->timer);
|
||||
d->bufpool = NULL; /* defer to aoeblk_gdalloc */
|
||||
INIT_LIST_HEAD(&d->bufq);
|
||||
d->next = devlist;
|
||||
devlist = d;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
void
|
||||
aoedev_downdev(struct aoedev *d)
|
||||
{
|
||||
struct frame *f, *e;
|
||||
struct buf *buf;
|
||||
struct bio *bio;
|
||||
|
||||
f = d->frames;
|
||||
e = f + d->nframes;
|
||||
for (; f<e; f->tag = FREETAG, f->buf = NULL, f++) {
|
||||
if (f->tag == FREETAG || f->buf == NULL)
|
||||
continue;
|
||||
buf = f->buf;
|
||||
bio = buf->bio;
|
||||
if (--buf->nframesout == 0) {
|
||||
mempool_free(buf, d->bufpool);
|
||||
bio_endio(bio, bio->bi_size, -EIO);
|
||||
}
|
||||
skb_shinfo(f->skb)->nr_frags = f->skb->data_len = 0;
|
||||
}
|
||||
d->inprocess = NULL;
|
||||
|
||||
while (!list_empty(&d->bufq)) {
|
||||
buf = container_of(d->bufq.next, struct buf, bufs);
|
||||
list_del(d->bufq.next);
|
||||
bio = buf->bio;
|
||||
mempool_free(buf, d->bufpool);
|
||||
bio_endio(bio, bio->bi_size, -EIO);
|
||||
}
|
||||
|
||||
if (d->gd)
|
||||
d->gd->capacity = 0;
|
||||
|
||||
d->flags &= ~(DEVFL_UP | DEVFL_PAUSE);
|
||||
}
|
||||
|
||||
/* find it or malloc it */
|
||||
struct aoedev *
|
||||
aoedev_by_sysminor_m(ulong sysminor, ulong bufcnt)
|
||||
{
|
||||
struct aoedev *d;
|
||||
ulong flags;
|
||||
|
||||
spin_lock_irqsave(&devlist_lock, flags);
|
||||
|
||||
for (d=devlist; d; d=d->next)
|
||||
if (d->sysminor == sysminor)
|
||||
break;
|
||||
|
||||
if (d == NULL) {
|
||||
d = aoedev_newdev(bufcnt);
|
||||
if (d == NULL) {
|
||||
spin_unlock_irqrestore(&devlist_lock, flags);
|
||||
printk(KERN_INFO "aoe: aoedev_newdev failure.\n");
|
||||
return NULL;
|
||||
}
|
||||
d->sysminor = sysminor;
|
||||
d->aoemajor = AOEMAJOR(sysminor);
|
||||
d->aoeminor = AOEMINOR(sysminor);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&devlist_lock, flags);
|
||||
return d;
|
||||
}
|
||||
|
||||
static void
|
||||
aoedev_freedev(struct aoedev *d)
|
||||
{
|
||||
struct frame *f, *e;
|
||||
|
||||
if (d->gd) {
|
||||
aoedisk_rm_sysfs(d);
|
||||
del_gendisk(d->gd);
|
||||
put_disk(d->gd);
|
||||
}
|
||||
f = d->frames;
|
||||
e = f + d->nframes;
|
||||
for (; f<e; f++) {
|
||||
skb_shinfo(f->skb)->nr_frags = 0;
|
||||
dev_kfree_skb(f->skb);
|
||||
}
|
||||
kfree(d->frames);
|
||||
if (d->bufpool)
|
||||
mempool_destroy(d->bufpool);
|
||||
kfree(d);
|
||||
}
|
||||
|
||||
void
|
||||
aoedev_exit(void)
|
||||
{
|
||||
struct aoedev *d;
|
||||
ulong flags;
|
||||
|
||||
flush_scheduled_work();
|
||||
|
||||
while ((d = devlist)) {
|
||||
devlist = d->next;
|
||||
|
||||
spin_lock_irqsave(&d->lock, flags);
|
||||
aoedev_downdev(d);
|
||||
d->flags |= DEVFL_TKILL;
|
||||
spin_unlock_irqrestore(&d->lock, flags);
|
||||
|
||||
del_timer_sync(&d->timer);
|
||||
aoedev_freedev(d);
|
||||
}
|
||||
}
|
||||
|
||||
int __init
|
||||
aoedev_init(void)
|
||||
{
|
||||
spin_lock_init(&devlist_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
110
drivers/block/aoe/aoemain.c
Normal file
110
drivers/block/aoe/aoemain.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
|
||||
/*
|
||||
* aoemain.c
|
||||
* Module initialization routines, discover timer
|
||||
*/
|
||||
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/module.h>
|
||||
#include "aoe.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Sam Hopkins <sah@coraid.com>");
|
||||
MODULE_DESCRIPTION("AoE block/char driver for 2.6.2 and newer 2.6 kernels");
|
||||
MODULE_VERSION(VERSION);
|
||||
|
||||
enum { TINIT, TRUN, TKILL };
|
||||
|
||||
static void
|
||||
discover_timer(ulong vp)
|
||||
{
|
||||
static struct timer_list t;
|
||||
static volatile ulong die;
|
||||
static spinlock_t lock;
|
||||
ulong flags;
|
||||
enum { DTIMERTICK = HZ * 60 }; /* one minute */
|
||||
|
||||
switch (vp) {
|
||||
case TINIT:
|
||||
init_timer(&t);
|
||||
spin_lock_init(&lock);
|
||||
t.data = TRUN;
|
||||
t.function = discover_timer;
|
||||
die = 0;
|
||||
case TRUN:
|
||||
spin_lock_irqsave(&lock, flags);
|
||||
if (!die) {
|
||||
t.expires = jiffies + DTIMERTICK;
|
||||
add_timer(&t);
|
||||
}
|
||||
spin_unlock_irqrestore(&lock, flags);
|
||||
|
||||
aoecmd_cfg(0xffff, 0xff);
|
||||
return;
|
||||
case TKILL:
|
||||
spin_lock_irqsave(&lock, flags);
|
||||
die = 1;
|
||||
spin_unlock_irqrestore(&lock, flags);
|
||||
|
||||
del_timer_sync(&t);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
aoe_exit(void)
|
||||
{
|
||||
discover_timer(TKILL);
|
||||
|
||||
aoenet_exit();
|
||||
unregister_blkdev(AOE_MAJOR, DEVICE_NAME);
|
||||
aoechr_exit();
|
||||
aoedev_exit();
|
||||
aoeblk_exit(); /* free cache after de-allocating bufs */
|
||||
}
|
||||
|
||||
static int __init
|
||||
aoe_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = aoedev_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = aoechr_init();
|
||||
if (ret)
|
||||
goto chr_fail;
|
||||
ret = aoeblk_init();
|
||||
if (ret)
|
||||
goto blk_fail;
|
||||
ret = aoenet_init();
|
||||
if (ret)
|
||||
goto net_fail;
|
||||
ret = register_blkdev(AOE_MAJOR, DEVICE_NAME);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "aoe: can't register major\n");
|
||||
goto blkreg_fail;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "aoe: AoE v%s initialised.\n", VERSION);
|
||||
discover_timer(TINIT);
|
||||
return 0;
|
||||
|
||||
blkreg_fail:
|
||||
aoenet_exit();
|
||||
net_fail:
|
||||
aoeblk_exit();
|
||||
blk_fail:
|
||||
aoechr_exit();
|
||||
chr_fail:
|
||||
aoedev_exit();
|
||||
|
||||
printk(KERN_INFO "aoe: initialisation failure.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
module_init(aoe_init);
|
||||
module_exit(aoe_exit);
|
||||
|
||||
174
drivers/block/aoe/aoenet.c
Normal file
174
drivers/block/aoe/aoenet.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
|
||||
/*
|
||||
* aoenet.c
|
||||
* Ethernet portion of AoE driver
|
||||
*/
|
||||
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "aoe.h"
|
||||
|
||||
#define NECODES 5
|
||||
|
||||
static char *aoe_errlist[] =
|
||||
{
|
||||
"no such error",
|
||||
"unrecognized command code",
|
||||
"bad argument parameter",
|
||||
"device unavailable",
|
||||
"config string present",
|
||||
"unsupported version"
|
||||
};
|
||||
|
||||
enum {
|
||||
IFLISTSZ = 1024,
|
||||
};
|
||||
|
||||
static char aoe_iflist[IFLISTSZ];
|
||||
module_param_string(aoe_iflist, aoe_iflist, IFLISTSZ, 0600);
|
||||
MODULE_PARM_DESC(aoe_iflist, "aoe_iflist=\"dev1 [dev2 ...]\"\n");
|
||||
|
||||
#ifndef MODULE
|
||||
static int __init aoe_iflist_setup(char *str)
|
||||
{
|
||||
strncpy(aoe_iflist, str, IFLISTSZ);
|
||||
aoe_iflist[IFLISTSZ - 1] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("aoe_iflist=", aoe_iflist_setup);
|
||||
#endif
|
||||
|
||||
int
|
||||
is_aoe_netif(struct net_device *ifp)
|
||||
{
|
||||
register char *p, *q;
|
||||
register int len;
|
||||
|
||||
if (aoe_iflist[0] == '\0')
|
||||
return 1;
|
||||
|
||||
p = aoe_iflist + strspn(aoe_iflist, WHITESPACE);
|
||||
for (; *p; p = q + strspn(q, WHITESPACE)) {
|
||||
q = p + strcspn(p, WHITESPACE);
|
||||
if (q != p)
|
||||
len = q - p;
|
||||
else
|
||||
len = strlen(p); /* last token in aoe_iflist */
|
||||
|
||||
if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len))
|
||||
return 1;
|
||||
if (q == p)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
set_aoe_iflist(const char __user *user_str, size_t size)
|
||||
{
|
||||
if (size >= IFLISTSZ)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(aoe_iflist, user_str, size)) {
|
||||
printk(KERN_INFO "aoe: copy from user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
aoe_iflist[size] = 0x00;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64
|
||||
mac_addr(char addr[6])
|
||||
{
|
||||
__be64 n = 0;
|
||||
char *p = (char *) &n;
|
||||
|
||||
memcpy(p + 2, addr, 6); /* (sizeof addr != 6) */
|
||||
|
||||
return __be64_to_cpu(n);
|
||||
}
|
||||
|
||||
void
|
||||
aoenet_xmit(struct sk_buff *sl)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
while ((skb = sl)) {
|
||||
sl = sl->next;
|
||||
skb->next = skb->prev = NULL;
|
||||
dev_queue_xmit(skb);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (1) len doesn't include the header by default. I want this.
|
||||
*/
|
||||
static int
|
||||
aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev)
|
||||
{
|
||||
struct aoe_hdr *h;
|
||||
u32 n;
|
||||
|
||||
skb = skb_share_check(skb, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return 0;
|
||||
if (skb_linearize(skb))
|
||||
goto exit;
|
||||
if (!is_aoe_netif(ifp))
|
||||
goto exit;
|
||||
skb_push(skb, ETH_HLEN); /* (1) */
|
||||
|
||||
h = (struct aoe_hdr *) skb->mac.raw;
|
||||
n = be32_to_cpu(get_unaligned(&h->tag));
|
||||
if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31))
|
||||
goto exit;
|
||||
|
||||
if (h->verfl & AOEFL_ERR) {
|
||||
n = h->err;
|
||||
if (n > NECODES)
|
||||
n = 0;
|
||||
if (net_ratelimit())
|
||||
printk(KERN_ERR "aoe: error packet from %d.%d; ecode=%d '%s'\n",
|
||||
be16_to_cpu(get_unaligned(&h->major)), h->minor,
|
||||
h->err, aoe_errlist[n]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
switch (h->cmd) {
|
||||
case AOECMD_ATA:
|
||||
aoecmd_ata_rsp(skb);
|
||||
break;
|
||||
case AOECMD_CFG:
|
||||
aoecmd_cfg_rsp(skb);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd);
|
||||
}
|
||||
exit:
|
||||
dev_kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct packet_type aoe_pt = {
|
||||
.type = __constant_htons(ETH_P_AOE),
|
||||
.func = aoenet_rcv,
|
||||
};
|
||||
|
||||
int __init
|
||||
aoenet_init(void)
|
||||
{
|
||||
dev_add_pack(&aoe_pt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
aoenet_exit(void)
|
||||
{
|
||||
dev_remove_pack(&aoe_pt);
|
||||
}
|
||||
|
||||
2011
drivers/block/ataflop.c
Normal file
2011
drivers/block/ataflop.c
Normal file
File diff suppressed because it is too large
Load Diff
3574
drivers/block/cciss.c
Normal file
3574
drivers/block/cciss.c
Normal file
File diff suppressed because it is too large
Load Diff
292
drivers/block/cciss.h
Normal file
292
drivers/block/cciss.h
Normal file
@@ -0,0 +1,292 @@
|
||||
#ifndef CCISS_H
|
||||
#define CCISS_H
|
||||
|
||||
#include <linux/genhd.h>
|
||||
|
||||
#include "cciss_cmd.h"
|
||||
|
||||
|
||||
#define NWD_SHIFT 4
|
||||
#define MAX_PART (1 << NWD_SHIFT)
|
||||
|
||||
#define IO_OK 0
|
||||
#define IO_ERROR 1
|
||||
|
||||
struct ctlr_info;
|
||||
typedef struct ctlr_info ctlr_info_t;
|
||||
|
||||
struct access_method {
|
||||
void (*submit_command)(ctlr_info_t *h, CommandList_struct *c);
|
||||
void (*set_intr_mask)(ctlr_info_t *h, unsigned long val);
|
||||
unsigned long (*fifo_full)(ctlr_info_t *h);
|
||||
unsigned long (*intr_pending)(ctlr_info_t *h);
|
||||
unsigned long (*command_completed)(ctlr_info_t *h);
|
||||
};
|
||||
typedef struct _drive_info_struct
|
||||
{
|
||||
__u32 LunID;
|
||||
int usage_count;
|
||||
struct request_queue *queue;
|
||||
sector_t nr_blocks;
|
||||
int block_size;
|
||||
int heads;
|
||||
int sectors;
|
||||
int cylinders;
|
||||
int raid_level; /* set to -1 to indicate that
|
||||
* the drive is not in use/configured
|
||||
*/
|
||||
int busy_configuring; /*This is set when the drive is being removed
|
||||
*to prevent it from being opened or it's queue
|
||||
*from being started.
|
||||
*/
|
||||
} drive_info_struct;
|
||||
|
||||
#ifdef CONFIG_CISS_SCSI_TAPE
|
||||
|
||||
struct sendcmd_reject_list {
|
||||
int ncompletions;
|
||||
unsigned long *complete; /* array of NR_CMDS tags */
|
||||
};
|
||||
|
||||
#endif
|
||||
struct ctlr_info
|
||||
{
|
||||
int ctlr;
|
||||
char devname[8];
|
||||
char *product_name;
|
||||
char firm_ver[4]; // Firmware version
|
||||
struct pci_dev *pdev;
|
||||
__u32 board_id;
|
||||
void __iomem *vaddr;
|
||||
unsigned long paddr;
|
||||
int nr_cmds; /* Number of commands allowed on this controller */
|
||||
CfgTable_struct __iomem *cfgtable;
|
||||
int interrupts_enabled;
|
||||
int major;
|
||||
int max_commands;
|
||||
int commands_outstanding;
|
||||
int max_outstanding; /* Debug */
|
||||
int num_luns;
|
||||
int highest_lun;
|
||||
int usage_count; /* number of opens all all minor devices */
|
||||
# define DOORBELL_INT 0
|
||||
# define PERF_MODE_INT 1
|
||||
# define SIMPLE_MODE_INT 2
|
||||
# define MEMQ_MODE_INT 3
|
||||
unsigned int intr[4];
|
||||
unsigned int msix_vector;
|
||||
unsigned int msi_vector;
|
||||
int cciss_max_sectors;
|
||||
BYTE cciss_read;
|
||||
BYTE cciss_write;
|
||||
BYTE cciss_read_capacity;
|
||||
|
||||
// information about each logical volume
|
||||
drive_info_struct drv[CISS_MAX_LUN];
|
||||
|
||||
struct access_method access;
|
||||
|
||||
/* queue and queue Info */
|
||||
CommandList_struct *reqQ;
|
||||
CommandList_struct *cmpQ;
|
||||
unsigned int Qdepth;
|
||||
unsigned int maxQsinceinit;
|
||||
unsigned int maxSG;
|
||||
spinlock_t lock;
|
||||
|
||||
//* pointers to command and error info pool */
|
||||
CommandList_struct *cmd_pool;
|
||||
dma_addr_t cmd_pool_dhandle;
|
||||
ErrorInfo_struct *errinfo_pool;
|
||||
dma_addr_t errinfo_pool_dhandle;
|
||||
unsigned long *cmd_pool_bits;
|
||||
int nr_allocs;
|
||||
int nr_frees;
|
||||
int busy_configuring;
|
||||
int busy_initializing;
|
||||
|
||||
/* This element holds the zero based queue number of the last
|
||||
* queue to be started. It is used for fairness.
|
||||
*/
|
||||
int next_to_run;
|
||||
|
||||
// Disk structures we need to pass back
|
||||
struct gendisk *gendisk[CISS_MAX_LUN];
|
||||
#ifdef CONFIG_CISS_SCSI_TAPE
|
||||
void *scsi_ctlr; /* ptr to structure containing scsi related stuff */
|
||||
/* list of block side commands the scsi error handling sucked up */
|
||||
/* and saved for later processing */
|
||||
struct sendcmd_reject_list scsi_rejects;
|
||||
#endif
|
||||
unsigned char alive;
|
||||
};
|
||||
|
||||
/* Defining the diffent access_menthods */
|
||||
/*
|
||||
* Memory mapped FIFO interface (SMART 53xx cards)
|
||||
*/
|
||||
#define SA5_DOORBELL 0x20
|
||||
#define SA5_REQUEST_PORT_OFFSET 0x40
|
||||
#define SA5_REPLY_INTR_MASK_OFFSET 0x34
|
||||
#define SA5_REPLY_PORT_OFFSET 0x44
|
||||
#define SA5_INTR_STATUS 0x30
|
||||
#define SA5_SCRATCHPAD_OFFSET 0xB0
|
||||
|
||||
#define SA5_CTCFG_OFFSET 0xB4
|
||||
#define SA5_CTMEM_OFFSET 0xB8
|
||||
|
||||
#define SA5_INTR_OFF 0x08
|
||||
#define SA5B_INTR_OFF 0x04
|
||||
#define SA5_INTR_PENDING 0x08
|
||||
#define SA5B_INTR_PENDING 0x04
|
||||
#define FIFO_EMPTY 0xffffffff
|
||||
#define CCISS_FIRMWARE_READY 0xffff0000 /* value in scratchpad register */
|
||||
|
||||
#define CISS_ERROR_BIT 0x02
|
||||
|
||||
#define CCISS_INTR_ON 1
|
||||
#define CCISS_INTR_OFF 0
|
||||
/*
|
||||
Send the command to the hardware
|
||||
*/
|
||||
static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c)
|
||||
{
|
||||
#ifdef CCISS_DEBUG
|
||||
printk("Sending %x - down to controller\n", c->busaddr );
|
||||
#endif /* CCISS_DEBUG */
|
||||
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
|
||||
h->commands_outstanding++;
|
||||
if ( h->commands_outstanding > h->max_outstanding)
|
||||
h->max_outstanding = h->commands_outstanding;
|
||||
}
|
||||
|
||||
/*
|
||||
* This card is the opposite of the other cards.
|
||||
* 0 turns interrupts on...
|
||||
* 0x08 turns them off...
|
||||
*/
|
||||
static void SA5_intr_mask(ctlr_info_t *h, unsigned long val)
|
||||
{
|
||||
if (val)
|
||||
{ /* Turn interrupts on */
|
||||
h->interrupts_enabled = 1;
|
||||
writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
|
||||
} else /* Turn them off */
|
||||
{
|
||||
h->interrupts_enabled = 0;
|
||||
writel( SA5_INTR_OFF,
|
||||
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This card is the opposite of the other cards.
|
||||
* 0 turns interrupts on...
|
||||
* 0x04 turns them off...
|
||||
*/
|
||||
static void SA5B_intr_mask(ctlr_info_t *h, unsigned long val)
|
||||
{
|
||||
if (val)
|
||||
{ /* Turn interrupts on */
|
||||
h->interrupts_enabled = 1;
|
||||
writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
|
||||
} else /* Turn them off */
|
||||
{
|
||||
h->interrupts_enabled = 0;
|
||||
writel( SA5B_INTR_OFF,
|
||||
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Returns true if fifo is full.
|
||||
*
|
||||
*/
|
||||
static unsigned long SA5_fifo_full(ctlr_info_t *h)
|
||||
{
|
||||
if( h->commands_outstanding >= h->max_commands)
|
||||
return(1);
|
||||
else
|
||||
return(0);
|
||||
|
||||
}
|
||||
/*
|
||||
* returns value read from hardware.
|
||||
* returns FIFO_EMPTY if there is nothing to read
|
||||
*/
|
||||
static unsigned long SA5_completed(ctlr_info_t *h)
|
||||
{
|
||||
unsigned long register_value
|
||||
= readl(h->vaddr + SA5_REPLY_PORT_OFFSET);
|
||||
if(register_value != FIFO_EMPTY)
|
||||
{
|
||||
h->commands_outstanding--;
|
||||
#ifdef CCISS_DEBUG
|
||||
printk("cciss: Read %lx back from board\n", register_value);
|
||||
#endif /* CCISS_DEBUG */
|
||||
}
|
||||
#ifdef CCISS_DEBUG
|
||||
else
|
||||
{
|
||||
printk("cciss: FIFO Empty read\n");
|
||||
}
|
||||
#endif
|
||||
return ( register_value);
|
||||
|
||||
}
|
||||
/*
|
||||
* Returns true if an interrupt is pending..
|
||||
*/
|
||||
static unsigned long SA5_intr_pending(ctlr_info_t *h)
|
||||
{
|
||||
unsigned long register_value =
|
||||
readl(h->vaddr + SA5_INTR_STATUS);
|
||||
#ifdef CCISS_DEBUG
|
||||
printk("cciss: intr_pending %lx\n", register_value);
|
||||
#endif /* CCISS_DEBUG */
|
||||
if( register_value & SA5_INTR_PENDING)
|
||||
return 1;
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if an interrupt is pending..
|
||||
*/
|
||||
static unsigned long SA5B_intr_pending(ctlr_info_t *h)
|
||||
{
|
||||
unsigned long register_value =
|
||||
readl(h->vaddr + SA5_INTR_STATUS);
|
||||
#ifdef CCISS_DEBUG
|
||||
printk("cciss: intr_pending %lx\n", register_value);
|
||||
#endif /* CCISS_DEBUG */
|
||||
if( register_value & SA5B_INTR_PENDING)
|
||||
return 1;
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
|
||||
static struct access_method SA5_access = {
|
||||
SA5_submit_command,
|
||||
SA5_intr_mask,
|
||||
SA5_fifo_full,
|
||||
SA5_intr_pending,
|
||||
SA5_completed,
|
||||
};
|
||||
|
||||
static struct access_method SA5B_access = {
|
||||
SA5_submit_command,
|
||||
SA5B_intr_mask,
|
||||
SA5_fifo_full,
|
||||
SA5B_intr_pending,
|
||||
SA5_completed,
|
||||
};
|
||||
|
||||
struct board_type {
|
||||
__u32 board_id;
|
||||
char *product_name;
|
||||
struct access_method *access;
|
||||
int nr_cmds; /* Max cmds this kind of ctlr can handle. */
|
||||
};
|
||||
|
||||
#define CCISS_LOCK(i) (&hba[i]->lock)
|
||||
|
||||
#endif /* CCISS_H */
|
||||
|
||||
299
drivers/block/cciss_cmd.h
Normal file
299
drivers/block/cciss_cmd.h
Normal file
@@ -0,0 +1,299 @@
|
||||
#ifndef CCISS_CMD_H
|
||||
#define CCISS_CMD_H
|
||||
//###########################################################################
|
||||
//DEFINES
|
||||
//###########################################################################
|
||||
#define CISS_VERSION "1.00"
|
||||
|
||||
//general boundary defintions
|
||||
#define SENSEINFOBYTES 32//note that this value may vary between host implementations
|
||||
#define MAXSGENTRIES 31
|
||||
#define MAXREPLYQS 256
|
||||
|
||||
//Command Status value
|
||||
#define CMD_SUCCESS 0x0000
|
||||
#define CMD_TARGET_STATUS 0x0001
|
||||
#define CMD_DATA_UNDERRUN 0x0002
|
||||
#define CMD_DATA_OVERRUN 0x0003
|
||||
#define CMD_INVALID 0x0004
|
||||
#define CMD_PROTOCOL_ERR 0x0005
|
||||
#define CMD_HARDWARE_ERR 0x0006
|
||||
#define CMD_CONNECTION_LOST 0x0007
|
||||
#define CMD_ABORTED 0x0008
|
||||
#define CMD_ABORT_FAILED 0x0009
|
||||
#define CMD_UNSOLICITED_ABORT 0x000A
|
||||
#define CMD_TIMEOUT 0x000B
|
||||
#define CMD_UNABORTABLE 0x000C
|
||||
|
||||
//transfer direction
|
||||
#define XFER_NONE 0x00
|
||||
#define XFER_WRITE 0x01
|
||||
#define XFER_READ 0x02
|
||||
#define XFER_RSVD 0x03
|
||||
|
||||
//task attribute
|
||||
#define ATTR_UNTAGGED 0x00
|
||||
#define ATTR_SIMPLE 0x04
|
||||
#define ATTR_HEADOFQUEUE 0x05
|
||||
#define ATTR_ORDERED 0x06
|
||||
#define ATTR_ACA 0x07
|
||||
|
||||
//cdb type
|
||||
#define TYPE_CMD 0x00
|
||||
#define TYPE_MSG 0x01
|
||||
|
||||
//config space register offsets
|
||||
#define CFG_VENDORID 0x00
|
||||
#define CFG_DEVICEID 0x02
|
||||
#define CFG_I2OBAR 0x10
|
||||
#define CFG_MEM1BAR 0x14
|
||||
|
||||
//i2o space register offsets
|
||||
#define I2O_IBDB_SET 0x20
|
||||
#define I2O_IBDB_CLEAR 0x70
|
||||
#define I2O_INT_STATUS 0x30
|
||||
#define I2O_INT_MASK 0x34
|
||||
#define I2O_IBPOST_Q 0x40
|
||||
#define I2O_OBPOST_Q 0x44
|
||||
#define I2O_DMA1_CFG 0x214
|
||||
|
||||
//Configuration Table
|
||||
#define CFGTBL_ChangeReq 0x00000001l
|
||||
#define CFGTBL_AccCmds 0x00000001l
|
||||
|
||||
#define CFGTBL_Trans_Simple 0x00000002l
|
||||
|
||||
#define CFGTBL_BusType_Ultra2 0x00000001l
|
||||
#define CFGTBL_BusType_Ultra3 0x00000002l
|
||||
#define CFGTBL_BusType_Fibre1G 0x00000100l
|
||||
#define CFGTBL_BusType_Fibre2G 0x00000200l
|
||||
typedef struct _vals32
|
||||
{
|
||||
__u32 lower;
|
||||
__u32 upper;
|
||||
} vals32;
|
||||
|
||||
typedef union _u64bit
|
||||
{
|
||||
vals32 val32;
|
||||
__u64 val;
|
||||
} u64bit;
|
||||
|
||||
// Type defs used in the following structs
|
||||
#define BYTE __u8
|
||||
#define WORD __u16
|
||||
#define HWORD __u16
|
||||
#define DWORD __u32
|
||||
#define QWORD vals32
|
||||
|
||||
//###########################################################################
|
||||
//STRUCTURES
|
||||
//###########################################################################
|
||||
#define CISS_MAX_LUN 1024
|
||||
#define CISS_MAX_PHYS_LUN 1024
|
||||
// SCSI-3 Cmmands
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
#define CISS_INQUIRY 0x12
|
||||
//Date returned
|
||||
typedef struct _InquiryData_struct
|
||||
{
|
||||
BYTE data_byte[36];
|
||||
} InquiryData_struct;
|
||||
|
||||
#define CISS_REPORT_LOG 0xc2 /* Report Logical LUNs */
|
||||
#define CISS_REPORT_PHYS 0xc3 /* Report Physical LUNs */
|
||||
// Data returned
|
||||
typedef struct _ReportLUNdata_struct
|
||||
{
|
||||
BYTE LUNListLength[4];
|
||||
DWORD reserved;
|
||||
BYTE LUN[CISS_MAX_LUN][8];
|
||||
} ReportLunData_struct;
|
||||
|
||||
#define CCISS_READ_CAPACITY 0x25 /* Read Capacity */
|
||||
typedef struct _ReadCapdata_struct
|
||||
{
|
||||
BYTE total_size[4]; // Total size in blocks
|
||||
BYTE block_size[4]; // Size of blocks in bytes
|
||||
} ReadCapdata_struct;
|
||||
|
||||
#define CCISS_READ_CAPACITY_16 0x9e /* Read Capacity 16 */
|
||||
|
||||
/* service action to differentiate a 16 byte read capacity from
|
||||
other commands that use the 0x9e SCSI op code */
|
||||
|
||||
#define CCISS_READ_CAPACITY_16_SERVICE_ACT 0x10
|
||||
|
||||
typedef struct _ReadCapdata_struct_16
|
||||
{
|
||||
BYTE total_size[8]; /* Total size in blocks */
|
||||
BYTE block_size[4]; /* Size of blocks in bytes */
|
||||
BYTE prot_en:1; /* protection enable bit */
|
||||
BYTE rto_en:1; /* reference tag own enable bit */
|
||||
BYTE reserved:6; /* reserved bits */
|
||||
BYTE reserved2[18]; /* reserved bytes per spec */
|
||||
} ReadCapdata_struct_16;
|
||||
|
||||
/* Define the supported read/write commands for cciss based controllers */
|
||||
|
||||
#define CCISS_READ_10 0x28 /* Read(10) */
|
||||
#define CCISS_WRITE_10 0x2a /* Write(10) */
|
||||
#define CCISS_READ_16 0x88 /* Read(16) */
|
||||
#define CCISS_WRITE_16 0x8a /* Write(16) */
|
||||
|
||||
/* Define the CDB lengths supported by cciss based controllers */
|
||||
|
||||
#define CDB_LEN10 10
|
||||
#define CDB_LEN16 16
|
||||
|
||||
// BMIC commands
|
||||
#define BMIC_READ 0x26
|
||||
#define BMIC_WRITE 0x27
|
||||
#define BMIC_CACHE_FLUSH 0xc2
|
||||
#define CCISS_CACHE_FLUSH 0x01 //C2 was already being used by CCISS
|
||||
|
||||
//Command List Structure
|
||||
typedef union _SCSI3Addr_struct {
|
||||
struct {
|
||||
BYTE Dev;
|
||||
BYTE Bus:6;
|
||||
BYTE Mode:2; // b00
|
||||
} PeripDev;
|
||||
struct {
|
||||
BYTE DevLSB;
|
||||
BYTE DevMSB:6;
|
||||
BYTE Mode:2; // b01
|
||||
} LogDev;
|
||||
struct {
|
||||
BYTE Dev:5;
|
||||
BYTE Bus:3;
|
||||
BYTE Targ:6;
|
||||
BYTE Mode:2; // b10
|
||||
} LogUnit;
|
||||
} SCSI3Addr_struct;
|
||||
|
||||
typedef struct _PhysDevAddr_struct {
|
||||
DWORD TargetId:24;
|
||||
DWORD Bus:6;
|
||||
DWORD Mode:2;
|
||||
SCSI3Addr_struct Target[2]; //2 level target device addr
|
||||
} PhysDevAddr_struct;
|
||||
|
||||
typedef struct _LogDevAddr_struct {
|
||||
DWORD VolId:30;
|
||||
DWORD Mode:2;
|
||||
BYTE reserved[4];
|
||||
} LogDevAddr_struct;
|
||||
|
||||
typedef union _LUNAddr_struct {
|
||||
BYTE LunAddrBytes[8];
|
||||
SCSI3Addr_struct SCSI3Lun[4];
|
||||
PhysDevAddr_struct PhysDev;
|
||||
LogDevAddr_struct LogDev;
|
||||
} LUNAddr_struct;
|
||||
|
||||
typedef struct _CommandListHeader_struct {
|
||||
BYTE ReplyQueue;
|
||||
BYTE SGList;
|
||||
HWORD SGTotal;
|
||||
QWORD Tag;
|
||||
LUNAddr_struct LUN;
|
||||
} CommandListHeader_struct;
|
||||
typedef struct _RequestBlock_struct {
|
||||
BYTE CDBLen;
|
||||
struct {
|
||||
BYTE Type:3;
|
||||
BYTE Attribute:3;
|
||||
BYTE Direction:2;
|
||||
} Type;
|
||||
HWORD Timeout;
|
||||
BYTE CDB[16];
|
||||
} RequestBlock_struct;
|
||||
typedef struct _ErrDescriptor_struct {
|
||||
QWORD Addr;
|
||||
DWORD Len;
|
||||
} ErrDescriptor_struct;
|
||||
typedef struct _SGDescriptor_struct {
|
||||
QWORD Addr;
|
||||
DWORD Len;
|
||||
DWORD Ext;
|
||||
} SGDescriptor_struct;
|
||||
|
||||
typedef union _MoreErrInfo_struct{
|
||||
struct {
|
||||
BYTE Reserved[3];
|
||||
BYTE Type;
|
||||
DWORD ErrorInfo;
|
||||
}Common_Info;
|
||||
struct{
|
||||
BYTE Reserved[2];
|
||||
BYTE offense_size;//size of offending entry
|
||||
BYTE offense_num; //byte # of offense 0-base
|
||||
DWORD offense_value;
|
||||
}Invalid_Cmd;
|
||||
}MoreErrInfo_struct;
|
||||
typedef struct _ErrorInfo_struct {
|
||||
BYTE ScsiStatus;
|
||||
BYTE SenseLen;
|
||||
HWORD CommandStatus;
|
||||
DWORD ResidualCnt;
|
||||
MoreErrInfo_struct MoreErrInfo;
|
||||
BYTE SenseInfo[SENSEINFOBYTES];
|
||||
} ErrorInfo_struct;
|
||||
|
||||
/* Command types */
|
||||
#define CMD_RWREQ 0x00
|
||||
#define CMD_IOCTL_PEND 0x01
|
||||
#define CMD_SCSI 0x03
|
||||
#define CMD_MSG_DONE 0x04
|
||||
#define CMD_MSG_TIMEOUT 0x05
|
||||
|
||||
/* This structure needs to be divisible by 8 for new
|
||||
* indexing method.
|
||||
*/
|
||||
#define PADSIZE (sizeof(long) - 4)
|
||||
typedef struct _CommandList_struct {
|
||||
CommandListHeader_struct Header;
|
||||
RequestBlock_struct Request;
|
||||
ErrDescriptor_struct ErrDesc;
|
||||
SGDescriptor_struct SG[MAXSGENTRIES];
|
||||
/* information associated with the command */
|
||||
__u32 busaddr; /* physical address of this record */
|
||||
ErrorInfo_struct * err_info; /* pointer to the allocated mem */
|
||||
int ctlr;
|
||||
int cmd_type;
|
||||
long cmdindex;
|
||||
struct _CommandList_struct *prev;
|
||||
struct _CommandList_struct *next;
|
||||
struct request * rq;
|
||||
struct completion *waiting;
|
||||
int retry_count;
|
||||
void * scsi_cmd;
|
||||
char pad[PADSIZE];
|
||||
} CommandList_struct;
|
||||
|
||||
//Configuration Table Structure
|
||||
typedef struct _HostWrite_struct {
|
||||
DWORD TransportRequest;
|
||||
DWORD Reserved;
|
||||
DWORD CoalIntDelay;
|
||||
DWORD CoalIntCount;
|
||||
} HostWrite_struct;
|
||||
|
||||
typedef struct _CfgTable_struct {
|
||||
BYTE Signature[4];
|
||||
DWORD SpecValence;
|
||||
DWORD TransportSupport;
|
||||
DWORD TransportActive;
|
||||
HostWrite_struct HostWrite;
|
||||
DWORD CmdsOutMax;
|
||||
DWORD BusTypes;
|
||||
DWORD Reserved;
|
||||
BYTE ServerName[16];
|
||||
DWORD HeartBeat;
|
||||
DWORD SCSI_Prefetch;
|
||||
} CfgTable_struct;
|
||||
#pragma pack()
|
||||
#endif // CCISS_CMD_H
|
||||
1535
drivers/block/cciss_scsi.c
Normal file
1535
drivers/block/cciss_scsi.c
Normal file
File diff suppressed because it is too large
Load Diff
79
drivers/block/cciss_scsi.h
Normal file
79
drivers/block/cciss_scsi.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Disk Array driver for Compaq SA53xx Controllers, SCSI Tape module
|
||||
* Copyright 2001 Compaq Computer Corporation
|
||||
*
|
||||
* 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, GOOD TITLE or
|
||||
* NON INFRINGEMENT. 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.
|
||||
*
|
||||
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
|
||||
*
|
||||
*/
|
||||
#ifdef CONFIG_CISS_SCSI_TAPE
|
||||
#ifndef _CCISS_SCSI_H_
|
||||
#define _CCISS_SCSI_H_
|
||||
|
||||
#include <scsi/scsicam.h> /* possibly irrelevant, since we don't show disks */
|
||||
|
||||
// the scsi id of the adapter...
|
||||
#define SELF_SCSI_ID 15
|
||||
// 15 is somewhat arbitrary, since the scsi-2 bus
|
||||
// that's presented by the driver to the OS is
|
||||
// fabricated. The "real" scsi-3 bus the
|
||||
// hardware presents is fabricated too.
|
||||
// The actual, honest-to-goodness physical
|
||||
// bus that the devices are attached to is not
|
||||
// addressible natively, and may in fact turn
|
||||
// out to be not scsi at all.
|
||||
|
||||
#define SCSI_CCISS_CAN_QUEUE 2
|
||||
|
||||
/*
|
||||
|
||||
Note, cmd_per_lun could give us some trouble, so I'm setting it very low.
|
||||
Likewise, SCSI_CCISS_CAN_QUEUE is set very conservatively.
|
||||
|
||||
If the upper scsi layer tries to track how many commands we have
|
||||
outstanding, it will be operating under the misapprehension that it is
|
||||
the only one sending us requests. We also have the block interface,
|
||||
which is where most requests must surely come from, so the upper layer's
|
||||
notion of how many requests we have outstanding will be wrong most or
|
||||
all of the time.
|
||||
|
||||
Note, the normal SCSI mid-layer error handling doesn't work well
|
||||
for this driver because 1) it takes the io_request_lock before
|
||||
calling error handlers and uses a local variable to store flags,
|
||||
so the io_request_lock cannot be released and interrupts enabled
|
||||
inside the error handlers, and, the error handlers cannot poll
|
||||
for command completion because they might get commands from the
|
||||
block half of the driver completing, and not know what to do
|
||||
with them. That's what we get for making a hybrid scsi/block
|
||||
driver, I suppose.
|
||||
|
||||
*/
|
||||
|
||||
struct cciss_scsi_dev_t {
|
||||
int devtype;
|
||||
int bus, target, lun; /* as presented to the OS */
|
||||
unsigned char scsi3addr[8]; /* as presented to the HW */
|
||||
};
|
||||
|
||||
struct cciss_scsi_hba_t {
|
||||
char *name;
|
||||
int ndevices;
|
||||
#define CCISS_MAX_SCSI_DEVS_PER_HBA 16
|
||||
struct cciss_scsi_dev_t dev[CCISS_MAX_SCSI_DEVS_PER_HBA];
|
||||
};
|
||||
|
||||
#endif /* _CCISS_SCSI_H_ */
|
||||
#endif /* CONFIG_CISS_SCSI_TAPE */
|
||||
1850
drivers/block/cpqarray.c
Normal file
1850
drivers/block/cpqarray.c
Normal file
File diff suppressed because it is too large
Load Diff
126
drivers/block/cpqarray.h
Normal file
126
drivers/block/cpqarray.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Disk Array driver for Compaq SMART2 Controllers
|
||||
* Copyright 1998 Compaq Computer Corporation
|
||||
*
|
||||
* 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, GOOD TITLE or
|
||||
* NON INFRINGEMENT. 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.
|
||||
*
|
||||
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
|
||||
*
|
||||
* If you want to make changes, improve or add functionality to this
|
||||
* driver, you'll probably need the Compaq Array Controller Interface
|
||||
* Specificiation (Document number ECG086/1198)
|
||||
*/
|
||||
#ifndef CPQARRAY_H
|
||||
#define CPQARRAY_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/timer.h>
|
||||
#endif
|
||||
|
||||
#include "ida_cmd.h"
|
||||
|
||||
#define IO_OK 0
|
||||
#define IO_ERROR 1
|
||||
#define NWD 16
|
||||
#define NWD_SHIFT 4
|
||||
|
||||
#define IDA_TIMER (5*HZ)
|
||||
#define IDA_TIMEOUT (10*HZ)
|
||||
|
||||
#define MISC_NONFATAL_WARN 0x01
|
||||
|
||||
typedef struct {
|
||||
unsigned blk_size;
|
||||
unsigned nr_blks;
|
||||
unsigned cylinders;
|
||||
unsigned heads;
|
||||
unsigned sectors;
|
||||
int usage_count;
|
||||
} drv_info_t;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
struct ctlr_info;
|
||||
typedef struct ctlr_info ctlr_info_t;
|
||||
|
||||
struct access_method {
|
||||
void (*submit_command)(ctlr_info_t *h, cmdlist_t *c);
|
||||
void (*set_intr_mask)(ctlr_info_t *h, unsigned long val);
|
||||
unsigned long (*fifo_full)(ctlr_info_t *h);
|
||||
unsigned long (*intr_pending)(ctlr_info_t *h);
|
||||
unsigned long (*command_completed)(ctlr_info_t *h);
|
||||
};
|
||||
|
||||
struct board_type {
|
||||
__u32 board_id;
|
||||
char *product_name;
|
||||
struct access_method *access;
|
||||
};
|
||||
|
||||
struct ctlr_info {
|
||||
int ctlr;
|
||||
char devname[8];
|
||||
__u32 log_drv_map;
|
||||
__u32 drv_assign_map;
|
||||
__u32 drv_spare_map;
|
||||
__u32 mp_failed_drv_map;
|
||||
|
||||
char firm_rev[4];
|
||||
int ctlr_sig;
|
||||
|
||||
int log_drives;
|
||||
int phys_drives;
|
||||
|
||||
struct pci_dev *pci_dev; /* NULL if EISA */
|
||||
__u32 board_id;
|
||||
char *product_name;
|
||||
|
||||
void __iomem *vaddr;
|
||||
unsigned long paddr;
|
||||
unsigned long io_mem_addr;
|
||||
unsigned long io_mem_length;
|
||||
int intr;
|
||||
int usage_count;
|
||||
drv_info_t drv[NWD];
|
||||
struct proc_dir_entry *proc;
|
||||
|
||||
struct access_method access;
|
||||
|
||||
cmdlist_t *reqQ;
|
||||
cmdlist_t *cmpQ;
|
||||
cmdlist_t *cmd_pool;
|
||||
dma_addr_t cmd_pool_dhandle;
|
||||
unsigned long *cmd_pool_bits;
|
||||
struct request_queue *queue;
|
||||
spinlock_t lock;
|
||||
|
||||
unsigned int Qdepth;
|
||||
unsigned int maxQsinceinit;
|
||||
|
||||
unsigned int nr_requests;
|
||||
unsigned int nr_allocs;
|
||||
unsigned int nr_frees;
|
||||
struct timer_list timer;
|
||||
unsigned int misc_tflags;
|
||||
};
|
||||
|
||||
#define IDA_LOCK(i) (&hba[i]->lock)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* CPQARRAY_H */
|
||||
218
drivers/block/cryptoloop.c
Normal file
218
drivers/block/cryptoloop.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
Linux loop encryption enabling module
|
||||
|
||||
Copyright (C) 2002 Herbert Valerio Riedel <hvr@gnu.org>
|
||||
Copyright (C) 2003 Fruhwirth Clemens <clemens@endorphin.org>
|
||||
|
||||
This module 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 module 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 module; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/loop.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("loop blockdevice transferfunction adaptor / CryptoAPI");
|
||||
MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
|
||||
|
||||
#define LOOP_IV_SECTOR_BITS 9
|
||||
#define LOOP_IV_SECTOR_SIZE (1 << LOOP_IV_SECTOR_BITS)
|
||||
|
||||
static int
|
||||
cryptoloop_init(struct loop_device *lo, const struct loop_info64 *info)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
int cipher_len;
|
||||
int mode_len;
|
||||
char cms[LO_NAME_SIZE]; /* cipher-mode string */
|
||||
char *cipher;
|
||||
char *mode;
|
||||
char *cmsp = cms; /* c-m string pointer */
|
||||
struct crypto_blkcipher *tfm;
|
||||
|
||||
/* encryption breaks for non sector aligned offsets */
|
||||
|
||||
if (info->lo_offset % LOOP_IV_SECTOR_SIZE)
|
||||
goto out;
|
||||
|
||||
strncpy(cms, info->lo_crypt_name, LO_NAME_SIZE);
|
||||
cms[LO_NAME_SIZE - 1] = 0;
|
||||
|
||||
cipher = cmsp;
|
||||
cipher_len = strcspn(cmsp, "-");
|
||||
|
||||
mode = cmsp + cipher_len;
|
||||
mode_len = 0;
|
||||
if (*mode) {
|
||||
mode++;
|
||||
mode_len = strcspn(mode, "-");
|
||||
}
|
||||
|
||||
if (!mode_len) {
|
||||
mode = "cbc";
|
||||
mode_len = 3;
|
||||
}
|
||||
|
||||
if (cipher_len + mode_len + 3 > LO_NAME_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
memmove(cms, mode, mode_len);
|
||||
cmsp = cms + mode_len;
|
||||
*cmsp++ = '(';
|
||||
memcpy(cmsp, info->lo_crypt_name, cipher_len);
|
||||
cmsp += cipher_len;
|
||||
*cmsp++ = ')';
|
||||
*cmsp = 0;
|
||||
|
||||
tfm = crypto_alloc_blkcipher(cms, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(tfm))
|
||||
return PTR_ERR(tfm);
|
||||
|
||||
err = crypto_blkcipher_setkey(tfm, info->lo_encrypt_key,
|
||||
info->lo_encrypt_key_size);
|
||||
|
||||
if (err != 0)
|
||||
goto out_free_tfm;
|
||||
|
||||
lo->key_data = tfm;
|
||||
return 0;
|
||||
|
||||
out_free_tfm:
|
||||
crypto_free_blkcipher(tfm);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
typedef int (*encdec_cbc_t)(struct blkcipher_desc *desc,
|
||||
struct scatterlist *sg_out,
|
||||
struct scatterlist *sg_in,
|
||||
unsigned int nsg);
|
||||
|
||||
static int
|
||||
cryptoloop_transfer(struct loop_device *lo, int cmd,
|
||||
struct page *raw_page, unsigned raw_off,
|
||||
struct page *loop_page, unsigned loop_off,
|
||||
int size, sector_t IV)
|
||||
{
|
||||
struct crypto_blkcipher *tfm = lo->key_data;
|
||||
struct blkcipher_desc desc = {
|
||||
.tfm = tfm,
|
||||
.flags = CRYPTO_TFM_REQ_MAY_SLEEP,
|
||||
};
|
||||
struct scatterlist sg_out = { NULL, };
|
||||
struct scatterlist sg_in = { NULL, };
|
||||
|
||||
encdec_cbc_t encdecfunc;
|
||||
struct page *in_page, *out_page;
|
||||
unsigned in_offs, out_offs;
|
||||
int err;
|
||||
|
||||
if (cmd == READ) {
|
||||
in_page = raw_page;
|
||||
in_offs = raw_off;
|
||||
out_page = loop_page;
|
||||
out_offs = loop_off;
|
||||
encdecfunc = crypto_blkcipher_crt(tfm)->decrypt;
|
||||
} else {
|
||||
in_page = loop_page;
|
||||
in_offs = loop_off;
|
||||
out_page = raw_page;
|
||||
out_offs = raw_off;
|
||||
encdecfunc = crypto_blkcipher_crt(tfm)->encrypt;
|
||||
}
|
||||
|
||||
while (size > 0) {
|
||||
const int sz = min(size, LOOP_IV_SECTOR_SIZE);
|
||||
u32 iv[4] = { 0, };
|
||||
iv[0] = cpu_to_le32(IV & 0xffffffff);
|
||||
|
||||
sg_in.page = in_page;
|
||||
sg_in.offset = in_offs;
|
||||
sg_in.length = sz;
|
||||
|
||||
sg_out.page = out_page;
|
||||
sg_out.offset = out_offs;
|
||||
sg_out.length = sz;
|
||||
|
||||
desc.info = iv;
|
||||
err = encdecfunc(&desc, &sg_out, &sg_in, sz);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
IV++;
|
||||
size -= sz;
|
||||
in_offs += sz;
|
||||
out_offs += sz;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cryptoloop_ioctl(struct loop_device *lo, int cmd, unsigned long arg)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
cryptoloop_release(struct loop_device *lo)
|
||||
{
|
||||
struct crypto_blkcipher *tfm = lo->key_data;
|
||||
if (tfm != NULL) {
|
||||
crypto_free_blkcipher(tfm);
|
||||
lo->key_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
printk(KERN_ERR "cryptoloop_release(): tfm == NULL?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct loop_func_table cryptoloop_funcs = {
|
||||
.number = LO_CRYPT_CRYPTOAPI,
|
||||
.init = cryptoloop_init,
|
||||
.ioctl = cryptoloop_ioctl,
|
||||
.transfer = cryptoloop_transfer,
|
||||
.release = cryptoloop_release,
|
||||
.owner = THIS_MODULE
|
||||
};
|
||||
|
||||
static int __init
|
||||
init_cryptoloop(void)
|
||||
{
|
||||
int rc = loop_register_transfer(&cryptoloop_funcs);
|
||||
|
||||
if (rc)
|
||||
printk(KERN_ERR "cryptoloop: loop_register_transfer failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit
|
||||
cleanup_cryptoloop(void)
|
||||
{
|
||||
if (loop_unregister_transfer(LO_CRYPT_CRYPTOAPI))
|
||||
printk(KERN_ERR
|
||||
"cryptoloop: loop_unregister_transfer failed\n");
|
||||
}
|
||||
|
||||
module_init(init_cryptoloop);
|
||||
module_exit(cleanup_cryptoloop);
|
||||
4585
drivers/block/floppy.c
Normal file
4585
drivers/block/floppy.c
Normal file
File diff suppressed because it is too large
Load Diff
349
drivers/block/ida_cmd.h
Normal file
349
drivers/block/ida_cmd.h
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Disk Array driver for Compaq SMART2 Controllers
|
||||
* Copyright 1998 Compaq Computer Corporation
|
||||
*
|
||||
* 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, GOOD TITLE or
|
||||
* NON INFRINGEMENT. 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.
|
||||
*
|
||||
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
|
||||
*
|
||||
*/
|
||||
#ifndef ARRAYCMD_H
|
||||
#define ARRAYCMD_H
|
||||
|
||||
#include <asm/types.h>
|
||||
#if 0
|
||||
#include <linux/blkdev.h>
|
||||
#endif
|
||||
|
||||
/* for the Smart Array 42XX cards */
|
||||
#define S42XX_REQUEST_PORT_OFFSET 0x40
|
||||
#define S42XX_REPLY_INTR_MASK_OFFSET 0x34
|
||||
#define S42XX_REPLY_PORT_OFFSET 0x44
|
||||
#define S42XX_INTR_STATUS 0x30
|
||||
|
||||
#define S42XX_INTR_OFF 0x08
|
||||
#define S42XX_INTR_PENDING 0x08
|
||||
|
||||
#define COMMAND_FIFO 0x04
|
||||
#define COMMAND_COMPLETE_FIFO 0x08
|
||||
#define INTR_MASK 0x0C
|
||||
#define INTR_STATUS 0x10
|
||||
#define INTR_PENDING 0x14
|
||||
|
||||
#define FIFO_NOT_EMPTY 0x01
|
||||
#define FIFO_NOT_FULL 0x02
|
||||
|
||||
#define BIG_PROBLEM 0x40
|
||||
#define LOG_NOT_CONF 2
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct {
|
||||
__u32 size;
|
||||
__u32 addr;
|
||||
} sg_t;
|
||||
|
||||
#define RCODE_NONFATAL 0x02
|
||||
#define RCODE_FATAL 0x04
|
||||
#define RCODE_INVREQ 0x10
|
||||
typedef struct {
|
||||
__u16 next;
|
||||
__u8 cmd;
|
||||
__u8 rcode;
|
||||
__u32 blk;
|
||||
__u16 blk_cnt;
|
||||
__u8 sg_cnt;
|
||||
__u8 reserved;
|
||||
} rhdr_t;
|
||||
|
||||
#define SG_MAX 32
|
||||
typedef struct {
|
||||
rhdr_t hdr;
|
||||
sg_t sg[SG_MAX];
|
||||
__u32 bp;
|
||||
} rblk_t;
|
||||
|
||||
typedef struct {
|
||||
__u8 unit;
|
||||
__u8 prio;
|
||||
__u16 size;
|
||||
} chdr_t;
|
||||
|
||||
#define CMD_RWREQ 0x00
|
||||
#define CMD_IOCTL_PEND 0x01
|
||||
#define CMD_IOCTL_DONE 0x02
|
||||
|
||||
typedef struct cmdlist {
|
||||
chdr_t hdr;
|
||||
rblk_t req;
|
||||
__u32 size;
|
||||
int retry_cnt;
|
||||
__u32 busaddr;
|
||||
int ctlr;
|
||||
struct cmdlist *prev;
|
||||
struct cmdlist *next;
|
||||
struct request *rq;
|
||||
int type;
|
||||
} cmdlist_t;
|
||||
|
||||
#define ID_CTLR 0x11
|
||||
typedef struct {
|
||||
__u8 nr_drvs;
|
||||
__u32 cfg_sig;
|
||||
__u8 firm_rev[4];
|
||||
__u8 rom_rev[4];
|
||||
__u8 hw_rev;
|
||||
__u32 bb_rev;
|
||||
__u32 drv_present_map;
|
||||
__u32 ext_drv_map;
|
||||
__u32 board_id;
|
||||
__u8 cfg_error;
|
||||
__u32 non_disk_bits;
|
||||
__u8 bad_ram_addr;
|
||||
__u8 cpu_rev;
|
||||
__u8 pdpi_rev;
|
||||
__u8 epic_rev;
|
||||
__u8 wcxc_rev;
|
||||
__u8 marketing_rev;
|
||||
__u8 ctlr_flags;
|
||||
__u8 host_flags;
|
||||
__u8 expand_dis;
|
||||
__u8 scsi_chips;
|
||||
__u32 max_req_blocks;
|
||||
__u32 ctlr_clock;
|
||||
__u8 drvs_per_bus;
|
||||
__u16 big_drv_present_map[8];
|
||||
__u16 big_ext_drv_map[8];
|
||||
__u16 big_non_disk_map[8];
|
||||
__u16 task_flags;
|
||||
__u8 icl_bus;
|
||||
__u8 red_modes;
|
||||
__u8 cur_red_mode;
|
||||
__u8 red_ctlr_stat;
|
||||
__u8 red_fail_reason;
|
||||
__u8 reserved[403];
|
||||
} id_ctlr_t;
|
||||
|
||||
typedef struct {
|
||||
__u16 cyl;
|
||||
__u8 heads;
|
||||
__u8 xsig;
|
||||
__u8 psectors;
|
||||
__u16 wpre;
|
||||
__u8 maxecc;
|
||||
__u8 drv_ctrl;
|
||||
__u16 pcyls;
|
||||
__u8 pheads;
|
||||
__u16 landz;
|
||||
__u8 sect_per_track;
|
||||
__u8 cksum;
|
||||
} drv_param_t;
|
||||
|
||||
#define ID_LOG_DRV 0x10
|
||||
typedef struct {
|
||||
__u16 blk_size;
|
||||
__u32 nr_blks;
|
||||
drv_param_t drv;
|
||||
__u8 fault_tol;
|
||||
__u8 reserved;
|
||||
__u8 bios_disable;
|
||||
} id_log_drv_t;
|
||||
|
||||
#define ID_LOG_DRV_EXT 0x18
|
||||
typedef struct {
|
||||
__u32 log_drv_id;
|
||||
__u8 log_drv_label[64];
|
||||
__u8 reserved[418];
|
||||
} id_log_drv_ext_t;
|
||||
|
||||
#define SENSE_LOG_DRV_STAT 0x12
|
||||
typedef struct {
|
||||
__u8 status;
|
||||
__u32 fail_map;
|
||||
__u16 read_err[32];
|
||||
__u16 write_err[32];
|
||||
__u8 drv_err_data[256];
|
||||
__u8 drq_timeout[32];
|
||||
__u32 blks_to_recover;
|
||||
__u8 drv_recovering;
|
||||
__u16 remap_cnt[32];
|
||||
__u32 replace_drv_map;
|
||||
__u32 act_spare_map;
|
||||
__u8 spare_stat;
|
||||
__u8 spare_repl_map[32];
|
||||
__u32 repl_ok_map;
|
||||
__u8 media_exch;
|
||||
__u8 cache_fail;
|
||||
__u8 expn_fail;
|
||||
__u8 unit_flags;
|
||||
__u16 big_fail_map[8];
|
||||
__u16 big_remap_map[128];
|
||||
__u16 big_repl_map[8];
|
||||
__u16 big_act_spare_map[8];
|
||||
__u8 big_spar_repl_map[128];
|
||||
__u16 big_repl_ok_map[8];
|
||||
__u8 big_drv_rebuild;
|
||||
__u8 reserved[36];
|
||||
} sense_log_drv_stat_t;
|
||||
|
||||
#define START_RECOVER 0x13
|
||||
|
||||
#define ID_PHYS_DRV 0x15
|
||||
typedef struct {
|
||||
__u8 scsi_bus;
|
||||
__u8 scsi_id;
|
||||
__u16 blk_size;
|
||||
__u32 nr_blks;
|
||||
__u32 rsvd_blks;
|
||||
__u8 drv_model[40];
|
||||
__u8 drv_sn[40];
|
||||
__u8 drv_fw[8];
|
||||
__u8 scsi_iq_bits;
|
||||
__u8 compaq_drv_stmp;
|
||||
__u8 last_fail;
|
||||
__u8 phys_drv_flags;
|
||||
__u8 phys_drv_flags1;
|
||||
__u8 scsi_lun;
|
||||
__u8 phys_drv_flags2;
|
||||
__u8 reserved;
|
||||
__u32 spi_speed_rules;
|
||||
__u8 phys_connector[2];
|
||||
__u8 phys_box_on_bus;
|
||||
__u8 phys_bay_in_box;
|
||||
} id_phys_drv_t;
|
||||
|
||||
#define BLINK_DRV_LEDS 0x16
|
||||
typedef struct {
|
||||
__u32 blink_duration;
|
||||
__u32 reserved;
|
||||
__u8 blink[256];
|
||||
__u8 reserved1[248];
|
||||
} blink_drv_leds_t;
|
||||
|
||||
#define SENSE_BLINK_LEDS 0x17
|
||||
typedef struct {
|
||||
__u32 blink_duration;
|
||||
__u32 btime_elap;
|
||||
__u8 blink[256];
|
||||
__u8 reserved1[248];
|
||||
} sense_blink_leds_t;
|
||||
|
||||
#define IDA_READ 0x20
|
||||
#define IDA_WRITE 0x30
|
||||
#define IDA_WRITE_MEDIA 0x31
|
||||
#define RESET_TO_DIAG 0x40
|
||||
#define DIAG_PASS_THRU 0x41
|
||||
|
||||
#define SENSE_CONFIG 0x50
|
||||
#define SET_CONFIG 0x51
|
||||
typedef struct {
|
||||
__u32 cfg_sig;
|
||||
__u16 compat_port;
|
||||
__u8 data_dist_mode;
|
||||
__u8 surf_an_ctrl;
|
||||
__u16 ctlr_phys_drv;
|
||||
__u16 log_unit_phys_drv;
|
||||
__u16 fault_tol_mode;
|
||||
__u8 phys_drv_param[16];
|
||||
drv_param_t drv;
|
||||
__u32 drv_asgn_map;
|
||||
__u16 dist_factor;
|
||||
__u32 spare_asgn_map;
|
||||
__u8 reserved[6];
|
||||
__u16 os;
|
||||
__u8 ctlr_order;
|
||||
__u8 extra_info;
|
||||
__u32 data_offs;
|
||||
__u8 parity_backedout_write_drvs;
|
||||
__u8 parity_dist_mode;
|
||||
__u8 parity_shift_fact;
|
||||
__u8 bios_disable_flag;
|
||||
__u32 blks_on_vol;
|
||||
__u32 blks_per_drv;
|
||||
__u8 scratch[16];
|
||||
__u16 big_drv_map[8];
|
||||
__u16 big_spare_map[8];
|
||||
__u8 ss_source_vol;
|
||||
__u8 mix_drv_cap_range;
|
||||
struct {
|
||||
__u16 big_drv_map[8];
|
||||
__u32 blks_per_drv;
|
||||
__u16 fault_tol_mode;
|
||||
__u16 dist_factor;
|
||||
} MDC_range[4];
|
||||
__u8 reserved1[248];
|
||||
} config_t;
|
||||
|
||||
#define BYPASS_VOL_STATE 0x52
|
||||
#define SS_CREATE_VOL 0x53
|
||||
#define CHANGE_CONFIG 0x54
|
||||
#define SENSE_ORIG_CONF 0x55
|
||||
#define REORDER_LOG_DRV 0x56
|
||||
typedef struct {
|
||||
__u8 old_units[32];
|
||||
} reorder_log_drv_t;
|
||||
|
||||
#define LABEL_LOG_DRV 0x57
|
||||
typedef struct {
|
||||
__u8 log_drv_label[64];
|
||||
} label_log_drv_t;
|
||||
|
||||
#define SS_TO_VOL 0x58
|
||||
|
||||
#define SET_SURF_DELAY 0x60
|
||||
typedef struct {
|
||||
__u16 delay;
|
||||
__u8 reserved[510];
|
||||
} surf_delay_t;
|
||||
|
||||
#define SET_OVERHEAT_DELAY 0x61
|
||||
typedef struct {
|
||||
__u16 delay;
|
||||
} overhead_delay_t;
|
||||
|
||||
#define SET_MP_DELAY
|
||||
typedef struct {
|
||||
__u16 delay;
|
||||
__u8 reserved[510];
|
||||
} mp_delay_t;
|
||||
|
||||
#define PASSTHRU_A 0x91
|
||||
typedef struct {
|
||||
__u8 target;
|
||||
__u8 bus;
|
||||
__u8 lun;
|
||||
__u32 timeout;
|
||||
__u32 flags;
|
||||
__u8 status;
|
||||
__u8 error;
|
||||
__u8 cdb_len;
|
||||
__u8 sense_error;
|
||||
__u8 sense_key;
|
||||
__u32 sense_info;
|
||||
__u8 sense_code;
|
||||
__u8 sense_qual;
|
||||
__u32 residual;
|
||||
__u8 reserved[4];
|
||||
__u8 cdb[12];
|
||||
} scsi_param_t;
|
||||
|
||||
#define RESUME_BACKGROUND_ACTIVITY 0x99
|
||||
#define SENSE_CONTROLLER_PERFORMANCE 0xa8
|
||||
#define FLUSH_CACHE 0xc2
|
||||
#define COLLECT_BUFFER 0xd2
|
||||
#define READ_FLASH_ROM 0xf6
|
||||
#define WRITE_FLASH_ROM 0xf7
|
||||
#pragma pack()
|
||||
|
||||
#endif /* ARRAYCMD_H */
|
||||
87
drivers/block/ida_ioctl.h
Normal file
87
drivers/block/ida_ioctl.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Disk Array driver for Compaq SMART2 Controllers
|
||||
* Copyright 1998 Compaq Computer Corporation
|
||||
*
|
||||
* 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, GOOD TITLE or
|
||||
* NON INFRINGEMENT. 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.
|
||||
*
|
||||
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
|
||||
*
|
||||
*/
|
||||
#ifndef IDA_IOCTL_H
|
||||
#define IDA_IOCTL_H
|
||||
|
||||
#include "ida_cmd.h"
|
||||
#include "cpqarray.h"
|
||||
|
||||
#define IDAGETDRVINFO 0x27272828
|
||||
#define IDAPASSTHRU 0x28282929
|
||||
#define IDAGETCTLRSIG 0x29293030
|
||||
#define IDAREVALIDATEVOLS 0x30303131
|
||||
#define IDADRIVERVERSION 0x31313232
|
||||
#define IDAGETPCIINFO 0x32323333
|
||||
|
||||
typedef struct _ida_pci_info_struct
|
||||
{
|
||||
unsigned char bus;
|
||||
unsigned char dev_fn;
|
||||
__u32 board_id;
|
||||
} ida_pci_info_struct;
|
||||
/*
|
||||
* Normally, the ioctl determines the logical unit for this command by
|
||||
* the major,minor number of the fd passed to ioctl. If you need to send
|
||||
* a command to a different/nonexistant unit (such as during config), you
|
||||
* can override the normal behavior by setting the unit valid bit. (Normally,
|
||||
* it should be zero) The controller the command is sent to is still
|
||||
* determined by the major number of the open device.
|
||||
*/
|
||||
|
||||
#define UNITVALID 0x80
|
||||
typedef struct {
|
||||
__u8 cmd;
|
||||
__u8 rcode;
|
||||
__u8 unit;
|
||||
__u32 blk;
|
||||
__u16 blk_cnt;
|
||||
|
||||
/* currently, sg_cnt is assumed to be 1: only the 0th element of sg is used */
|
||||
struct {
|
||||
void __user *addr;
|
||||
size_t size;
|
||||
} sg[SG_MAX];
|
||||
int sg_cnt;
|
||||
|
||||
union ctlr_cmds {
|
||||
drv_info_t drv;
|
||||
unsigned char buf[1024];
|
||||
|
||||
id_ctlr_t id_ctlr;
|
||||
drv_param_t drv_param;
|
||||
id_log_drv_t id_log_drv;
|
||||
id_log_drv_ext_t id_log_drv_ext;
|
||||
sense_log_drv_stat_t sense_log_drv_stat;
|
||||
id_phys_drv_t id_phys_drv;
|
||||
blink_drv_leds_t blink_drv_leds;
|
||||
sense_blink_leds_t sense_blink_leds;
|
||||
config_t config;
|
||||
reorder_log_drv_t reorder_log_drv;
|
||||
label_log_drv_t label_log_drv;
|
||||
surf_delay_t surf_delay;
|
||||
overhead_delay_t overhead_delay;
|
||||
mp_delay_t mp_delay;
|
||||
scsi_param_t scsi_param;
|
||||
} c;
|
||||
} ida_ioctl_t;
|
||||
|
||||
#endif /* IDA_IOCTL_H */
|
||||
1499
drivers/block/loop.c
Normal file
1499
drivers/block/loop.c
Normal file
File diff suppressed because it is too large
Load Diff
719
drivers/block/nbd.c
Normal file
719
drivers/block/nbd.c
Normal file
@@ -0,0 +1,719 @@
|
||||
/*
|
||||
* Network block device - make block devices work over TCP
|
||||
*
|
||||
* Note that you can not swap over this thing, yet. Seems to work but
|
||||
* deadlocks sometimes - you can not swap over TCP in general.
|
||||
*
|
||||
* Copyright 1997-2000 Pavel Machek <pavel@ucw.cz>
|
||||
* Parts copyright 2001 Steven Whitehouse <steve@chygwyn.com>
|
||||
*
|
||||
* This file is released under GPLv2 or later.
|
||||
*
|
||||
* (part of code stolen from loop.c)
|
||||
*/
|
||||
|
||||
#include <linux/major.h>
|
||||
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/types.h>
|
||||
|
||||
#include <linux/nbd.h>
|
||||
|
||||
#define LO_MAGIC 0x68797548
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define dprintk(flags, fmt...)
|
||||
#else /* NDEBUG */
|
||||
#define dprintk(flags, fmt...) do { \
|
||||
if (debugflags & (flags)) printk(KERN_DEBUG fmt); \
|
||||
} while (0)
|
||||
#define DBG_IOCTL 0x0004
|
||||
#define DBG_INIT 0x0010
|
||||
#define DBG_EXIT 0x0020
|
||||
#define DBG_BLKDEV 0x0100
|
||||
#define DBG_RX 0x0200
|
||||
#define DBG_TX 0x0400
|
||||
static unsigned int debugflags;
|
||||
#endif /* NDEBUG */
|
||||
|
||||
static unsigned int nbds_max = 16;
|
||||
static struct nbd_device nbd_dev[MAX_NBD];
|
||||
|
||||
/*
|
||||
* Use just one lock (or at most 1 per NIC). Two arguments for this:
|
||||
* 1. Each NIC is essentially a synchronization point for all servers
|
||||
* accessed through that NIC so there's no need to have more locks
|
||||
* than NICs anyway.
|
||||
* 2. More locks lead to more "Dirty cache line bouncing" which will slow
|
||||
* down each lock to the point where they're actually slower than just
|
||||
* a single lock.
|
||||
* Thanks go to Jens Axboe and Al Viro for their LKML emails explaining this!
|
||||
*/
|
||||
static DEFINE_SPINLOCK(nbd_lock);
|
||||
|
||||
#ifndef NDEBUG
|
||||
static const char *ioctl_cmd_to_ascii(int cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case NBD_SET_SOCK: return "set-sock";
|
||||
case NBD_SET_BLKSIZE: return "set-blksize";
|
||||
case NBD_SET_SIZE: return "set-size";
|
||||
case NBD_DO_IT: return "do-it";
|
||||
case NBD_CLEAR_SOCK: return "clear-sock";
|
||||
case NBD_CLEAR_QUE: return "clear-que";
|
||||
case NBD_PRINT_DEBUG: return "print-debug";
|
||||
case NBD_SET_SIZE_BLOCKS: return "set-size-blocks";
|
||||
case NBD_DISCONNECT: return "disconnect";
|
||||
case BLKROSET: return "set-read-only";
|
||||
case BLKFLSBUF: return "flush-buffer-cache";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static const char *nbdcmd_to_ascii(int cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case NBD_CMD_READ: return "read";
|
||||
case NBD_CMD_WRITE: return "write";
|
||||
case NBD_CMD_DISC: return "disconnect";
|
||||
}
|
||||
return "invalid";
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
|
||||
static void nbd_end_request(struct request *req)
|
||||
{
|
||||
int uptodate = (req->errors == 0) ? 1 : 0;
|
||||
request_queue_t *q = req->q;
|
||||
unsigned long flags;
|
||||
|
||||
dprintk(DBG_BLKDEV, "%s: request %p: %s\n", req->rq_disk->disk_name,
|
||||
req, uptodate? "done": "failed");
|
||||
|
||||
spin_lock_irqsave(q->queue_lock, flags);
|
||||
if (!end_that_request_first(req, uptodate, req->nr_sectors)) {
|
||||
end_that_request_last(req, uptodate);
|
||||
}
|
||||
spin_unlock_irqrestore(q->queue_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send or receive packet.
|
||||
*/
|
||||
static int sock_xmit(struct socket *sock, int send, void *buf, int size,
|
||||
int msg_flags)
|
||||
{
|
||||
int result;
|
||||
struct msghdr msg;
|
||||
struct kvec iov;
|
||||
unsigned long flags;
|
||||
sigset_t oldset;
|
||||
|
||||
/* Allow interception of SIGKILL only
|
||||
* Don't allow other signals to interrupt the transmission */
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
oldset = current->blocked;
|
||||
sigfillset(¤t->blocked);
|
||||
sigdelsetmask(¤t->blocked, sigmask(SIGKILL));
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
|
||||
do {
|
||||
sock->sk->sk_allocation = GFP_NOIO;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = size;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = msg_flags | MSG_NOSIGNAL;
|
||||
|
||||
if (send)
|
||||
result = kernel_sendmsg(sock, &msg, &iov, 1, size);
|
||||
else
|
||||
result = kernel_recvmsg(sock, &msg, &iov, 1, size, 0);
|
||||
|
||||
if (signal_pending(current)) {
|
||||
siginfo_t info;
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
printk(KERN_WARNING "nbd (pid %d: %s) got signal %d\n",
|
||||
current->pid, current->comm,
|
||||
dequeue_signal(current, ¤t->blocked, &info));
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
result = -EINTR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (result <= 0) {
|
||||
if (result == 0)
|
||||
result = -EPIPE; /* short read */
|
||||
break;
|
||||
}
|
||||
size -= result;
|
||||
buf += result;
|
||||
} while (size > 0);
|
||||
|
||||
spin_lock_irqsave(¤t->sighand->siglock, flags);
|
||||
current->blocked = oldset;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irqrestore(¤t->sighand->siglock, flags);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int sock_send_bvec(struct socket *sock, struct bio_vec *bvec,
|
||||
int flags)
|
||||
{
|
||||
int result;
|
||||
void *kaddr = kmap(bvec->bv_page);
|
||||
result = sock_xmit(sock, 1, kaddr + bvec->bv_offset, bvec->bv_len,
|
||||
flags);
|
||||
kunmap(bvec->bv_page);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int nbd_send_req(struct nbd_device *lo, struct request *req)
|
||||
{
|
||||
int result, i, flags;
|
||||
struct nbd_request request;
|
||||
unsigned long size = req->nr_sectors << 9;
|
||||
struct socket *sock = lo->sock;
|
||||
|
||||
request.magic = htonl(NBD_REQUEST_MAGIC);
|
||||
request.type = htonl(nbd_cmd(req));
|
||||
request.from = cpu_to_be64((u64) req->sector << 9);
|
||||
request.len = htonl(size);
|
||||
memcpy(request.handle, &req, sizeof(req));
|
||||
|
||||
dprintk(DBG_TX, "%s: request %p: sending control (%s@%llu,%luB)\n",
|
||||
lo->disk->disk_name, req,
|
||||
nbdcmd_to_ascii(nbd_cmd(req)),
|
||||
(unsigned long long)req->sector << 9,
|
||||
req->nr_sectors << 9);
|
||||
result = sock_xmit(sock, 1, &request, sizeof(request),
|
||||
(nbd_cmd(req) == NBD_CMD_WRITE)? MSG_MORE: 0);
|
||||
if (result <= 0) {
|
||||
printk(KERN_ERR "%s: Send control failed (result %d)\n",
|
||||
lo->disk->disk_name, result);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if (nbd_cmd(req) == NBD_CMD_WRITE) {
|
||||
struct bio *bio;
|
||||
/*
|
||||
* we are really probing at internals to determine
|
||||
* whether to set MSG_MORE or not...
|
||||
*/
|
||||
rq_for_each_bio(bio, req) {
|
||||
struct bio_vec *bvec;
|
||||
bio_for_each_segment(bvec, bio, i) {
|
||||
flags = 0;
|
||||
if ((i < (bio->bi_vcnt - 1)) || bio->bi_next)
|
||||
flags = MSG_MORE;
|
||||
dprintk(DBG_TX, "%s: request %p: sending %d bytes data\n",
|
||||
lo->disk->disk_name, req,
|
||||
bvec->bv_len);
|
||||
result = sock_send_bvec(sock, bvec, flags);
|
||||
if (result <= 0) {
|
||||
printk(KERN_ERR "%s: Send data failed (result %d)\n",
|
||||
lo->disk->disk_name,
|
||||
result);
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct request *nbd_find_request(struct nbd_device *lo, char *handle)
|
||||
{
|
||||
struct request *req;
|
||||
struct list_head *tmp;
|
||||
struct request *xreq;
|
||||
int err;
|
||||
|
||||
memcpy(&xreq, handle, sizeof(xreq));
|
||||
|
||||
err = wait_event_interruptible(lo->active_wq, lo->active_req != xreq);
|
||||
if (unlikely(err))
|
||||
goto out;
|
||||
|
||||
spin_lock(&lo->queue_lock);
|
||||
list_for_each(tmp, &lo->queue_head) {
|
||||
req = list_entry(tmp, struct request, queuelist);
|
||||
if (req != xreq)
|
||||
continue;
|
||||
list_del_init(&req->queuelist);
|
||||
spin_unlock(&lo->queue_lock);
|
||||
return req;
|
||||
}
|
||||
spin_unlock(&lo->queue_lock);
|
||||
|
||||
err = -ENOENT;
|
||||
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static inline int sock_recv_bvec(struct socket *sock, struct bio_vec *bvec)
|
||||
{
|
||||
int result;
|
||||
void *kaddr = kmap(bvec->bv_page);
|
||||
result = sock_xmit(sock, 0, kaddr + bvec->bv_offset, bvec->bv_len,
|
||||
MSG_WAITALL);
|
||||
kunmap(bvec->bv_page);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* NULL returned = something went wrong, inform userspace */
|
||||
static struct request *nbd_read_stat(struct nbd_device *lo)
|
||||
{
|
||||
int result;
|
||||
struct nbd_reply reply;
|
||||
struct request *req;
|
||||
struct socket *sock = lo->sock;
|
||||
|
||||
reply.magic = 0;
|
||||
result = sock_xmit(sock, 0, &reply, sizeof(reply), MSG_WAITALL);
|
||||
if (result <= 0) {
|
||||
printk(KERN_ERR "%s: Receive control failed (result %d)\n",
|
||||
lo->disk->disk_name, result);
|
||||
goto harderror;
|
||||
}
|
||||
|
||||
if (ntohl(reply.magic) != NBD_REPLY_MAGIC) {
|
||||
printk(KERN_ERR "%s: Wrong magic (0x%lx)\n",
|
||||
lo->disk->disk_name,
|
||||
(unsigned long)ntohl(reply.magic));
|
||||
result = -EPROTO;
|
||||
goto harderror;
|
||||
}
|
||||
|
||||
req = nbd_find_request(lo, reply.handle);
|
||||
if (unlikely(IS_ERR(req))) {
|
||||
result = PTR_ERR(req);
|
||||
if (result != -ENOENT)
|
||||
goto harderror;
|
||||
|
||||
printk(KERN_ERR "%s: Unexpected reply (%p)\n",
|
||||
lo->disk->disk_name, reply.handle);
|
||||
result = -EBADR;
|
||||
goto harderror;
|
||||
}
|
||||
|
||||
if (ntohl(reply.error)) {
|
||||
printk(KERN_ERR "%s: Other side returned error (%d)\n",
|
||||
lo->disk->disk_name, ntohl(reply.error));
|
||||
req->errors++;
|
||||
return req;
|
||||
}
|
||||
|
||||
dprintk(DBG_RX, "%s: request %p: got reply\n",
|
||||
lo->disk->disk_name, req);
|
||||
if (nbd_cmd(req) == NBD_CMD_READ) {
|
||||
int i;
|
||||
struct bio *bio;
|
||||
rq_for_each_bio(bio, req) {
|
||||
struct bio_vec *bvec;
|
||||
bio_for_each_segment(bvec, bio, i) {
|
||||
result = sock_recv_bvec(sock, bvec);
|
||||
if (result <= 0) {
|
||||
printk(KERN_ERR "%s: Receive data failed (result %d)\n",
|
||||
lo->disk->disk_name,
|
||||
result);
|
||||
req->errors++;
|
||||
return req;
|
||||
}
|
||||
dprintk(DBG_RX, "%s: request %p: got %d bytes data\n",
|
||||
lo->disk->disk_name, req, bvec->bv_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
return req;
|
||||
harderror:
|
||||
lo->harderror = result;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t pid_show(struct gendisk *disk, char *page)
|
||||
{
|
||||
return sprintf(page, "%ld\n",
|
||||
(long) ((struct nbd_device *)disk->private_data)->pid);
|
||||
}
|
||||
|
||||
static struct disk_attribute pid_attr = {
|
||||
.attr = { .name = "pid", .mode = S_IRUGO },
|
||||
.show = pid_show,
|
||||
};
|
||||
|
||||
static void nbd_do_it(struct nbd_device *lo)
|
||||
{
|
||||
struct request *req;
|
||||
|
||||
BUG_ON(lo->magic != LO_MAGIC);
|
||||
|
||||
lo->pid = current->pid;
|
||||
sysfs_create_file(&lo->disk->kobj, &pid_attr.attr);
|
||||
|
||||
while ((req = nbd_read_stat(lo)) != NULL)
|
||||
nbd_end_request(req);
|
||||
|
||||
sysfs_remove_file(&lo->disk->kobj, &pid_attr.attr);
|
||||
return;
|
||||
}
|
||||
|
||||
static void nbd_clear_que(struct nbd_device *lo)
|
||||
{
|
||||
struct request *req;
|
||||
|
||||
BUG_ON(lo->magic != LO_MAGIC);
|
||||
|
||||
/*
|
||||
* Because we have set lo->sock to NULL under the tx_lock, all
|
||||
* modifications to the list must have completed by now. For
|
||||
* the same reason, the active_req must be NULL.
|
||||
*
|
||||
* As a consequence, we don't need to take the spin lock while
|
||||
* purging the list here.
|
||||
*/
|
||||
BUG_ON(lo->sock);
|
||||
BUG_ON(lo->active_req);
|
||||
|
||||
while (!list_empty(&lo->queue_head)) {
|
||||
req = list_entry(lo->queue_head.next, struct request,
|
||||
queuelist);
|
||||
list_del_init(&req->queuelist);
|
||||
req->errors++;
|
||||
nbd_end_request(req);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We always wait for result of write, for now. It would be nice to make it optional
|
||||
* in future
|
||||
* if ((req->cmd == WRITE) && (lo->flags & NBD_WRITE_NOCHK))
|
||||
* { printk( "Warning: Ignoring result!\n"); nbd_end_request( req ); }
|
||||
*/
|
||||
|
||||
static void do_nbd_request(request_queue_t * q)
|
||||
{
|
||||
struct request *req;
|
||||
|
||||
while ((req = elv_next_request(q)) != NULL) {
|
||||
struct nbd_device *lo;
|
||||
|
||||
blkdev_dequeue_request(req);
|
||||
dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%x)\n",
|
||||
req->rq_disk->disk_name, req, req->cmd_type);
|
||||
|
||||
if (!blk_fs_request(req))
|
||||
goto error_out;
|
||||
|
||||
lo = req->rq_disk->private_data;
|
||||
|
||||
BUG_ON(lo->magic != LO_MAGIC);
|
||||
|
||||
nbd_cmd(req) = NBD_CMD_READ;
|
||||
if (rq_data_dir(req) == WRITE) {
|
||||
nbd_cmd(req) = NBD_CMD_WRITE;
|
||||
if (lo->flags & NBD_READ_ONLY) {
|
||||
printk(KERN_ERR "%s: Write on read-only\n",
|
||||
lo->disk->disk_name);
|
||||
goto error_out;
|
||||
}
|
||||
}
|
||||
|
||||
req->errors = 0;
|
||||
spin_unlock_irq(q->queue_lock);
|
||||
|
||||
mutex_lock(&lo->tx_lock);
|
||||
if (unlikely(!lo->sock)) {
|
||||
mutex_unlock(&lo->tx_lock);
|
||||
printk(KERN_ERR "%s: Attempted send on closed socket\n",
|
||||
lo->disk->disk_name);
|
||||
req->errors++;
|
||||
nbd_end_request(req);
|
||||
spin_lock_irq(q->queue_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
lo->active_req = req;
|
||||
|
||||
if (nbd_send_req(lo, req) != 0) {
|
||||
printk(KERN_ERR "%s: Request send failed\n",
|
||||
lo->disk->disk_name);
|
||||
req->errors++;
|
||||
nbd_end_request(req);
|
||||
} else {
|
||||
spin_lock(&lo->queue_lock);
|
||||
list_add(&req->queuelist, &lo->queue_head);
|
||||
spin_unlock(&lo->queue_lock);
|
||||
}
|
||||
|
||||
lo->active_req = NULL;
|
||||
mutex_unlock(&lo->tx_lock);
|
||||
wake_up_all(&lo->active_wq);
|
||||
|
||||
spin_lock_irq(q->queue_lock);
|
||||
continue;
|
||||
|
||||
error_out:
|
||||
req->errors++;
|
||||
spin_unlock(q->queue_lock);
|
||||
nbd_end_request(req);
|
||||
spin_lock(q->queue_lock);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int nbd_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct nbd_device *lo = inode->i_bdev->bd_disk->private_data;
|
||||
int error;
|
||||
struct request sreq ;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
BUG_ON(lo->magic != LO_MAGIC);
|
||||
|
||||
/* Anyone capable of this syscall can do *real bad* things */
|
||||
dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n",
|
||||
lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg);
|
||||
|
||||
switch (cmd) {
|
||||
case NBD_DISCONNECT:
|
||||
printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name);
|
||||
sreq.cmd_type = REQ_TYPE_SPECIAL;
|
||||
nbd_cmd(&sreq) = NBD_CMD_DISC;
|
||||
/*
|
||||
* Set these to sane values in case server implementation
|
||||
* fails to check the request type first and also to keep
|
||||
* debugging output cleaner.
|
||||
*/
|
||||
sreq.sector = 0;
|
||||
sreq.nr_sectors = 0;
|
||||
if (!lo->sock)
|
||||
return -EINVAL;
|
||||
nbd_send_req(lo, &sreq);
|
||||
return 0;
|
||||
|
||||
case NBD_CLEAR_SOCK:
|
||||
error = 0;
|
||||
mutex_lock(&lo->tx_lock);
|
||||
lo->sock = NULL;
|
||||
mutex_unlock(&lo->tx_lock);
|
||||
file = lo->file;
|
||||
lo->file = NULL;
|
||||
nbd_clear_que(lo);
|
||||
BUG_ON(!list_empty(&lo->queue_head));
|
||||
if (file)
|
||||
fput(file);
|
||||
return error;
|
||||
case NBD_SET_SOCK:
|
||||
if (lo->file)
|
||||
return -EBUSY;
|
||||
error = -EINVAL;
|
||||
file = fget(arg);
|
||||
if (file) {
|
||||
inode = file->f_path.dentry->d_inode;
|
||||
if (S_ISSOCK(inode->i_mode)) {
|
||||
lo->file = file;
|
||||
lo->sock = SOCKET_I(inode);
|
||||
error = 0;
|
||||
} else {
|
||||
fput(file);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
case NBD_SET_BLKSIZE:
|
||||
lo->blksize = arg;
|
||||
lo->bytesize &= ~(lo->blksize-1);
|
||||
inode->i_bdev->bd_inode->i_size = lo->bytesize;
|
||||
set_blocksize(inode->i_bdev, lo->blksize);
|
||||
set_capacity(lo->disk, lo->bytesize >> 9);
|
||||
return 0;
|
||||
case NBD_SET_SIZE:
|
||||
lo->bytesize = arg & ~(lo->blksize-1);
|
||||
inode->i_bdev->bd_inode->i_size = lo->bytesize;
|
||||
set_blocksize(inode->i_bdev, lo->blksize);
|
||||
set_capacity(lo->disk, lo->bytesize >> 9);
|
||||
return 0;
|
||||
case NBD_SET_SIZE_BLOCKS:
|
||||
lo->bytesize = ((u64) arg) * lo->blksize;
|
||||
inode->i_bdev->bd_inode->i_size = lo->bytesize;
|
||||
set_blocksize(inode->i_bdev, lo->blksize);
|
||||
set_capacity(lo->disk, lo->bytesize >> 9);
|
||||
return 0;
|
||||
case NBD_DO_IT:
|
||||
if (!lo->file)
|
||||
return -EINVAL;
|
||||
nbd_do_it(lo);
|
||||
/* on return tidy up in case we have a signal */
|
||||
/* Forcibly shutdown the socket causing all listeners
|
||||
* to error
|
||||
*
|
||||
* FIXME: This code is duplicated from sys_shutdown, but
|
||||
* there should be a more generic interface rather than
|
||||
* calling socket ops directly here */
|
||||
mutex_lock(&lo->tx_lock);
|
||||
if (lo->sock) {
|
||||
printk(KERN_WARNING "%s: shutting down socket\n",
|
||||
lo->disk->disk_name);
|
||||
lo->sock->ops->shutdown(lo->sock,
|
||||
SEND_SHUTDOWN|RCV_SHUTDOWN);
|
||||
lo->sock = NULL;
|
||||
}
|
||||
mutex_unlock(&lo->tx_lock);
|
||||
file = lo->file;
|
||||
lo->file = NULL;
|
||||
nbd_clear_que(lo);
|
||||
printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name);
|
||||
if (file)
|
||||
fput(file);
|
||||
return lo->harderror;
|
||||
case NBD_CLEAR_QUE:
|
||||
/*
|
||||
* This is for compatibility only. The queue is always cleared
|
||||
* by NBD_DO_IT or NBD_CLEAR_SOCK.
|
||||
*/
|
||||
BUG_ON(!lo->sock && !list_empty(&lo->queue_head));
|
||||
return 0;
|
||||
case NBD_PRINT_DEBUG:
|
||||
printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n",
|
||||
inode->i_bdev->bd_disk->disk_name,
|
||||
lo->queue_head.next, lo->queue_head.prev,
|
||||
&lo->queue_head);
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct block_device_operations nbd_fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.ioctl = nbd_ioctl,
|
||||
};
|
||||
|
||||
/*
|
||||
* And here should be modules and kernel interface
|
||||
* (Just smiley confuses emacs :-)
|
||||
*/
|
||||
|
||||
static int __init nbd_init(void)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct nbd_request) != 28);
|
||||
|
||||
if (nbds_max > MAX_NBD) {
|
||||
printk(KERN_CRIT "nbd: cannot allocate more than %u nbds; %u requested.\n", MAX_NBD,
|
||||
nbds_max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nbds_max; i++) {
|
||||
struct gendisk *disk = alloc_disk(1);
|
||||
if (!disk)
|
||||
goto out;
|
||||
nbd_dev[i].disk = disk;
|
||||
/*
|
||||
* The new linux 2.5 block layer implementation requires
|
||||
* every gendisk to have its very own request_queue struct.
|
||||
* These structs are big so we dynamically allocate them.
|
||||
*/
|
||||
disk->queue = blk_init_queue(do_nbd_request, &nbd_lock);
|
||||
if (!disk->queue) {
|
||||
put_disk(disk);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (register_blkdev(NBD_MAJOR, "nbd")) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "nbd: registered device at major %d\n", NBD_MAJOR);
|
||||
dprintk(DBG_INIT, "nbd: debugflags=0x%x\n", debugflags);
|
||||
|
||||
for (i = 0; i < nbds_max; i++) {
|
||||
struct gendisk *disk = nbd_dev[i].disk;
|
||||
nbd_dev[i].file = NULL;
|
||||
nbd_dev[i].magic = LO_MAGIC;
|
||||
nbd_dev[i].flags = 0;
|
||||
spin_lock_init(&nbd_dev[i].queue_lock);
|
||||
INIT_LIST_HEAD(&nbd_dev[i].queue_head);
|
||||
mutex_init(&nbd_dev[i].tx_lock);
|
||||
init_waitqueue_head(&nbd_dev[i].active_wq);
|
||||
nbd_dev[i].blksize = 1024;
|
||||
nbd_dev[i].bytesize = 0x7ffffc00ULL << 10; /* 2TB */
|
||||
disk->major = NBD_MAJOR;
|
||||
disk->first_minor = i;
|
||||
disk->fops = &nbd_fops;
|
||||
disk->private_data = &nbd_dev[i];
|
||||
disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
|
||||
sprintf(disk->disk_name, "nbd%d", i);
|
||||
set_capacity(disk, 0x7ffffc00ULL << 1); /* 2 TB */
|
||||
add_disk(disk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
while (i--) {
|
||||
blk_cleanup_queue(nbd_dev[i].disk->queue);
|
||||
put_disk(nbd_dev[i].disk);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit nbd_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < nbds_max; i++) {
|
||||
struct gendisk *disk = nbd_dev[i].disk;
|
||||
nbd_dev[i].magic = 0;
|
||||
if (disk) {
|
||||
del_gendisk(disk);
|
||||
blk_cleanup_queue(disk->queue);
|
||||
put_disk(disk);
|
||||
}
|
||||
}
|
||||
unregister_blkdev(NBD_MAJOR, "nbd");
|
||||
printk(KERN_INFO "nbd: unregistered device at major %d\n", NBD_MAJOR);
|
||||
}
|
||||
|
||||
module_init(nbd_init);
|
||||
module_exit(nbd_cleanup);
|
||||
|
||||
MODULE_DESCRIPTION("Network Block Device");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_param(nbds_max, int, 0444);
|
||||
MODULE_PARM_DESC(nbds_max, "How many network block devices to initialize.");
|
||||
#ifndef NDEBUG
|
||||
module_param(debugflags, int, 0644);
|
||||
MODULE_PARM_DESC(debugflags, "flags for controlling debug output");
|
||||
#endif
|
||||
300
drivers/block/paride/Kconfig
Normal file
300
drivers/block/paride/Kconfig
Normal file
@@ -0,0 +1,300 @@
|
||||
#
|
||||
# PARIDE configuration
|
||||
#
|
||||
# PARIDE doesn't need PARPORT, but if PARPORT is configured as a module,
|
||||
# PARIDE must also be a module.
|
||||
# PARIDE only supports PC style parports. Tough for USB or other parports...
|
||||
|
||||
comment "Parallel IDE high-level drivers"
|
||||
depends on PARIDE
|
||||
|
||||
config PARIDE_PD
|
||||
tristate "Parallel port IDE disks"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables the high-level driver for IDE-type disk devices
|
||||
connected through a parallel port. If you chose to build PARIDE
|
||||
support into your kernel, you may answer Y here to build in the
|
||||
parallel port IDE driver, otherwise you should answer M to build
|
||||
it as a loadable module. The module will be called pd. You
|
||||
must also have at least one parallel port protocol driver in your
|
||||
system. Among the devices supported by this driver are the SyQuest
|
||||
EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack
|
||||
hard drives from MicroSolutions.
|
||||
|
||||
config PARIDE_PCD
|
||||
tristate "Parallel port ATAPI CD-ROMs"
|
||||
depends on PARIDE
|
||||
---help---
|
||||
This option enables the high-level driver for ATAPI CD-ROM devices
|
||||
connected through a parallel port. If you chose to build PARIDE
|
||||
support into your kernel, you may answer Y here to build in the
|
||||
parallel port ATAPI CD-ROM driver, otherwise you should answer M to
|
||||
build it as a loadable module. The module will be called pcd. You
|
||||
must also have at least one parallel port protocol driver in your
|
||||
system. Among the devices supported by this driver are the
|
||||
MicroSolutions backpack CD-ROM drives and the Freecom Power CD. If
|
||||
you have such a CD-ROM drive, you should also say Y or M to "ISO
|
||||
9660 CD-ROM file system support" below, because that's the file
|
||||
system used on CD-ROMs.
|
||||
|
||||
config PARIDE_PF
|
||||
tristate "Parallel port ATAPI disks"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables the high-level driver for ATAPI disk devices
|
||||
connected through a parallel port. If you chose to build PARIDE
|
||||
support into your kernel, you may answer Y here to build in the
|
||||
parallel port ATAPI disk driver, otherwise you should answer M
|
||||
to build it as a loadable module. The module will be called pf.
|
||||
You must also have at least one parallel port protocol driver in
|
||||
your system. Among the devices supported by this driver are the
|
||||
MicroSolutions backpack PD/CD drive and the Imation Superdisk
|
||||
LS-120 drive.
|
||||
|
||||
config PARIDE_PT
|
||||
tristate "Parallel port ATAPI tapes"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables the high-level driver for ATAPI tape devices
|
||||
connected through a parallel port. If you chose to build PARIDE
|
||||
support into your kernel, you may answer Y here to build in the
|
||||
parallel port ATAPI disk driver, otherwise you should answer M
|
||||
to build it as a loadable module. The module will be called pt.
|
||||
You must also have at least one parallel port protocol driver in
|
||||
your system. Among the devices supported by this driver is the
|
||||
parallel port version of the HP 5GB drive.
|
||||
|
||||
config PARIDE_PG
|
||||
tristate "Parallel port generic ATAPI devices"
|
||||
depends on PARIDE
|
||||
---help---
|
||||
This option enables a special high-level driver for generic ATAPI
|
||||
devices connected through a parallel port. The driver allows user
|
||||
programs, such as cdrtools, to send ATAPI commands directly to a
|
||||
device.
|
||||
|
||||
If you chose to build PARIDE support into your kernel, you may
|
||||
answer Y here to build in the parallel port generic ATAPI driver,
|
||||
otherwise you should answer M to build it as a loadable module. The
|
||||
module will be called pg.
|
||||
|
||||
You must also have at least one parallel port protocol driver in
|
||||
your system.
|
||||
|
||||
This driver implements an API loosely related to the generic SCSI
|
||||
driver. See <file:include/linux/pg.h>. for details.
|
||||
|
||||
You can obtain the most recent version of cdrtools from
|
||||
<ftp://ftp.berlios.de/pub/cdrecord/>. Versions 1.6.1a3 and
|
||||
later fully support this driver.
|
||||
|
||||
comment "Parallel IDE protocol modules"
|
||||
depends on PARIDE
|
||||
|
||||
config PARIDE_ATEN
|
||||
tristate "ATEN EH-100 protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the ATEN EH-100 parallel port IDE
|
||||
protocol. This protocol is used in some inexpensive low performance
|
||||
parallel port kits made in Hong Kong. If you chose to build PARIDE
|
||||
support into your kernel, you may answer Y here to build in the
|
||||
protocol driver, otherwise you should answer M to build it as a
|
||||
loadable module. The module will be called aten. You must also
|
||||
have a high-level driver for the type of device that you want to
|
||||
support.
|
||||
|
||||
config PARIDE_BPCK
|
||||
tristate "MicroSolutions backpack (Series 5) protocol"
|
||||
depends on PARIDE
|
||||
---help---
|
||||
This option enables support for the Micro Solutions BACKPACK
|
||||
parallel port Series 5 IDE protocol. (Most BACKPACK drives made
|
||||
before 1999 were Series 5) Series 5 drives will NOT always have the
|
||||
Series noted on the bottom of the drive. Series 6 drivers will.
|
||||
|
||||
In other words, if your BACKPACK drive doesn't say "Series 6" on the
|
||||
bottom, enable this option.
|
||||
|
||||
If you chose to build PARIDE support into your kernel, you may
|
||||
answer Y here to build in the protocol driver, otherwise you should
|
||||
answer M to build it as a loadable module. The module will be
|
||||
called bpck. You must also have a high-level driver for the type
|
||||
of device that you want to support.
|
||||
|
||||
config PARIDE_BPCK6
|
||||
tristate "MicroSolutions backpack (Series 6) protocol"
|
||||
depends on PARIDE && !64BIT
|
||||
---help---
|
||||
This option enables support for the Micro Solutions BACKPACK
|
||||
parallel port Series 6 IDE protocol. (Most BACKPACK drives made
|
||||
after 1999 were Series 6) Series 6 drives will have the Series noted
|
||||
on the bottom of the drive. Series 5 drivers don't always have it
|
||||
noted.
|
||||
|
||||
In other words, if your BACKPACK drive says "Series 6" on the
|
||||
bottom, enable this option.
|
||||
|
||||
If you chose to build PARIDE support into your kernel, you may
|
||||
answer Y here to build in the protocol driver, otherwise you should
|
||||
answer M to build it as a loadable module. The module will be
|
||||
called bpck6. You must also have a high-level driver for the type
|
||||
of device that you want to support.
|
||||
|
||||
config PARIDE_COMM
|
||||
tristate "DataStor Commuter protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the Commuter parallel port IDE
|
||||
protocol from DataStor. If you chose to build PARIDE support
|
||||
into your kernel, you may answer Y here to build in the protocol
|
||||
driver, otherwise you should answer M to build it as a loadable
|
||||
module. The module will be called comm. You must also have
|
||||
a high-level driver for the type of device that you want to support.
|
||||
|
||||
config PARIDE_DSTR
|
||||
tristate "DataStor EP-2000 protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the EP-2000 parallel port IDE
|
||||
protocol from DataStor. If you chose to build PARIDE support
|
||||
into your kernel, you may answer Y here to build in the protocol
|
||||
driver, otherwise you should answer M to build it as a loadable
|
||||
module. The module will be called dstr. You must also have
|
||||
a high-level driver for the type of device that you want to support.
|
||||
|
||||
config PARIDE_FIT2
|
||||
tristate "FIT TD-2000 protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the TD-2000 parallel port IDE
|
||||
protocol from Fidelity International Technology. This is a simple
|
||||
(low speed) adapter that is used in some portable hard drives. If
|
||||
you chose to build PARIDE support into your kernel, you may answer Y
|
||||
here to build in the protocol driver, otherwise you should answer M
|
||||
to build it as a loadable module. The module will be called ktti.
|
||||
You must also have a high-level driver for the type of device that
|
||||
you want to support.
|
||||
|
||||
config PARIDE_FIT3
|
||||
tristate "FIT TD-3000 protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the TD-3000 parallel port IDE
|
||||
protocol from Fidelity International Technology. This protocol is
|
||||
used in newer models of their portable disk, CD-ROM and PD/CD
|
||||
devices. If you chose to build PARIDE support into your kernel, you
|
||||
may answer Y here to build in the protocol driver, otherwise you
|
||||
should answer M to build it as a loadable module. The module will be
|
||||
called fit3. You must also have a high-level driver for the type
|
||||
of device that you want to support.
|
||||
|
||||
config PARIDE_EPAT
|
||||
tristate "Shuttle EPAT/EPEZ protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the EPAT parallel port IDE protocol.
|
||||
EPAT is a parallel port IDE adapter manufactured by Shuttle
|
||||
Technology and widely used in devices from major vendors such as
|
||||
Hewlett-Packard, SyQuest, Imation and Avatar. If you chose to build
|
||||
PARIDE support into your kernel, you may answer Y here to build in
|
||||
the protocol driver, otherwise you should answer M to build it as a
|
||||
loadable module. The module will be called epat. You must also
|
||||
have a high-level driver for the type of device that you want to
|
||||
support.
|
||||
|
||||
config PARIDE_EPATC8
|
||||
bool "Support c7/c8 chips (EXPERIMENTAL)"
|
||||
depends on PARIDE_EPAT && EXPERIMENTAL
|
||||
help
|
||||
This option enables support for the newer Shuttle EP1284 (aka c7 and
|
||||
c8) chip. You need this if you are using any recent Imation SuperDisk
|
||||
(LS-120) drive.
|
||||
|
||||
config PARIDE_EPIA
|
||||
tristate "Shuttle EPIA protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the (obsolete) EPIA parallel port
|
||||
IDE protocol from Shuttle Technology. This adapter can still be
|
||||
found in some no-name kits. If you chose to build PARIDE support
|
||||
into your kernel, you may answer Y here to build in the protocol
|
||||
driver, otherwise you should answer M to build it as a loadable
|
||||
module. The module will be called epia. You must also have a
|
||||
high-level driver for the type of device that you want to support.
|
||||
|
||||
config PARIDE_FRIQ
|
||||
tristate "Freecom IQ ASIC-2 protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for version 2 of the Freecom IQ parallel
|
||||
port IDE adapter. This adapter is used by the Maxell Superdisk
|
||||
drive. If you chose to build PARIDE support into your kernel, you
|
||||
may answer Y here to build in the protocol driver, otherwise you
|
||||
should answer M to build it as a loadable module. The module will be
|
||||
called friq. You must also have a high-level driver for the type
|
||||
of device that you want to support.
|
||||
|
||||
config PARIDE_FRPW
|
||||
tristate "FreeCom power protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the Freecom power parallel port IDE
|
||||
protocol. If you chose to build PARIDE support into your kernel, you
|
||||
may answer Y here to build in the protocol driver, otherwise you
|
||||
should answer M to build it as a loadable module. The module will be
|
||||
called frpw. You must also have a high-level driver for the type
|
||||
of device that you want to support.
|
||||
|
||||
config PARIDE_KBIC
|
||||
tristate "KingByte KBIC-951A/971A protocols"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the KBIC-951A and KBIC-971A parallel
|
||||
port IDE protocols from KingByte Information Corp. KingByte's
|
||||
adapters appear in many no-name portable disk and CD-ROM products,
|
||||
especially in Europe. If you chose to build PARIDE support into your
|
||||
kernel, you may answer Y here to build in the protocol driver,
|
||||
otherwise you should answer M to build it as a loadable module. The
|
||||
module will be called kbic. You must also have a high-level driver
|
||||
for the type of device that you want to support.
|
||||
|
||||
config PARIDE_KTTI
|
||||
tristate "KT PHd protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the "PHd" parallel port IDE protocol
|
||||
from KT Technology. This is a simple (low speed) adapter that is
|
||||
used in some 2.5" portable hard drives. If you chose to build PARIDE
|
||||
support into your kernel, you may answer Y here to build in the
|
||||
protocol driver, otherwise you should answer M to build it as a
|
||||
loadable module. The module will be called ktti. You must also
|
||||
have a high-level driver for the type of device that you want to
|
||||
support.
|
||||
|
||||
config PARIDE_ON20
|
||||
tristate "OnSpec 90c20 protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the (obsolete) 90c20 parallel port
|
||||
IDE protocol from OnSpec (often marketed under the ValuStore brand
|
||||
name). If you chose to build PARIDE support into your kernel, you
|
||||
may answer Y here to build in the protocol driver, otherwise you
|
||||
should answer M to build it as a loadable module. The module will
|
||||
be called on20. You must also have a high-level driver for the
|
||||
type of device that you want to support.
|
||||
|
||||
config PARIDE_ON26
|
||||
tristate "OnSpec 90c26 protocol"
|
||||
depends on PARIDE
|
||||
help
|
||||
This option enables support for the 90c26 parallel port IDE protocol
|
||||
from OnSpec Electronics (often marketed under the ValuStore brand
|
||||
name). If you chose to build PARIDE support into your kernel, you
|
||||
may answer Y here to build in the protocol driver, otherwise you
|
||||
should answer M to build it as a loadable module. The module will be
|
||||
called on26. You must also have a high-level driver for the type
|
||||
of device that you want to support.
|
||||
|
||||
#
|
||||
28
drivers/block/paride/Makefile
Normal file
28
drivers/block/paride/Makefile
Normal file
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# Makefile for Parallel port IDE device drivers.
|
||||
#
|
||||
# 7 October 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
|
||||
# Rewritten to use lists instead of if-statements.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PARIDE) += paride.o
|
||||
obj-$(CONFIG_PARIDE_ATEN) += aten.o
|
||||
obj-$(CONFIG_PARIDE_BPCK) += bpck.o
|
||||
obj-$(CONFIG_PARIDE_COMM) += comm.o
|
||||
obj-$(CONFIG_PARIDE_DSTR) += dstr.o
|
||||
obj-$(CONFIG_PARIDE_KBIC) += kbic.o
|
||||
obj-$(CONFIG_PARIDE_EPAT) += epat.o
|
||||
obj-$(CONFIG_PARIDE_EPIA) += epia.o
|
||||
obj-$(CONFIG_PARIDE_FRPW) += frpw.o
|
||||
obj-$(CONFIG_PARIDE_FRIQ) += friq.o
|
||||
obj-$(CONFIG_PARIDE_FIT2) += fit2.o
|
||||
obj-$(CONFIG_PARIDE_FIT3) += fit3.o
|
||||
obj-$(CONFIG_PARIDE_ON20) += on20.o
|
||||
obj-$(CONFIG_PARIDE_ON26) += on26.o
|
||||
obj-$(CONFIG_PARIDE_KTTI) += ktti.o
|
||||
obj-$(CONFIG_PARIDE_BPCK6) += bpck6.o
|
||||
obj-$(CONFIG_PARIDE_PD) += pd.o
|
||||
obj-$(CONFIG_PARIDE_PCD) += pcd.o
|
||||
obj-$(CONFIG_PARIDE_PF) += pf.o
|
||||
obj-$(CONFIG_PARIDE_PT) += pt.o
|
||||
obj-$(CONFIG_PARIDE_PG) += pg.o
|
||||
128
drivers/block/paride/Transition-notes
Normal file
128
drivers/block/paride/Transition-notes
Normal file
@@ -0,0 +1,128 @@
|
||||
Lemma 1:
|
||||
If ps_tq is scheduled, ps_tq_active is 1. ps_tq_int() can be called
|
||||
only when ps_tq_active is 1.
|
||||
Proof: All assignments to ps_tq_active and all scheduling of ps_tq happen
|
||||
under ps_spinlock. There are three places where that can happen:
|
||||
one in ps_set_intr() (A) and two in ps_tq_int() (B and C).
|
||||
Consider the sequnce of these events. A can not be preceded by
|
||||
anything except B, since it is under if (!ps_tq_active) under
|
||||
ps_spinlock. C is always preceded by B, since we can't reach it
|
||||
other than through B and we don't drop ps_spinlock between them.
|
||||
IOW, the sequence is A?(BA|BC|B)*. OTOH, number of B can not exceed
|
||||
the sum of numbers of A and C, since each call of ps_tq_int() is
|
||||
the result of ps_tq execution. Therefore, the sequence starts with
|
||||
A and each B is preceded by either A or C. Moments when we enter
|
||||
ps_tq_int() are sandwiched between {A,C} and B in that sequence,
|
||||
since at any time number of B can not exceed the number of these
|
||||
moments which, in turn, can not exceed the number of A and C.
|
||||
In other words, the sequence of events is (A or C set ps_tq_active to
|
||||
1 and schedule ps_tq, ps_tq is executed, ps_tq_int() is entered,
|
||||
B resets ps_tq_active)*.
|
||||
|
||||
|
||||
consider the following area:
|
||||
* in do_pd_request1(): to calls of pi_do_claimed() and return in
|
||||
case when pd_req is NULL.
|
||||
* in next_request(): to call of do_pd_request1()
|
||||
* in do_pd_read(): to call of ps_set_intr()
|
||||
* in do_pd_read_start(): to calls of pi_do_claimed(), next_request()
|
||||
and ps_set_intr()
|
||||
* in do_pd_read_drq(): to calls of pi_do_claimed() and next_request()
|
||||
* in do_pd_write(): to call of ps_set_intr()
|
||||
* in do_pd_write_start(): to calls of pi_do_claimed(), next_request()
|
||||
and ps_set_intr()
|
||||
* in do_pd_write_done(): to calls of pi_do_claimed() and next_request()
|
||||
* in ps_set_intr(): to check for ps_tq_active and to scheduling
|
||||
ps_tq if ps_tq_active was 0.
|
||||
* in ps_tq_int(): from the moment when we get ps_spinlock() to the
|
||||
return, call of con() or scheduling ps_tq.
|
||||
* in pi_schedule_claimed() when called from pi_do_claimed() called from
|
||||
pd.c, everything until returning 1 or setting or setting ->claim_cont
|
||||
on the path that returns 0
|
||||
* in pi_do_claimed() when called from pd.c, everything until the call
|
||||
of pi_do_claimed() plus the everything until the call of cont() if
|
||||
pi_do_claimed() has returned 1.
|
||||
* in pi_wake_up() called for PIA that belongs to pd.c, everything from
|
||||
the moment when pi_spinlock has been acquired.
|
||||
|
||||
Lemma 2:
|
||||
1) at any time at most one thread of execution can be in that area or
|
||||
be preempted there.
|
||||
2) When there is such a thread, pd_busy is set or pd_lock is held by
|
||||
that thread.
|
||||
3) When there is such a thread, ps_tq_active is 0 or ps_spinlock is
|
||||
held by that thread.
|
||||
4) When there is such a thread, all PIA belonging to pd.c have NULL
|
||||
->claim_cont or pi_spinlock is held by thread in question.
|
||||
|
||||
Proof: consider the first moment when the above is not true.
|
||||
|
||||
(1) can become not true if some thread enters that area while another is there.
|
||||
a) do_pd_request1() can be called from next_request() or do_pd_request()
|
||||
In the first case the thread was already in the area. In the second,
|
||||
the thread was holding pd_lock and found pd_busy not set, which would
|
||||
mean that (2) was already not true.
|
||||
b) ps_set_intr() and pi_schedule_claimed() can be called only from the
|
||||
area.
|
||||
c) pi_do_claimed() is called by pd.c only from the area.
|
||||
d) ps_tq_int() can enter the area only when the thread is holding
|
||||
ps_spinlock and ps_tq_active is 1 (due to Lemma 1). It means that
|
||||
(3) was already not true.
|
||||
e) do_pd_{read,write}* could be called only from the area. The only
|
||||
case that needs consideration is call from pi_wake_up() and there
|
||||
we would have to be called for the PIA that got ->claimed_cont
|
||||
from pd.c. That could happen only if pi_do_claimed() had been
|
||||
called from pd.c for that PIA, which happens only for PIA belonging
|
||||
to pd.c.
|
||||
f) pi_wake_up() can enter the area only when the thread is holding
|
||||
pi_spinlock and ->claimed_cont is non-NULL for PIA belonging to
|
||||
pd.c. It means that (4) was already not true.
|
||||
|
||||
(2) can become not true only when pd_lock is released by the thread in question.
|
||||
Indeed, pd_busy is reset only in the area and thread that resets
|
||||
it is holding pd_lock. The only place within the area where we
|
||||
release pd_lock is in pd_next_buf() (called from within the area).
|
||||
But that code does not reset pd_busy, so pd_busy would have to be
|
||||
0 when pd_next_buf() had acquired pd_lock. If it become 0 while
|
||||
we were acquiring the lock, (1) would be already false, since
|
||||
the thread that had reset it would be in the area simulateously.
|
||||
If it was 0 before we tried to acquire pd_lock, (2) would be
|
||||
already false.
|
||||
|
||||
For similar reasons, (3) can become not true only when ps_spinlock is released
|
||||
by the thread in question. However, all such places within the area are right
|
||||
after resetting ps_tq_active to 0.
|
||||
|
||||
(4) is done the same way - all places where we release pi_spinlock within
|
||||
the area are either after resetting ->claimed_cont to NULL while holding
|
||||
pi_spinlock, or after not tocuhing ->claimed_cont since acquiring pi_spinlock
|
||||
also in the area. The only place where ->claimed_cont is made non-NULL is
|
||||
in the area, under pi_spinlock and we do not release it until after leaving
|
||||
the area.
|
||||
|
||||
QED.
|
||||
|
||||
|
||||
Corollary 1: ps_tq_active can be killed. Indeed, the only place where we
|
||||
check its value is in ps_set_intr() and if it had been non-zero at that
|
||||
point, we would have violated either (2.1) (if it was set while ps_set_intr()
|
||||
was acquiring ps_spinlock) or (2.3) (if it was set when we started to
|
||||
acquire ps_spinlock).
|
||||
|
||||
Corollary 2: ps_spinlock can be killed. Indeed, Lemma 1 and Lemma 2 show
|
||||
that the only possible contention is between scheduling ps_tq followed by
|
||||
immediate release of spinlock and beginning of execution of ps_tq on
|
||||
another CPU.
|
||||
|
||||
Corollary 3: assignment to pd_busy in do_pd_read_start() and do_pd_write_start()
|
||||
can be killed. Indeed, we are not holding pd_lock and thus pd_busy is already
|
||||
1 here.
|
||||
|
||||
Corollary 4: in ps_tq_int() uses of con can be replaced with uses of
|
||||
ps_continuation, since the latter is changed only from the area.
|
||||
We don't need to reset it to NULL, since we are guaranteed that there
|
||||
will be a call of ps_set_intr() before we look at ps_continuation again.
|
||||
We can remove the check for ps_continuation being NULL for the same
|
||||
reason - the value is guaranteed to be set by the last ps_set_intr() and
|
||||
we never pass it NULL. Assignements in the beginning of ps_set_intr()
|
||||
can be taken to callers as long as they remain within the area.
|
||||
162
drivers/block/paride/aten.c
Normal file
162
drivers/block/paride/aten.c
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
aten.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
aten.c is a low-level protocol driver for the ATEN EH-100
|
||||
parallel port adapter. The EH-100 supports 4-bit and 8-bit
|
||||
modes only. There is also an EH-132 which supports EPP mode
|
||||
transfers. The EH-132 is not yet supported.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.05 init_proto, release_proto
|
||||
|
||||
*/
|
||||
|
||||
#define ATEN_VERSION "1.01"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
*/
|
||||
|
||||
static int cont_map[2] = { 0x08, 0x20 };
|
||||
|
||||
static void aten_write_regr( PIA *pi, int cont, int regr, int val)
|
||||
|
||||
{ int r;
|
||||
|
||||
r = regr + cont_map[cont] + 0x80;
|
||||
|
||||
w0(r); w2(0xe); w2(6); w0(val); w2(7); w2(6); w2(0xc);
|
||||
}
|
||||
|
||||
static int aten_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int a, b, r;
|
||||
|
||||
r = regr + cont_map[cont] + 0x40;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(r); w2(0xe); w2(6);
|
||||
w2(7); w2(6); w2(0);
|
||||
a = r1(); w0(0x10); b = r1(); w2(0xc);
|
||||
return j44(a,b);
|
||||
|
||||
case 1: r |= 0x10;
|
||||
w0(r); w2(0xe); w2(6); w0(0xff);
|
||||
w2(0x27); w2(0x26); w2(0x20);
|
||||
a = r0();
|
||||
w2(0x26); w2(0xc);
|
||||
return a;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void aten_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, a, b, c, d;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(0x48); w2(0xe); w2(6);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w2(7); w2(6); w2(2);
|
||||
a = r1(); w0(0x58); b = r1();
|
||||
w2(0); d = r1(); w0(0x48); c = r1();
|
||||
buf[2*k] = j44(c,d);
|
||||
buf[2*k+1] = j44(a,b);
|
||||
}
|
||||
w2(0xc);
|
||||
break;
|
||||
|
||||
case 1: w0(0x58); w2(0xe); w2(6);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w2(0x27); w2(0x26); w2(0x22);
|
||||
a = r0(); w2(0x20); b = r0();
|
||||
buf[2*k] = b; buf[2*k+1] = a;
|
||||
}
|
||||
w2(0x26); w2(0xc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void aten_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
w0(0x88); w2(0xe); w2(6);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w0(buf[2*k+1]); w2(0xe); w2(6);
|
||||
w0(buf[2*k]); w2(7); w2(6);
|
||||
}
|
||||
w2(0xc);
|
||||
}
|
||||
|
||||
static void aten_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
w2(0xc);
|
||||
}
|
||||
|
||||
static void aten_disconnect ( PIA *pi )
|
||||
|
||||
{ w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static void aten_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[2] = {"4-bit","8-bit"};
|
||||
|
||||
printk("%s: aten %s, ATEN EH-100 at 0x%x, ",
|
||||
pi->device,ATEN_VERSION,pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol aten = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "aten",
|
||||
.max_mode = 2,
|
||||
.epp_first = 2,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = aten_write_regr,
|
||||
.read_regr = aten_read_regr,
|
||||
.write_block = aten_write_block,
|
||||
.read_block = aten_read_block,
|
||||
.connect = aten_connect,
|
||||
.disconnect = aten_disconnect,
|
||||
.log_adapter = aten_log_adapter,
|
||||
};
|
||||
|
||||
static int __init aten_init(void)
|
||||
{
|
||||
return paride_register(&aten);
|
||||
}
|
||||
|
||||
static void __exit aten_exit(void)
|
||||
{
|
||||
paride_unregister( &aten );
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(aten_init)
|
||||
module_exit(aten_exit)
|
||||
477
drivers/block/paride/bpck.c
Normal file
477
drivers/block/paride/bpck.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
bpck.c (c) 1996-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
bpck.c is a low-level protocol driver for the MicroSolutions
|
||||
"backpack" parallel port IDE adapter.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.05 init_proto, release_proto, pi->delay
|
||||
1.02 GRG 1998.08.15 default pi->delay returned to 4
|
||||
|
||||
*/
|
||||
|
||||
#define BPCK_VERSION "1.02"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#undef r2
|
||||
#undef w2
|
||||
|
||||
#define PC pi->private
|
||||
#define r2() (PC=(in_p(2) & 0xff))
|
||||
#define w2(byte) {out_p(2,byte); PC = byte;}
|
||||
#define t2(pat) {PC ^= pat; out_p(2,PC);}
|
||||
#define e2() {PC &= 0xfe; out_p(2,PC);}
|
||||
#define o2() {PC |= 1; out_p(2,PC);}
|
||||
|
||||
#define j44(l,h) (((l>>3)&0x7)|((l>>4)&0x8)|((h<<1)&0x70)|(h&0x80))
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
cont = 2 - use internal bpck register addressing
|
||||
*/
|
||||
|
||||
static int cont_map[3] = { 0x40, 0x48, 0 };
|
||||
|
||||
static int bpck_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int r, l, h;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(r & 0xf); w0(r); t2(2); t2(4);
|
||||
l = r1();
|
||||
t2(4);
|
||||
h = r1();
|
||||
return j44(l,h);
|
||||
|
||||
case 1: w0(r & 0xf); w0(r); t2(2);
|
||||
e2(); t2(0x20);
|
||||
t2(4); h = r0();
|
||||
t2(1); t2(0x20);
|
||||
return h;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: w0(r); w2(9); w2(0); w2(0x20);
|
||||
h = r4();
|
||||
w2(0);
|
||||
return h;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void bpck_write_regr( PIA *pi, int cont, int regr, int val )
|
||||
|
||||
{ int r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: w0(r);
|
||||
t2(2);
|
||||
w0(val);
|
||||
o2(); t2(4); t2(1);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: w0(r); w2(9); w2(0);
|
||||
w0(val); w2(1); w2(3); w2(0);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* These macros access the bpck registers in native addressing */
|
||||
|
||||
#define WR(r,v) bpck_write_regr(pi,2,r,v)
|
||||
#define RR(r) (bpck_read_regr(pi,2,r))
|
||||
|
||||
static void bpck_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int i;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: WR(4,0x40);
|
||||
w0(0x40); t2(2); t2(1);
|
||||
for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
|
||||
WR(4,0);
|
||||
break;
|
||||
|
||||
case 1: WR(4,0x50);
|
||||
w0(0x40); t2(2); t2(1);
|
||||
for (i=0;i<count;i++) { w0(buf[i]); t2(4); }
|
||||
WR(4,0x10);
|
||||
break;
|
||||
|
||||
case 2: WR(4,0x48);
|
||||
w0(0x40); w2(9); w2(0); w2(1);
|
||||
for (i=0;i<count;i++) w4(buf[i]);
|
||||
w2(0);
|
||||
WR(4,8);
|
||||
break;
|
||||
|
||||
case 3: WR(4,0x48);
|
||||
w0(0x40); w2(9); w2(0); w2(1);
|
||||
for (i=0;i<count/2;i++) w4w(((u16 *)buf)[i]);
|
||||
w2(0);
|
||||
WR(4,8);
|
||||
break;
|
||||
|
||||
case 4: WR(4,0x48);
|
||||
w0(0x40); w2(9); w2(0); w2(1);
|
||||
for (i=0;i<count/4;i++) w4l(((u32 *)buf)[i]);
|
||||
w2(0);
|
||||
WR(4,8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void bpck_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int i, l, h;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: WR(4,0x40);
|
||||
w0(0x40); t2(2);
|
||||
for (i=0;i<count;i++) {
|
||||
t2(4); l = r1();
|
||||
t2(4); h = r1();
|
||||
buf[i] = j44(l,h);
|
||||
}
|
||||
WR(4,0);
|
||||
break;
|
||||
|
||||
case 1: WR(4,0x50);
|
||||
w0(0x40); t2(2); t2(0x20);
|
||||
for(i=0;i<count;i++) { t2(4); buf[i] = r0(); }
|
||||
t2(1); t2(0x20);
|
||||
WR(4,0x10);
|
||||
break;
|
||||
|
||||
case 2: WR(4,0x48);
|
||||
w0(0x40); w2(9); w2(0); w2(0x20);
|
||||
for (i=0;i<count;i++) buf[i] = r4();
|
||||
w2(0);
|
||||
WR(4,8);
|
||||
break;
|
||||
|
||||
case 3: WR(4,0x48);
|
||||
w0(0x40); w2(9); w2(0); w2(0x20);
|
||||
for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
|
||||
w2(0);
|
||||
WR(4,8);
|
||||
break;
|
||||
|
||||
case 4: WR(4,0x48);
|
||||
w0(0x40); w2(9); w2(0); w2(0x20);
|
||||
for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
|
||||
w2(0);
|
||||
WR(4,8);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int bpck_probe_unit ( PIA *pi )
|
||||
|
||||
{ int o1, o0, f7, id;
|
||||
int t, s;
|
||||
|
||||
id = pi->unit;
|
||||
s = 0;
|
||||
w2(4); w2(0xe); r2(); t2(2);
|
||||
o1 = r1()&0xf8;
|
||||
o0 = r0();
|
||||
w0(255-id); w2(4); w0(id);
|
||||
t2(8); t2(8); t2(8);
|
||||
t2(2); t = r1()&0xf8;
|
||||
f7 = ((id % 8) == 7);
|
||||
if ((f7) || (t != o1)) { t2(2); s = r1()&0xf8; }
|
||||
if ((t == o1) && ((!f7) || (s == o1))) {
|
||||
w2(0x4c); w0(o0);
|
||||
return 0;
|
||||
}
|
||||
t2(8); w0(0); t2(2); w2(0x4c); w0(o0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void bpck_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
w0(0xff-pi->unit); w2(4); w0(pi->unit);
|
||||
t2(8); t2(8); t2(8);
|
||||
t2(2); t2(2);
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: t2(8); WR(4,0);
|
||||
break;
|
||||
|
||||
case 1: t2(8); WR(4,0x10);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: w2(0); WR(4,8);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
WR(5,8);
|
||||
|
||||
if (pi->devtype == PI_PCD) {
|
||||
WR(0x46,0x10); /* fiddle with ESS logic ??? */
|
||||
WR(0x4c,0x38);
|
||||
WR(0x4d,0x88);
|
||||
WR(0x46,0xa0);
|
||||
WR(0x41,0);
|
||||
WR(0x4e,8);
|
||||
}
|
||||
}
|
||||
|
||||
static void bpck_disconnect ( PIA *pi )
|
||||
|
||||
{ w0(0);
|
||||
if (pi->mode >= 2) { w2(9); w2(0); } else t2(2);
|
||||
w2(0x4c); w0(pi->saved_r0);
|
||||
}
|
||||
|
||||
static void bpck_force_spp ( PIA *pi )
|
||||
|
||||
/* This fakes the EPP protocol to turn off EPP ... */
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
w0(0xff-pi->unit); w2(4); w0(pi->unit);
|
||||
t2(8); t2(8); t2(8);
|
||||
t2(2); t2(2);
|
||||
|
||||
w2(0);
|
||||
w0(4); w2(9); w2(0);
|
||||
w0(0); w2(1); w2(3); w2(0);
|
||||
w0(0); w2(9); w2(0);
|
||||
w2(0x4c); w0(pi->saved_r0);
|
||||
}
|
||||
|
||||
#define TEST_LEN 16
|
||||
|
||||
static int bpck_test_proto( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ int i, e, l, h, om;
|
||||
char buf[TEST_LEN];
|
||||
|
||||
bpck_force_spp(pi);
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: bpck_connect(pi);
|
||||
WR(0x13,0x7f);
|
||||
w0(0x13); t2(2);
|
||||
for(i=0;i<TEST_LEN;i++) {
|
||||
t2(4); l = r1();
|
||||
t2(4); h = r1();
|
||||
buf[i] = j44(l,h);
|
||||
}
|
||||
bpck_disconnect(pi);
|
||||
break;
|
||||
|
||||
case 1: bpck_connect(pi);
|
||||
WR(0x13,0x7f);
|
||||
w0(0x13); t2(2); t2(0x20);
|
||||
for(i=0;i<TEST_LEN;i++) { t2(4); buf[i] = r0(); }
|
||||
t2(1); t2(0x20);
|
||||
bpck_disconnect(pi);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: om = pi->mode;
|
||||
pi->mode = 0;
|
||||
bpck_connect(pi);
|
||||
WR(7,3);
|
||||
WR(4,8);
|
||||
bpck_disconnect(pi);
|
||||
|
||||
pi->mode = om;
|
||||
bpck_connect(pi);
|
||||
w0(0x13); w2(9); w2(1); w0(0); w2(3); w2(0); w2(0xe0);
|
||||
|
||||
switch (pi->mode) {
|
||||
case 2: for (i=0;i<TEST_LEN;i++) buf[i] = r4();
|
||||
break;
|
||||
case 3: for (i=0;i<TEST_LEN/2;i++) ((u16 *)buf)[i] = r4w();
|
||||
break;
|
||||
case 4: for (i=0;i<TEST_LEN/4;i++) ((u32 *)buf)[i] = r4l();
|
||||
break;
|
||||
}
|
||||
|
||||
w2(0);
|
||||
WR(7,0);
|
||||
bpck_disconnect(pi);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printk("%s: bpck: 0x%x unit %d mode %d: ",
|
||||
pi->device,pi->port,pi->unit,pi->mode);
|
||||
for (i=0;i<TEST_LEN;i++) printk("%3d",buf[i]);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
e = 0;
|
||||
for (i=0;i<TEST_LEN;i++) if (buf[i] != (i+1)) e++;
|
||||
return e;
|
||||
}
|
||||
|
||||
static void bpck_read_eeprom ( PIA *pi, char * buf )
|
||||
|
||||
{ int i,j,k,n,p,v,f, om, od;
|
||||
|
||||
bpck_force_spp(pi);
|
||||
|
||||
om = pi->mode; od = pi->delay;
|
||||
pi->mode = 0; pi->delay = 6;
|
||||
|
||||
bpck_connect(pi);
|
||||
|
||||
n = 0;
|
||||
WR(4,0);
|
||||
for (i=0;i<64;i++) {
|
||||
WR(6,8);
|
||||
WR(6,0xc);
|
||||
p = 0x100;
|
||||
for (k=0;k<9;k++) {
|
||||
f = (((i + 0x180) & p) != 0) * 2;
|
||||
WR(6,f+0xc);
|
||||
WR(6,f+0xd);
|
||||
WR(6,f+0xc);
|
||||
p = (p >> 1);
|
||||
}
|
||||
for (j=0;j<2;j++) {
|
||||
v = 0;
|
||||
for (k=0;k<8;k++) {
|
||||
WR(6,0xc);
|
||||
WR(6,0xd);
|
||||
WR(6,0xc);
|
||||
f = RR(0);
|
||||
v = 2*v + (f == 0x84);
|
||||
}
|
||||
buf[2*i+1-j] = v;
|
||||
}
|
||||
}
|
||||
WR(6,8);
|
||||
WR(6,0);
|
||||
WR(5,8);
|
||||
|
||||
bpck_disconnect(pi);
|
||||
|
||||
if (om >= 2) {
|
||||
bpck_connect(pi);
|
||||
WR(7,3);
|
||||
WR(4,8);
|
||||
bpck_disconnect(pi);
|
||||
}
|
||||
|
||||
pi->mode = om; pi->delay = od;
|
||||
}
|
||||
|
||||
static int bpck_test_port ( PIA *pi ) /* check for 8-bit port */
|
||||
|
||||
{ int i, r, m;
|
||||
|
||||
w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i);
|
||||
m = -1;
|
||||
if (r == i) m = 2;
|
||||
if (r == (255-i)) m = 0;
|
||||
|
||||
w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i);
|
||||
if (r != (255-i)) m = -1;
|
||||
|
||||
if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); }
|
||||
if (m == 2) { w2(0x26); w2(0xc); }
|
||||
|
||||
if (m == -1) return 0;
|
||||
return 5;
|
||||
}
|
||||
|
||||
static void bpck_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[5] = { "4-bit","8-bit","EPP-8",
|
||||
"EPP-16","EPP-32" };
|
||||
|
||||
#ifdef DUMP_EEPROM
|
||||
int i;
|
||||
#endif
|
||||
|
||||
bpck_read_eeprom(pi,scratch);
|
||||
|
||||
#ifdef DUMP_EEPROM
|
||||
if (verbose) {
|
||||
for(i=0;i<128;i++)
|
||||
if ((scratch[i] < ' ') || (scratch[i] > '~'))
|
||||
scratch[i] = '.';
|
||||
printk("%s: bpck EEPROM: %64.64s\n",pi->device,scratch);
|
||||
printk("%s: %64.64s\n",pi->device,&scratch[64]);
|
||||
}
|
||||
#endif
|
||||
|
||||
printk("%s: bpck %s, backpack %8.8s unit %d",
|
||||
pi->device,BPCK_VERSION,&scratch[110],pi->unit);
|
||||
printk(" at 0x%x, mode %d (%s), delay %d\n",pi->port,
|
||||
pi->mode,mode_string[pi->mode],pi->delay);
|
||||
}
|
||||
|
||||
static struct pi_protocol bpck = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "bpck",
|
||||
.max_mode = 5,
|
||||
.epp_first = 2,
|
||||
.default_delay = 4,
|
||||
.max_units = 255,
|
||||
.write_regr = bpck_write_regr,
|
||||
.read_regr = bpck_read_regr,
|
||||
.write_block = bpck_write_block,
|
||||
.read_block = bpck_read_block,
|
||||
.connect = bpck_connect,
|
||||
.disconnect = bpck_disconnect,
|
||||
.test_port = bpck_test_port,
|
||||
.probe_unit = bpck_probe_unit,
|
||||
.test_proto = bpck_test_proto,
|
||||
.log_adapter = bpck_log_adapter,
|
||||
};
|
||||
|
||||
static int __init bpck_init(void)
|
||||
{
|
||||
return paride_register(&bpck);
|
||||
}
|
||||
|
||||
static void __exit bpck_exit(void)
|
||||
{
|
||||
paride_unregister(&bpck);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(bpck_init)
|
||||
module_exit(bpck_exit)
|
||||
268
drivers/block/paride/bpck6.c
Normal file
268
drivers/block/paride/bpck6.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
backpack.c (c) 2001 Micro Solutions Inc.
|
||||
Released under the terms of the GNU General Public license
|
||||
|
||||
backpack.c is a low-level protocol driver for the Micro Solutions
|
||||
"BACKPACK" parallel port IDE adapter
|
||||
(Works on Series 6 drives)
|
||||
|
||||
Written by: Ken Hahn (linux-dev@micro-solutions.com)
|
||||
Clive Turvey (linux-dev@micro-solutions.com)
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
This is Ken's linux wrapper for the PPC library
|
||||
Version 1.0.0 is the backpack driver for which source is not available
|
||||
Version 2.0.0 is the first to have source released
|
||||
Version 2.0.1 is the "Cox-ified" source code
|
||||
Version 2.0.2 - fixed version string usage, and made ppc functions static
|
||||
*/
|
||||
|
||||
|
||||
/* PARAMETERS */
|
||||
static int verbose; /* set this to 1 to see debugging messages and whatnot */
|
||||
|
||||
#define BACKPACK_VERSION "2.0.2"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/parport.h>
|
||||
|
||||
#include "ppc6lnx.c"
|
||||
#include "paride.h"
|
||||
|
||||
|
||||
|
||||
#define PPCSTRUCT(pi) ((Interface *)(pi->private))
|
||||
|
||||
/****************************************************************/
|
||||
/*
|
||||
ATAPI CDROM DRIVE REGISTERS
|
||||
*/
|
||||
#define ATAPI_DATA 0 /* data port */
|
||||
#define ATAPI_ERROR 1 /* error register (read) */
|
||||
#define ATAPI_FEATURES 1 /* feature register (write) */
|
||||
#define ATAPI_INT_REASON 2 /* interrupt reason register */
|
||||
#define ATAPI_COUNT_LOW 4 /* byte count register (low) */
|
||||
#define ATAPI_COUNT_HIGH 5 /* byte count register (high) */
|
||||
#define ATAPI_DRIVE_SEL 6 /* drive select register */
|
||||
#define ATAPI_STATUS 7 /* status port (read) */
|
||||
#define ATAPI_COMMAND 7 /* command port (write) */
|
||||
#define ATAPI_ALT_STATUS 0x0e /* alternate status reg (read) */
|
||||
#define ATAPI_DEVICE_CONTROL 0x0e /* device control (write) */
|
||||
/****************************************************************/
|
||||
|
||||
static int bpck6_read_regr(PIA *pi, int cont, int reg)
|
||||
{
|
||||
unsigned int out;
|
||||
|
||||
/* check for bad settings */
|
||||
if (reg<0 || reg>7 || cont<0 || cont>2)
|
||||
{
|
||||
return(-1);
|
||||
}
|
||||
out=ppc6_rd_port(PPCSTRUCT(pi),cont?reg|8:reg);
|
||||
return(out);
|
||||
}
|
||||
|
||||
static void bpck6_write_regr(PIA *pi, int cont, int reg, int val)
|
||||
{
|
||||
/* check for bad settings */
|
||||
if (reg>=0 && reg<=7 && cont>=0 && cont<=1)
|
||||
{
|
||||
ppc6_wr_port(PPCSTRUCT(pi),cont?reg|8:reg,(u8)val);
|
||||
}
|
||||
}
|
||||
|
||||
static void bpck6_write_block( PIA *pi, char * buf, int len )
|
||||
{
|
||||
ppc6_wr_port16_blk(PPCSTRUCT(pi),ATAPI_DATA,buf,(u32)len>>1);
|
||||
}
|
||||
|
||||
static void bpck6_read_block( PIA *pi, char * buf, int len )
|
||||
{
|
||||
ppc6_rd_port16_blk(PPCSTRUCT(pi),ATAPI_DATA,buf,(u32)len>>1);
|
||||
}
|
||||
|
||||
static void bpck6_connect ( PIA *pi )
|
||||
{
|
||||
if(verbose)
|
||||
{
|
||||
printk(KERN_DEBUG "connect\n");
|
||||
}
|
||||
|
||||
if(pi->mode >=2)
|
||||
{
|
||||
PPCSTRUCT(pi)->mode=4+pi->mode-2;
|
||||
}
|
||||
else if(pi->mode==1)
|
||||
{
|
||||
PPCSTRUCT(pi)->mode=3;
|
||||
}
|
||||
else
|
||||
{
|
||||
PPCSTRUCT(pi)->mode=1;
|
||||
}
|
||||
|
||||
ppc6_open(PPCSTRUCT(pi));
|
||||
ppc6_wr_extout(PPCSTRUCT(pi),0x3);
|
||||
}
|
||||
|
||||
static void bpck6_disconnect ( PIA *pi )
|
||||
{
|
||||
if(verbose)
|
||||
{
|
||||
printk("disconnect\n");
|
||||
}
|
||||
ppc6_wr_extout(PPCSTRUCT(pi),0x0);
|
||||
ppc6_close(PPCSTRUCT(pi));
|
||||
}
|
||||
|
||||
static int bpck6_test_port ( PIA *pi ) /* check for 8-bit port */
|
||||
{
|
||||
if(verbose)
|
||||
{
|
||||
printk(KERN_DEBUG "PARPORT indicates modes=%x for lp=0x%lx\n",
|
||||
((struct pardevice*)(pi->pardev))->port->modes,
|
||||
((struct pardevice *)(pi->pardev))->port->base);
|
||||
}
|
||||
|
||||
/*copy over duplicate stuff.. initialize state info*/
|
||||
PPCSTRUCT(pi)->ppc_id=pi->unit;
|
||||
PPCSTRUCT(pi)->lpt_addr=pi->port;
|
||||
|
||||
/* look at the parport device to see if what modes we can use */
|
||||
if(((struct pardevice *)(pi->pardev))->port->modes &
|
||||
(PARPORT_MODE_EPP)
|
||||
)
|
||||
{
|
||||
return 5; /* Can do EPP*/
|
||||
}
|
||||
else if(((struct pardevice *)(pi->pardev))->port->modes &
|
||||
(PARPORT_MODE_TRISTATE)
|
||||
)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else /*Just flat SPP*/
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int bpck6_probe_unit ( PIA *pi )
|
||||
{
|
||||
int out;
|
||||
|
||||
if(verbose)
|
||||
{
|
||||
printk(KERN_DEBUG "PROBE UNIT %x on port:%x\n",pi->unit,pi->port);
|
||||
}
|
||||
|
||||
/*SET PPC UNIT NUMBER*/
|
||||
PPCSTRUCT(pi)->ppc_id=pi->unit;
|
||||
|
||||
/*LOWER DOWN TO UNIDIRECTIONAL*/
|
||||
PPCSTRUCT(pi)->mode=1;
|
||||
|
||||
out=ppc6_open(PPCSTRUCT(pi));
|
||||
|
||||
if(verbose)
|
||||
{
|
||||
printk(KERN_DEBUG "ppc_open returned %2x\n",out);
|
||||
}
|
||||
|
||||
if(out)
|
||||
{
|
||||
ppc6_close(PPCSTRUCT(pi));
|
||||
if(verbose)
|
||||
{
|
||||
printk(KERN_DEBUG "leaving probe\n");
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(verbose)
|
||||
{
|
||||
printk(KERN_DEBUG "Failed open\n");
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void bpck6_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
{
|
||||
char *mode_string[5]=
|
||||
{"4-bit","8-bit","EPP-8","EPP-16","EPP-32"};
|
||||
|
||||
printk("%s: BACKPACK Protocol Driver V"BACKPACK_VERSION"\n",pi->device);
|
||||
printk("%s: Copyright 2001 by Micro Solutions, Inc., DeKalb IL.\n",pi->device);
|
||||
printk("%s: BACKPACK %s, Micro Solutions BACKPACK Drive at 0x%x\n",
|
||||
pi->device,BACKPACK_VERSION,pi->port);
|
||||
printk("%s: Unit: %d Mode:%d (%s) Delay %d\n",pi->device,
|
||||
pi->unit,pi->mode,mode_string[pi->mode],pi->delay);
|
||||
}
|
||||
|
||||
static int bpck6_init_proto(PIA *pi)
|
||||
{
|
||||
Interface *p = kzalloc(sizeof(Interface), GFP_KERNEL);
|
||||
|
||||
if (p) {
|
||||
pi->private = (unsigned long)p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_ERR "%s: ERROR COULDN'T ALLOCATE MEMORY\n", pi->device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void bpck6_release_proto(PIA *pi)
|
||||
{
|
||||
kfree((void *)(pi->private));
|
||||
}
|
||||
|
||||
static struct pi_protocol bpck6 = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "bpck6",
|
||||
.max_mode = 5,
|
||||
.epp_first = 2, /* 2-5 use epp (need 8 ports) */
|
||||
.max_units = 255,
|
||||
.write_regr = bpck6_write_regr,
|
||||
.read_regr = bpck6_read_regr,
|
||||
.write_block = bpck6_write_block,
|
||||
.read_block = bpck6_read_block,
|
||||
.connect = bpck6_connect,
|
||||
.disconnect = bpck6_disconnect,
|
||||
.test_port = bpck6_test_port,
|
||||
.probe_unit = bpck6_probe_unit,
|
||||
.log_adapter = bpck6_log_adapter,
|
||||
.init_proto = bpck6_init_proto,
|
||||
.release_proto = bpck6_release_proto,
|
||||
};
|
||||
|
||||
static int __init bpck6_init(void)
|
||||
{
|
||||
printk(KERN_INFO "bpck6: BACKPACK Protocol Driver V"BACKPACK_VERSION"\n");
|
||||
printk(KERN_INFO "bpck6: Copyright 2001 by Micro Solutions, Inc., DeKalb IL. USA\n");
|
||||
if(verbose)
|
||||
printk(KERN_DEBUG "bpck6: verbose debug enabled.\n");
|
||||
return paride_register(&bpck6);
|
||||
}
|
||||
|
||||
static void __exit bpck6_exit(void)
|
||||
{
|
||||
paride_unregister(&bpck6);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Micro Solutions Inc.");
|
||||
MODULE_DESCRIPTION("BACKPACK Protocol module, compatible with PARIDE");
|
||||
module_param(verbose, bool, 0644);
|
||||
module_init(bpck6_init)
|
||||
module_exit(bpck6_exit)
|
||||
218
drivers/block/paride/comm.c
Normal file
218
drivers/block/paride/comm.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
comm.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
comm.c is a low-level protocol driver for some older models
|
||||
of the DataStor "Commuter" parallel to IDE adapter. Some of
|
||||
the parallel port devices marketed by Arista currently
|
||||
use this adapter.
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.05 init_proto, release_proto
|
||||
|
||||
*/
|
||||
|
||||
#define COMM_VERSION "1.01"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
/* mode codes: 0 nybble reads, 8-bit writes
|
||||
1 8-bit reads and writes
|
||||
2 8-bit EPP mode
|
||||
*/
|
||||
|
||||
#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0))
|
||||
|
||||
#define P1 w2(5);w2(0xd);w2(0xd);w2(5);w2(4);
|
||||
#define P2 w2(5);w2(7);w2(7);w2(5);w2(4);
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
*/
|
||||
|
||||
static int cont_map[2] = { 0x08, 0x10 };
|
||||
|
||||
static int comm_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int l, h, r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(r); P1; w0(0);
|
||||
w2(6); l = r1(); w0(0x80); h = r1(); w2(4);
|
||||
return j44(l,h);
|
||||
|
||||
case 1: w0(r+0x20); P1;
|
||||
w0(0); w2(0x26); h = r0(); w2(4);
|
||||
return h;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: w3(r+0x20); (void)r1();
|
||||
w2(0x24); h = r4(); w2(4);
|
||||
return h;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void comm_write_regr( PIA *pi, int cont, int regr, int val )
|
||||
|
||||
{ int r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: w0(r); P1; w0(val); P2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: w3(r); (void)r1(); w4(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void comm_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
w2(4); w0(0xff); w2(6);
|
||||
w2(4); w0(0xaa); w2(6);
|
||||
w2(4); w0(0x00); w2(6);
|
||||
w2(4); w0(0x87); w2(6);
|
||||
w2(4); w0(0xe0); w2(0xc); w2(0xc); w2(4);
|
||||
}
|
||||
|
||||
static void comm_disconnect ( PIA *pi )
|
||||
|
||||
{ w2(0); w2(0); w2(0); w2(4);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static void comm_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int i, l, h;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(0x48); P1;
|
||||
for(i=0;i<count;i++) {
|
||||
w0(0); w2(6); l = r1();
|
||||
w0(0x80); h = r1(); w2(4);
|
||||
buf[i] = j44(l,h);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: w0(0x68); P1; w0(0);
|
||||
for(i=0;i<count;i++) {
|
||||
w2(0x26); buf[i] = r0(); w2(0x24);
|
||||
}
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 2: w3(0x68); (void)r1(); w2(0x24);
|
||||
for (i=0;i<count;i++) buf[i] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 3: w3(0x68); (void)r1(); w2(0x24);
|
||||
for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 4: w3(0x68); (void)r1(); w2(0x24);
|
||||
for (i=0;i<count/4;i++) ((u32 *)buf)[i] = r4l();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* NB: Watch out for the byte swapped writes ! */
|
||||
|
||||
static void comm_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: w0(0x68); P1;
|
||||
for (k=0;k<count;k++) {
|
||||
w2(5); w0(buf[k^1]); w2(7);
|
||||
}
|
||||
w2(5); w2(4);
|
||||
break;
|
||||
|
||||
case 2: w3(0x48); (void)r1();
|
||||
for (k=0;k<count;k++) w4(buf[k^1]);
|
||||
break;
|
||||
|
||||
case 3: w3(0x48); (void)r1();
|
||||
for (k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
|
||||
break;
|
||||
|
||||
case 4: w3(0x48); (void)r1();
|
||||
for (k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void comm_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[5] = {"4-bit","8-bit","EPP-8","EPP-16","EPP-32"};
|
||||
|
||||
printk("%s: comm %s, DataStor Commuter at 0x%x, ",
|
||||
pi->device,COMM_VERSION,pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol comm = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "comm",
|
||||
.max_mode = 5,
|
||||
.epp_first = 2,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = comm_write_regr,
|
||||
.read_regr = comm_read_regr,
|
||||
.write_block = comm_write_block,
|
||||
.read_block = comm_read_block,
|
||||
.connect = comm_connect,
|
||||
.disconnect = comm_disconnect,
|
||||
.log_adapter = comm_log_adapter,
|
||||
};
|
||||
|
||||
static int __init comm_init(void)
|
||||
{
|
||||
return paride_register(&comm);
|
||||
}
|
||||
|
||||
static void __exit comm_exit(void)
|
||||
{
|
||||
paride_unregister(&comm);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(comm_init)
|
||||
module_exit(comm_exit)
|
||||
233
drivers/block/paride/dstr.c
Normal file
233
drivers/block/paride/dstr.c
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
dstr.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
dstr.c is a low-level protocol driver for the
|
||||
DataStor EP2000 parallel to IDE adapter chip.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.06 init_proto, release_proto
|
||||
|
||||
*/
|
||||
|
||||
#define DSTR_VERSION "1.01"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
/* mode codes: 0 nybble reads, 8-bit writes
|
||||
1 8-bit reads and writes
|
||||
2 8-bit EPP mode
|
||||
3 EPP-16
|
||||
4 EPP-32
|
||||
*/
|
||||
|
||||
#define j44(a,b) (((a>>3)&0x07)|((~a>>4)&0x08)|((b<<1)&0x70)|((~b)&0x80))
|
||||
|
||||
#define P1 w2(5);w2(0xd);w2(5);w2(4);
|
||||
#define P2 w2(5);w2(7);w2(5);w2(4);
|
||||
#define P3 w2(6);w2(4);w2(6);w2(4);
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
*/
|
||||
|
||||
static int cont_map[2] = { 0x20, 0x40 };
|
||||
|
||||
static int dstr_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int a, b, r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
w0(0x81); P1;
|
||||
if (pi->mode) { w0(0x11); } else { w0(1); }
|
||||
P2; w0(r); P1;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w2(6); a = r1(); w2(4); w2(6); b = r1(); w2(4);
|
||||
return j44(a,b);
|
||||
|
||||
case 1: w0(0); w2(0x26); a = r0(); w2(4);
|
||||
return a;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: w2(0x24); a = r4(); w2(4);
|
||||
return a;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void dstr_write_regr( PIA *pi, int cont, int regr, int val )
|
||||
|
||||
{ int r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
w0(0x81); P1;
|
||||
if (pi->mode >= 2) { w0(0x11); } else { w0(1); }
|
||||
P2; w0(r); P1;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: w0(val); w2(5); w2(7); w2(5); w2(4);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: w4(val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define CCP(x) w0(0xff);w2(0xc);w2(4);\
|
||||
w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);w0(0x78);\
|
||||
w0(x);w2(5);w2(4);
|
||||
|
||||
static void dstr_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
w2(4); CCP(0xe0); w0(0xff);
|
||||
}
|
||||
|
||||
static void dstr_disconnect ( PIA *pi )
|
||||
|
||||
{ CCP(0x30);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static void dstr_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, a, b;
|
||||
|
||||
w0(0x81); P1;
|
||||
if (pi->mode) { w0(0x19); } else { w0(9); }
|
||||
P2; w0(0x82); P1; P3; w0(0x20); P1;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: for (k=0;k<count;k++) {
|
||||
w2(6); a = r1(); w2(4);
|
||||
w2(6); b = r1(); w2(4);
|
||||
buf[k] = j44(a,b);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: w0(0);
|
||||
for (k=0;k<count;k++) {
|
||||
w2(0x26); buf[k] = r0(); w2(0x24);
|
||||
}
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 2: w2(0x24);
|
||||
for (k=0;k<count;k++) buf[k] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 3: w2(0x24);
|
||||
for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 4: w2(0x24);
|
||||
for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void dstr_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
w0(0x81); P1;
|
||||
if (pi->mode) { w0(0x19); } else { w0(9); }
|
||||
P2; w0(0x82); P1; P3; w0(0x20); P1;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: for (k=0;k<count;k++) {
|
||||
w2(5); w0(buf[k]); w2(7);
|
||||
}
|
||||
w2(5); w2(4);
|
||||
break;
|
||||
|
||||
case 2: w2(0xc5);
|
||||
for (k=0;k<count;k++) w4(buf[k]);
|
||||
w2(0xc4);
|
||||
break;
|
||||
|
||||
case 3: w2(0xc5);
|
||||
for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
|
||||
w2(0xc4);
|
||||
break;
|
||||
|
||||
case 4: w2(0xc5);
|
||||
for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
|
||||
w2(0xc4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void dstr_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[5] = {"4-bit","8-bit","EPP-8",
|
||||
"EPP-16","EPP-32"};
|
||||
|
||||
printk("%s: dstr %s, DataStor EP2000 at 0x%x, ",
|
||||
pi->device,DSTR_VERSION,pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol dstr = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "dstr",
|
||||
.max_mode = 5,
|
||||
.epp_first = 2,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = dstr_write_regr,
|
||||
.read_regr = dstr_read_regr,
|
||||
.write_block = dstr_write_block,
|
||||
.read_block = dstr_read_block,
|
||||
.connect = dstr_connect,
|
||||
.disconnect = dstr_disconnect,
|
||||
.log_adapter = dstr_log_adapter,
|
||||
};
|
||||
|
||||
static int __init dstr_init(void)
|
||||
{
|
||||
return paride_register(&dstr);
|
||||
}
|
||||
|
||||
static void __exit dstr_exit(void)
|
||||
{
|
||||
paride_unregister(&dstr);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(dstr_init)
|
||||
module_exit(dstr_exit)
|
||||
340
drivers/block/paride/epat.c
Normal file
340
drivers/block/paride/epat.c
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
epat.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
This is the low level protocol driver for the EPAT parallel
|
||||
to IDE adapter from Shuttle Technologies. This adapter is
|
||||
used in many popular parallel port disk products such as the
|
||||
SyQuest EZ drives, the Avatar Shark and the Imation SuperDisk.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.06 init_proto, release_proto
|
||||
1.02 Joshua b. Jore CPP(renamed), epat_connect, epat_disconnect
|
||||
|
||||
*/
|
||||
|
||||
#define EPAT_VERSION "1.02"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0))
|
||||
#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0))
|
||||
|
||||
static int epatc8;
|
||||
|
||||
module_param(epatc8, int, 0);
|
||||
MODULE_PARM_DESC(epatc8, "support for the Shuttle EP1284 chip, "
|
||||
"used in any recent Imation SuperDisk (LS-120) drive.");
|
||||
|
||||
/* cont = 0 IDE register file
|
||||
cont = 1 IDE control registers
|
||||
cont = 2 internal EPAT registers
|
||||
*/
|
||||
|
||||
static int cont_map[3] = { 0x18, 0x10, 0 };
|
||||
|
||||
static void epat_write_regr( PIA *pi, int cont, int regr, int val)
|
||||
|
||||
{ int r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
case 2: w0(0x60+r); w2(1); w0(val); w2(4);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
case 5: w3(0x40+r); w4(val);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int epat_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int a, b, r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(r); w2(1); w2(3);
|
||||
a = r1(); w2(4); b = r1();
|
||||
return j44(a,b);
|
||||
|
||||
case 1: w0(0x40+r); w2(1); w2(4);
|
||||
a = r1(); b = r2(); w0(0xff);
|
||||
return j53(a,b);
|
||||
|
||||
case 2: w0(0x20+r); w2(1); w2(0x25);
|
||||
a = r0(); w2(4);
|
||||
return a;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
case 5: w3(r); w2(0x24); a = r4(); w2(4);
|
||||
return a;
|
||||
|
||||
}
|
||||
return -1; /* never gets here */
|
||||
}
|
||||
|
||||
static void epat_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, ph, a, b;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(7); w2(1); w2(3); w0(0xff);
|
||||
ph = 0;
|
||||
for(k=0;k<count;k++) {
|
||||
if (k == count-1) w0(0xfd);
|
||||
w2(6+ph); a = r1();
|
||||
if (a & 8) b = a;
|
||||
else { w2(4+ph); b = r1(); }
|
||||
buf[k] = j44(a,b);
|
||||
ph = 1 - ph;
|
||||
}
|
||||
w0(0); w2(4);
|
||||
break;
|
||||
|
||||
case 1: w0(0x47); w2(1); w2(5); w0(0xff);
|
||||
ph = 0;
|
||||
for(k=0;k<count;k++) {
|
||||
if (k == count-1) w0(0xfd);
|
||||
w2(4+ph);
|
||||
a = r1(); b = r2();
|
||||
buf[k] = j53(a,b);
|
||||
ph = 1 - ph;
|
||||
}
|
||||
w0(0); w2(4);
|
||||
break;
|
||||
|
||||
case 2: w0(0x27); w2(1); w2(0x25); w0(0);
|
||||
ph = 0;
|
||||
for(k=0;k<count-1;k++) {
|
||||
w2(0x24+ph);
|
||||
buf[k] = r0();
|
||||
ph = 1 - ph;
|
||||
}
|
||||
w2(0x26); w2(0x27); buf[count-1] = r0();
|
||||
w2(0x25); w2(4);
|
||||
break;
|
||||
|
||||
case 3: w3(0x80); w2(0x24);
|
||||
for(k=0;k<count-1;k++) buf[k] = r4();
|
||||
w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 4: w3(0x80); w2(0x24);
|
||||
for(k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
|
||||
buf[count-2] = r4();
|
||||
w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 5: w3(0x80); w2(0x24);
|
||||
for(k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
|
||||
for(k=count-4;k<count-1;k++) buf[k] = r4();
|
||||
w2(4); w3(0xa0); w2(0x24); buf[count-1] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void epat_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int ph, k;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
case 2: w0(0x67); w2(1); w2(5);
|
||||
ph = 0;
|
||||
for(k=0;k<count;k++) {
|
||||
w0(buf[k]);
|
||||
w2(4+ph);
|
||||
ph = 1 - ph;
|
||||
}
|
||||
w2(7); w2(4);
|
||||
break;
|
||||
|
||||
case 3: w3(0xc0);
|
||||
for(k=0;k<count;k++) w4(buf[k]);
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 4: w3(0xc0);
|
||||
for(k=0;k<(count/2);k++) w4w(((u16 *)buf)[k]);
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 5: w3(0xc0);
|
||||
for(k=0;k<(count/4);k++) w4l(((u32 *)buf)[k]);
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* these macros access the EPAT registers in native addressing */
|
||||
|
||||
#define WR(r,v) epat_write_regr(pi,2,r,v)
|
||||
#define RR(r) (epat_read_regr(pi,2,r))
|
||||
|
||||
/* and these access the IDE task file */
|
||||
|
||||
#define WRi(r,v) epat_write_regr(pi,0,r,v)
|
||||
#define RRi(r) (epat_read_regr(pi,0,r))
|
||||
|
||||
/* FIXME: the CPP stuff should be fixed to handle multiple EPATs on a chain */
|
||||
|
||||
#define CPP(x) w2(4);w0(0x22);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
|
||||
w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff);
|
||||
|
||||
static void epat_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
|
||||
/* Initialize the chip */
|
||||
CPP(0);
|
||||
|
||||
if (epatc8) {
|
||||
CPP(0x40);CPP(0xe0);
|
||||
w0(0);w2(1);w2(4);
|
||||
WR(0x8,0x12);WR(0xc,0x14);WR(0x12,0x10);
|
||||
WR(0xe,0xf);WR(0xf,4);
|
||||
/* WR(0xe,0xa);WR(0xf,4); */
|
||||
WR(0xe,0xd);WR(0xf,0);
|
||||
/* CPP(0x30); */
|
||||
}
|
||||
|
||||
/* Connect to the chip */
|
||||
CPP(0xe0);
|
||||
w0(0);w2(1);w2(4); /* Idle into SPP */
|
||||
if (pi->mode >= 3) {
|
||||
w0(0);w2(1);w2(4);w2(0xc);
|
||||
/* Request EPP */
|
||||
w0(0x40);w2(6);w2(7);w2(4);w2(0xc);w2(4);
|
||||
}
|
||||
|
||||
if (!epatc8) {
|
||||
WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10);
|
||||
}
|
||||
}
|
||||
|
||||
static void epat_disconnect (PIA *pi)
|
||||
{ CPP(0x30);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static int epat_test_proto( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ int k, j, f, cc;
|
||||
int e[2] = {0,0};
|
||||
|
||||
epat_connect(pi);
|
||||
cc = RR(0xd);
|
||||
epat_disconnect(pi);
|
||||
|
||||
epat_connect(pi);
|
||||
for (j=0;j<2;j++) {
|
||||
WRi(6,0xa0+j*0x10);
|
||||
for (k=0;k<256;k++) {
|
||||
WRi(2,k^0xaa);
|
||||
WRi(3,k^0x55);
|
||||
if (RRi(2) != (k^0xaa)) e[j]++;
|
||||
}
|
||||
}
|
||||
epat_disconnect(pi);
|
||||
|
||||
f = 0;
|
||||
epat_connect(pi);
|
||||
WR(0x13,1); WR(0x13,0); WR(0xa,0x11);
|
||||
epat_read_block(pi,scratch,512);
|
||||
|
||||
for (k=0;k<256;k++) {
|
||||
if ((scratch[2*k] & 0xff) != k) f++;
|
||||
if ((scratch[2*k+1] & 0xff) != (0xff-k)) f++;
|
||||
}
|
||||
epat_disconnect(pi);
|
||||
|
||||
if (verbose) {
|
||||
printk("%s: epat: port 0x%x, mode %d, ccr %x, test=(%d,%d,%d)\n",
|
||||
pi->device,pi->port,pi->mode,cc,e[0],e[1],f);
|
||||
}
|
||||
|
||||
return (e[0] && e[1]) || f;
|
||||
}
|
||||
|
||||
static void epat_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ int ver;
|
||||
char *mode_string[6] =
|
||||
{"4-bit","5/3","8-bit","EPP-8","EPP-16","EPP-32"};
|
||||
|
||||
epat_connect(pi);
|
||||
WR(0xa,0x38); /* read the version code */
|
||||
ver = RR(0xb);
|
||||
epat_disconnect(pi);
|
||||
|
||||
printk("%s: epat %s, Shuttle EPAT chip %x at 0x%x, ",
|
||||
pi->device,EPAT_VERSION,ver,pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol epat = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "epat",
|
||||
.max_mode = 6,
|
||||
.epp_first = 3,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = epat_write_regr,
|
||||
.read_regr = epat_read_regr,
|
||||
.write_block = epat_write_block,
|
||||
.read_block = epat_read_block,
|
||||
.connect = epat_connect,
|
||||
.disconnect = epat_disconnect,
|
||||
.test_proto = epat_test_proto,
|
||||
.log_adapter = epat_log_adapter,
|
||||
};
|
||||
|
||||
static int __init epat_init(void)
|
||||
{
|
||||
#ifdef CONFIG_PARIDE_EPATC8
|
||||
epatc8 = 1;
|
||||
#endif
|
||||
return paride_register(&epat);
|
||||
}
|
||||
|
||||
static void __exit epat_exit(void)
|
||||
{
|
||||
paride_unregister(&epat);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(epat_init)
|
||||
module_exit(epat_exit)
|
||||
316
drivers/block/paride/epia.c
Normal file
316
drivers/block/paride/epia.c
Normal file
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
epia.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
epia.c is a low-level protocol driver for Shuttle Technologies
|
||||
EPIA parallel to IDE adapter chip. This device is now obsolete
|
||||
and has been replaced with the EPAT chip, which is supported
|
||||
by epat.c, however, some devices based on EPIA are still
|
||||
available.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.06 init_proto, release_proto
|
||||
1.02 GRG 1998.06.17 support older versions of EPIA
|
||||
|
||||
*/
|
||||
|
||||
#define EPIA_VERSION "1.02"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
/* mode codes: 0 nybble reads on port 1, 8-bit writes
|
||||
1 5/3 reads on ports 1 & 2, 8-bit writes
|
||||
2 8-bit reads and writes
|
||||
3 8-bit EPP mode
|
||||
4 16-bit EPP
|
||||
5 32-bit EPP
|
||||
*/
|
||||
|
||||
#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0))
|
||||
#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0))
|
||||
|
||||
/* cont = 0 IDE register file
|
||||
cont = 1 IDE control registers
|
||||
*/
|
||||
|
||||
static int cont_map[2] = { 0, 0x80 };
|
||||
|
||||
static int epia_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int a, b, r;
|
||||
|
||||
regr += cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: r = regr^0x39;
|
||||
w0(r); w2(1); w2(3); w0(r);
|
||||
a = r1(); w2(1); b = r1(); w2(4);
|
||||
return j44(a,b);
|
||||
|
||||
case 1: r = regr^0x31;
|
||||
w0(r); w2(1); w0(r&0x37);
|
||||
w2(3); w2(5); w0(r|0xf0);
|
||||
a = r1(); b = r2(); w2(4);
|
||||
return j53(a,b);
|
||||
|
||||
case 2: r = regr^0x29;
|
||||
w0(r); w2(1); w2(0X21); w2(0x23);
|
||||
a = r0(); w2(4);
|
||||
return a;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
case 5: w3(regr); w2(0x24); a = r4(); w2(4);
|
||||
return a;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void epia_write_regr( PIA *pi, int cont, int regr, int val)
|
||||
|
||||
{ int r;
|
||||
|
||||
regr += cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
case 2: r = regr^0x19;
|
||||
w0(r); w2(1); w0(val); w2(3); w2(4);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
case 5: r = regr^0x40;
|
||||
w3(r); w4(val); w2(4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define WR(r,v) epia_write_regr(pi,0,r,v)
|
||||
#define RR(r) (epia_read_regr(pi,0,r))
|
||||
|
||||
/* The use of register 0x84 is entirely unclear - it seems to control
|
||||
some EPP counters ... currently we know about 3 different block
|
||||
sizes: the standard 512 byte reads and writes, 12 byte writes and
|
||||
2048 byte reads (the last two being used in the CDrom drivers.
|
||||
*/
|
||||
|
||||
static void epia_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
|
||||
w2(4); w0(0xa0); w0(0x50); w0(0xc0); w0(0x30); w0(0xa0); w0(0);
|
||||
w2(1); w2(4);
|
||||
if (pi->mode >= 3) {
|
||||
w0(0xa); w2(1); w2(4); w0(0x82); w2(4); w2(0xc); w2(4);
|
||||
w2(0x24); w2(0x26); w2(4);
|
||||
}
|
||||
WR(0x86,8);
|
||||
}
|
||||
|
||||
static void epia_disconnect ( PIA *pi )
|
||||
|
||||
{ /* WR(0x84,0x10); */
|
||||
w0(pi->saved_r0);
|
||||
w2(1); w2(4);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static void epia_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, ph, a, b;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(0x81); w2(1); w2(3); w0(0xc1);
|
||||
ph = 1;
|
||||
for (k=0;k<count;k++) {
|
||||
w2(2+ph); a = r1();
|
||||
w2(4+ph); b = r1();
|
||||
buf[k] = j44(a,b);
|
||||
ph = 1 - ph;
|
||||
}
|
||||
w0(0); w2(4);
|
||||
break;
|
||||
|
||||
case 1: w0(0x91); w2(1); w0(0x10); w2(3);
|
||||
w0(0x51); w2(5); w0(0xd1);
|
||||
ph = 1;
|
||||
for (k=0;k<count;k++) {
|
||||
w2(4+ph);
|
||||
a = r1(); b = r2();
|
||||
buf[k] = j53(a,b);
|
||||
ph = 1 - ph;
|
||||
}
|
||||
w0(0); w2(4);
|
||||
break;
|
||||
|
||||
case 2: w0(0x89); w2(1); w2(0x23); w2(0x21);
|
||||
ph = 1;
|
||||
for (k=0;k<count;k++) {
|
||||
w2(0x24+ph);
|
||||
buf[k] = r0();
|
||||
ph = 1 - ph;
|
||||
}
|
||||
w2(6); w2(4);
|
||||
break;
|
||||
|
||||
case 3: if (count > 512) WR(0x84,3);
|
||||
w3(0); w2(0x24);
|
||||
for (k=0;k<count;k++) buf[k] = r4();
|
||||
w2(4); WR(0x84,0);
|
||||
break;
|
||||
|
||||
case 4: if (count > 512) WR(0x84,3);
|
||||
w3(0); w2(0x24);
|
||||
for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
|
||||
w2(4); WR(0x84,0);
|
||||
break;
|
||||
|
||||
case 5: if (count > 512) WR(0x84,3);
|
||||
w3(0); w2(0x24);
|
||||
for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
|
||||
w2(4); WR(0x84,0);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void epia_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int ph, k, last, d;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
case 2: w0(0xa1); w2(1); w2(3); w2(1); w2(5);
|
||||
ph = 0; last = 0x8000;
|
||||
for (k=0;k<count;k++) {
|
||||
d = buf[k];
|
||||
if (d != last) { last = d; w0(d); }
|
||||
w2(4+ph);
|
||||
ph = 1 - ph;
|
||||
}
|
||||
w2(7); w2(4);
|
||||
break;
|
||||
|
||||
case 3: if (count < 512) WR(0x84,1);
|
||||
w3(0x40);
|
||||
for (k=0;k<count;k++) w4(buf[k]);
|
||||
if (count < 512) WR(0x84,0);
|
||||
break;
|
||||
|
||||
case 4: if (count < 512) WR(0x84,1);
|
||||
w3(0x40);
|
||||
for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
|
||||
if (count < 512) WR(0x84,0);
|
||||
break;
|
||||
|
||||
case 5: if (count < 512) WR(0x84,1);
|
||||
w3(0x40);
|
||||
for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
|
||||
if (count < 512) WR(0x84,0);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int epia_test_proto( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ int j, k, f;
|
||||
int e[2] = {0,0};
|
||||
|
||||
epia_connect(pi);
|
||||
for (j=0;j<2;j++) {
|
||||
WR(6,0xa0+j*0x10);
|
||||
for (k=0;k<256;k++) {
|
||||
WR(2,k^0xaa);
|
||||
WR(3,k^0x55);
|
||||
if (RR(2) != (k^0xaa)) e[j]++;
|
||||
}
|
||||
WR(2,1); WR(3,1);
|
||||
}
|
||||
epia_disconnect(pi);
|
||||
|
||||
f = 0;
|
||||
epia_connect(pi);
|
||||
WR(0x84,8);
|
||||
epia_read_block(pi,scratch,512);
|
||||
for (k=0;k<256;k++) {
|
||||
if ((scratch[2*k] & 0xff) != ((k+1) & 0xff)) f++;
|
||||
if ((scratch[2*k+1] & 0xff) != ((-2-k) & 0xff)) f++;
|
||||
}
|
||||
WR(0x84,0);
|
||||
epia_disconnect(pi);
|
||||
|
||||
if (verbose) {
|
||||
printk("%s: epia: port 0x%x, mode %d, test=(%d,%d,%d)\n",
|
||||
pi->device,pi->port,pi->mode,e[0],e[1],f);
|
||||
}
|
||||
|
||||
return (e[0] && e[1]) || f;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void epia_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[6] = {"4-bit","5/3","8-bit",
|
||||
"EPP-8","EPP-16","EPP-32"};
|
||||
|
||||
printk("%s: epia %s, Shuttle EPIA at 0x%x, ",
|
||||
pi->device,EPIA_VERSION,pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol epia = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "epia",
|
||||
.max_mode = 6,
|
||||
.epp_first = 3,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = epia_write_regr,
|
||||
.read_regr = epia_read_regr,
|
||||
.write_block = epia_write_block,
|
||||
.read_block = epia_read_block,
|
||||
.connect = epia_connect,
|
||||
.disconnect = epia_disconnect,
|
||||
.test_proto = epia_test_proto,
|
||||
.log_adapter = epia_log_adapter,
|
||||
};
|
||||
|
||||
static int __init epia_init(void)
|
||||
{
|
||||
return paride_register(&epia);
|
||||
}
|
||||
|
||||
static void __exit epia_exit(void)
|
||||
{
|
||||
paride_unregister(&epia);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(epia_init)
|
||||
module_exit(epia_exit)
|
||||
151
drivers/block/paride/fit2.c
Normal file
151
drivers/block/paride/fit2.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
fit2.c (c) 1998 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
fit2.c is a low-level protocol driver for the older version
|
||||
of the Fidelity International Technology parallel port adapter.
|
||||
This adapter is used in their TransDisk 2000 and older TransDisk
|
||||
3000 portable hard-drives. As far as I can tell, this device
|
||||
supports 4-bit mode _only_.
|
||||
|
||||
Newer models of the FIT products use an enhanced protocol.
|
||||
The "fit3" protocol module should support current drives.
|
||||
|
||||
*/
|
||||
|
||||
#define FIT2_VERSION "1.0"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
|
||||
NB: The FIT adapter does not appear to use the control registers.
|
||||
So, we map ALT_STATUS to STATUS and NO-OP writes to the device
|
||||
control register - this means that IDE reset will not work on these
|
||||
devices.
|
||||
|
||||
*/
|
||||
|
||||
static void fit2_write_regr( PIA *pi, int cont, int regr, int val)
|
||||
|
||||
{ if (cont == 1) return;
|
||||
w2(0xc); w0(regr); w2(4); w0(val); w2(5); w0(0); w2(4);
|
||||
}
|
||||
|
||||
static int fit2_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int a, b, r;
|
||||
|
||||
if (cont) {
|
||||
if (regr != 6) return 0xff;
|
||||
r = 7;
|
||||
} else r = regr + 0x10;
|
||||
|
||||
w2(0xc); w0(r); w2(4); w2(5);
|
||||
w0(0); a = r1();
|
||||
w0(1); b = r1();
|
||||
w2(4);
|
||||
|
||||
return j44(a,b);
|
||||
|
||||
}
|
||||
|
||||
static void fit2_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, a, b, c, d;
|
||||
|
||||
w2(0xc); w0(0x10);
|
||||
|
||||
for (k=0;k<count/4;k++) {
|
||||
|
||||
w2(4); w2(5);
|
||||
w0(0); a = r1(); w0(1); b = r1();
|
||||
w0(3); c = r1(); w0(2); d = r1();
|
||||
buf[4*k+0] = j44(a,b);
|
||||
buf[4*k+1] = j44(d,c);
|
||||
|
||||
w2(4); w2(5);
|
||||
a = r1(); w0(3); b = r1();
|
||||
w0(1); c = r1(); w0(0); d = r1();
|
||||
buf[4*k+2] = j44(d,c);
|
||||
buf[4*k+3] = j44(a,b);
|
||||
|
||||
}
|
||||
|
||||
w2(4);
|
||||
|
||||
}
|
||||
|
||||
static void fit2_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
|
||||
w2(0xc); w0(0);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w2(4); w0(buf[2*k]);
|
||||
w2(5); w0(buf[2*k+1]);
|
||||
}
|
||||
w2(4);
|
||||
}
|
||||
|
||||
static void fit2_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
w2(0xcc);
|
||||
}
|
||||
|
||||
static void fit2_disconnect ( PIA *pi )
|
||||
|
||||
{ w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static void fit2_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ printk("%s: fit2 %s, FIT 2000 adapter at 0x%x, delay %d\n",
|
||||
pi->device,FIT2_VERSION,pi->port,pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol fit2 = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "fit2",
|
||||
.max_mode = 1,
|
||||
.epp_first = 2,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = fit2_write_regr,
|
||||
.read_regr = fit2_read_regr,
|
||||
.write_block = fit2_write_block,
|
||||
.read_block = fit2_read_block,
|
||||
.connect = fit2_connect,
|
||||
.disconnect = fit2_disconnect,
|
||||
.log_adapter = fit2_log_adapter,
|
||||
};
|
||||
|
||||
static int __init fit2_init(void)
|
||||
{
|
||||
return paride_register(&fit2);
|
||||
}
|
||||
|
||||
static void __exit fit2_exit(void)
|
||||
{
|
||||
paride_unregister(&fit2);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(fit2_init)
|
||||
module_exit(fit2_exit)
|
||||
211
drivers/block/paride/fit3.c
Normal file
211
drivers/block/paride/fit3.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
fit3.c (c) 1998 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
fit3.c is a low-level protocol driver for newer models
|
||||
of the Fidelity International Technology parallel port adapter.
|
||||
This adapter is used in their TransDisk 3000 portable
|
||||
hard-drives, as well as CD-ROM, PD-CD and other devices.
|
||||
|
||||
The TD-2000 and certain older devices use a different protocol.
|
||||
Try the fit2 protocol module with them.
|
||||
|
||||
NB: The FIT adapters do not appear to support the control
|
||||
registers. So, we map ALT_STATUS to STATUS and NO-OP writes
|
||||
to the device control register - this means that IDE reset
|
||||
will not work on these devices.
|
||||
|
||||
*/
|
||||
|
||||
#define FIT3_VERSION "1.0"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0))
|
||||
|
||||
#define w7(byte) {out_p(7,byte);}
|
||||
#define r7() (in_p(7) & 0xff)
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
|
||||
*/
|
||||
|
||||
static void fit3_write_regr( PIA *pi, int cont, int regr, int val)
|
||||
|
||||
{ if (cont == 1) return;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: w2(0xc); w0(regr); w2(0x8); w2(0xc);
|
||||
w0(val); w2(0xd);
|
||||
w0(0); w2(0xc);
|
||||
break;
|
||||
|
||||
case 2: w2(0xc); w0(regr); w2(0x8); w2(0xc);
|
||||
w4(val); w4(0);
|
||||
w2(0xc);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int fit3_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int a, b;
|
||||
|
||||
if (cont) {
|
||||
if (regr != 6) return 0xff;
|
||||
regr = 7;
|
||||
}
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w2(0xc); w0(regr + 0x10); w2(0x8); w2(0xc);
|
||||
w2(0xd); a = r1();
|
||||
w2(0xf); b = r1();
|
||||
w2(0xc);
|
||||
return j44(a,b);
|
||||
|
||||
case 1: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
|
||||
w2(0xec); w2(0xee); w2(0xef); a = r0();
|
||||
w2(0xc);
|
||||
return a;
|
||||
|
||||
case 2: w2(0xc); w0(regr + 0x90); w2(0x8); w2(0xc);
|
||||
w2(0xec);
|
||||
a = r4(); b = r4();
|
||||
w2(0xc);
|
||||
return a;
|
||||
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
static void fit3_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, a, b, c, d;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w2(0xc); w0(0x10); w2(0x8); w2(0xc);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w2(0xd); a = r1();
|
||||
w2(0xf); b = r1();
|
||||
w2(0xc); c = r1();
|
||||
w2(0xe); d = r1();
|
||||
buf[2*k ] = j44(a,b);
|
||||
buf[2*k+1] = j44(c,d);
|
||||
}
|
||||
w2(0xc);
|
||||
break;
|
||||
|
||||
case 1: w2(0xc); w0(0x90); w2(0x8); w2(0xc);
|
||||
w2(0xec); w2(0xee);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w2(0xef); a = r0();
|
||||
w2(0xee); b = r0();
|
||||
buf[2*k ] = a;
|
||||
buf[2*k+1] = b;
|
||||
}
|
||||
w2(0xec);
|
||||
w2(0xc);
|
||||
break;
|
||||
|
||||
case 2: w2(0xc); w0(0x90); w2(0x8); w2(0xc);
|
||||
w2(0xec);
|
||||
for (k=0;k<count;k++) buf[k] = r4();
|
||||
w2(0xc);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void fit3_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: w2(0xc); w0(0); w2(0x8); w2(0xc);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w0(buf[2*k ]); w2(0xd);
|
||||
w0(buf[2*k+1]); w2(0xc);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: w2(0xc); w0(0); w2(0x8); w2(0xc);
|
||||
for (k=0;k<count;k++) w4(buf[k]);
|
||||
w2(0xc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void fit3_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
w2(0xc); w0(0); w2(0xa);
|
||||
if (pi->mode == 2) {
|
||||
w2(0xc); w0(0x9); w2(0x8); w2(0xc);
|
||||
}
|
||||
}
|
||||
|
||||
static void fit3_disconnect ( PIA *pi )
|
||||
|
||||
{ w2(0xc); w0(0xa); w2(0x8); w2(0xc);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static void fit3_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[3] = {"4-bit","8-bit","EPP"};
|
||||
|
||||
printk("%s: fit3 %s, FIT 3000 adapter at 0x%x, "
|
||||
"mode %d (%s), delay %d\n",
|
||||
pi->device,FIT3_VERSION,pi->port,
|
||||
pi->mode,mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol fit3 = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "fit3",
|
||||
.max_mode = 3,
|
||||
.epp_first = 2,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = fit3_write_regr,
|
||||
.read_regr = fit3_read_regr,
|
||||
.write_block = fit3_write_block,
|
||||
.read_block = fit3_read_block,
|
||||
.connect = fit3_connect,
|
||||
.disconnect = fit3_disconnect,
|
||||
.log_adapter = fit3_log_adapter,
|
||||
};
|
||||
|
||||
static int __init fit3_init(void)
|
||||
{
|
||||
return paride_register(&fit3);
|
||||
}
|
||||
|
||||
static void __exit fit3_exit(void)
|
||||
{
|
||||
paride_unregister(&fit3);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(fit3_init)
|
||||
module_exit(fit3_exit)
|
||||
276
drivers/block/paride/friq.c
Normal file
276
drivers/block/paride/friq.c
Normal file
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
friq.c (c) 1998 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License
|
||||
|
||||
friq.c is a low-level protocol driver for the Freecom "IQ"
|
||||
parallel port IDE adapter. Early versions of this adapter
|
||||
use the 'frpw' protocol.
|
||||
|
||||
Freecom uses this adapter in a battery powered external
|
||||
CD-ROM drive. It is also used in LS-120 drives by
|
||||
Maxell and Panasonic, and other devices.
|
||||
|
||||
The battery powered drive requires software support to
|
||||
control the power to the drive. This module enables the
|
||||
drive power when the high level driver (pcd) is loaded
|
||||
and disables it when the module is unloaded. Note, if
|
||||
the friq module is built in to the kernel, the power
|
||||
will never be switched off, so other means should be
|
||||
used to conserve battery power.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.12.20 Added support for soft power switch
|
||||
*/
|
||||
|
||||
#define FRIQ_VERSION "1.01"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define CMD(x) w2(4);w0(0xff);w0(0xff);w0(0x73);w0(0x73);\
|
||||
w0(0xc9);w0(0xc9);w0(0x26);w0(0x26);w0(x);w0(x);
|
||||
|
||||
#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0))
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
*/
|
||||
|
||||
static int cont_map[2] = { 0x08, 0x10 };
|
||||
|
||||
static int friq_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int h,l,r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
CMD(r);
|
||||
w2(6); l = r1();
|
||||
w2(4); h = r1();
|
||||
w2(4);
|
||||
|
||||
return j44(l,h);
|
||||
|
||||
}
|
||||
|
||||
static void friq_write_regr( PIA *pi, int cont, int regr, int val)
|
||||
|
||||
{ int r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
CMD(r);
|
||||
w0(val);
|
||||
w2(5);w2(7);w2(5);w2(4);
|
||||
}
|
||||
|
||||
static void friq_read_block_int( PIA *pi, char * buf, int count, int regr )
|
||||
|
||||
{ int h, l, k, ph;
|
||||
|
||||
switch(pi->mode) {
|
||||
|
||||
case 0: CMD(regr);
|
||||
for (k=0;k<count;k++) {
|
||||
w2(6); l = r1();
|
||||
w2(4); h = r1();
|
||||
buf[k] = j44(l,h);
|
||||
}
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 1: ph = 2;
|
||||
CMD(regr+0xc0);
|
||||
w0(0xff);
|
||||
for (k=0;k<count;k++) {
|
||||
w2(0xa4 + ph);
|
||||
buf[k] = r0();
|
||||
ph = 2 - ph;
|
||||
}
|
||||
w2(0xac); w2(0xa4); w2(4);
|
||||
break;
|
||||
|
||||
case 2: CMD(regr+0x80);
|
||||
for (k=0;k<count-2;k++) buf[k] = r4();
|
||||
w2(0xac); w2(0xa4);
|
||||
buf[count-2] = r4();
|
||||
buf[count-1] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 3: CMD(regr+0x80);
|
||||
for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
|
||||
w2(0xac); w2(0xa4);
|
||||
buf[count-2] = r4();
|
||||
buf[count-1] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 4: CMD(regr+0x80);
|
||||
for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
|
||||
buf[count-4] = r4();
|
||||
buf[count-3] = r4();
|
||||
w2(0xac); w2(0xa4);
|
||||
buf[count-2] = r4();
|
||||
buf[count-1] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void friq_read_block( PIA *pi, char * buf, int count)
|
||||
|
||||
{ friq_read_block_int(pi,buf,count,0x08);
|
||||
}
|
||||
|
||||
static void friq_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
switch(pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: CMD(8); w2(5);
|
||||
for (k=0;k<count;k++) {
|
||||
w0(buf[k]);
|
||||
w2(7);w2(5);
|
||||
}
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 2: CMD(0xc8); w2(5);
|
||||
for (k=0;k<count;k++) w4(buf[k]);
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 3: CMD(0xc8); w2(5);
|
||||
for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 4: CMD(0xc8); w2(5);
|
||||
for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
|
||||
w2(4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void friq_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
w2(4);
|
||||
}
|
||||
|
||||
static void friq_disconnect ( PIA *pi )
|
||||
|
||||
{ CMD(0x20);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static int friq_test_proto( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ int j, k, r;
|
||||
int e[2] = {0,0};
|
||||
|
||||
pi->saved_r0 = r0();
|
||||
w0(0xff); udelay(20); CMD(0x3d); /* turn the power on */
|
||||
udelay(500);
|
||||
w0(pi->saved_r0);
|
||||
|
||||
friq_connect(pi);
|
||||
for (j=0;j<2;j++) {
|
||||
friq_write_regr(pi,0,6,0xa0+j*0x10);
|
||||
for (k=0;k<256;k++) {
|
||||
friq_write_regr(pi,0,2,k^0xaa);
|
||||
friq_write_regr(pi,0,3,k^0x55);
|
||||
if (friq_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
|
||||
}
|
||||
}
|
||||
friq_disconnect(pi);
|
||||
|
||||
friq_connect(pi);
|
||||
friq_read_block_int(pi,scratch,512,0x10);
|
||||
r = 0;
|
||||
for (k=0;k<128;k++) if (scratch[k] != k) r++;
|
||||
friq_disconnect(pi);
|
||||
|
||||
if (verbose) {
|
||||
printk("%s: friq: port 0x%x, mode %d, test=(%d,%d,%d)\n",
|
||||
pi->device,pi->port,pi->mode,e[0],e[1],r);
|
||||
}
|
||||
|
||||
return (r || (e[0] && e[1]));
|
||||
}
|
||||
|
||||
|
||||
static void friq_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[6] = {"4-bit","8-bit",
|
||||
"EPP-8","EPP-16","EPP-32"};
|
||||
|
||||
printk("%s: friq %s, Freecom IQ ASIC-2 adapter at 0x%x, ", pi->device,
|
||||
FRIQ_VERSION,pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
pi->private = 1;
|
||||
friq_connect(pi);
|
||||
CMD(0x9e); /* disable sleep timer */
|
||||
friq_disconnect(pi);
|
||||
|
||||
}
|
||||
|
||||
static void friq_release_proto( PIA *pi)
|
||||
{
|
||||
if (pi->private) { /* turn off the power */
|
||||
friq_connect(pi);
|
||||
CMD(0x1d); CMD(0x1e);
|
||||
friq_disconnect(pi);
|
||||
pi->private = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct pi_protocol friq = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "friq",
|
||||
.max_mode = 5,
|
||||
.epp_first = 2,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = friq_write_regr,
|
||||
.read_regr = friq_read_regr,
|
||||
.write_block = friq_write_block,
|
||||
.read_block = friq_read_block,
|
||||
.connect = friq_connect,
|
||||
.disconnect = friq_disconnect,
|
||||
.test_proto = friq_test_proto,
|
||||
.log_adapter = friq_log_adapter,
|
||||
.release_proto = friq_release_proto,
|
||||
};
|
||||
|
||||
static int __init friq_init(void)
|
||||
{
|
||||
return paride_register(&friq);
|
||||
}
|
||||
|
||||
static void __exit friq_exit(void)
|
||||
{
|
||||
paride_unregister(&friq);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(friq_init)
|
||||
module_exit(friq_exit)
|
||||
313
drivers/block/paride/frpw.c
Normal file
313
drivers/block/paride/frpw.c
Normal file
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
frpw.c (c) 1996-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License
|
||||
|
||||
frpw.c is a low-level protocol driver for the Freecom "Power"
|
||||
parallel port IDE adapter.
|
||||
|
||||
Some applications of this adapter may require a "printer" reset
|
||||
prior to loading the driver. This can be done by loading and
|
||||
unloading the "lp" driver, or it can be done by this driver
|
||||
if you define FRPW_HARD_RESET. The latter is not recommended
|
||||
as it may upset devices on other ports.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.06 init_proto, release_proto
|
||||
fix chip detect
|
||||
added EPP-16 and EPP-32
|
||||
1.02 GRG 1998.09.23 added hard reset to initialisation process
|
||||
1.03 GRG 1998.12.14 made hard reset conditional
|
||||
|
||||
*/
|
||||
|
||||
#define FRPW_VERSION "1.03"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
|
||||
#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0))
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
*/
|
||||
|
||||
static int cont_map[2] = { 0x08, 0x10 };
|
||||
|
||||
static int frpw_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int h,l,r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
w2(4);
|
||||
w0(r); cec4;
|
||||
w2(6); l = r1();
|
||||
w2(4); h = r1();
|
||||
w2(4);
|
||||
|
||||
return j44(l,h);
|
||||
|
||||
}
|
||||
|
||||
static void frpw_write_regr( PIA *pi, int cont, int regr, int val)
|
||||
|
||||
{ int r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
w2(4); w0(r); cec4;
|
||||
w0(val);
|
||||
w2(5);w2(7);w2(5);w2(4);
|
||||
}
|
||||
|
||||
static void frpw_read_block_int( PIA *pi, char * buf, int count, int regr )
|
||||
|
||||
{ int h, l, k, ph;
|
||||
|
||||
switch(pi->mode) {
|
||||
|
||||
case 0: w2(4); w0(regr); cec4;
|
||||
for (k=0;k<count;k++) {
|
||||
w2(6); l = r1();
|
||||
w2(4); h = r1();
|
||||
buf[k] = j44(l,h);
|
||||
}
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 1: ph = 2;
|
||||
w2(4); w0(regr + 0xc0); cec4;
|
||||
w0(0xff);
|
||||
for (k=0;k<count;k++) {
|
||||
w2(0xa4 + ph);
|
||||
buf[k] = r0();
|
||||
ph = 2 - ph;
|
||||
}
|
||||
w2(0xac); w2(0xa4); w2(4);
|
||||
break;
|
||||
|
||||
case 2: w2(4); w0(regr + 0x80); cec4;
|
||||
for (k=0;k<count;k++) buf[k] = r4();
|
||||
w2(0xac); w2(0xa4);
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 3: w2(4); w0(regr + 0x80); cec4;
|
||||
for (k=0;k<count-2;k++) buf[k] = r4();
|
||||
w2(0xac); w2(0xa4);
|
||||
buf[count-2] = r4();
|
||||
buf[count-1] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 4: w2(4); w0(regr + 0x80); cec4;
|
||||
for (k=0;k<(count/2)-1;k++) ((u16 *)buf)[k] = r4w();
|
||||
w2(0xac); w2(0xa4);
|
||||
buf[count-2] = r4();
|
||||
buf[count-1] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 5: w2(4); w0(regr + 0x80); cec4;
|
||||
for (k=0;k<(count/4)-1;k++) ((u32 *)buf)[k] = r4l();
|
||||
buf[count-4] = r4();
|
||||
buf[count-3] = r4();
|
||||
w2(0xac); w2(0xa4);
|
||||
buf[count-2] = r4();
|
||||
buf[count-1] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void frpw_read_block( PIA *pi, char * buf, int count)
|
||||
|
||||
{ frpw_read_block_int(pi,buf,count,0x08);
|
||||
}
|
||||
|
||||
static void frpw_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
switch(pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
case 2: w2(4); w0(8); cec4; w2(5);
|
||||
for (k=0;k<count;k++) {
|
||||
w0(buf[k]);
|
||||
w2(7);w2(5);
|
||||
}
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 3: w2(4); w0(0xc8); cec4; w2(5);
|
||||
for (k=0;k<count;k++) w4(buf[k]);
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 4: w2(4); w0(0xc8); cec4; w2(5);
|
||||
for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 5: w2(4); w0(0xc8); cec4; w2(5);
|
||||
for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
|
||||
w2(4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void frpw_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
w2(4);
|
||||
}
|
||||
|
||||
static void frpw_disconnect ( PIA *pi )
|
||||
|
||||
{ w2(4); w0(0x20); cec4;
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
/* Stub logic to see if PNP string is available - used to distinguish
|
||||
between the Xilinx and ASIC implementations of the Freecom adapter.
|
||||
*/
|
||||
|
||||
static int frpw_test_pnp ( PIA *pi )
|
||||
|
||||
/* returns chip_type: 0 = Xilinx, 1 = ASIC */
|
||||
|
||||
{ int olddelay, a, b;
|
||||
|
||||
#ifdef FRPW_HARD_RESET
|
||||
w0(0); w2(8); udelay(50); w2(0xc); /* parallel bus reset */
|
||||
mdelay(1500);
|
||||
#endif
|
||||
|
||||
olddelay = pi->delay;
|
||||
pi->delay = 10;
|
||||
|
||||
pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
|
||||
w2(4); w0(4); w2(6); w2(7);
|
||||
a = r1() & 0xff; w2(4); b = r1() & 0xff;
|
||||
w2(0xc); w2(0xe); w2(4);
|
||||
|
||||
pi->delay = olddelay;
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
|
||||
return ((~a&0x40) && (b&0x40));
|
||||
}
|
||||
|
||||
/* We use the pi->private to remember the result of the PNP test.
|
||||
To make this work, private = port*2 + chip. Yes, I know it's
|
||||
a hack :-(
|
||||
*/
|
||||
|
||||
static int frpw_test_proto( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ int j, k, r;
|
||||
int e[2] = {0,0};
|
||||
|
||||
if ((pi->private>>1) != pi->port)
|
||||
pi->private = frpw_test_pnp(pi) + 2*pi->port;
|
||||
|
||||
if (((pi->private%2) == 0) && (pi->mode > 2)) {
|
||||
if (verbose)
|
||||
printk("%s: frpw: Xilinx does not support mode %d\n",
|
||||
pi->device, pi->mode);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (((pi->private%2) == 1) && (pi->mode == 2)) {
|
||||
if (verbose)
|
||||
printk("%s: frpw: ASIC does not support mode 2\n",
|
||||
pi->device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
frpw_connect(pi);
|
||||
for (j=0;j<2;j++) {
|
||||
frpw_write_regr(pi,0,6,0xa0+j*0x10);
|
||||
for (k=0;k<256;k++) {
|
||||
frpw_write_regr(pi,0,2,k^0xaa);
|
||||
frpw_write_regr(pi,0,3,k^0x55);
|
||||
if (frpw_read_regr(pi,0,2) != (k^0xaa)) e[j]++;
|
||||
}
|
||||
}
|
||||
frpw_disconnect(pi);
|
||||
|
||||
frpw_connect(pi);
|
||||
frpw_read_block_int(pi,scratch,512,0x10);
|
||||
r = 0;
|
||||
for (k=0;k<128;k++) if (scratch[k] != k) r++;
|
||||
frpw_disconnect(pi);
|
||||
|
||||
if (verbose) {
|
||||
printk("%s: frpw: port 0x%x, chip %ld, mode %d, test=(%d,%d,%d)\n",
|
||||
pi->device,pi->port,(pi->private%2),pi->mode,e[0],e[1],r);
|
||||
}
|
||||
|
||||
return (r || (e[0] && e[1]));
|
||||
}
|
||||
|
||||
|
||||
static void frpw_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[6] = {"4-bit","8-bit","EPP",
|
||||
"EPP-8","EPP-16","EPP-32"};
|
||||
|
||||
printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device,
|
||||
FRPW_VERSION,((pi->private%2) == 0)?"Xilinx":"ASIC",pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol frpw = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "frpw",
|
||||
.max_mode = 6,
|
||||
.epp_first = 2,
|
||||
.default_delay = 2,
|
||||
.max_units = 1,
|
||||
.write_regr = frpw_write_regr,
|
||||
.read_regr = frpw_read_regr,
|
||||
.write_block = frpw_write_block,
|
||||
.read_block = frpw_read_block,
|
||||
.connect = frpw_connect,
|
||||
.disconnect = frpw_disconnect,
|
||||
.test_proto = frpw_test_proto,
|
||||
.log_adapter = frpw_log_adapter,
|
||||
};
|
||||
|
||||
static int __init frpw_init(void)
|
||||
{
|
||||
return paride_register(&frpw);
|
||||
}
|
||||
|
||||
static void __exit frpw_exit(void)
|
||||
{
|
||||
paride_unregister(&frpw);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(frpw_init)
|
||||
module_exit(frpw_exit)
|
||||
305
drivers/block/paride/kbic.c
Normal file
305
drivers/block/paride/kbic.c
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
kbic.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
This is a low-level driver for the KBIC-951A and KBIC-971A
|
||||
parallel to IDE adapter chips from KingByte Information Systems.
|
||||
|
||||
The chips are almost identical, however, the wakeup code
|
||||
required for the 971A interferes with the correct operation of
|
||||
the 951A, so this driver registers itself twice, once for
|
||||
each chip.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.06 init_proto, release_proto
|
||||
|
||||
*/
|
||||
|
||||
#define KBIC_VERSION "1.01"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define r12w() (delay_p,inw(pi->port+1)&0xffff)
|
||||
|
||||
#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
|
||||
#define j53(w) (((w>>3)&0x1f)|((w>>4)&0xe0))
|
||||
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
*/
|
||||
|
||||
static int cont_map[2] = { 0x80, 0x40 };
|
||||
|
||||
static int kbic_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int a, b, s;
|
||||
|
||||
s = cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(regr|0x18|s); w2(4); w2(6); w2(4); w2(1); w0(8);
|
||||
a = r1(); w0(0x28); b = r1(); w2(4);
|
||||
return j44(a,b);
|
||||
|
||||
case 1: w0(regr|0x38|s); w2(4); w2(6); w2(4); w2(5); w0(8);
|
||||
a = r12w(); w2(4);
|
||||
return j53(a);
|
||||
|
||||
case 2: w0(regr|0x08|s); w2(4); w2(6); w2(4); w2(0xa5); w2(0xa1);
|
||||
a = r0(); w2(4);
|
||||
return a;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
|
||||
a = r4(); b = r4(); w2(4); w2(0); w2(4);
|
||||
return a;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void kbic_write_regr( PIA *pi, int cont, int regr, int val)
|
||||
|
||||
{ int s;
|
||||
|
||||
s = cont_map[cont];
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
case 2: w0(regr|0x10|s); w2(4); w2(6); w2(4);
|
||||
w0(val); w2(5); w2(4);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
case 5: w0(0x20|s); w2(4); w2(6); w2(4); w3(regr);
|
||||
w4(val); w4(val);
|
||||
w2(4); w2(0); w2(4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void k951_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
w2(4);
|
||||
}
|
||||
|
||||
static void k951_disconnect ( PIA *pi )
|
||||
|
||||
{ w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
#define CCP(x) w2(0xc4);w0(0xaa);w0(0x55);w0(0);w0(0xff);w0(0x87);\
|
||||
w0(0x78);w0(x);w2(0xc5);w2(0xc4);w0(0xff);
|
||||
|
||||
static void k971_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
CCP(0x20);
|
||||
w2(4);
|
||||
}
|
||||
|
||||
static void k971_disconnect ( PIA *pi )
|
||||
|
||||
{ CCP(0x30);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
/* counts must be congruent to 0 MOD 4, but all known applications
|
||||
have this property.
|
||||
*/
|
||||
|
||||
static void kbic_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, a, b;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(0x98); w2(4); w2(6); w2(4);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w2(1); w0(8); a = r1();
|
||||
w0(0x28); b = r1();
|
||||
buf[2*k] = j44(a,b);
|
||||
w2(5); b = r1();
|
||||
w0(8); a = r1();
|
||||
buf[2*k+1] = j44(a,b);
|
||||
w2(4);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: w0(0xb8); w2(4); w2(6); w2(4);
|
||||
for (k=0;k<count/4;k++) {
|
||||
w0(0xb8);
|
||||
w2(4); w2(5);
|
||||
w0(8); buf[4*k] = j53(r12w());
|
||||
w0(0xb8); buf[4*k+1] = j53(r12w());
|
||||
w2(4); w2(5);
|
||||
buf[4*k+3] = j53(r12w());
|
||||
w0(8); buf[4*k+2] = j53(r12w());
|
||||
}
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 2: w0(0x88); w2(4); w2(6); w2(4);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w2(0xa0); w2(0xa1); buf[2*k] = r0();
|
||||
w2(0xa5); buf[2*k+1] = r0();
|
||||
}
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
|
||||
for (k=0;k<count;k++) buf[k] = r4();
|
||||
w2(4); w2(0); w2(4);
|
||||
break;
|
||||
|
||||
case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
|
||||
for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
|
||||
w2(4); w2(0); w2(4);
|
||||
break;
|
||||
|
||||
case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
|
||||
for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
|
||||
w2(4); w2(0); w2(4);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void kbic_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
case 2: w0(0x90); w2(4); w2(6); w2(4);
|
||||
for(k=0;k<count/2;k++) {
|
||||
w0(buf[2*k+1]); w2(0); w2(4);
|
||||
w0(buf[2*k]); w2(5); w2(4);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
|
||||
for(k=0;k<count/2;k++) {
|
||||
w4(buf[2*k+1]);
|
||||
w4(buf[2*k]);
|
||||
}
|
||||
w2(4); w2(0); w2(4);
|
||||
break;
|
||||
|
||||
case 4: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
|
||||
for(k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
|
||||
w2(4); w2(0); w2(4);
|
||||
break;
|
||||
|
||||
case 5: w0(0xa0); w2(4); w2(6); w2(4); w3(0);
|
||||
for(k=0;k<count/4;k++) w4l(pi_swab32(buf,k));
|
||||
w2(4); w2(0); w2(4);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void kbic_log_adapter( PIA *pi, char * scratch,
|
||||
int verbose, char * chip )
|
||||
|
||||
{ char *mode_string[6] = {"4-bit","5/3","8-bit",
|
||||
"EPP-8","EPP_16","EPP-32"};
|
||||
|
||||
printk("%s: kbic %s, KingByte %s at 0x%x, ",
|
||||
pi->device,KBIC_VERSION,chip,pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static void k951_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ kbic_log_adapter(pi,scratch,verbose,"KBIC-951A");
|
||||
}
|
||||
|
||||
static void k971_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ kbic_log_adapter(pi,scratch,verbose,"KBIC-971A");
|
||||
}
|
||||
|
||||
static struct pi_protocol k951 = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "k951",
|
||||
.max_mode = 6,
|
||||
.epp_first = 3,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = kbic_write_regr,
|
||||
.read_regr = kbic_read_regr,
|
||||
.write_block = kbic_write_block,
|
||||
.read_block = kbic_read_block,
|
||||
.connect = k951_connect,
|
||||
.disconnect = k951_disconnect,
|
||||
.log_adapter = k951_log_adapter,
|
||||
};
|
||||
|
||||
static struct pi_protocol k971 = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "k971",
|
||||
.max_mode = 6,
|
||||
.epp_first = 3,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = kbic_write_regr,
|
||||
.read_regr = kbic_read_regr,
|
||||
.write_block = kbic_write_block,
|
||||
.read_block = kbic_read_block,
|
||||
.connect = k971_connect,
|
||||
.disconnect = k971_disconnect,
|
||||
.log_adapter = k971_log_adapter,
|
||||
};
|
||||
|
||||
static int __init kbic_init(void)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = paride_register(&k951);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
rv = paride_register(&k971);
|
||||
if (rv < 0)
|
||||
paride_unregister(&k951);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void __exit kbic_exit(void)
|
||||
{
|
||||
paride_unregister(&k951);
|
||||
paride_unregister(&k971);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(kbic_init)
|
||||
module_exit(kbic_exit)
|
||||
128
drivers/block/paride/ktti.c
Normal file
128
drivers/block/paride/ktti.c
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
ktti.c (c) 1998 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
ktti.c is a low-level protocol driver for the KT Technology
|
||||
parallel port adapter. This adapter is used in the "PHd"
|
||||
portable hard-drives. As far as I can tell, this device
|
||||
supports 4-bit mode _only_.
|
||||
|
||||
*/
|
||||
|
||||
#define KTTI_VERSION "1.0"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
*/
|
||||
|
||||
static int cont_map[2] = { 0x10, 0x08 };
|
||||
|
||||
static void ktti_write_regr( PIA *pi, int cont, int regr, int val)
|
||||
|
||||
{ int r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
w0(r); w2(0xb); w2(0xa); w2(3); w2(6);
|
||||
w0(val); w2(3); w0(0); w2(6); w2(0xb);
|
||||
}
|
||||
|
||||
static int ktti_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int a, b, r;
|
||||
|
||||
r = regr + cont_map[cont];
|
||||
|
||||
w0(r); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
|
||||
a = r1(); w2(0xc); b = r1(); w2(9); w2(0xc); w2(9);
|
||||
return j44(a,b);
|
||||
|
||||
}
|
||||
|
||||
static void ktti_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, a, b;
|
||||
|
||||
for (k=0;k<count/2;k++) {
|
||||
w0(0x10); w2(0xb); w2(0xa); w2(9); w2(0xc); w2(9);
|
||||
a = r1(); w2(0xc); b = r1(); w2(9);
|
||||
buf[2*k] = j44(a,b);
|
||||
a = r1(); w2(0xc); b = r1(); w2(9);
|
||||
buf[2*k+1] = j44(a,b);
|
||||
}
|
||||
}
|
||||
|
||||
static void ktti_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
for (k=0;k<count/2;k++) {
|
||||
w0(0x10); w2(0xb); w2(0xa); w2(3); w2(6);
|
||||
w0(buf[2*k]); w2(3);
|
||||
w0(buf[2*k+1]); w2(6);
|
||||
w2(0xb);
|
||||
}
|
||||
}
|
||||
|
||||
static void ktti_connect ( PIA *pi )
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
w2(0xb); w2(0xa); w0(0); w2(3); w2(6);
|
||||
}
|
||||
|
||||
static void ktti_disconnect ( PIA *pi )
|
||||
|
||||
{ w2(0xb); w2(0xa); w0(0xa0); w2(3); w2(4);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static void ktti_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ printk("%s: ktti %s, KT adapter at 0x%x, delay %d\n",
|
||||
pi->device,KTTI_VERSION,pi->port,pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol ktti = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ktti",
|
||||
.max_mode = 1,
|
||||
.epp_first = 2,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = ktti_write_regr,
|
||||
.read_regr = ktti_read_regr,
|
||||
.write_block = ktti_write_block,
|
||||
.read_block = ktti_read_block,
|
||||
.connect = ktti_connect,
|
||||
.disconnect = ktti_disconnect,
|
||||
.log_adapter = ktti_log_adapter,
|
||||
};
|
||||
|
||||
static int __init ktti_init(void)
|
||||
{
|
||||
return paride_register(&ktti);
|
||||
}
|
||||
|
||||
static void __exit ktti_exit(void)
|
||||
{
|
||||
paride_unregister(&ktti);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(ktti_init)
|
||||
module_exit(ktti_exit)
|
||||
30
drivers/block/paride/mkd
Normal file
30
drivers/block/paride/mkd
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# mkd -- a script to create the device special files for the PARIDE subsystem
|
||||
#
|
||||
# block devices: pd (45), pcd (46), pf (47)
|
||||
# character devices: pt (96), pg (97)
|
||||
#
|
||||
function mkdev {
|
||||
mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1
|
||||
}
|
||||
#
|
||||
function pd {
|
||||
D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) )
|
||||
mkdev pd$D b 45 $[ $1 * 16 ]
|
||||
for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
||||
do mkdev pd$D$P b 45 $[ $1 * 16 + $P ]
|
||||
done
|
||||
}
|
||||
#
|
||||
cd /dev
|
||||
#
|
||||
for u in 0 1 2 3 ; do pd $u ; done
|
||||
for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done
|
||||
for u in 0 1 2 3 ; do mkdev pf$u b 47 $u ; done
|
||||
for u in 0 1 2 3 ; do mkdev pt$u c 96 $u ; done
|
||||
for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done
|
||||
for u in 0 1 2 3 ; do mkdev pg$u c 97 $u ; done
|
||||
#
|
||||
# end of mkd
|
||||
|
||||
153
drivers/block/paride/on20.c
Normal file
153
drivers/block/paride/on20.c
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
on20.c (c) 1996-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
on20.c is a low-level protocol driver for the
|
||||
Onspec 90c20 parallel to IDE adapter.
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.06 init_proto, release_proto
|
||||
|
||||
*/
|
||||
|
||||
#define ON20_VERSION "1.01"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define op(f) w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
|
||||
#define vl(v) w2(4);w0(v);w2(5);w2(7);w2(5);w2(4);
|
||||
|
||||
#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
*/
|
||||
|
||||
static int on20_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int h,l, r ;
|
||||
|
||||
r = (regr<<2) + 1 + cont;
|
||||
|
||||
op(1); vl(r); op(0);
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w2(4); w2(6); l = r1();
|
||||
w2(4); w2(6); h = r1();
|
||||
w2(4); w2(6); w2(4); w2(6); w2(4);
|
||||
return j44(l,h);
|
||||
|
||||
case 1: w2(4); w2(0x26); r = r0();
|
||||
w2(4); w2(0x26); w2(4);
|
||||
return r;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void on20_write_regr( PIA *pi, int cont, int regr, int val )
|
||||
|
||||
{ int r;
|
||||
|
||||
r = (regr<<2) + 1 + cont;
|
||||
|
||||
op(1); vl(r);
|
||||
op(0); vl(val);
|
||||
op(0); vl(val);
|
||||
}
|
||||
|
||||
static void on20_connect ( PIA *pi)
|
||||
|
||||
{ pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
|
||||
w2(4);w0(0);w2(0xc);w2(4);w2(6);w2(4);w2(6);w2(4);
|
||||
if (pi->mode) { op(2); vl(8); op(2); vl(9); }
|
||||
else { op(2); vl(0); op(2); vl(8); }
|
||||
}
|
||||
|
||||
static void on20_disconnect ( PIA *pi )
|
||||
|
||||
{ w2(4);w0(7);w2(4);w2(0xc);w2(4);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
static void on20_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, l, h;
|
||||
|
||||
op(1); vl(1); op(0);
|
||||
|
||||
for (k=0;k<count;k++)
|
||||
if (pi->mode) {
|
||||
w2(4); w2(0x26); buf[k] = r0();
|
||||
} else {
|
||||
w2(6); l = r1(); w2(4);
|
||||
w2(6); h = r1(); w2(4);
|
||||
buf[k] = j44(l,h);
|
||||
}
|
||||
w2(4);
|
||||
}
|
||||
|
||||
static void on20_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
op(1); vl(1); op(0);
|
||||
|
||||
for (k=0;k<count;k++) { w2(5); w0(buf[k]); w2(7); }
|
||||
w2(4);
|
||||
}
|
||||
|
||||
static void on20_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[2] = {"4-bit","8-bit"};
|
||||
|
||||
printk("%s: on20 %s, OnSpec 90c20 at 0x%x, ",
|
||||
pi->device,ON20_VERSION,pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol on20 = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "on20",
|
||||
.max_mode = 2,
|
||||
.epp_first = 2,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = on20_write_regr,
|
||||
.read_regr = on20_read_regr,
|
||||
.write_block = on20_write_block,
|
||||
.read_block = on20_read_block,
|
||||
.connect = on20_connect,
|
||||
.disconnect = on20_disconnect,
|
||||
.log_adapter = on20_log_adapter,
|
||||
};
|
||||
|
||||
static int __init on20_init(void)
|
||||
{
|
||||
return paride_register(&on20);
|
||||
}
|
||||
|
||||
static void __exit on20_exit(void)
|
||||
{
|
||||
paride_unregister(&on20);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(on20_init)
|
||||
module_exit(on20_exit)
|
||||
319
drivers/block/paride/on26.c
Normal file
319
drivers/block/paride/on26.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
on26.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
on26.c is a low-level protocol driver for the
|
||||
OnSpec 90c26 parallel to IDE adapter chip.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.06 init_proto, release_proto
|
||||
1.02 GRG 1998.09.23 updates for the -E rev chip
|
||||
1.03 GRG 1998.12.14 fix for slave drives
|
||||
1.04 GRG 1998.12.20 yet another bug fix
|
||||
|
||||
*/
|
||||
|
||||
#define ON26_VERSION "1.04"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
/* mode codes: 0 nybble reads, 8-bit writes
|
||||
1 8-bit reads and writes
|
||||
2 8-bit EPP mode
|
||||
3 EPP-16
|
||||
4 EPP-32
|
||||
*/
|
||||
|
||||
#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
|
||||
|
||||
#define P1 w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
|
||||
#define P2 w2(5);w2(7);w2(5);w2(4);
|
||||
|
||||
/* cont = 0 - access the IDE register file
|
||||
cont = 1 - access the IDE command set
|
||||
*/
|
||||
|
||||
static int on26_read_regr( PIA *pi, int cont, int regr )
|
||||
|
||||
{ int a, b, r;
|
||||
|
||||
r = (regr<<2) + 1 + cont;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(1); P1; w0(r); P2; w0(0); P1;
|
||||
w2(6); a = r1(); w2(4);
|
||||
w2(6); b = r1(); w2(4);
|
||||
w2(6); w2(4); w2(6); w2(4);
|
||||
return j44(a,b);
|
||||
|
||||
case 1: w0(1); P1; w0(r); P2; w0(0); P1;
|
||||
w2(0x26); a = r0(); w2(4); w2(0x26); w2(4);
|
||||
return a;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
|
||||
w3(0); w3(0); w2(0x24); a = r4(); w2(4);
|
||||
w2(0x24); (void)r4(); w2(4);
|
||||
return a;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void on26_write_regr( PIA *pi, int cont, int regr, int val )
|
||||
|
||||
{ int r;
|
||||
|
||||
r = (regr<<2) + 1 + cont;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: w0(1); P1; w0(r); P2; w0(0); P1;
|
||||
w0(val); P2; w0(val); P2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: w3(1); w3(1); w2(5); w4(r); w2(4);
|
||||
w3(0); w3(0);
|
||||
w2(5); w4(val); w2(4);
|
||||
w2(5); w4(val); w2(4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define CCP(x) w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
|
||||
w0(0x87);w0(0x78);w0(x);w2(4);w2(5);w2(4);w0(0xff);
|
||||
|
||||
static void on26_connect ( PIA *pi )
|
||||
|
||||
{ int x;
|
||||
|
||||
pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
|
||||
CCP(0x20);
|
||||
x = 8; if (pi->mode) x = 9;
|
||||
|
||||
w0(2); P1; w0(8); P2;
|
||||
w0(2); P1; w0(x); P2;
|
||||
}
|
||||
|
||||
static void on26_disconnect ( PIA *pi )
|
||||
|
||||
{ if (pi->mode >= 2) { w3(4); w3(4); w3(4); w3(4); }
|
||||
else { w0(4); P1; w0(4); P1; }
|
||||
CCP(0x30);
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
}
|
||||
|
||||
#define RESET_WAIT 200
|
||||
|
||||
static int on26_test_port( PIA *pi) /* hard reset */
|
||||
|
||||
{ int i, m, d, x=0, y=0;
|
||||
|
||||
pi->saved_r0 = r0();
|
||||
pi->saved_r2 = r2();
|
||||
|
||||
d = pi->delay;
|
||||
m = pi->mode;
|
||||
pi->delay = 5;
|
||||
pi->mode = 0;
|
||||
|
||||
w2(0xc);
|
||||
|
||||
CCP(0x30); CCP(0);
|
||||
|
||||
w0(0xfe);w0(0xaa);w0(0x55);w0(0);w0(0xff);
|
||||
i = ((r1() & 0xf0) << 4); w0(0x87);
|
||||
i |= (r1() & 0xf0); w0(0x78);
|
||||
w0(0x20);w2(4);w2(5);
|
||||
i |= ((r1() & 0xf0) >> 4);
|
||||
w2(4);w0(0xff);
|
||||
|
||||
if (i == 0xb5f) {
|
||||
|
||||
w0(2); P1; w0(0); P2;
|
||||
w0(3); P1; w0(0); P2;
|
||||
w0(2); P1; w0(8); P2; udelay(100);
|
||||
w0(2); P1; w0(0xa); P2; udelay(100);
|
||||
w0(2); P1; w0(8); P2; udelay(1000);
|
||||
|
||||
on26_write_regr(pi,0,6,0xa0);
|
||||
|
||||
for (i=0;i<RESET_WAIT;i++) {
|
||||
on26_write_regr(pi,0,6,0xa0);
|
||||
x = on26_read_regr(pi,0,7);
|
||||
on26_write_regr(pi,0,6,0xb0);
|
||||
y = on26_read_regr(pi,0,7);
|
||||
if (!((x&0x80)||(y&0x80))) break;
|
||||
mdelay(100);
|
||||
}
|
||||
|
||||
if (i == RESET_WAIT)
|
||||
printk("on26: Device reset failed (%x,%x)\n",x,y);
|
||||
|
||||
w0(4); P1; w0(4); P1;
|
||||
}
|
||||
|
||||
CCP(0x30);
|
||||
|
||||
pi->delay = d;
|
||||
pi->mode = m;
|
||||
w0(pi->saved_r0);
|
||||
w2(pi->saved_r2);
|
||||
|
||||
return 5;
|
||||
}
|
||||
|
||||
|
||||
static void on26_read_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k, a, b;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x18); P2; w0(0); P1;
|
||||
udelay(10);
|
||||
for (k=0;k<count;k++) {
|
||||
w2(6); a = r1();
|
||||
w2(4); b = r1();
|
||||
buf[k] = j44(a,b);
|
||||
}
|
||||
w0(2); P1; w0(8); P2;
|
||||
break;
|
||||
|
||||
case 1: w0(1); P1; w0(1); P2; w0(2); P1; w0(0x19); P2; w0(0); P1;
|
||||
udelay(10);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w2(0x26); buf[2*k] = r0();
|
||||
w2(0x24); buf[2*k+1] = r0();
|
||||
}
|
||||
w0(2); P1; w0(9); P2;
|
||||
break;
|
||||
|
||||
case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
|
||||
w3(0); w3(0); w2(0x24);
|
||||
udelay(10);
|
||||
for (k=0;k<count;k++) buf[k] = r4();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
|
||||
w3(0); w3(0); w2(0x24);
|
||||
udelay(10);
|
||||
for (k=0;k<count/2;k++) ((u16 *)buf)[k] = r4w();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
|
||||
w3(0); w3(0); w2(0x24);
|
||||
udelay(10);
|
||||
for (k=0;k<count/4;k++) ((u32 *)buf)[k] = r4l();
|
||||
w2(4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void on26_write_block( PIA *pi, char * buf, int count )
|
||||
|
||||
{ int k;
|
||||
|
||||
switch (pi->mode) {
|
||||
|
||||
case 0:
|
||||
case 1: w0(1); P1; w0(1); P2;
|
||||
w0(2); P1; w0(0x18+pi->mode); P2; w0(0); P1;
|
||||
udelay(10);
|
||||
for (k=0;k<count/2;k++) {
|
||||
w2(5); w0(buf[2*k]);
|
||||
w2(7); w0(buf[2*k+1]);
|
||||
}
|
||||
w2(5); w2(4);
|
||||
w0(2); P1; w0(8+pi->mode); P2;
|
||||
break;
|
||||
|
||||
case 2: w3(1); w3(1); w2(5); w4(1); w2(4);
|
||||
w3(0); w3(0); w2(0xc5);
|
||||
udelay(10);
|
||||
for (k=0;k<count;k++) w4(buf[k]);
|
||||
w2(0xc4);
|
||||
break;
|
||||
|
||||
case 3: w3(1); w3(1); w2(5); w4(1); w2(4);
|
||||
w3(0); w3(0); w2(0xc5);
|
||||
udelay(10);
|
||||
for (k=0;k<count/2;k++) w4w(((u16 *)buf)[k]);
|
||||
w2(0xc4);
|
||||
break;
|
||||
|
||||
case 4: w3(1); w3(1); w2(5); w4(1); w2(4);
|
||||
w3(0); w3(0); w2(0xc5);
|
||||
udelay(10);
|
||||
for (k=0;k<count/4;k++) w4l(((u32 *)buf)[k]);
|
||||
w2(0xc4);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void on26_log_adapter( PIA *pi, char * scratch, int verbose )
|
||||
|
||||
{ char *mode_string[5] = {"4-bit","8-bit","EPP-8",
|
||||
"EPP-16","EPP-32"};
|
||||
|
||||
printk("%s: on26 %s, OnSpec 90c26 at 0x%x, ",
|
||||
pi->device,ON26_VERSION,pi->port);
|
||||
printk("mode %d (%s), delay %d\n",pi->mode,
|
||||
mode_string[pi->mode],pi->delay);
|
||||
|
||||
}
|
||||
|
||||
static struct pi_protocol on26 = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "on26",
|
||||
.max_mode = 5,
|
||||
.epp_first = 2,
|
||||
.default_delay = 1,
|
||||
.max_units = 1,
|
||||
.write_regr = on26_write_regr,
|
||||
.read_regr = on26_read_regr,
|
||||
.write_block = on26_write_block,
|
||||
.read_block = on26_read_block,
|
||||
.connect = on26_connect,
|
||||
.disconnect = on26_disconnect,
|
||||
.test_port = on26_test_port,
|
||||
.log_adapter = on26_log_adapter,
|
||||
};
|
||||
|
||||
static int __init on26_init(void)
|
||||
{
|
||||
return paride_register(&on26);
|
||||
}
|
||||
|
||||
static void __exit on26_exit(void)
|
||||
{
|
||||
paride_unregister(&on26);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(on26_init)
|
||||
module_exit(on26_exit)
|
||||
434
drivers/block/paride/paride.c
Normal file
434
drivers/block/paride/paride.c
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
paride.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
This is the base module for the family of device drivers
|
||||
that support parallel port IDE devices.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.03 Use spinlocks
|
||||
1.02 GRG 1998.05.05 init_proto, release_proto, ktti
|
||||
1.03 GRG 1998.08.15 eliminate compiler warning
|
||||
1.04 GRG 1998.11.28 added support for FRIQ
|
||||
1.05 TMW 2000.06.06 use parport_find_number instead of
|
||||
parport_enumerate
|
||||
1.06 TMW 2001.03.26 more sane parport-or-not resource management
|
||||
*/
|
||||
|
||||
#define PI_VERSION "1.06"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h> /* TASK_* */
|
||||
#include <linux/parport.h>
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define MAX_PROTOS 32
|
||||
|
||||
static struct pi_protocol *protocols[MAX_PROTOS];
|
||||
|
||||
static DEFINE_SPINLOCK(pi_spinlock);
|
||||
|
||||
void pi_write_regr(PIA * pi, int cont, int regr, int val)
|
||||
{
|
||||
pi->proto->write_regr(pi, cont, regr, val);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_write_regr);
|
||||
|
||||
int pi_read_regr(PIA * pi, int cont, int regr)
|
||||
{
|
||||
return pi->proto->read_regr(pi, cont, regr);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_read_regr);
|
||||
|
||||
void pi_write_block(PIA * pi, char *buf, int count)
|
||||
{
|
||||
pi->proto->write_block(pi, buf, count);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_write_block);
|
||||
|
||||
void pi_read_block(PIA * pi, char *buf, int count)
|
||||
{
|
||||
pi->proto->read_block(pi, buf, count);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_read_block);
|
||||
|
||||
static void pi_wake_up(void *p)
|
||||
{
|
||||
PIA *pi = (PIA *) p;
|
||||
unsigned long flags;
|
||||
void (*cont) (void) = NULL;
|
||||
|
||||
spin_lock_irqsave(&pi_spinlock, flags);
|
||||
|
||||
if (pi->claim_cont && !parport_claim(pi->pardev)) {
|
||||
cont = pi->claim_cont;
|
||||
pi->claim_cont = NULL;
|
||||
pi->claimed = 1;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&pi_spinlock, flags);
|
||||
|
||||
wake_up(&(pi->parq));
|
||||
|
||||
if (cont)
|
||||
cont();
|
||||
}
|
||||
|
||||
int pi_schedule_claimed(PIA * pi, void (*cont) (void))
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pi_spinlock, flags);
|
||||
if (pi->pardev && parport_claim(pi->pardev)) {
|
||||
pi->claim_cont = cont;
|
||||
spin_unlock_irqrestore(&pi_spinlock, flags);
|
||||
return 0;
|
||||
}
|
||||
pi->claimed = 1;
|
||||
spin_unlock_irqrestore(&pi_spinlock, flags);
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(pi_schedule_claimed);
|
||||
|
||||
void pi_do_claimed(PIA * pi, void (*cont) (void))
|
||||
{
|
||||
if (pi_schedule_claimed(pi, cont))
|
||||
cont();
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_do_claimed);
|
||||
|
||||
static void pi_claim(PIA * pi)
|
||||
{
|
||||
if (pi->claimed)
|
||||
return;
|
||||
pi->claimed = 1;
|
||||
if (pi->pardev)
|
||||
wait_event(pi->parq,
|
||||
!parport_claim((struct pardevice *) pi->pardev));
|
||||
}
|
||||
|
||||
static void pi_unclaim(PIA * pi)
|
||||
{
|
||||
pi->claimed = 0;
|
||||
if (pi->pardev)
|
||||
parport_release((struct pardevice *) (pi->pardev));
|
||||
}
|
||||
|
||||
void pi_connect(PIA * pi)
|
||||
{
|
||||
pi_claim(pi);
|
||||
pi->proto->connect(pi);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_connect);
|
||||
|
||||
void pi_disconnect(PIA * pi)
|
||||
{
|
||||
pi->proto->disconnect(pi);
|
||||
pi_unclaim(pi);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_disconnect);
|
||||
|
||||
static void pi_unregister_parport(PIA * pi)
|
||||
{
|
||||
if (pi->pardev) {
|
||||
parport_unregister_device((struct pardevice *) (pi->pardev));
|
||||
pi->pardev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void pi_release(PIA * pi)
|
||||
{
|
||||
pi_unregister_parport(pi);
|
||||
if (pi->proto->release_proto)
|
||||
pi->proto->release_proto(pi);
|
||||
module_put(pi->proto->owner);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_release);
|
||||
|
||||
static int default_test_proto(PIA * pi, char *scratch, int verbose)
|
||||
{
|
||||
int j, k;
|
||||
int e[2] = { 0, 0 };
|
||||
|
||||
pi->proto->connect(pi);
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
pi_write_regr(pi, 0, 6, 0xa0 + j * 0x10);
|
||||
for (k = 0; k < 256; k++) {
|
||||
pi_write_regr(pi, 0, 2, k ^ 0xaa);
|
||||
pi_write_regr(pi, 0, 3, k ^ 0x55);
|
||||
if (pi_read_regr(pi, 0, 2) != (k ^ 0xaa))
|
||||
e[j]++;
|
||||
}
|
||||
}
|
||||
pi->proto->disconnect(pi);
|
||||
|
||||
if (verbose)
|
||||
printk("%s: %s: port 0x%x, mode %d, test=(%d,%d)\n",
|
||||
pi->device, pi->proto->name, pi->port,
|
||||
pi->mode, e[0], e[1]);
|
||||
|
||||
return (e[0] && e[1]); /* not here if both > 0 */
|
||||
}
|
||||
|
||||
static int pi_test_proto(PIA * pi, char *scratch, int verbose)
|
||||
{
|
||||
int res;
|
||||
|
||||
pi_claim(pi);
|
||||
if (pi->proto->test_proto)
|
||||
res = pi->proto->test_proto(pi, scratch, verbose);
|
||||
else
|
||||
res = default_test_proto(pi, scratch, verbose);
|
||||
pi_unclaim(pi);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int paride_register(PIP * pr)
|
||||
{
|
||||
int k;
|
||||
|
||||
for (k = 0; k < MAX_PROTOS; k++)
|
||||
if (protocols[k] && !strcmp(pr->name, protocols[k]->name)) {
|
||||
printk("paride: %s protocol already registered\n",
|
||||
pr->name);
|
||||
return -1;
|
||||
}
|
||||
k = 0;
|
||||
while ((k < MAX_PROTOS) && (protocols[k]))
|
||||
k++;
|
||||
if (k == MAX_PROTOS) {
|
||||
printk("paride: protocol table full\n");
|
||||
return -1;
|
||||
}
|
||||
protocols[k] = pr;
|
||||
pr->index = k;
|
||||
printk("paride: %s registered as protocol %d\n", pr->name, k);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(paride_register);
|
||||
|
||||
void paride_unregister(PIP * pr)
|
||||
{
|
||||
if (!pr)
|
||||
return;
|
||||
if (protocols[pr->index] != pr) {
|
||||
printk("paride: %s not registered\n", pr->name);
|
||||
return;
|
||||
}
|
||||
protocols[pr->index] = NULL;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(paride_unregister);
|
||||
|
||||
static int pi_register_parport(PIA * pi, int verbose)
|
||||
{
|
||||
struct parport *port;
|
||||
|
||||
port = parport_find_base(pi->port);
|
||||
if (!port)
|
||||
return 0;
|
||||
|
||||
pi->pardev = parport_register_device(port,
|
||||
pi->device, NULL,
|
||||
pi_wake_up, NULL, 0, (void *) pi);
|
||||
parport_put_port(port);
|
||||
if (!pi->pardev)
|
||||
return 0;
|
||||
|
||||
init_waitqueue_head(&pi->parq);
|
||||
|
||||
if (verbose)
|
||||
printk("%s: 0x%x is %s\n", pi->device, pi->port, port->name);
|
||||
|
||||
pi->parname = (char *) port->name;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pi_probe_mode(PIA * pi, int max, char *scratch, int verbose)
|
||||
{
|
||||
int best, range;
|
||||
|
||||
if (pi->mode != -1) {
|
||||
if (pi->mode >= max)
|
||||
return 0;
|
||||
range = 3;
|
||||
if (pi->mode >= pi->proto->epp_first)
|
||||
range = 8;
|
||||
if ((range == 8) && (pi->port % 8))
|
||||
return 0;
|
||||
pi->reserved = range;
|
||||
return (!pi_test_proto(pi, scratch, verbose));
|
||||
}
|
||||
best = -1;
|
||||
for (pi->mode = 0; pi->mode < max; pi->mode++) {
|
||||
range = 3;
|
||||
if (pi->mode >= pi->proto->epp_first)
|
||||
range = 8;
|
||||
if ((range == 8) && (pi->port % 8))
|
||||
break;
|
||||
pi->reserved = range;
|
||||
if (!pi_test_proto(pi, scratch, verbose))
|
||||
best = pi->mode;
|
||||
}
|
||||
pi->mode = best;
|
||||
return (best > -1);
|
||||
}
|
||||
|
||||
static int pi_probe_unit(PIA * pi, int unit, char *scratch, int verbose)
|
||||
{
|
||||
int max, s, e;
|
||||
|
||||
s = unit;
|
||||
e = s + 1;
|
||||
|
||||
if (s == -1) {
|
||||
s = 0;
|
||||
e = pi->proto->max_units;
|
||||
}
|
||||
|
||||
if (!pi_register_parport(pi, verbose))
|
||||
return 0;
|
||||
|
||||
if (pi->proto->test_port) {
|
||||
pi_claim(pi);
|
||||
max = pi->proto->test_port(pi);
|
||||
pi_unclaim(pi);
|
||||
} else
|
||||
max = pi->proto->max_mode;
|
||||
|
||||
if (pi->proto->probe_unit) {
|
||||
pi_claim(pi);
|
||||
for (pi->unit = s; pi->unit < e; pi->unit++)
|
||||
if (pi->proto->probe_unit(pi)) {
|
||||
pi_unclaim(pi);
|
||||
if (pi_probe_mode(pi, max, scratch, verbose))
|
||||
return 1;
|
||||
pi_unregister_parport(pi);
|
||||
return 0;
|
||||
}
|
||||
pi_unclaim(pi);
|
||||
pi_unregister_parport(pi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pi_probe_mode(pi, max, scratch, verbose)) {
|
||||
pi_unregister_parport(pi);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
int pi_init(PIA * pi, int autoprobe, int port, int mode,
|
||||
int unit, int protocol, int delay, char *scratch,
|
||||
int devtype, int verbose, char *device)
|
||||
{
|
||||
int p, k, s, e;
|
||||
int lpts[7] = { 0x3bc, 0x378, 0x278, 0x268, 0x27c, 0x26c, 0 };
|
||||
|
||||
s = protocol;
|
||||
e = s + 1;
|
||||
|
||||
if (!protocols[0])
|
||||
request_module("paride_protocol");
|
||||
|
||||
if (autoprobe) {
|
||||
s = 0;
|
||||
e = MAX_PROTOS;
|
||||
} else if ((s < 0) || (s >= MAX_PROTOS) || (port <= 0) ||
|
||||
(!protocols[s]) || (unit < 0) ||
|
||||
(unit >= protocols[s]->max_units)) {
|
||||
printk("%s: Invalid parameters\n", device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (p = s; p < e; p++) {
|
||||
struct pi_protocol *proto = protocols[p];
|
||||
if (!proto)
|
||||
continue;
|
||||
/* still racy */
|
||||
if (!try_module_get(proto->owner))
|
||||
continue;
|
||||
pi->proto = proto;
|
||||
pi->private = 0;
|
||||
if (proto->init_proto && proto->init_proto(pi) < 0) {
|
||||
pi->proto = NULL;
|
||||
module_put(proto->owner);
|
||||
continue;
|
||||
}
|
||||
if (delay == -1)
|
||||
pi->delay = pi->proto->default_delay;
|
||||
else
|
||||
pi->delay = delay;
|
||||
pi->devtype = devtype;
|
||||
pi->device = device;
|
||||
|
||||
pi->parname = NULL;
|
||||
pi->pardev = NULL;
|
||||
init_waitqueue_head(&pi->parq);
|
||||
pi->claimed = 0;
|
||||
pi->claim_cont = NULL;
|
||||
|
||||
pi->mode = mode;
|
||||
if (port != -1) {
|
||||
pi->port = port;
|
||||
if (pi_probe_unit(pi, unit, scratch, verbose))
|
||||
break;
|
||||
pi->port = 0;
|
||||
} else {
|
||||
k = 0;
|
||||
while ((pi->port = lpts[k++]))
|
||||
if (pi_probe_unit
|
||||
(pi, unit, scratch, verbose))
|
||||
break;
|
||||
if (pi->port)
|
||||
break;
|
||||
}
|
||||
if (pi->proto->release_proto)
|
||||
pi->proto->release_proto(pi);
|
||||
module_put(proto->owner);
|
||||
}
|
||||
|
||||
if (!pi->port) {
|
||||
if (autoprobe)
|
||||
printk("%s: Autoprobe failed\n", device);
|
||||
else
|
||||
printk("%s: Adapter not found\n", device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pi->parname)
|
||||
printk("%s: Sharing %s at 0x%x\n", pi->device,
|
||||
pi->parname, pi->port);
|
||||
|
||||
pi->proto->log_adapter(pi, scratch, verbose);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(pi_init);
|
||||
170
drivers/block/paride/paride.h
Normal file
170
drivers/block/paride/paride.h
Normal file
@@ -0,0 +1,170 @@
|
||||
#ifndef __DRIVERS_PARIDE_H__
|
||||
#define __DRIVERS_PARIDE_H__
|
||||
|
||||
/*
|
||||
paride.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GPL.
|
||||
|
||||
This file defines the interface between the high-level parallel
|
||||
IDE device drivers (pd, pf, pcd, pt) and the adapter chips.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.05 init_proto, release_proto
|
||||
*/
|
||||
|
||||
#define PARIDE_H_VERSION "1.01"
|
||||
|
||||
/* Some adapters need to know what kind of device they are in
|
||||
|
||||
Values for devtype:
|
||||
*/
|
||||
|
||||
#define PI_PD 0 /* IDE disk */
|
||||
#define PI_PCD 1 /* ATAPI CDrom */
|
||||
#define PI_PF 2 /* ATAPI disk */
|
||||
#define PI_PT 3 /* ATAPI tape */
|
||||
#define PI_PG 4 /* ATAPI generic */
|
||||
|
||||
/* The paride module contains no state, instead the drivers allocate
|
||||
a pi_adapter data structure and pass it to paride in every operation.
|
||||
|
||||
*/
|
||||
|
||||
struct pi_adapter {
|
||||
|
||||
struct pi_protocol *proto; /* adapter protocol */
|
||||
int port; /* base address of parallel port */
|
||||
int mode; /* transfer mode in use */
|
||||
int delay; /* adapter delay setting */
|
||||
int devtype; /* device type: PI_PD etc. */
|
||||
char *device; /* name of driver */
|
||||
int unit; /* unit number for chained adapters */
|
||||
int saved_r0; /* saved port state */
|
||||
int saved_r2; /* saved port state */
|
||||
int reserved; /* number of ports reserved */
|
||||
unsigned long private; /* for protocol module */
|
||||
|
||||
wait_queue_head_t parq; /* semaphore for parport sharing */
|
||||
void *pardev; /* pointer to pardevice */
|
||||
char *parname; /* parport name */
|
||||
int claimed; /* parport has already been claimed */
|
||||
void (*claim_cont)(void); /* continuation for parport wait */
|
||||
};
|
||||
|
||||
typedef struct pi_adapter PIA;
|
||||
|
||||
/* functions exported by paride to the high level drivers */
|
||||
|
||||
extern int pi_init(PIA *pi,
|
||||
int autoprobe, /* 1 to autoprobe */
|
||||
int port, /* base port address */
|
||||
int mode, /* -1 for autoprobe */
|
||||
int unit, /* unit number, if supported */
|
||||
int protocol, /* protocol to use */
|
||||
int delay, /* -1 to use adapter specific default */
|
||||
char * scratch, /* address of 512 byte buffer */
|
||||
int devtype, /* device type: PI_PD, PI_PCD, etc ... */
|
||||
int verbose, /* log verbose data while probing */
|
||||
char *device /* name of the driver */
|
||||
); /* returns 0 on failure, 1 on success */
|
||||
|
||||
extern void pi_release(PIA *pi);
|
||||
|
||||
/* registers are addressed as (cont,regr)
|
||||
|
||||
cont: 0 for command register file, 1 for control register(s)
|
||||
regr: 0-7 for register number.
|
||||
|
||||
*/
|
||||
|
||||
extern void pi_write_regr(PIA *pi, int cont, int regr, int val);
|
||||
|
||||
extern int pi_read_regr(PIA *pi, int cont, int regr);
|
||||
|
||||
extern void pi_write_block(PIA *pi, char * buf, int count);
|
||||
|
||||
extern void pi_read_block(PIA *pi, char * buf, int count);
|
||||
|
||||
extern void pi_connect(PIA *pi);
|
||||
|
||||
extern void pi_disconnect(PIA *pi);
|
||||
|
||||
extern void pi_do_claimed(PIA *pi, void (*cont)(void));
|
||||
extern int pi_schedule_claimed(PIA *pi, void (*cont)(void));
|
||||
|
||||
/* macros and functions exported to the protocol modules */
|
||||
|
||||
#define delay_p (pi->delay?udelay(pi->delay):(void)0)
|
||||
#define out_p(offs,byte) outb(byte,pi->port+offs); delay_p;
|
||||
#define in_p(offs) (delay_p,inb(pi->port+offs))
|
||||
|
||||
#define w0(byte) {out_p(0,byte);}
|
||||
#define r0() (in_p(0) & 0xff)
|
||||
#define w1(byte) {out_p(1,byte);}
|
||||
#define r1() (in_p(1) & 0xff)
|
||||
#define w2(byte) {out_p(2,byte);}
|
||||
#define r2() (in_p(2) & 0xff)
|
||||
#define w3(byte) {out_p(3,byte);}
|
||||
#define w4(byte) {out_p(4,byte);}
|
||||
#define r4() (in_p(4) & 0xff)
|
||||
#define w4w(data) {outw(data,pi->port+4); delay_p;}
|
||||
#define w4l(data) {outl(data,pi->port+4); delay_p;}
|
||||
#define r4w() (delay_p,inw(pi->port+4)&0xffff)
|
||||
#define r4l() (delay_p,inl(pi->port+4)&0xffffffff)
|
||||
|
||||
static inline u16 pi_swab16( char *b, int k)
|
||||
|
||||
{ union { u16 u; char t[2]; } r;
|
||||
|
||||
r.t[0]=b[2*k+1]; r.t[1]=b[2*k];
|
||||
return r.u;
|
||||
}
|
||||
|
||||
static inline u32 pi_swab32( char *b, int k)
|
||||
|
||||
{ union { u32 u; char f[4]; } r;
|
||||
|
||||
r.f[0]=b[4*k+1]; r.f[1]=b[4*k];
|
||||
r.f[2]=b[4*k+3]; r.f[3]=b[4*k+2];
|
||||
return r.u;
|
||||
}
|
||||
|
||||
struct pi_protocol {
|
||||
|
||||
char name[8]; /* name for this protocol */
|
||||
int index; /* index into protocol table */
|
||||
|
||||
int max_mode; /* max mode number */
|
||||
int epp_first; /* modes >= this use 8 ports */
|
||||
|
||||
int default_delay; /* delay parameter if not specified */
|
||||
int max_units; /* max chained units probed for */
|
||||
|
||||
void (*write_regr)(PIA *,int,int,int);
|
||||
int (*read_regr)(PIA *,int,int);
|
||||
void (*write_block)(PIA *,char *,int);
|
||||
void (*read_block)(PIA *,char *,int);
|
||||
|
||||
void (*connect)(PIA *);
|
||||
void (*disconnect)(PIA *);
|
||||
|
||||
int (*test_port)(PIA *);
|
||||
int (*probe_unit)(PIA *);
|
||||
int (*test_proto)(PIA *,char *,int);
|
||||
void (*log_adapter)(PIA *,char *,int);
|
||||
|
||||
int (*init_proto)(PIA *);
|
||||
void (*release_proto)(PIA *);
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
typedef struct pi_protocol PIP;
|
||||
|
||||
extern int paride_register( PIP * );
|
||||
extern void paride_unregister ( PIP * );
|
||||
|
||||
#endif /* __DRIVERS_PARIDE_H__ */
|
||||
/* end of paride.h */
|
||||
970
drivers/block/paride/pcd.c
Normal file
970
drivers/block/paride/pcd.c
Normal file
@@ -0,0 +1,970 @@
|
||||
/*
|
||||
pcd.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
This is a high-level driver for parallel port ATAPI CD-ROM
|
||||
drives based on chips supported by the paride module.
|
||||
|
||||
By default, the driver will autoprobe for a single parallel
|
||||
port ATAPI CD-ROM drive, but if their individual parameters are
|
||||
specified, the driver can handle up to 4 drives.
|
||||
|
||||
The behaviour of the pcd driver can be altered by setting
|
||||
some parameters from the insmod command line. The following
|
||||
parameters are adjustable:
|
||||
|
||||
drive0 These four arguments can be arrays of
|
||||
drive1 1-6 integers as follows:
|
||||
drive2
|
||||
drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
|
||||
|
||||
Where,
|
||||
|
||||
<prt> is the base of the parallel port address for
|
||||
the corresponding drive. (required)
|
||||
|
||||
<pro> is the protocol number for the adapter that
|
||||
supports this drive. These numbers are
|
||||
logged by 'paride' when the protocol modules
|
||||
are initialised. (0 if not given)
|
||||
|
||||
<uni> for those adapters that support chained
|
||||
devices, this is the unit selector for the
|
||||
chain of devices on the given port. It should
|
||||
be zero for devices that don't support chaining.
|
||||
(0 if not given)
|
||||
|
||||
<mod> this can be -1 to choose the best mode, or one
|
||||
of the mode numbers supported by the adapter.
|
||||
(-1 if not given)
|
||||
|
||||
<slv> ATAPI CD-ROMs can be jumpered to master or slave.
|
||||
Set this to 0 to choose the master drive, 1 to
|
||||
choose the slave, -1 (the default) to choose the
|
||||
first drive found.
|
||||
|
||||
<dly> some parallel ports require the driver to
|
||||
go more slowly. -1 sets a default value that
|
||||
should work with the chosen protocol. Otherwise,
|
||||
set this to a small integer, the larger it is
|
||||
the slower the port i/o. In some cases, setting
|
||||
this to zero will speed up the device. (default -1)
|
||||
|
||||
major You may use this parameter to overide the
|
||||
default major number (46) that this driver
|
||||
will use. Be sure to change the device
|
||||
name as well.
|
||||
|
||||
name This parameter is a character string that
|
||||
contains the name the kernel will use for this
|
||||
device (in /proc output, for instance).
|
||||
(default "pcd")
|
||||
|
||||
verbose This parameter controls the amount of logging
|
||||
that the driver will do. Set it to 0 for
|
||||
normal operation, 1 to see autoprobe progress
|
||||
messages, or 2 to see additional debugging
|
||||
output. (default 0)
|
||||
|
||||
nice This parameter controls the driver's use of
|
||||
idle CPU time, at the expense of some speed.
|
||||
|
||||
If this driver is built into the kernel, you can use kernel
|
||||
the following command line parameters, with the same values
|
||||
as the corresponding module parameters listed above:
|
||||
|
||||
pcd.drive0
|
||||
pcd.drive1
|
||||
pcd.drive2
|
||||
pcd.drive3
|
||||
pcd.nice
|
||||
|
||||
In addition, you can use the parameter pcd.disable to disable
|
||||
the driver entirely.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.01.24 Added test unit ready support
|
||||
1.02 GRG 1998.05.06 Changes to pcd_completion, ready_wait,
|
||||
and loosen interpretation of ATAPI
|
||||
standard for clearing error status.
|
||||
Use spinlocks. Eliminate sti().
|
||||
1.03 GRG 1998.06.16 Eliminated an Ugh
|
||||
1.04 GRG 1998.08.15 Added extra debugging, improvements to
|
||||
pcd_completion, use HZ in loop timing
|
||||
1.05 GRG 1998.08.16 Conformed to "Uniform CD-ROM" standard
|
||||
1.06 GRG 1998.08.19 Added audio ioctl support
|
||||
1.07 GRG 1998.09.24 Increased reset timeout, added jumbo support
|
||||
|
||||
*/
|
||||
|
||||
#define PCD_VERSION "1.07"
|
||||
#define PCD_MAJOR 46
|
||||
#define PCD_NAME "pcd"
|
||||
#define PCD_UNITS 4
|
||||
|
||||
/* Here are things one can override from the insmod command.
|
||||
Most are autoprobed by paride unless set here. Verbose is off
|
||||
by default.
|
||||
|
||||
*/
|
||||
|
||||
static int verbose = 0;
|
||||
static int major = PCD_MAJOR;
|
||||
static char *name = PCD_NAME;
|
||||
static int nice = 0;
|
||||
static int disable = 0;
|
||||
|
||||
static int drive0[6] = { 0, 0, 0, -1, -1, -1 };
|
||||
static int drive1[6] = { 0, 0, 0, -1, -1, -1 };
|
||||
static int drive2[6] = { 0, 0, 0, -1, -1, -1 };
|
||||
static int drive3[6] = { 0, 0, 0, -1, -1, -1 };
|
||||
|
||||
static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3};
|
||||
static int pcd_drive_count;
|
||||
|
||||
enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY};
|
||||
|
||||
/* end of parameters */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/cdrom.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
static DEFINE_SPINLOCK(pcd_lock);
|
||||
|
||||
module_param(verbose, bool, 0644);
|
||||
module_param(major, int, 0);
|
||||
module_param(name, charp, 0);
|
||||
module_param(nice, int, 0);
|
||||
module_param_array(drive0, int, NULL, 0);
|
||||
module_param_array(drive1, int, NULL, 0);
|
||||
module_param_array(drive2, int, NULL, 0);
|
||||
module_param_array(drive3, int, NULL, 0);
|
||||
|
||||
#include "paride.h"
|
||||
#include "pseudo.h"
|
||||
|
||||
#define PCD_RETRIES 5
|
||||
#define PCD_TMO 800 /* timeout in jiffies */
|
||||
#define PCD_DELAY 50 /* spin delay in uS */
|
||||
#define PCD_READY_TMO 20 /* in seconds */
|
||||
#define PCD_RESET_TMO 100 /* in tenths of a second */
|
||||
|
||||
#define PCD_SPIN (1000000*PCD_TMO)/(HZ*PCD_DELAY)
|
||||
|
||||
#define IDE_ERR 0x01
|
||||
#define IDE_DRQ 0x08
|
||||
#define IDE_READY 0x40
|
||||
#define IDE_BUSY 0x80
|
||||
|
||||
static int pcd_open(struct cdrom_device_info *cdi, int purpose);
|
||||
static void pcd_release(struct cdrom_device_info *cdi);
|
||||
static int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr);
|
||||
static int pcd_media_changed(struct cdrom_device_info *cdi, int slot_nr);
|
||||
static int pcd_tray_move(struct cdrom_device_info *cdi, int position);
|
||||
static int pcd_lock_door(struct cdrom_device_info *cdi, int lock);
|
||||
static int pcd_drive_reset(struct cdrom_device_info *cdi);
|
||||
static int pcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn);
|
||||
static int pcd_audio_ioctl(struct cdrom_device_info *cdi,
|
||||
unsigned int cmd, void *arg);
|
||||
static int pcd_packet(struct cdrom_device_info *cdi,
|
||||
struct packet_command *cgc);
|
||||
|
||||
static int pcd_detect(void);
|
||||
static void pcd_probe_capabilities(void);
|
||||
static void do_pcd_read_drq(void);
|
||||
static void do_pcd_request(request_queue_t * q);
|
||||
static void do_pcd_read(void);
|
||||
|
||||
struct pcd_unit {
|
||||
struct pi_adapter pia; /* interface to paride layer */
|
||||
struct pi_adapter *pi;
|
||||
int drive; /* master/slave */
|
||||
int last_sense; /* result of last request sense */
|
||||
int changed; /* media change seen */
|
||||
int present; /* does this unit exist ? */
|
||||
char *name; /* pcd0, pcd1, etc */
|
||||
struct cdrom_device_info info; /* uniform cdrom interface */
|
||||
struct gendisk *disk;
|
||||
};
|
||||
|
||||
static struct pcd_unit pcd[PCD_UNITS];
|
||||
|
||||
static char pcd_scratch[64];
|
||||
static char pcd_buffer[2048]; /* raw block buffer */
|
||||
static int pcd_bufblk = -1; /* block in buffer, in CD units,
|
||||
-1 for nothing there. See also
|
||||
pd_unit.
|
||||
*/
|
||||
|
||||
/* the variables below are used mainly in the I/O request engine, which
|
||||
processes only one request at a time.
|
||||
*/
|
||||
|
||||
static struct pcd_unit *pcd_current; /* current request's drive */
|
||||
static struct request *pcd_req;
|
||||
static int pcd_retries; /* retries on current request */
|
||||
static int pcd_busy; /* request being processed ? */
|
||||
static int pcd_sector; /* address of next requested sector */
|
||||
static int pcd_count; /* number of blocks still to do */
|
||||
static char *pcd_buf; /* buffer for request in progress */
|
||||
|
||||
static int pcd_warned; /* Have we logged a phase warning ? */
|
||||
|
||||
/* kernel glue structures */
|
||||
|
||||
static int pcd_block_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pcd_unit *cd = inode->i_bdev->bd_disk->private_data;
|
||||
return cdrom_open(&cd->info, inode, file);
|
||||
}
|
||||
|
||||
static int pcd_block_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pcd_unit *cd = inode->i_bdev->bd_disk->private_data;
|
||||
return cdrom_release(&cd->info, file);
|
||||
}
|
||||
|
||||
static int pcd_block_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned cmd, unsigned long arg)
|
||||
{
|
||||
struct pcd_unit *cd = inode->i_bdev->bd_disk->private_data;
|
||||
return cdrom_ioctl(file, &cd->info, inode, cmd, arg);
|
||||
}
|
||||
|
||||
static int pcd_block_media_changed(struct gendisk *disk)
|
||||
{
|
||||
struct pcd_unit *cd = disk->private_data;
|
||||
return cdrom_media_changed(&cd->info);
|
||||
}
|
||||
|
||||
static struct block_device_operations pcd_bdops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pcd_block_open,
|
||||
.release = pcd_block_release,
|
||||
.ioctl = pcd_block_ioctl,
|
||||
.media_changed = pcd_block_media_changed,
|
||||
};
|
||||
|
||||
static struct cdrom_device_ops pcd_dops = {
|
||||
.open = pcd_open,
|
||||
.release = pcd_release,
|
||||
.drive_status = pcd_drive_status,
|
||||
.media_changed = pcd_media_changed,
|
||||
.tray_move = pcd_tray_move,
|
||||
.lock_door = pcd_lock_door,
|
||||
.get_mcn = pcd_get_mcn,
|
||||
.reset = pcd_drive_reset,
|
||||
.audio_ioctl = pcd_audio_ioctl,
|
||||
.generic_packet = pcd_packet,
|
||||
.capability = CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK |
|
||||
CDC_MCN | CDC_MEDIA_CHANGED | CDC_RESET |
|
||||
CDC_PLAY_AUDIO | CDC_GENERIC_PACKET | CDC_CD_R |
|
||||
CDC_CD_RW,
|
||||
};
|
||||
|
||||
static void pcd_init_units(void)
|
||||
{
|
||||
struct pcd_unit *cd;
|
||||
int unit;
|
||||
|
||||
pcd_drive_count = 0;
|
||||
for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
|
||||
struct gendisk *disk = alloc_disk(1);
|
||||
if (!disk)
|
||||
continue;
|
||||
cd->disk = disk;
|
||||
cd->pi = &cd->pia;
|
||||
cd->present = 0;
|
||||
cd->last_sense = 0;
|
||||
cd->changed = 1;
|
||||
cd->drive = (*drives[unit])[D_SLV];
|
||||
if ((*drives[unit])[D_PRT])
|
||||
pcd_drive_count++;
|
||||
|
||||
cd->name = &cd->info.name[0];
|
||||
snprintf(cd->name, sizeof(cd->info.name), "%s%d", name, unit);
|
||||
cd->info.ops = &pcd_dops;
|
||||
cd->info.handle = cd;
|
||||
cd->info.speed = 0;
|
||||
cd->info.capacity = 1;
|
||||
cd->info.mask = 0;
|
||||
disk->major = major;
|
||||
disk->first_minor = unit;
|
||||
strcpy(disk->disk_name, cd->name); /* umm... */
|
||||
disk->fops = &pcd_bdops;
|
||||
}
|
||||
}
|
||||
|
||||
static int pcd_open(struct cdrom_device_info *cdi, int purpose)
|
||||
{
|
||||
struct pcd_unit *cd = cdi->handle;
|
||||
if (!cd->present)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcd_release(struct cdrom_device_info *cdi)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int status_reg(struct pcd_unit *cd)
|
||||
{
|
||||
return pi_read_regr(cd->pi, 1, 6);
|
||||
}
|
||||
|
||||
static inline int read_reg(struct pcd_unit *cd, int reg)
|
||||
{
|
||||
return pi_read_regr(cd->pi, 0, reg);
|
||||
}
|
||||
|
||||
static inline void write_reg(struct pcd_unit *cd, int reg, int val)
|
||||
{
|
||||
pi_write_regr(cd->pi, 0, reg, val);
|
||||
}
|
||||
|
||||
static int pcd_wait(struct pcd_unit *cd, int go, int stop, char *fun, char *msg)
|
||||
{
|
||||
int j, r, e, s, p;
|
||||
|
||||
j = 0;
|
||||
while ((((r = status_reg(cd)) & go) || (stop && (!(r & stop))))
|
||||
&& (j++ < PCD_SPIN))
|
||||
udelay(PCD_DELAY);
|
||||
|
||||
if ((r & (IDE_ERR & stop)) || (j >= PCD_SPIN)) {
|
||||
s = read_reg(cd, 7);
|
||||
e = read_reg(cd, 1);
|
||||
p = read_reg(cd, 2);
|
||||
if (j >= PCD_SPIN)
|
||||
e |= 0x100;
|
||||
if (fun)
|
||||
printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
|
||||
" loop=%d phase=%d\n",
|
||||
cd->name, fun, msg, r, s, e, j, p);
|
||||
return (s << 8) + r;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcd_command(struct pcd_unit *cd, char *cmd, int dlen, char *fun)
|
||||
{
|
||||
pi_connect(cd->pi);
|
||||
|
||||
write_reg(cd, 6, 0xa0 + 0x10 * cd->drive);
|
||||
|
||||
if (pcd_wait(cd, IDE_BUSY | IDE_DRQ, 0, fun, "before command")) {
|
||||
pi_disconnect(cd->pi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_reg(cd, 4, dlen % 256);
|
||||
write_reg(cd, 5, dlen / 256);
|
||||
write_reg(cd, 7, 0xa0); /* ATAPI packet command */
|
||||
|
||||
if (pcd_wait(cd, IDE_BUSY, IDE_DRQ, fun, "command DRQ")) {
|
||||
pi_disconnect(cd->pi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_reg(cd, 2) != 1) {
|
||||
printk("%s: %s: command phase error\n", cd->name, fun);
|
||||
pi_disconnect(cd->pi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pi_write_block(cd->pi, cmd, 12);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcd_completion(struct pcd_unit *cd, char *buf, char *fun)
|
||||
{
|
||||
int r, d, p, n, k, j;
|
||||
|
||||
r = -1;
|
||||
k = 0;
|
||||
j = 0;
|
||||
|
||||
if (!pcd_wait(cd, IDE_BUSY, IDE_DRQ | IDE_READY | IDE_ERR,
|
||||
fun, "completion")) {
|
||||
r = 0;
|
||||
while (read_reg(cd, 7) & IDE_DRQ) {
|
||||
d = read_reg(cd, 4) + 256 * read_reg(cd, 5);
|
||||
n = (d + 3) & 0xfffc;
|
||||
p = read_reg(cd, 2) & 3;
|
||||
|
||||
if ((p == 2) && (n > 0) && (j == 0)) {
|
||||
pi_read_block(cd->pi, buf, n);
|
||||
if (verbose > 1)
|
||||
printk("%s: %s: Read %d bytes\n",
|
||||
cd->name, fun, n);
|
||||
r = 0;
|
||||
j++;
|
||||
} else {
|
||||
if (verbose > 1)
|
||||
printk
|
||||
("%s: %s: Unexpected phase %d, d=%d, k=%d\n",
|
||||
cd->name, fun, p, d, k);
|
||||
if ((verbose < 2) && !pcd_warned) {
|
||||
pcd_warned = 1;
|
||||
printk
|
||||
("%s: WARNING: ATAPI phase errors\n",
|
||||
cd->name);
|
||||
}
|
||||
mdelay(1);
|
||||
}
|
||||
if (k++ > PCD_TMO) {
|
||||
printk("%s: Stuck DRQ\n", cd->name);
|
||||
break;
|
||||
}
|
||||
if (pcd_wait
|
||||
(cd, IDE_BUSY, IDE_DRQ | IDE_READY | IDE_ERR, fun,
|
||||
"completion")) {
|
||||
r = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pi_disconnect(cd->pi);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void pcd_req_sense(struct pcd_unit *cd, char *fun)
|
||||
{
|
||||
char rs_cmd[12] = { 0x03, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
|
||||
char buf[16];
|
||||
int r, c;
|
||||
|
||||
r = pcd_command(cd, rs_cmd, 16, "Request sense");
|
||||
mdelay(1);
|
||||
if (!r)
|
||||
pcd_completion(cd, buf, "Request sense");
|
||||
|
||||
cd->last_sense = -1;
|
||||
c = 2;
|
||||
if (!r) {
|
||||
if (fun)
|
||||
printk("%s: %s: Sense key: %x, ASC: %x, ASQ: %x\n",
|
||||
cd->name, fun, buf[2] & 0xf, buf[12], buf[13]);
|
||||
c = buf[2] & 0xf;
|
||||
cd->last_sense =
|
||||
c | ((buf[12] & 0xff) << 8) | ((buf[13] & 0xff) << 16);
|
||||
}
|
||||
if ((c == 2) || (c == 6))
|
||||
cd->changed = 1;
|
||||
}
|
||||
|
||||
static int pcd_atapi(struct pcd_unit *cd, char *cmd, int dlen, char *buf, char *fun)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = pcd_command(cd, cmd, dlen, fun);
|
||||
mdelay(1);
|
||||
if (!r)
|
||||
r = pcd_completion(cd, buf, fun);
|
||||
if (r)
|
||||
pcd_req_sense(cd, fun);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int pcd_packet(struct cdrom_device_info *cdi, struct packet_command *cgc)
|
||||
{
|
||||
return pcd_atapi(cdi->handle, cgc->cmd, cgc->buflen, cgc->buffer,
|
||||
"generic packet");
|
||||
}
|
||||
|
||||
#define DBMSG(msg) ((verbose>1)?(msg):NULL)
|
||||
|
||||
static int pcd_media_changed(struct cdrom_device_info *cdi, int slot_nr)
|
||||
{
|
||||
struct pcd_unit *cd = cdi->handle;
|
||||
int res = cd->changed;
|
||||
if (res)
|
||||
cd->changed = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int pcd_lock_door(struct cdrom_device_info *cdi, int lock)
|
||||
{
|
||||
char un_cmd[12] = { 0x1e, 0, 0, 0, lock, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
return pcd_atapi(cdi->handle, un_cmd, 0, pcd_scratch,
|
||||
lock ? "lock door" : "unlock door");
|
||||
}
|
||||
|
||||
static int pcd_tray_move(struct cdrom_device_info *cdi, int position)
|
||||
{
|
||||
char ej_cmd[12] = { 0x1b, 0, 0, 0, 3 - position, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
return pcd_atapi(cdi->handle, ej_cmd, 0, pcd_scratch,
|
||||
position ? "eject" : "close tray");
|
||||
}
|
||||
|
||||
static void pcd_sleep(int cs)
|
||||
{
|
||||
schedule_timeout_interruptible(cs);
|
||||
}
|
||||
|
||||
static int pcd_reset(struct pcd_unit *cd)
|
||||
{
|
||||
int i, k, flg;
|
||||
int expect[5] = { 1, 1, 1, 0x14, 0xeb };
|
||||
|
||||
pi_connect(cd->pi);
|
||||
write_reg(cd, 6, 0xa0 + 0x10 * cd->drive);
|
||||
write_reg(cd, 7, 8);
|
||||
|
||||
pcd_sleep(20 * HZ / 1000); /* delay a bit */
|
||||
|
||||
k = 0;
|
||||
while ((k++ < PCD_RESET_TMO) && (status_reg(cd) & IDE_BUSY))
|
||||
pcd_sleep(HZ / 10);
|
||||
|
||||
flg = 1;
|
||||
for (i = 0; i < 5; i++)
|
||||
flg &= (read_reg(cd, i + 1) == expect[i]);
|
||||
|
||||
if (verbose) {
|
||||
printk("%s: Reset (%d) signature = ", cd->name, k);
|
||||
for (i = 0; i < 5; i++)
|
||||
printk("%3x", read_reg(cd, i + 1));
|
||||
if (!flg)
|
||||
printk(" (incorrect)");
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
pi_disconnect(cd->pi);
|
||||
return flg - 1;
|
||||
}
|
||||
|
||||
static int pcd_drive_reset(struct cdrom_device_info *cdi)
|
||||
{
|
||||
return pcd_reset(cdi->handle);
|
||||
}
|
||||
|
||||
static int pcd_ready_wait(struct pcd_unit *cd, int tmo)
|
||||
{
|
||||
char tr_cmd[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
int k, p;
|
||||
|
||||
k = 0;
|
||||
while (k < tmo) {
|
||||
cd->last_sense = 0;
|
||||
pcd_atapi(cd, tr_cmd, 0, NULL, DBMSG("test unit ready"));
|
||||
p = cd->last_sense;
|
||||
if (!p)
|
||||
return 0;
|
||||
if (!(((p & 0xffff) == 0x0402) || ((p & 0xff) == 6)))
|
||||
return p;
|
||||
k++;
|
||||
pcd_sleep(HZ);
|
||||
}
|
||||
return 0x000020; /* timeout */
|
||||
}
|
||||
|
||||
static int pcd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
|
||||
{
|
||||
char rc_cmd[12] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
struct pcd_unit *cd = cdi->handle;
|
||||
|
||||
if (pcd_ready_wait(cd, PCD_READY_TMO))
|
||||
return CDS_DRIVE_NOT_READY;
|
||||
if (pcd_atapi(cd, rc_cmd, 8, pcd_scratch, DBMSG("check media")))
|
||||
return CDS_NO_DISC;
|
||||
return CDS_DISC_OK;
|
||||
}
|
||||
|
||||
static int pcd_identify(struct pcd_unit *cd, char *id)
|
||||
{
|
||||
int k, s;
|
||||
char id_cmd[12] = { 0x12, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
pcd_bufblk = -1;
|
||||
|
||||
s = pcd_atapi(cd, id_cmd, 36, pcd_buffer, "identify");
|
||||
|
||||
if (s)
|
||||
return -1;
|
||||
if ((pcd_buffer[0] & 0x1f) != 5) {
|
||||
if (verbose)
|
||||
printk("%s: %s is not a CD-ROM\n",
|
||||
cd->name, cd->drive ? "Slave" : "Master");
|
||||
return -1;
|
||||
}
|
||||
memcpy(id, pcd_buffer + 16, 16);
|
||||
id[16] = 0;
|
||||
k = 16;
|
||||
while ((k >= 0) && (id[k] <= 0x20)) {
|
||||
id[k] = 0;
|
||||
k--;
|
||||
}
|
||||
|
||||
printk("%s: %s: %s\n", cd->name, cd->drive ? "Slave" : "Master", id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns 0, with id set if drive is detected
|
||||
* -1, if drive detection failed
|
||||
*/
|
||||
static int pcd_probe(struct pcd_unit *cd, int ms, char *id)
|
||||
{
|
||||
if (ms == -1) {
|
||||
for (cd->drive = 0; cd->drive <= 1; cd->drive++)
|
||||
if (!pcd_reset(cd) && !pcd_identify(cd, id))
|
||||
return 0;
|
||||
} else {
|
||||
cd->drive = ms;
|
||||
if (!pcd_reset(cd) && !pcd_identify(cd, id))
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pcd_probe_capabilities(void)
|
||||
{
|
||||
int unit, r;
|
||||
char buffer[32];
|
||||
char cmd[12] = { 0x5a, 1 << 3, 0x2a, 0, 0, 0, 0, 18, 0, 0, 0, 0 };
|
||||
struct pcd_unit *cd;
|
||||
|
||||
for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
|
||||
if (!cd->present)
|
||||
continue;
|
||||
r = pcd_atapi(cd, cmd, 18, buffer, "mode sense capabilities");
|
||||
if (r)
|
||||
continue;
|
||||
/* we should now have the cap page */
|
||||
if ((buffer[11] & 1) == 0)
|
||||
cd->info.mask |= CDC_CD_R;
|
||||
if ((buffer[11] & 2) == 0)
|
||||
cd->info.mask |= CDC_CD_RW;
|
||||
if ((buffer[12] & 1) == 0)
|
||||
cd->info.mask |= CDC_PLAY_AUDIO;
|
||||
if ((buffer[14] & 1) == 0)
|
||||
cd->info.mask |= CDC_LOCK;
|
||||
if ((buffer[14] & 8) == 0)
|
||||
cd->info.mask |= CDC_OPEN_TRAY;
|
||||
if ((buffer[14] >> 6) == 0)
|
||||
cd->info.mask |= CDC_CLOSE_TRAY;
|
||||
}
|
||||
}
|
||||
|
||||
static int pcd_detect(void)
|
||||
{
|
||||
char id[18];
|
||||
int k, unit;
|
||||
struct pcd_unit *cd;
|
||||
|
||||
printk("%s: %s version %s, major %d, nice %d\n",
|
||||
name, name, PCD_VERSION, major, nice);
|
||||
|
||||
k = 0;
|
||||
if (pcd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
|
||||
cd = pcd;
|
||||
if (pi_init(cd->pi, 1, -1, -1, -1, -1, -1, pcd_buffer,
|
||||
PI_PCD, verbose, cd->name)) {
|
||||
if (!pcd_probe(cd, -1, id) && cd->disk) {
|
||||
cd->present = 1;
|
||||
k++;
|
||||
} else
|
||||
pi_release(cd->pi);
|
||||
}
|
||||
} else {
|
||||
for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
|
||||
int *conf = *drives[unit];
|
||||
if (!conf[D_PRT])
|
||||
continue;
|
||||
if (!pi_init(cd->pi, 0, conf[D_PRT], conf[D_MOD],
|
||||
conf[D_UNI], conf[D_PRO], conf[D_DLY],
|
||||
pcd_buffer, PI_PCD, verbose, cd->name))
|
||||
continue;
|
||||
if (!pcd_probe(cd, conf[D_SLV], id) && cd->disk) {
|
||||
cd->present = 1;
|
||||
k++;
|
||||
} else
|
||||
pi_release(cd->pi);
|
||||
}
|
||||
}
|
||||
if (k)
|
||||
return 0;
|
||||
|
||||
printk("%s: No CD-ROM drive found\n", name);
|
||||
for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++)
|
||||
put_disk(cd->disk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* I/O request processing */
|
||||
static struct request_queue *pcd_queue;
|
||||
|
||||
static void do_pcd_request(request_queue_t * q)
|
||||
{
|
||||
if (pcd_busy)
|
||||
return;
|
||||
while (1) {
|
||||
pcd_req = elv_next_request(q);
|
||||
if (!pcd_req)
|
||||
return;
|
||||
|
||||
if (rq_data_dir(pcd_req) == READ) {
|
||||
struct pcd_unit *cd = pcd_req->rq_disk->private_data;
|
||||
if (cd != pcd_current)
|
||||
pcd_bufblk = -1;
|
||||
pcd_current = cd;
|
||||
pcd_sector = pcd_req->sector;
|
||||
pcd_count = pcd_req->current_nr_sectors;
|
||||
pcd_buf = pcd_req->buffer;
|
||||
pcd_busy = 1;
|
||||
ps_set_intr(do_pcd_read, NULL, 0, nice);
|
||||
return;
|
||||
} else
|
||||
end_request(pcd_req, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void next_request(int success)
|
||||
{
|
||||
unsigned long saved_flags;
|
||||
|
||||
spin_lock_irqsave(&pcd_lock, saved_flags);
|
||||
end_request(pcd_req, success);
|
||||
pcd_busy = 0;
|
||||
do_pcd_request(pcd_queue);
|
||||
spin_unlock_irqrestore(&pcd_lock, saved_flags);
|
||||
}
|
||||
|
||||
static int pcd_ready(void)
|
||||
{
|
||||
return (((status_reg(pcd_current) & (IDE_BUSY | IDE_DRQ)) == IDE_DRQ));
|
||||
}
|
||||
|
||||
static void pcd_transfer(void)
|
||||
{
|
||||
|
||||
while (pcd_count && (pcd_sector / 4 == pcd_bufblk)) {
|
||||
int o = (pcd_sector % 4) * 512;
|
||||
memcpy(pcd_buf, pcd_buffer + o, 512);
|
||||
pcd_count--;
|
||||
pcd_buf += 512;
|
||||
pcd_sector++;
|
||||
}
|
||||
}
|
||||
|
||||
static void pcd_start(void)
|
||||
{
|
||||
int b, i;
|
||||
char rd_cmd[12] = { 0xa8, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
|
||||
|
||||
pcd_bufblk = pcd_sector / 4;
|
||||
b = pcd_bufblk;
|
||||
for (i = 0; i < 4; i++) {
|
||||
rd_cmd[5 - i] = b & 0xff;
|
||||
b = b >> 8;
|
||||
}
|
||||
|
||||
if (pcd_command(pcd_current, rd_cmd, 2048, "read block")) {
|
||||
pcd_bufblk = -1;
|
||||
next_request(0);
|
||||
return;
|
||||
}
|
||||
|
||||
mdelay(1);
|
||||
|
||||
ps_set_intr(do_pcd_read_drq, pcd_ready, PCD_TMO, nice);
|
||||
}
|
||||
|
||||
static void do_pcd_read(void)
|
||||
{
|
||||
pcd_busy = 1;
|
||||
pcd_retries = 0;
|
||||
pcd_transfer();
|
||||
if (!pcd_count) {
|
||||
next_request(1);
|
||||
return;
|
||||
}
|
||||
|
||||
pi_do_claimed(pcd_current->pi, pcd_start);
|
||||
}
|
||||
|
||||
static void do_pcd_read_drq(void)
|
||||
{
|
||||
unsigned long saved_flags;
|
||||
|
||||
if (pcd_completion(pcd_current, pcd_buffer, "read block")) {
|
||||
if (pcd_retries < PCD_RETRIES) {
|
||||
mdelay(1);
|
||||
pcd_retries++;
|
||||
pi_do_claimed(pcd_current->pi, pcd_start);
|
||||
return;
|
||||
}
|
||||
pcd_bufblk = -1;
|
||||
next_request(0);
|
||||
return;
|
||||
}
|
||||
|
||||
do_pcd_read();
|
||||
spin_lock_irqsave(&pcd_lock, saved_flags);
|
||||
do_pcd_request(pcd_queue);
|
||||
spin_unlock_irqrestore(&pcd_lock, saved_flags);
|
||||
}
|
||||
|
||||
/* the audio_ioctl stuff is adapted from sr_ioctl.c */
|
||||
|
||||
static int pcd_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct pcd_unit *cd = cdi->handle;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case CDROMREADTOCHDR:
|
||||
|
||||
{
|
||||
char cmd[12] =
|
||||
{ GPCMD_READ_TOC_PMA_ATIP, 0, 0, 0, 0, 0, 0, 0, 12,
|
||||
0, 0, 0 };
|
||||
struct cdrom_tochdr *tochdr =
|
||||
(struct cdrom_tochdr *) arg;
|
||||
char buffer[32];
|
||||
int r;
|
||||
|
||||
r = pcd_atapi(cd, cmd, 12, buffer, "read toc header");
|
||||
|
||||
tochdr->cdth_trk0 = buffer[2];
|
||||
tochdr->cdth_trk1 = buffer[3];
|
||||
|
||||
return r ? -EIO : 0;
|
||||
}
|
||||
|
||||
case CDROMREADTOCENTRY:
|
||||
|
||||
{
|
||||
char cmd[12] =
|
||||
{ GPCMD_READ_TOC_PMA_ATIP, 0, 0, 0, 0, 0, 0, 0, 12,
|
||||
0, 0, 0 };
|
||||
|
||||
struct cdrom_tocentry *tocentry =
|
||||
(struct cdrom_tocentry *) arg;
|
||||
unsigned char buffer[32];
|
||||
int r;
|
||||
|
||||
cmd[1] =
|
||||
(tocentry->cdte_format == CDROM_MSF ? 0x02 : 0);
|
||||
cmd[6] = tocentry->cdte_track;
|
||||
|
||||
r = pcd_atapi(cd, cmd, 12, buffer, "read toc entry");
|
||||
|
||||
tocentry->cdte_ctrl = buffer[5] & 0xf;
|
||||
tocentry->cdte_adr = buffer[5] >> 4;
|
||||
tocentry->cdte_datamode =
|
||||
(tocentry->cdte_ctrl & 0x04) ? 1 : 0;
|
||||
if (tocentry->cdte_format == CDROM_MSF) {
|
||||
tocentry->cdte_addr.msf.minute = buffer[9];
|
||||
tocentry->cdte_addr.msf.second = buffer[10];
|
||||
tocentry->cdte_addr.msf.frame = buffer[11];
|
||||
} else
|
||||
tocentry->cdte_addr.lba =
|
||||
(((((buffer[8] << 8) + buffer[9]) << 8)
|
||||
+ buffer[10]) << 8) + buffer[11];
|
||||
|
||||
return r ? -EIO : 0;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
return -ENOSYS;
|
||||
}
|
||||
}
|
||||
|
||||
static int pcd_get_mcn(struct cdrom_device_info *cdi, struct cdrom_mcn *mcn)
|
||||
{
|
||||
char cmd[12] =
|
||||
{ GPCMD_READ_SUBCHANNEL, 0, 0x40, 2, 0, 0, 0, 0, 24, 0, 0, 0 };
|
||||
char buffer[32];
|
||||
|
||||
if (pcd_atapi(cdi->handle, cmd, 24, buffer, "get mcn"))
|
||||
return -EIO;
|
||||
|
||||
memcpy(mcn->medium_catalog_number, buffer + 9, 13);
|
||||
mcn->medium_catalog_number[13] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init pcd_init(void)
|
||||
{
|
||||
struct pcd_unit *cd;
|
||||
int unit;
|
||||
|
||||
if (disable)
|
||||
return -EINVAL;
|
||||
|
||||
pcd_init_units();
|
||||
|
||||
if (pcd_detect())
|
||||
return -ENODEV;
|
||||
|
||||
/* get the atapi capabilities page */
|
||||
pcd_probe_capabilities();
|
||||
|
||||
if (register_blkdev(major, name)) {
|
||||
for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++)
|
||||
put_disk(cd->disk);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
pcd_queue = blk_init_queue(do_pcd_request, &pcd_lock);
|
||||
if (!pcd_queue) {
|
||||
unregister_blkdev(major, name);
|
||||
for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++)
|
||||
put_disk(cd->disk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
|
||||
if (cd->present) {
|
||||
register_cdrom(&cd->info);
|
||||
cd->disk->private_data = cd;
|
||||
cd->disk->queue = pcd_queue;
|
||||
add_disk(cd->disk);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit pcd_exit(void)
|
||||
{
|
||||
struct pcd_unit *cd;
|
||||
int unit;
|
||||
|
||||
for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
|
||||
if (cd->present) {
|
||||
del_gendisk(cd->disk);
|
||||
pi_release(cd->pi);
|
||||
unregister_cdrom(&cd->info);
|
||||
}
|
||||
put_disk(cd->disk);
|
||||
}
|
||||
blk_cleanup_queue(pcd_queue);
|
||||
unregister_blkdev(major, name);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(pcd_init)
|
||||
module_exit(pcd_exit)
|
||||
950
drivers/block/paride/pd.c
Normal file
950
drivers/block/paride/pd.c
Normal file
@@ -0,0 +1,950 @@
|
||||
/*
|
||||
pd.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
This is the high-level driver for parallel port IDE hard
|
||||
drives based on chips supported by the paride module.
|
||||
|
||||
By default, the driver will autoprobe for a single parallel
|
||||
port IDE drive, but if their individual parameters are
|
||||
specified, the driver can handle up to 4 drives.
|
||||
|
||||
The behaviour of the pd driver can be altered by setting
|
||||
some parameters from the insmod command line. The following
|
||||
parameters are adjustable:
|
||||
|
||||
drive0 These four arguments can be arrays of
|
||||
drive1 1-8 integers as follows:
|
||||
drive2
|
||||
drive3 <prt>,<pro>,<uni>,<mod>,<geo>,<sby>,<dly>,<slv>
|
||||
|
||||
Where,
|
||||
|
||||
<prt> is the base of the parallel port address for
|
||||
the corresponding drive. (required)
|
||||
|
||||
<pro> is the protocol number for the adapter that
|
||||
supports this drive. These numbers are
|
||||
logged by 'paride' when the protocol modules
|
||||
are initialised. (0 if not given)
|
||||
|
||||
<uni> for those adapters that support chained
|
||||
devices, this is the unit selector for the
|
||||
chain of devices on the given port. It should
|
||||
be zero for devices that don't support chaining.
|
||||
(0 if not given)
|
||||
|
||||
<mod> this can be -1 to choose the best mode, or one
|
||||
of the mode numbers supported by the adapter.
|
||||
(-1 if not given)
|
||||
|
||||
<geo> this defaults to 0 to indicate that the driver
|
||||
should use the CHS geometry provided by the drive
|
||||
itself. If set to 1, the driver will provide
|
||||
a logical geometry with 64 heads and 32 sectors
|
||||
per track, to be consistent with most SCSI
|
||||
drivers. (0 if not given)
|
||||
|
||||
<sby> set this to zero to disable the power saving
|
||||
standby mode, if needed. (1 if not given)
|
||||
|
||||
<dly> some parallel ports require the driver to
|
||||
go more slowly. -1 sets a default value that
|
||||
should work with the chosen protocol. Otherwise,
|
||||
set this to a small integer, the larger it is
|
||||
the slower the port i/o. In some cases, setting
|
||||
this to zero will speed up the device. (default -1)
|
||||
|
||||
<slv> IDE disks can be jumpered to master or slave.
|
||||
Set this to 0 to choose the master drive, 1 to
|
||||
choose the slave, -1 (the default) to choose the
|
||||
first drive found.
|
||||
|
||||
|
||||
major You may use this parameter to overide the
|
||||
default major number (45) that this driver
|
||||
will use. Be sure to change the device
|
||||
name as well.
|
||||
|
||||
name This parameter is a character string that
|
||||
contains the name the kernel will use for this
|
||||
device (in /proc output, for instance).
|
||||
(default "pd")
|
||||
|
||||
cluster The driver will attempt to aggregate requests
|
||||
for adjacent blocks into larger multi-block
|
||||
clusters. The maximum cluster size (in 512
|
||||
byte sectors) is set with this parameter.
|
||||
(default 64)
|
||||
|
||||
verbose This parameter controls the amount of logging
|
||||
that the driver will do. Set it to 0 for
|
||||
normal operation, 1 to see autoprobe progress
|
||||
messages, or 2 to see additional debugging
|
||||
output. (default 0)
|
||||
|
||||
nice This parameter controls the driver's use of
|
||||
idle CPU time, at the expense of some speed.
|
||||
|
||||
If this driver is built into the kernel, you can use kernel
|
||||
the following command line parameters, with the same values
|
||||
as the corresponding module parameters listed above:
|
||||
|
||||
pd.drive0
|
||||
pd.drive1
|
||||
pd.drive2
|
||||
pd.drive3
|
||||
pd.cluster
|
||||
pd.nice
|
||||
|
||||
In addition, you can use the parameter pd.disable to disable
|
||||
the driver entirely.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1997.01.24 Restored pd_reset()
|
||||
Added eject ioctl
|
||||
1.02 GRG 1998.05.06 SMP spinlock changes,
|
||||
Added slave support
|
||||
1.03 GRG 1998.06.16 Eliminate an Ugh.
|
||||
1.04 GRG 1998.08.15 Extra debugging, use HZ in loop timing
|
||||
1.05 GRG 1998.09.24 Added jumbo support
|
||||
|
||||
*/
|
||||
|
||||
#define PD_VERSION "1.05"
|
||||
#define PD_MAJOR 45
|
||||
#define PD_NAME "pd"
|
||||
#define PD_UNITS 4
|
||||
|
||||
/* Here are things one can override from the insmod command.
|
||||
Most are autoprobed by paride unless set here. Verbose is off
|
||||
by default.
|
||||
|
||||
*/
|
||||
|
||||
static int verbose = 0;
|
||||
static int major = PD_MAJOR;
|
||||
static char *name = PD_NAME;
|
||||
static int cluster = 64;
|
||||
static int nice = 0;
|
||||
static int disable = 0;
|
||||
|
||||
static int drive0[8] = { 0, 0, 0, -1, 0, 1, -1, -1 };
|
||||
static int drive1[8] = { 0, 0, 0, -1, 0, 1, -1, -1 };
|
||||
static int drive2[8] = { 0, 0, 0, -1, 0, 1, -1, -1 };
|
||||
static int drive3[8] = { 0, 0, 0, -1, 0, 1, -1, -1 };
|
||||
|
||||
static int (*drives[4])[8] = {&drive0, &drive1, &drive2, &drive3};
|
||||
|
||||
enum {D_PRT, D_PRO, D_UNI, D_MOD, D_GEO, D_SBY, D_DLY, D_SLV};
|
||||
|
||||
/* end of parameters */
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/cdrom.h> /* for the eject ioctl */
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/blkpg.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
static DEFINE_SPINLOCK(pd_lock);
|
||||
|
||||
module_param(verbose, bool, 0);
|
||||
module_param(major, int, 0);
|
||||
module_param(name, charp, 0);
|
||||
module_param(cluster, int, 0);
|
||||
module_param(nice, int, 0);
|
||||
module_param_array(drive0, int, NULL, 0);
|
||||
module_param_array(drive1, int, NULL, 0);
|
||||
module_param_array(drive2, int, NULL, 0);
|
||||
module_param_array(drive3, int, NULL, 0);
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define PD_BITS 4
|
||||
|
||||
/* numbers for "SCSI" geometry */
|
||||
|
||||
#define PD_LOG_HEADS 64
|
||||
#define PD_LOG_SECTS 32
|
||||
|
||||
#define PD_ID_OFF 54
|
||||
#define PD_ID_LEN 14
|
||||
|
||||
#define PD_MAX_RETRIES 5
|
||||
#define PD_TMO 800 /* interrupt timeout in jiffies */
|
||||
#define PD_SPIN_DEL 50 /* spin delay in micro-seconds */
|
||||
|
||||
#define PD_SPIN (1000000*PD_TMO)/(HZ*PD_SPIN_DEL)
|
||||
|
||||
#define STAT_ERR 0x00001
|
||||
#define STAT_INDEX 0x00002
|
||||
#define STAT_ECC 0x00004
|
||||
#define STAT_DRQ 0x00008
|
||||
#define STAT_SEEK 0x00010
|
||||
#define STAT_WRERR 0x00020
|
||||
#define STAT_READY 0x00040
|
||||
#define STAT_BUSY 0x00080
|
||||
|
||||
#define ERR_AMNF 0x00100
|
||||
#define ERR_TK0NF 0x00200
|
||||
#define ERR_ABRT 0x00400
|
||||
#define ERR_MCR 0x00800
|
||||
#define ERR_IDNF 0x01000
|
||||
#define ERR_MC 0x02000
|
||||
#define ERR_UNC 0x04000
|
||||
#define ERR_TMO 0x10000
|
||||
|
||||
#define IDE_READ 0x20
|
||||
#define IDE_WRITE 0x30
|
||||
#define IDE_READ_VRFY 0x40
|
||||
#define IDE_INIT_DEV_PARMS 0x91
|
||||
#define IDE_STANDBY 0x96
|
||||
#define IDE_ACKCHANGE 0xdb
|
||||
#define IDE_DOORLOCK 0xde
|
||||
#define IDE_DOORUNLOCK 0xdf
|
||||
#define IDE_IDENTIFY 0xec
|
||||
#define IDE_EJECT 0xed
|
||||
|
||||
#define PD_NAMELEN 8
|
||||
|
||||
struct pd_unit {
|
||||
struct pi_adapter pia; /* interface to paride layer */
|
||||
struct pi_adapter *pi;
|
||||
int access; /* count of active opens ... */
|
||||
int capacity; /* Size of this volume in sectors */
|
||||
int heads; /* physical geometry */
|
||||
int sectors;
|
||||
int cylinders;
|
||||
int can_lba;
|
||||
int drive; /* master=0 slave=1 */
|
||||
int changed; /* Have we seen a disk change ? */
|
||||
int removable; /* removable media device ? */
|
||||
int standby;
|
||||
int alt_geom;
|
||||
char name[PD_NAMELEN]; /* pda, pdb, etc ... */
|
||||
struct gendisk *gd;
|
||||
};
|
||||
|
||||
static struct pd_unit pd[PD_UNITS];
|
||||
|
||||
static char pd_scratch[512]; /* scratch block buffer */
|
||||
|
||||
static char *pd_errs[17] = { "ERR", "INDEX", "ECC", "DRQ", "SEEK", "WRERR",
|
||||
"READY", "BUSY", "AMNF", "TK0NF", "ABRT", "MCR",
|
||||
"IDNF", "MC", "UNC", "???", "TMO"
|
||||
};
|
||||
|
||||
static inline int status_reg(struct pd_unit *disk)
|
||||
{
|
||||
return pi_read_regr(disk->pi, 1, 6);
|
||||
}
|
||||
|
||||
static inline int read_reg(struct pd_unit *disk, int reg)
|
||||
{
|
||||
return pi_read_regr(disk->pi, 0, reg);
|
||||
}
|
||||
|
||||
static inline void write_status(struct pd_unit *disk, int val)
|
||||
{
|
||||
pi_write_regr(disk->pi, 1, 6, val);
|
||||
}
|
||||
|
||||
static inline void write_reg(struct pd_unit *disk, int reg, int val)
|
||||
{
|
||||
pi_write_regr(disk->pi, 0, reg, val);
|
||||
}
|
||||
|
||||
static inline u8 DRIVE(struct pd_unit *disk)
|
||||
{
|
||||
return 0xa0+0x10*disk->drive;
|
||||
}
|
||||
|
||||
/* ide command interface */
|
||||
|
||||
static void pd_print_error(struct pd_unit *disk, char *msg, int status)
|
||||
{
|
||||
int i;
|
||||
|
||||
printk("%s: %s: status = 0x%x =", disk->name, msg, status);
|
||||
for (i = 0; i < ARRAY_SIZE(pd_errs); i++)
|
||||
if (status & (1 << i))
|
||||
printk(" %s", pd_errs[i]);
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
static void pd_reset(struct pd_unit *disk)
|
||||
{ /* called only for MASTER drive */
|
||||
write_status(disk, 4);
|
||||
udelay(50);
|
||||
write_status(disk, 0);
|
||||
udelay(250);
|
||||
}
|
||||
|
||||
#define DBMSG(msg) ((verbose>1)?(msg):NULL)
|
||||
|
||||
static int pd_wait_for(struct pd_unit *disk, int w, char *msg)
|
||||
{ /* polled wait */
|
||||
int k, r, e;
|
||||
|
||||
k = 0;
|
||||
while (k < PD_SPIN) {
|
||||
r = status_reg(disk);
|
||||
k++;
|
||||
if (((r & w) == w) && !(r & STAT_BUSY))
|
||||
break;
|
||||
udelay(PD_SPIN_DEL);
|
||||
}
|
||||
e = (read_reg(disk, 1) << 8) + read_reg(disk, 7);
|
||||
if (k >= PD_SPIN)
|
||||
e |= ERR_TMO;
|
||||
if ((e & (STAT_ERR | ERR_TMO)) && (msg != NULL))
|
||||
pd_print_error(disk, msg, e);
|
||||
return e;
|
||||
}
|
||||
|
||||
static void pd_send_command(struct pd_unit *disk, int n, int s, int h, int c0, int c1, int func)
|
||||
{
|
||||
write_reg(disk, 6, DRIVE(disk) + h);
|
||||
write_reg(disk, 1, 0); /* the IDE task file */
|
||||
write_reg(disk, 2, n);
|
||||
write_reg(disk, 3, s);
|
||||
write_reg(disk, 4, c0);
|
||||
write_reg(disk, 5, c1);
|
||||
write_reg(disk, 7, func);
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
static void pd_ide_command(struct pd_unit *disk, int func, int block, int count)
|
||||
{
|
||||
int c1, c0, h, s;
|
||||
|
||||
if (disk->can_lba) {
|
||||
s = block & 255;
|
||||
c0 = (block >>= 8) & 255;
|
||||
c1 = (block >>= 8) & 255;
|
||||
h = ((block >>= 8) & 15) + 0x40;
|
||||
} else {
|
||||
s = (block % disk->sectors) + 1;
|
||||
h = (block /= disk->sectors) % disk->heads;
|
||||
c0 = (block /= disk->heads) % 256;
|
||||
c1 = (block >>= 8);
|
||||
}
|
||||
pd_send_command(disk, count, s, h, c0, c1, func);
|
||||
}
|
||||
|
||||
/* The i/o request engine */
|
||||
|
||||
enum action {Fail = 0, Ok = 1, Hold, Wait};
|
||||
|
||||
static struct request *pd_req; /* current request */
|
||||
static enum action (*phase)(void);
|
||||
|
||||
static void run_fsm(void);
|
||||
|
||||
static void ps_tq_int(struct work_struct *work);
|
||||
|
||||
static DECLARE_DELAYED_WORK(fsm_tq, ps_tq_int);
|
||||
|
||||
static void schedule_fsm(void)
|
||||
{
|
||||
if (!nice)
|
||||
schedule_delayed_work(&fsm_tq, 0);
|
||||
else
|
||||
schedule_delayed_work(&fsm_tq, nice-1);
|
||||
}
|
||||
|
||||
static void ps_tq_int(struct work_struct *work)
|
||||
{
|
||||
run_fsm();
|
||||
}
|
||||
|
||||
static enum action do_pd_io_start(void);
|
||||
static enum action pd_special(void);
|
||||
static enum action do_pd_read_start(void);
|
||||
static enum action do_pd_write_start(void);
|
||||
static enum action do_pd_read_drq(void);
|
||||
static enum action do_pd_write_done(void);
|
||||
|
||||
static struct request_queue *pd_queue;
|
||||
static int pd_claimed;
|
||||
|
||||
static struct pd_unit *pd_current; /* current request's drive */
|
||||
static PIA *pi_current; /* current request's PIA */
|
||||
|
||||
static void run_fsm(void)
|
||||
{
|
||||
while (1) {
|
||||
enum action res;
|
||||
unsigned long saved_flags;
|
||||
int stop = 0;
|
||||
|
||||
if (!phase) {
|
||||
pd_current = pd_req->rq_disk->private_data;
|
||||
pi_current = pd_current->pi;
|
||||
phase = do_pd_io_start;
|
||||
}
|
||||
|
||||
switch (pd_claimed) {
|
||||
case 0:
|
||||
pd_claimed = 1;
|
||||
if (!pi_schedule_claimed(pi_current, run_fsm))
|
||||
return;
|
||||
case 1:
|
||||
pd_claimed = 2;
|
||||
pi_current->proto->connect(pi_current);
|
||||
}
|
||||
|
||||
switch(res = phase()) {
|
||||
case Ok: case Fail:
|
||||
pi_disconnect(pi_current);
|
||||
pd_claimed = 0;
|
||||
phase = NULL;
|
||||
spin_lock_irqsave(&pd_lock, saved_flags);
|
||||
end_request(pd_req, res);
|
||||
pd_req = elv_next_request(pd_queue);
|
||||
if (!pd_req)
|
||||
stop = 1;
|
||||
spin_unlock_irqrestore(&pd_lock, saved_flags);
|
||||
if (stop)
|
||||
return;
|
||||
case Hold:
|
||||
schedule_fsm();
|
||||
return;
|
||||
case Wait:
|
||||
pi_disconnect(pi_current);
|
||||
pd_claimed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int pd_retries = 0; /* i/o error retry count */
|
||||
static int pd_block; /* address of next requested block */
|
||||
static int pd_count; /* number of blocks still to do */
|
||||
static int pd_run; /* sectors in current cluster */
|
||||
static int pd_cmd; /* current command READ/WRITE */
|
||||
static char *pd_buf; /* buffer for request in progress */
|
||||
|
||||
static enum action do_pd_io_start(void)
|
||||
{
|
||||
if (blk_special_request(pd_req)) {
|
||||
phase = pd_special;
|
||||
return pd_special();
|
||||
}
|
||||
|
||||
pd_cmd = rq_data_dir(pd_req);
|
||||
if (pd_cmd == READ || pd_cmd == WRITE) {
|
||||
pd_block = pd_req->sector;
|
||||
pd_count = pd_req->current_nr_sectors;
|
||||
if (pd_block + pd_count > get_capacity(pd_req->rq_disk))
|
||||
return Fail;
|
||||
pd_run = pd_req->nr_sectors;
|
||||
pd_buf = pd_req->buffer;
|
||||
pd_retries = 0;
|
||||
if (pd_cmd == READ)
|
||||
return do_pd_read_start();
|
||||
else
|
||||
return do_pd_write_start();
|
||||
}
|
||||
return Fail;
|
||||
}
|
||||
|
||||
static enum action pd_special(void)
|
||||
{
|
||||
enum action (*func)(struct pd_unit *) = pd_req->special;
|
||||
return func(pd_current);
|
||||
}
|
||||
|
||||
static int pd_next_buf(void)
|
||||
{
|
||||
unsigned long saved_flags;
|
||||
|
||||
pd_count--;
|
||||
pd_run--;
|
||||
pd_buf += 512;
|
||||
pd_block++;
|
||||
if (!pd_run)
|
||||
return 1;
|
||||
if (pd_count)
|
||||
return 0;
|
||||
spin_lock_irqsave(&pd_lock, saved_flags);
|
||||
end_request(pd_req, 1);
|
||||
pd_count = pd_req->current_nr_sectors;
|
||||
pd_buf = pd_req->buffer;
|
||||
spin_unlock_irqrestore(&pd_lock, saved_flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long pd_timeout;
|
||||
|
||||
static enum action do_pd_read_start(void)
|
||||
{
|
||||
if (pd_wait_for(pd_current, STAT_READY, "do_pd_read") & STAT_ERR) {
|
||||
if (pd_retries < PD_MAX_RETRIES) {
|
||||
pd_retries++;
|
||||
return Wait;
|
||||
}
|
||||
return Fail;
|
||||
}
|
||||
pd_ide_command(pd_current, IDE_READ, pd_block, pd_run);
|
||||
phase = do_pd_read_drq;
|
||||
pd_timeout = jiffies + PD_TMO;
|
||||
return Hold;
|
||||
}
|
||||
|
||||
static enum action do_pd_write_start(void)
|
||||
{
|
||||
if (pd_wait_for(pd_current, STAT_READY, "do_pd_write") & STAT_ERR) {
|
||||
if (pd_retries < PD_MAX_RETRIES) {
|
||||
pd_retries++;
|
||||
return Wait;
|
||||
}
|
||||
return Fail;
|
||||
}
|
||||
pd_ide_command(pd_current, IDE_WRITE, pd_block, pd_run);
|
||||
while (1) {
|
||||
if (pd_wait_for(pd_current, STAT_DRQ, "do_pd_write_drq") & STAT_ERR) {
|
||||
if (pd_retries < PD_MAX_RETRIES) {
|
||||
pd_retries++;
|
||||
return Wait;
|
||||
}
|
||||
return Fail;
|
||||
}
|
||||
pi_write_block(pd_current->pi, pd_buf, 512);
|
||||
if (pd_next_buf())
|
||||
break;
|
||||
}
|
||||
phase = do_pd_write_done;
|
||||
pd_timeout = jiffies + PD_TMO;
|
||||
return Hold;
|
||||
}
|
||||
|
||||
static inline int pd_ready(void)
|
||||
{
|
||||
return !(status_reg(pd_current) & STAT_BUSY);
|
||||
}
|
||||
|
||||
static enum action do_pd_read_drq(void)
|
||||
{
|
||||
if (!pd_ready() && !time_after_eq(jiffies, pd_timeout))
|
||||
return Hold;
|
||||
|
||||
while (1) {
|
||||
if (pd_wait_for(pd_current, STAT_DRQ, "do_pd_read_drq") & STAT_ERR) {
|
||||
if (pd_retries < PD_MAX_RETRIES) {
|
||||
pd_retries++;
|
||||
phase = do_pd_read_start;
|
||||
return Wait;
|
||||
}
|
||||
return Fail;
|
||||
}
|
||||
pi_read_block(pd_current->pi, pd_buf, 512);
|
||||
if (pd_next_buf())
|
||||
break;
|
||||
}
|
||||
return Ok;
|
||||
}
|
||||
|
||||
static enum action do_pd_write_done(void)
|
||||
{
|
||||
if (!pd_ready() && !time_after_eq(jiffies, pd_timeout))
|
||||
return Hold;
|
||||
|
||||
if (pd_wait_for(pd_current, STAT_READY, "do_pd_write_done") & STAT_ERR) {
|
||||
if (pd_retries < PD_MAX_RETRIES) {
|
||||
pd_retries++;
|
||||
phase = do_pd_write_start;
|
||||
return Wait;
|
||||
}
|
||||
return Fail;
|
||||
}
|
||||
return Ok;
|
||||
}
|
||||
|
||||
/* special io requests */
|
||||
|
||||
/* According to the ATA standard, the default CHS geometry should be
|
||||
available following a reset. Some Western Digital drives come up
|
||||
in a mode where only LBA addresses are accepted until the device
|
||||
parameters are initialised.
|
||||
*/
|
||||
|
||||
static void pd_init_dev_parms(struct pd_unit *disk)
|
||||
{
|
||||
pd_wait_for(disk, 0, DBMSG("before init_dev_parms"));
|
||||
pd_send_command(disk, disk->sectors, 0, disk->heads - 1, 0, 0,
|
||||
IDE_INIT_DEV_PARMS);
|
||||
udelay(300);
|
||||
pd_wait_for(disk, 0, "Initialise device parameters");
|
||||
}
|
||||
|
||||
static enum action pd_door_lock(struct pd_unit *disk)
|
||||
{
|
||||
if (!(pd_wait_for(disk, STAT_READY, "Lock") & STAT_ERR)) {
|
||||
pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORLOCK);
|
||||
pd_wait_for(disk, STAT_READY, "Lock done");
|
||||
}
|
||||
return Ok;
|
||||
}
|
||||
|
||||
static enum action pd_door_unlock(struct pd_unit *disk)
|
||||
{
|
||||
if (!(pd_wait_for(disk, STAT_READY, "Lock") & STAT_ERR)) {
|
||||
pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORUNLOCK);
|
||||
pd_wait_for(disk, STAT_READY, "Lock done");
|
||||
}
|
||||
return Ok;
|
||||
}
|
||||
|
||||
static enum action pd_eject(struct pd_unit *disk)
|
||||
{
|
||||
pd_wait_for(disk, 0, DBMSG("before unlock on eject"));
|
||||
pd_send_command(disk, 1, 0, 0, 0, 0, IDE_DOORUNLOCK);
|
||||
pd_wait_for(disk, 0, DBMSG("after unlock on eject"));
|
||||
pd_wait_for(disk, 0, DBMSG("before eject"));
|
||||
pd_send_command(disk, 0, 0, 0, 0, 0, IDE_EJECT);
|
||||
pd_wait_for(disk, 0, DBMSG("after eject"));
|
||||
return Ok;
|
||||
}
|
||||
|
||||
static enum action pd_media_check(struct pd_unit *disk)
|
||||
{
|
||||
int r = pd_wait_for(disk, STAT_READY, DBMSG("before media_check"));
|
||||
if (!(r & STAT_ERR)) {
|
||||
pd_send_command(disk, 1, 1, 0, 0, 0, IDE_READ_VRFY);
|
||||
r = pd_wait_for(disk, STAT_READY, DBMSG("RDY after READ_VRFY"));
|
||||
} else
|
||||
disk->changed = 1; /* say changed if other error */
|
||||
if (r & ERR_MC) {
|
||||
disk->changed = 1;
|
||||
pd_send_command(disk, 1, 0, 0, 0, 0, IDE_ACKCHANGE);
|
||||
pd_wait_for(disk, STAT_READY, DBMSG("RDY after ACKCHANGE"));
|
||||
pd_send_command(disk, 1, 1, 0, 0, 0, IDE_READ_VRFY);
|
||||
r = pd_wait_for(disk, STAT_READY, DBMSG("RDY after VRFY"));
|
||||
}
|
||||
return Ok;
|
||||
}
|
||||
|
||||
static void pd_standby_off(struct pd_unit *disk)
|
||||
{
|
||||
pd_wait_for(disk, 0, DBMSG("before STANDBY"));
|
||||
pd_send_command(disk, 0, 0, 0, 0, 0, IDE_STANDBY);
|
||||
pd_wait_for(disk, 0, DBMSG("after STANDBY"));
|
||||
}
|
||||
|
||||
static enum action pd_identify(struct pd_unit *disk)
|
||||
{
|
||||
int j;
|
||||
char id[PD_ID_LEN + 1];
|
||||
|
||||
/* WARNING: here there may be dragons. reset() applies to both drives,
|
||||
but we call it only on probing the MASTER. This should allow most
|
||||
common configurations to work, but be warned that a reset can clear
|
||||
settings on the SLAVE drive.
|
||||
*/
|
||||
|
||||
if (disk->drive == 0)
|
||||
pd_reset(disk);
|
||||
|
||||
write_reg(disk, 6, DRIVE(disk));
|
||||
pd_wait_for(disk, 0, DBMSG("before IDENT"));
|
||||
pd_send_command(disk, 1, 0, 0, 0, 0, IDE_IDENTIFY);
|
||||
|
||||
if (pd_wait_for(disk, STAT_DRQ, DBMSG("IDENT DRQ")) & STAT_ERR)
|
||||
return Fail;
|
||||
pi_read_block(disk->pi, pd_scratch, 512);
|
||||
disk->can_lba = pd_scratch[99] & 2;
|
||||
disk->sectors = le16_to_cpu(*(__le16 *) (pd_scratch + 12));
|
||||
disk->heads = le16_to_cpu(*(__le16 *) (pd_scratch + 6));
|
||||
disk->cylinders = le16_to_cpu(*(__le16 *) (pd_scratch + 2));
|
||||
if (disk->can_lba)
|
||||
disk->capacity = le32_to_cpu(*(__le32 *) (pd_scratch + 120));
|
||||
else
|
||||
disk->capacity = disk->sectors * disk->heads * disk->cylinders;
|
||||
|
||||
for (j = 0; j < PD_ID_LEN; j++)
|
||||
id[j ^ 1] = pd_scratch[j + PD_ID_OFF];
|
||||
j = PD_ID_LEN - 1;
|
||||
while ((j >= 0) && (id[j] <= 0x20))
|
||||
j--;
|
||||
j++;
|
||||
id[j] = 0;
|
||||
|
||||
disk->removable = pd_scratch[0] & 0x80;
|
||||
|
||||
printk("%s: %s, %s, %d blocks [%dM], (%d/%d/%d), %s media\n",
|
||||
disk->name, id,
|
||||
disk->drive ? "slave" : "master",
|
||||
disk->capacity, disk->capacity / 2048,
|
||||
disk->cylinders, disk->heads, disk->sectors,
|
||||
disk->removable ? "removable" : "fixed");
|
||||
|
||||
if (disk->capacity)
|
||||
pd_init_dev_parms(disk);
|
||||
if (!disk->standby)
|
||||
pd_standby_off(disk);
|
||||
|
||||
return Ok;
|
||||
}
|
||||
|
||||
/* end of io request engine */
|
||||
|
||||
static void do_pd_request(request_queue_t * q)
|
||||
{
|
||||
if (pd_req)
|
||||
return;
|
||||
pd_req = elv_next_request(q);
|
||||
if (!pd_req)
|
||||
return;
|
||||
|
||||
schedule_fsm();
|
||||
}
|
||||
|
||||
static int pd_special_command(struct pd_unit *disk,
|
||||
enum action (*func)(struct pd_unit *disk))
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(wait);
|
||||
struct request rq;
|
||||
int err = 0;
|
||||
|
||||
memset(&rq, 0, sizeof(rq));
|
||||
rq.errors = 0;
|
||||
rq.rq_disk = disk->gd;
|
||||
rq.ref_count = 1;
|
||||
rq.end_io_data = &wait;
|
||||
rq.end_io = blk_end_sync_rq;
|
||||
blk_insert_request(disk->gd->queue, &rq, 0, func);
|
||||
wait_for_completion(&wait);
|
||||
if (rq.errors)
|
||||
err = -EIO;
|
||||
blk_put_request(&rq);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* kernel glue structures */
|
||||
|
||||
static int pd_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pd_unit *disk = inode->i_bdev->bd_disk->private_data;
|
||||
|
||||
disk->access++;
|
||||
|
||||
if (disk->removable) {
|
||||
pd_special_command(disk, pd_media_check);
|
||||
pd_special_command(disk, pd_door_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
||||
{
|
||||
struct pd_unit *disk = bdev->bd_disk->private_data;
|
||||
|
||||
if (disk->alt_geom) {
|
||||
geo->heads = PD_LOG_HEADS;
|
||||
geo->sectors = PD_LOG_SECTS;
|
||||
geo->cylinders = disk->capacity / (geo->heads * geo->sectors);
|
||||
} else {
|
||||
geo->heads = disk->heads;
|
||||
geo->sectors = disk->sectors;
|
||||
geo->cylinders = disk->cylinders;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pd_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct pd_unit *disk = inode->i_bdev->bd_disk->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case CDROMEJECT:
|
||||
if (disk->access == 1)
|
||||
pd_special_command(disk, pd_eject);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int pd_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pd_unit *disk = inode->i_bdev->bd_disk->private_data;
|
||||
|
||||
if (!--disk->access && disk->removable)
|
||||
pd_special_command(disk, pd_door_unlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pd_check_media(struct gendisk *p)
|
||||
{
|
||||
struct pd_unit *disk = p->private_data;
|
||||
int r;
|
||||
if (!disk->removable)
|
||||
return 0;
|
||||
pd_special_command(disk, pd_media_check);
|
||||
r = disk->changed;
|
||||
disk->changed = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static int pd_revalidate(struct gendisk *p)
|
||||
{
|
||||
struct pd_unit *disk = p->private_data;
|
||||
if (pd_special_command(disk, pd_identify) == 0)
|
||||
set_capacity(p, disk->capacity);
|
||||
else
|
||||
set_capacity(p, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct block_device_operations pd_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pd_open,
|
||||
.release = pd_release,
|
||||
.ioctl = pd_ioctl,
|
||||
.getgeo = pd_getgeo,
|
||||
.media_changed = pd_check_media,
|
||||
.revalidate_disk= pd_revalidate
|
||||
};
|
||||
|
||||
/* probing */
|
||||
|
||||
static void pd_probe_drive(struct pd_unit *disk)
|
||||
{
|
||||
struct gendisk *p = alloc_disk(1 << PD_BITS);
|
||||
if (!p)
|
||||
return;
|
||||
strcpy(p->disk_name, disk->name);
|
||||
p->fops = &pd_fops;
|
||||
p->major = major;
|
||||
p->first_minor = (disk - pd) << PD_BITS;
|
||||
disk->gd = p;
|
||||
p->private_data = disk;
|
||||
p->queue = pd_queue;
|
||||
|
||||
if (disk->drive == -1) {
|
||||
for (disk->drive = 0; disk->drive <= 1; disk->drive++)
|
||||
if (pd_special_command(disk, pd_identify) == 0)
|
||||
return;
|
||||
} else if (pd_special_command(disk, pd_identify) == 0)
|
||||
return;
|
||||
disk->gd = NULL;
|
||||
put_disk(p);
|
||||
}
|
||||
|
||||
static int pd_detect(void)
|
||||
{
|
||||
int found = 0, unit, pd_drive_count = 0;
|
||||
struct pd_unit *disk;
|
||||
|
||||
for (unit = 0; unit < PD_UNITS; unit++) {
|
||||
int *parm = *drives[unit];
|
||||
struct pd_unit *disk = pd + unit;
|
||||
disk->pi = &disk->pia;
|
||||
disk->access = 0;
|
||||
disk->changed = 1;
|
||||
disk->capacity = 0;
|
||||
disk->drive = parm[D_SLV];
|
||||
snprintf(disk->name, PD_NAMELEN, "%s%c", name, 'a'+unit);
|
||||
disk->alt_geom = parm[D_GEO];
|
||||
disk->standby = parm[D_SBY];
|
||||
if (parm[D_PRT])
|
||||
pd_drive_count++;
|
||||
}
|
||||
|
||||
if (pd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
|
||||
disk = pd;
|
||||
if (pi_init(disk->pi, 1, -1, -1, -1, -1, -1, pd_scratch,
|
||||
PI_PD, verbose, disk->name)) {
|
||||
pd_probe_drive(disk);
|
||||
if (!disk->gd)
|
||||
pi_release(disk->pi);
|
||||
}
|
||||
|
||||
} else {
|
||||
for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) {
|
||||
int *parm = *drives[unit];
|
||||
if (!parm[D_PRT])
|
||||
continue;
|
||||
if (pi_init(disk->pi, 0, parm[D_PRT], parm[D_MOD],
|
||||
parm[D_UNI], parm[D_PRO], parm[D_DLY],
|
||||
pd_scratch, PI_PD, verbose, disk->name)) {
|
||||
pd_probe_drive(disk);
|
||||
if (!disk->gd)
|
||||
pi_release(disk->pi);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) {
|
||||
if (disk->gd) {
|
||||
set_capacity(disk->gd, disk->capacity);
|
||||
add_disk(disk->gd);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
printk("%s: no valid drive found\n", name);
|
||||
return found;
|
||||
}
|
||||
|
||||
static int __init pd_init(void)
|
||||
{
|
||||
if (disable)
|
||||
goto out1;
|
||||
|
||||
pd_queue = blk_init_queue(do_pd_request, &pd_lock);
|
||||
if (!pd_queue)
|
||||
goto out1;
|
||||
|
||||
blk_queue_max_sectors(pd_queue, cluster);
|
||||
|
||||
if (register_blkdev(major, name))
|
||||
goto out2;
|
||||
|
||||
printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
|
||||
name, name, PD_VERSION, major, cluster, nice);
|
||||
if (!pd_detect())
|
||||
goto out3;
|
||||
|
||||
return 0;
|
||||
|
||||
out3:
|
||||
unregister_blkdev(major, name);
|
||||
out2:
|
||||
blk_cleanup_queue(pd_queue);
|
||||
out1:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void __exit pd_exit(void)
|
||||
{
|
||||
struct pd_unit *disk;
|
||||
int unit;
|
||||
unregister_blkdev(major, name);
|
||||
for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) {
|
||||
struct gendisk *p = disk->gd;
|
||||
if (p) {
|
||||
disk->gd = NULL;
|
||||
del_gendisk(p);
|
||||
put_disk(p);
|
||||
pi_release(disk->pi);
|
||||
}
|
||||
}
|
||||
blk_cleanup_queue(pd_queue);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(pd_init)
|
||||
module_exit(pd_exit)
|
||||
989
drivers/block/paride/pf.c
Normal file
989
drivers/block/paride/pf.c
Normal file
@@ -0,0 +1,989 @@
|
||||
/*
|
||||
pf.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
This is the high-level driver for parallel port ATAPI disk
|
||||
drives based on chips supported by the paride module.
|
||||
|
||||
By default, the driver will autoprobe for a single parallel
|
||||
port ATAPI disk drive, but if their individual parameters are
|
||||
specified, the driver can handle up to 4 drives.
|
||||
|
||||
The behaviour of the pf driver can be altered by setting
|
||||
some parameters from the insmod command line. The following
|
||||
parameters are adjustable:
|
||||
|
||||
drive0 These four arguments can be arrays of
|
||||
drive1 1-7 integers as follows:
|
||||
drive2
|
||||
drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<lun>,<dly>
|
||||
|
||||
Where,
|
||||
|
||||
<prt> is the base of the parallel port address for
|
||||
the corresponding drive. (required)
|
||||
|
||||
<pro> is the protocol number for the adapter that
|
||||
supports this drive. These numbers are
|
||||
logged by 'paride' when the protocol modules
|
||||
are initialised. (0 if not given)
|
||||
|
||||
<uni> for those adapters that support chained
|
||||
devices, this is the unit selector for the
|
||||
chain of devices on the given port. It should
|
||||
be zero for devices that don't support chaining.
|
||||
(0 if not given)
|
||||
|
||||
<mod> this can be -1 to choose the best mode, or one
|
||||
of the mode numbers supported by the adapter.
|
||||
(-1 if not given)
|
||||
|
||||
<slv> ATAPI CDroms can be jumpered to master or slave.
|
||||
Set this to 0 to choose the master drive, 1 to
|
||||
choose the slave, -1 (the default) to choose the
|
||||
first drive found.
|
||||
|
||||
<lun> Some ATAPI devices support multiple LUNs.
|
||||
One example is the ATAPI PD/CD drive from
|
||||
Matshita/Panasonic. This device has a
|
||||
CD drive on LUN 0 and a PD drive on LUN 1.
|
||||
By default, the driver will search for the
|
||||
first LUN with a supported device. Set
|
||||
this parameter to force it to use a specific
|
||||
LUN. (default -1)
|
||||
|
||||
<dly> some parallel ports require the driver to
|
||||
go more slowly. -1 sets a default value that
|
||||
should work with the chosen protocol. Otherwise,
|
||||
set this to a small integer, the larger it is
|
||||
the slower the port i/o. In some cases, setting
|
||||
this to zero will speed up the device. (default -1)
|
||||
|
||||
major You may use this parameter to overide the
|
||||
default major number (47) that this driver
|
||||
will use. Be sure to change the device
|
||||
name as well.
|
||||
|
||||
name This parameter is a character string that
|
||||
contains the name the kernel will use for this
|
||||
device (in /proc output, for instance).
|
||||
(default "pf").
|
||||
|
||||
cluster The driver will attempt to aggregate requests
|
||||
for adjacent blocks into larger multi-block
|
||||
clusters. The maximum cluster size (in 512
|
||||
byte sectors) is set with this parameter.
|
||||
(default 64)
|
||||
|
||||
verbose This parameter controls the amount of logging
|
||||
that the driver will do. Set it to 0 for
|
||||
normal operation, 1 to see autoprobe progress
|
||||
messages, or 2 to see additional debugging
|
||||
output. (default 0)
|
||||
|
||||
nice This parameter controls the driver's use of
|
||||
idle CPU time, at the expense of some speed.
|
||||
|
||||
If this driver is built into the kernel, you can use the
|
||||
following command line parameters, with the same values
|
||||
as the corresponding module parameters listed above:
|
||||
|
||||
pf.drive0
|
||||
pf.drive1
|
||||
pf.drive2
|
||||
pf.drive3
|
||||
pf.cluster
|
||||
pf.nice
|
||||
|
||||
In addition, you can use the parameter pf.disable to disable
|
||||
the driver entirely.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.05.03 Changes for SMP. Eliminate sti().
|
||||
Fix for drives that don't clear STAT_ERR
|
||||
until after next CDB delivered.
|
||||
Small change in pf_completion to round
|
||||
up transfer size.
|
||||
1.02 GRG 1998.06.16 Eliminated an Ugh
|
||||
1.03 GRG 1998.08.16 Use HZ in loop timings, extra debugging
|
||||
1.04 GRG 1998.09.24 Added jumbo support
|
||||
|
||||
*/
|
||||
|
||||
#define PF_VERSION "1.04"
|
||||
#define PF_MAJOR 47
|
||||
#define PF_NAME "pf"
|
||||
#define PF_UNITS 4
|
||||
|
||||
/* Here are things one can override from the insmod command.
|
||||
Most are autoprobed by paride unless set here. Verbose is off
|
||||
by default.
|
||||
|
||||
*/
|
||||
|
||||
static int verbose = 0;
|
||||
static int major = PF_MAJOR;
|
||||
static char *name = PF_NAME;
|
||||
static int cluster = 64;
|
||||
static int nice = 0;
|
||||
static int disable = 0;
|
||||
|
||||
static int drive0[7] = { 0, 0, 0, -1, -1, -1, -1 };
|
||||
static int drive1[7] = { 0, 0, 0, -1, -1, -1, -1 };
|
||||
static int drive2[7] = { 0, 0, 0, -1, -1, -1, -1 };
|
||||
static int drive3[7] = { 0, 0, 0, -1, -1, -1, -1 };
|
||||
|
||||
static int (*drives[4])[7] = {&drive0, &drive1, &drive2, &drive3};
|
||||
static int pf_drive_count;
|
||||
|
||||
enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_LUN, D_DLY};
|
||||
|
||||
/* end of parameters */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/cdrom.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/blkpg.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
static DEFINE_SPINLOCK(pf_spin_lock);
|
||||
|
||||
module_param(verbose, bool, 0644);
|
||||
module_param(major, int, 0);
|
||||
module_param(name, charp, 0);
|
||||
module_param(cluster, int, 0);
|
||||
module_param(nice, int, 0);
|
||||
module_param_array(drive0, int, NULL, 0);
|
||||
module_param_array(drive1, int, NULL, 0);
|
||||
module_param_array(drive2, int, NULL, 0);
|
||||
module_param_array(drive3, int, NULL, 0);
|
||||
|
||||
#include "paride.h"
|
||||
#include "pseudo.h"
|
||||
|
||||
/* constants for faking geometry numbers */
|
||||
|
||||
#define PF_FD_MAX 8192 /* use FD geometry under this size */
|
||||
#define PF_FD_HDS 2
|
||||
#define PF_FD_SPT 18
|
||||
#define PF_HD_HDS 64
|
||||
#define PF_HD_SPT 32
|
||||
|
||||
#define PF_MAX_RETRIES 5
|
||||
#define PF_TMO 800 /* interrupt timeout in jiffies */
|
||||
#define PF_SPIN_DEL 50 /* spin delay in micro-seconds */
|
||||
|
||||
#define PF_SPIN (1000000*PF_TMO)/(HZ*PF_SPIN_DEL)
|
||||
|
||||
#define STAT_ERR 0x00001
|
||||
#define STAT_INDEX 0x00002
|
||||
#define STAT_ECC 0x00004
|
||||
#define STAT_DRQ 0x00008
|
||||
#define STAT_SEEK 0x00010
|
||||
#define STAT_WRERR 0x00020
|
||||
#define STAT_READY 0x00040
|
||||
#define STAT_BUSY 0x00080
|
||||
|
||||
#define ATAPI_REQ_SENSE 0x03
|
||||
#define ATAPI_LOCK 0x1e
|
||||
#define ATAPI_DOOR 0x1b
|
||||
#define ATAPI_MODE_SENSE 0x5a
|
||||
#define ATAPI_CAPACITY 0x25
|
||||
#define ATAPI_IDENTIFY 0x12
|
||||
#define ATAPI_READ_10 0x28
|
||||
#define ATAPI_WRITE_10 0x2a
|
||||
|
||||
static int pf_open(struct inode *inode, struct file *file);
|
||||
static void do_pf_request(request_queue_t * q);
|
||||
static int pf_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
static int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo);
|
||||
|
||||
static int pf_release(struct inode *inode, struct file *file);
|
||||
|
||||
static int pf_detect(void);
|
||||
static void do_pf_read(void);
|
||||
static void do_pf_read_start(void);
|
||||
static void do_pf_write(void);
|
||||
static void do_pf_write_start(void);
|
||||
static void do_pf_read_drq(void);
|
||||
static void do_pf_write_done(void);
|
||||
|
||||
#define PF_NM 0
|
||||
#define PF_RO 1
|
||||
#define PF_RW 2
|
||||
|
||||
#define PF_NAMELEN 8
|
||||
|
||||
struct pf_unit {
|
||||
struct pi_adapter pia; /* interface to paride layer */
|
||||
struct pi_adapter *pi;
|
||||
int removable; /* removable media device ? */
|
||||
int media_status; /* media present ? WP ? */
|
||||
int drive; /* drive */
|
||||
int lun;
|
||||
int access; /* count of active opens ... */
|
||||
int present; /* device present ? */
|
||||
char name[PF_NAMELEN]; /* pf0, pf1, ... */
|
||||
struct gendisk *disk;
|
||||
};
|
||||
|
||||
static struct pf_unit units[PF_UNITS];
|
||||
|
||||
static int pf_identify(struct pf_unit *pf);
|
||||
static void pf_lock(struct pf_unit *pf, int func);
|
||||
static void pf_eject(struct pf_unit *pf);
|
||||
static int pf_check_media(struct gendisk *disk);
|
||||
|
||||
static char pf_scratch[512]; /* scratch block buffer */
|
||||
|
||||
/* the variables below are used mainly in the I/O request engine, which
|
||||
processes only one request at a time.
|
||||
*/
|
||||
|
||||
static int pf_retries = 0; /* i/o error retry count */
|
||||
static int pf_busy = 0; /* request being processed ? */
|
||||
static struct request *pf_req; /* current request */
|
||||
static int pf_block; /* address of next requested block */
|
||||
static int pf_count; /* number of blocks still to do */
|
||||
static int pf_run; /* sectors in current cluster */
|
||||
static int pf_cmd; /* current command READ/WRITE */
|
||||
static struct pf_unit *pf_current;/* unit of current request */
|
||||
static int pf_mask; /* stopper for pseudo-int */
|
||||
static char *pf_buf; /* buffer for request in progress */
|
||||
|
||||
/* kernel glue structures */
|
||||
|
||||
static struct block_device_operations pf_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = pf_open,
|
||||
.release = pf_release,
|
||||
.ioctl = pf_ioctl,
|
||||
.getgeo = pf_getgeo,
|
||||
.media_changed = pf_check_media,
|
||||
};
|
||||
|
||||
static void __init pf_init_units(void)
|
||||
{
|
||||
struct pf_unit *pf;
|
||||
int unit;
|
||||
|
||||
pf_drive_count = 0;
|
||||
for (unit = 0, pf = units; unit < PF_UNITS; unit++, pf++) {
|
||||
struct gendisk *disk = alloc_disk(1);
|
||||
if (!disk)
|
||||
continue;
|
||||
pf->disk = disk;
|
||||
pf->pi = &pf->pia;
|
||||
pf->media_status = PF_NM;
|
||||
pf->drive = (*drives[unit])[D_SLV];
|
||||
pf->lun = (*drives[unit])[D_LUN];
|
||||
snprintf(pf->name, PF_NAMELEN, "%s%d", name, unit);
|
||||
disk->major = major;
|
||||
disk->first_minor = unit;
|
||||
strcpy(disk->disk_name, pf->name);
|
||||
disk->fops = &pf_fops;
|
||||
if (!(*drives[unit])[D_PRT])
|
||||
pf_drive_count++;
|
||||
}
|
||||
}
|
||||
|
||||
static int pf_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pf_unit *pf = inode->i_bdev->bd_disk->private_data;
|
||||
|
||||
pf_identify(pf);
|
||||
|
||||
if (pf->media_status == PF_NM)
|
||||
return -ENODEV;
|
||||
|
||||
if ((pf->media_status == PF_RO) && (file->f_mode & 2))
|
||||
return -EROFS;
|
||||
|
||||
pf->access++;
|
||||
if (pf->removable)
|
||||
pf_lock(pf, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
||||
{
|
||||
struct pf_unit *pf = bdev->bd_disk->private_data;
|
||||
sector_t capacity = get_capacity(pf->disk);
|
||||
|
||||
if (capacity < PF_FD_MAX) {
|
||||
geo->cylinders = sector_div(capacity, PF_FD_HDS * PF_FD_SPT);
|
||||
geo->heads = PF_FD_HDS;
|
||||
geo->sectors = PF_FD_SPT;
|
||||
} else {
|
||||
geo->cylinders = sector_div(capacity, PF_HD_HDS * PF_HD_SPT);
|
||||
geo->heads = PF_HD_HDS;
|
||||
geo->sectors = PF_HD_SPT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pf_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct pf_unit *pf = inode->i_bdev->bd_disk->private_data;
|
||||
|
||||
if (cmd != CDROMEJECT)
|
||||
return -EINVAL;
|
||||
|
||||
if (pf->access != 1)
|
||||
return -EBUSY;
|
||||
pf_eject(pf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pf_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pf_unit *pf = inode->i_bdev->bd_disk->private_data;
|
||||
|
||||
if (pf->access <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
pf->access--;
|
||||
|
||||
if (!pf->access && pf->removable)
|
||||
pf_lock(pf, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int pf_check_media(struct gendisk *disk)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int status_reg(struct pf_unit *pf)
|
||||
{
|
||||
return pi_read_regr(pf->pi, 1, 6);
|
||||
}
|
||||
|
||||
static inline int read_reg(struct pf_unit *pf, int reg)
|
||||
{
|
||||
return pi_read_regr(pf->pi, 0, reg);
|
||||
}
|
||||
|
||||
static inline void write_reg(struct pf_unit *pf, int reg, int val)
|
||||
{
|
||||
pi_write_regr(pf->pi, 0, reg, val);
|
||||
}
|
||||
|
||||
static int pf_wait(struct pf_unit *pf, int go, int stop, char *fun, char *msg)
|
||||
{
|
||||
int j, r, e, s, p;
|
||||
|
||||
j = 0;
|
||||
while ((((r = status_reg(pf)) & go) || (stop && (!(r & stop))))
|
||||
&& (j++ < PF_SPIN))
|
||||
udelay(PF_SPIN_DEL);
|
||||
|
||||
if ((r & (STAT_ERR & stop)) || (j >= PF_SPIN)) {
|
||||
s = read_reg(pf, 7);
|
||||
e = read_reg(pf, 1);
|
||||
p = read_reg(pf, 2);
|
||||
if (j >= PF_SPIN)
|
||||
e |= 0x100;
|
||||
if (fun)
|
||||
printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
|
||||
" loop=%d phase=%d\n",
|
||||
pf->name, fun, msg, r, s, e, j, p);
|
||||
return (e << 8) + s;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pf_command(struct pf_unit *pf, char *cmd, int dlen, char *fun)
|
||||
{
|
||||
pi_connect(pf->pi);
|
||||
|
||||
write_reg(pf, 6, 0xa0+0x10*pf->drive);
|
||||
|
||||
if (pf_wait(pf, STAT_BUSY | STAT_DRQ, 0, fun, "before command")) {
|
||||
pi_disconnect(pf->pi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_reg(pf, 4, dlen % 256);
|
||||
write_reg(pf, 5, dlen / 256);
|
||||
write_reg(pf, 7, 0xa0); /* ATAPI packet command */
|
||||
|
||||
if (pf_wait(pf, STAT_BUSY, STAT_DRQ, fun, "command DRQ")) {
|
||||
pi_disconnect(pf->pi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_reg(pf, 2) != 1) {
|
||||
printk("%s: %s: command phase error\n", pf->name, fun);
|
||||
pi_disconnect(pf->pi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pi_write_block(pf->pi, cmd, 12);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pf_completion(struct pf_unit *pf, char *buf, char *fun)
|
||||
{
|
||||
int r, s, n;
|
||||
|
||||
r = pf_wait(pf, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
|
||||
fun, "completion");
|
||||
|
||||
if ((read_reg(pf, 2) & 2) && (read_reg(pf, 7) & STAT_DRQ)) {
|
||||
n = (((read_reg(pf, 4) + 256 * read_reg(pf, 5)) +
|
||||
3) & 0xfffc);
|
||||
pi_read_block(pf->pi, buf, n);
|
||||
}
|
||||
|
||||
s = pf_wait(pf, STAT_BUSY, STAT_READY | STAT_ERR, fun, "data done");
|
||||
|
||||
pi_disconnect(pf->pi);
|
||||
|
||||
return (r ? r : s);
|
||||
}
|
||||
|
||||
static void pf_req_sense(struct pf_unit *pf, int quiet)
|
||||
{
|
||||
char rs_cmd[12] =
|
||||
{ ATAPI_REQ_SENSE, pf->lun << 5, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0 };
|
||||
char buf[16];
|
||||
int r;
|
||||
|
||||
r = pf_command(pf, rs_cmd, 16, "Request sense");
|
||||
mdelay(1);
|
||||
if (!r)
|
||||
pf_completion(pf, buf, "Request sense");
|
||||
|
||||
if ((!r) && (!quiet))
|
||||
printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
|
||||
pf->name, buf[2] & 0xf, buf[12], buf[13]);
|
||||
}
|
||||
|
||||
static int pf_atapi(struct pf_unit *pf, char *cmd, int dlen, char *buf, char *fun)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = pf_command(pf, cmd, dlen, fun);
|
||||
mdelay(1);
|
||||
if (!r)
|
||||
r = pf_completion(pf, buf, fun);
|
||||
if (r)
|
||||
pf_req_sense(pf, !fun);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#define DBMSG(msg) ((verbose>1)?(msg):NULL)
|
||||
|
||||
static void pf_lock(struct pf_unit *pf, int func)
|
||||
{
|
||||
char lo_cmd[12] = { ATAPI_LOCK, pf->lun << 5, 0, 0, func, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
pf_atapi(pf, lo_cmd, 0, pf_scratch, func ? "unlock" : "lock");
|
||||
}
|
||||
|
||||
static void pf_eject(struct pf_unit *pf)
|
||||
{
|
||||
char ej_cmd[12] = { ATAPI_DOOR, pf->lun << 5, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
pf_lock(pf, 0);
|
||||
pf_atapi(pf, ej_cmd, 0, pf_scratch, "eject");
|
||||
}
|
||||
|
||||
#define PF_RESET_TMO 30 /* in tenths of a second */
|
||||
|
||||
static void pf_sleep(int cs)
|
||||
{
|
||||
schedule_timeout_interruptible(cs);
|
||||
}
|
||||
|
||||
/* the ATAPI standard actually specifies the contents of all 7 registers
|
||||
after a reset, but the specification is ambiguous concerning the last
|
||||
two bytes, and different drives interpret the standard differently.
|
||||
*/
|
||||
|
||||
static int pf_reset(struct pf_unit *pf)
|
||||
{
|
||||
int i, k, flg;
|
||||
int expect[5] = { 1, 1, 1, 0x14, 0xeb };
|
||||
|
||||
pi_connect(pf->pi);
|
||||
write_reg(pf, 6, 0xa0+0x10*pf->drive);
|
||||
write_reg(pf, 7, 8);
|
||||
|
||||
pf_sleep(20 * HZ / 1000);
|
||||
|
||||
k = 0;
|
||||
while ((k++ < PF_RESET_TMO) && (status_reg(pf) & STAT_BUSY))
|
||||
pf_sleep(HZ / 10);
|
||||
|
||||
flg = 1;
|
||||
for (i = 0; i < 5; i++)
|
||||
flg &= (read_reg(pf, i + 1) == expect[i]);
|
||||
|
||||
if (verbose) {
|
||||
printk("%s: Reset (%d) signature = ", pf->name, k);
|
||||
for (i = 0; i < 5; i++)
|
||||
printk("%3x", read_reg(pf, i + 1));
|
||||
if (!flg)
|
||||
printk(" (incorrect)");
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
pi_disconnect(pf->pi);
|
||||
return flg - 1;
|
||||
}
|
||||
|
||||
static void pf_mode_sense(struct pf_unit *pf)
|
||||
{
|
||||
char ms_cmd[12] =
|
||||
{ ATAPI_MODE_SENSE, pf->lun << 5, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0 };
|
||||
char buf[8];
|
||||
|
||||
pf_atapi(pf, ms_cmd, 8, buf, DBMSG("mode sense"));
|
||||
pf->media_status = PF_RW;
|
||||
if (buf[3] & 0x80)
|
||||
pf->media_status = PF_RO;
|
||||
}
|
||||
|
||||
static void xs(char *buf, char *targ, int offs, int len)
|
||||
{
|
||||
int j, k, l;
|
||||
|
||||
j = 0;
|
||||
l = 0;
|
||||
for (k = 0; k < len; k++)
|
||||
if ((buf[k + offs] != 0x20) || (buf[k + offs] != l))
|
||||
l = targ[j++] = buf[k + offs];
|
||||
if (l == 0x20)
|
||||
j--;
|
||||
targ[j] = 0;
|
||||
}
|
||||
|
||||
static int xl(char *buf, int offs)
|
||||
{
|
||||
int v, k;
|
||||
|
||||
v = 0;
|
||||
for (k = 0; k < 4; k++)
|
||||
v = v * 256 + (buf[k + offs] & 0xff);
|
||||
return v;
|
||||
}
|
||||
|
||||
static void pf_get_capacity(struct pf_unit *pf)
|
||||
{
|
||||
char rc_cmd[12] = { ATAPI_CAPACITY, pf->lun << 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
char buf[8];
|
||||
int bs;
|
||||
|
||||
if (pf_atapi(pf, rc_cmd, 8, buf, DBMSG("get capacity"))) {
|
||||
pf->media_status = PF_NM;
|
||||
return;
|
||||
}
|
||||
set_capacity(pf->disk, xl(buf, 0) + 1);
|
||||
bs = xl(buf, 4);
|
||||
if (bs != 512) {
|
||||
set_capacity(pf->disk, 0);
|
||||
if (verbose)
|
||||
printk("%s: Drive %d, LUN %d,"
|
||||
" unsupported block size %d\n",
|
||||
pf->name, pf->drive, pf->lun, bs);
|
||||
}
|
||||
}
|
||||
|
||||
static int pf_identify(struct pf_unit *pf)
|
||||
{
|
||||
int dt, s;
|
||||
char *ms[2] = { "master", "slave" };
|
||||
char mf[10], id[18];
|
||||
char id_cmd[12] =
|
||||
{ ATAPI_IDENTIFY, pf->lun << 5, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
|
||||
char buf[36];
|
||||
|
||||
s = pf_atapi(pf, id_cmd, 36, buf, "identify");
|
||||
if (s)
|
||||
return -1;
|
||||
|
||||
dt = buf[0] & 0x1f;
|
||||
if ((dt != 0) && (dt != 7)) {
|
||||
if (verbose)
|
||||
printk("%s: Drive %d, LUN %d, unsupported type %d\n",
|
||||
pf->name, pf->drive, pf->lun, dt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
xs(buf, mf, 8, 8);
|
||||
xs(buf, id, 16, 16);
|
||||
|
||||
pf->removable = (buf[1] & 0x80);
|
||||
|
||||
pf_mode_sense(pf);
|
||||
pf_mode_sense(pf);
|
||||
pf_mode_sense(pf);
|
||||
|
||||
pf_get_capacity(pf);
|
||||
|
||||
printk("%s: %s %s, %s LUN %d, type %d",
|
||||
pf->name, mf, id, ms[pf->drive], pf->lun, dt);
|
||||
if (pf->removable)
|
||||
printk(", removable");
|
||||
if (pf->media_status == PF_NM)
|
||||
printk(", no media\n");
|
||||
else {
|
||||
if (pf->media_status == PF_RO)
|
||||
printk(", RO");
|
||||
printk(", %llu blocks\n",
|
||||
(unsigned long long)get_capacity(pf->disk));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* returns 0, with id set if drive is detected
|
||||
-1, if drive detection failed
|
||||
*/
|
||||
static int pf_probe(struct pf_unit *pf)
|
||||
{
|
||||
if (pf->drive == -1) {
|
||||
for (pf->drive = 0; pf->drive <= 1; pf->drive++)
|
||||
if (!pf_reset(pf)) {
|
||||
if (pf->lun != -1)
|
||||
return pf_identify(pf);
|
||||
else
|
||||
for (pf->lun = 0; pf->lun < 8; pf->lun++)
|
||||
if (!pf_identify(pf))
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (pf_reset(pf))
|
||||
return -1;
|
||||
if (pf->lun != -1)
|
||||
return pf_identify(pf);
|
||||
for (pf->lun = 0; pf->lun < 8; pf->lun++)
|
||||
if (!pf_identify(pf))
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pf_detect(void)
|
||||
{
|
||||
struct pf_unit *pf = units;
|
||||
int k, unit;
|
||||
|
||||
printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
|
||||
name, name, PF_VERSION, major, cluster, nice);
|
||||
|
||||
k = 0;
|
||||
if (pf_drive_count == 0) {
|
||||
if (pi_init(pf->pi, 1, -1, -1, -1, -1, -1, pf_scratch, PI_PF,
|
||||
verbose, pf->name)) {
|
||||
if (!pf_probe(pf) && pf->disk) {
|
||||
pf->present = 1;
|
||||
k++;
|
||||
} else
|
||||
pi_release(pf->pi);
|
||||
}
|
||||
|
||||
} else
|
||||
for (unit = 0; unit < PF_UNITS; unit++, pf++) {
|
||||
int *conf = *drives[unit];
|
||||
if (!conf[D_PRT])
|
||||
continue;
|
||||
if (pi_init(pf->pi, 0, conf[D_PRT], conf[D_MOD],
|
||||
conf[D_UNI], conf[D_PRO], conf[D_DLY],
|
||||
pf_scratch, PI_PF, verbose, pf->name)) {
|
||||
if (pf->disk && !pf_probe(pf)) {
|
||||
pf->present = 1;
|
||||
k++;
|
||||
} else
|
||||
pi_release(pf->pi);
|
||||
}
|
||||
}
|
||||
if (k)
|
||||
return 0;
|
||||
|
||||
printk("%s: No ATAPI disk detected\n", name);
|
||||
for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++)
|
||||
put_disk(pf->disk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* The i/o request engine */
|
||||
|
||||
static int pf_start(struct pf_unit *pf, int cmd, int b, int c)
|
||||
{
|
||||
int i;
|
||||
char io_cmd[12] = { cmd, pf->lun << 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
io_cmd[5 - i] = b & 0xff;
|
||||
b = b >> 8;
|
||||
}
|
||||
|
||||
io_cmd[8] = c & 0xff;
|
||||
io_cmd[7] = (c >> 8) & 0xff;
|
||||
|
||||
i = pf_command(pf, io_cmd, c * 512, "start i/o");
|
||||
|
||||
mdelay(1);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int pf_ready(void)
|
||||
{
|
||||
return (((status_reg(pf_current) & (STAT_BUSY | pf_mask)) == pf_mask));
|
||||
}
|
||||
|
||||
static struct request_queue *pf_queue;
|
||||
|
||||
static void pf_end_request(int uptodate)
|
||||
{
|
||||
if (pf_req) {
|
||||
end_request(pf_req, uptodate);
|
||||
pf_req = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_pf_request(request_queue_t * q)
|
||||
{
|
||||
if (pf_busy)
|
||||
return;
|
||||
repeat:
|
||||
pf_req = elv_next_request(q);
|
||||
if (!pf_req)
|
||||
return;
|
||||
|
||||
pf_current = pf_req->rq_disk->private_data;
|
||||
pf_block = pf_req->sector;
|
||||
pf_run = pf_req->nr_sectors;
|
||||
pf_count = pf_req->current_nr_sectors;
|
||||
|
||||
if (pf_block + pf_count > get_capacity(pf_req->rq_disk)) {
|
||||
pf_end_request(0);
|
||||
goto repeat;
|
||||
}
|
||||
|
||||
pf_cmd = rq_data_dir(pf_req);
|
||||
pf_buf = pf_req->buffer;
|
||||
pf_retries = 0;
|
||||
|
||||
pf_busy = 1;
|
||||
if (pf_cmd == READ)
|
||||
pi_do_claimed(pf_current->pi, do_pf_read);
|
||||
else if (pf_cmd == WRITE)
|
||||
pi_do_claimed(pf_current->pi, do_pf_write);
|
||||
else {
|
||||
pf_busy = 0;
|
||||
pf_end_request(0);
|
||||
goto repeat;
|
||||
}
|
||||
}
|
||||
|
||||
static int pf_next_buf(void)
|
||||
{
|
||||
unsigned long saved_flags;
|
||||
|
||||
pf_count--;
|
||||
pf_run--;
|
||||
pf_buf += 512;
|
||||
pf_block++;
|
||||
if (!pf_run)
|
||||
return 0;
|
||||
if (!pf_count)
|
||||
return 1;
|
||||
spin_lock_irqsave(&pf_spin_lock, saved_flags);
|
||||
pf_end_request(1);
|
||||
spin_unlock_irqrestore(&pf_spin_lock, saved_flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void next_request(int success)
|
||||
{
|
||||
unsigned long saved_flags;
|
||||
|
||||
spin_lock_irqsave(&pf_spin_lock, saved_flags);
|
||||
pf_end_request(success);
|
||||
pf_busy = 0;
|
||||
do_pf_request(pf_queue);
|
||||
spin_unlock_irqrestore(&pf_spin_lock, saved_flags);
|
||||
}
|
||||
|
||||
/* detach from the calling context - in case the spinlock is held */
|
||||
static void do_pf_read(void)
|
||||
{
|
||||
ps_set_intr(do_pf_read_start, NULL, 0, nice);
|
||||
}
|
||||
|
||||
static void do_pf_read_start(void)
|
||||
{
|
||||
pf_busy = 1;
|
||||
|
||||
if (pf_start(pf_current, ATAPI_READ_10, pf_block, pf_run)) {
|
||||
pi_disconnect(pf_current->pi);
|
||||
if (pf_retries < PF_MAX_RETRIES) {
|
||||
pf_retries++;
|
||||
pi_do_claimed(pf_current->pi, do_pf_read_start);
|
||||
return;
|
||||
}
|
||||
next_request(0);
|
||||
return;
|
||||
}
|
||||
pf_mask = STAT_DRQ;
|
||||
ps_set_intr(do_pf_read_drq, pf_ready, PF_TMO, nice);
|
||||
}
|
||||
|
||||
static void do_pf_read_drq(void)
|
||||
{
|
||||
while (1) {
|
||||
if (pf_wait(pf_current, STAT_BUSY, STAT_DRQ | STAT_ERR,
|
||||
"read block", "completion") & STAT_ERR) {
|
||||
pi_disconnect(pf_current->pi);
|
||||
if (pf_retries < PF_MAX_RETRIES) {
|
||||
pf_req_sense(pf_current, 0);
|
||||
pf_retries++;
|
||||
pi_do_claimed(pf_current->pi, do_pf_read_start);
|
||||
return;
|
||||
}
|
||||
next_request(0);
|
||||
return;
|
||||
}
|
||||
pi_read_block(pf_current->pi, pf_buf, 512);
|
||||
if (pf_next_buf())
|
||||
break;
|
||||
}
|
||||
pi_disconnect(pf_current->pi);
|
||||
next_request(1);
|
||||
}
|
||||
|
||||
static void do_pf_write(void)
|
||||
{
|
||||
ps_set_intr(do_pf_write_start, NULL, 0, nice);
|
||||
}
|
||||
|
||||
static void do_pf_write_start(void)
|
||||
{
|
||||
pf_busy = 1;
|
||||
|
||||
if (pf_start(pf_current, ATAPI_WRITE_10, pf_block, pf_run)) {
|
||||
pi_disconnect(pf_current->pi);
|
||||
if (pf_retries < PF_MAX_RETRIES) {
|
||||
pf_retries++;
|
||||
pi_do_claimed(pf_current->pi, do_pf_write_start);
|
||||
return;
|
||||
}
|
||||
next_request(0);
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (pf_wait(pf_current, STAT_BUSY, STAT_DRQ | STAT_ERR,
|
||||
"write block", "data wait") & STAT_ERR) {
|
||||
pi_disconnect(pf_current->pi);
|
||||
if (pf_retries < PF_MAX_RETRIES) {
|
||||
pf_retries++;
|
||||
pi_do_claimed(pf_current->pi, do_pf_write_start);
|
||||
return;
|
||||
}
|
||||
next_request(0);
|
||||
return;
|
||||
}
|
||||
pi_write_block(pf_current->pi, pf_buf, 512);
|
||||
if (pf_next_buf())
|
||||
break;
|
||||
}
|
||||
pf_mask = 0;
|
||||
ps_set_intr(do_pf_write_done, pf_ready, PF_TMO, nice);
|
||||
}
|
||||
|
||||
static void do_pf_write_done(void)
|
||||
{
|
||||
if (pf_wait(pf_current, STAT_BUSY, 0, "write block", "done") & STAT_ERR) {
|
||||
pi_disconnect(pf_current->pi);
|
||||
if (pf_retries < PF_MAX_RETRIES) {
|
||||
pf_retries++;
|
||||
pi_do_claimed(pf_current->pi, do_pf_write_start);
|
||||
return;
|
||||
}
|
||||
next_request(0);
|
||||
return;
|
||||
}
|
||||
pi_disconnect(pf_current->pi);
|
||||
next_request(1);
|
||||
}
|
||||
|
||||
static int __init pf_init(void)
|
||||
{ /* preliminary initialisation */
|
||||
struct pf_unit *pf;
|
||||
int unit;
|
||||
|
||||
if (disable)
|
||||
return -EINVAL;
|
||||
|
||||
pf_init_units();
|
||||
|
||||
if (pf_detect())
|
||||
return -ENODEV;
|
||||
pf_busy = 0;
|
||||
|
||||
if (register_blkdev(major, name)) {
|
||||
for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++)
|
||||
put_disk(pf->disk);
|
||||
return -EBUSY;
|
||||
}
|
||||
pf_queue = blk_init_queue(do_pf_request, &pf_spin_lock);
|
||||
if (!pf_queue) {
|
||||
unregister_blkdev(major, name);
|
||||
for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++)
|
||||
put_disk(pf->disk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
blk_queue_max_phys_segments(pf_queue, cluster);
|
||||
blk_queue_max_hw_segments(pf_queue, cluster);
|
||||
|
||||
for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
|
||||
struct gendisk *disk = pf->disk;
|
||||
|
||||
if (!pf->present)
|
||||
continue;
|
||||
disk->private_data = pf;
|
||||
disk->queue = pf_queue;
|
||||
add_disk(disk);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit pf_exit(void)
|
||||
{
|
||||
struct pf_unit *pf;
|
||||
int unit;
|
||||
unregister_blkdev(major, name);
|
||||
for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
|
||||
if (!pf->present)
|
||||
continue;
|
||||
del_gendisk(pf->disk);
|
||||
put_disk(pf->disk);
|
||||
pi_release(pf->pi);
|
||||
}
|
||||
blk_cleanup_queue(pf_queue);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(pf_init)
|
||||
module_exit(pf_exit)
|
||||
712
drivers/block/paride/pg.c
Normal file
712
drivers/block/paride/pg.c
Normal file
@@ -0,0 +1,712 @@
|
||||
/*
|
||||
pg.c (c) 1998 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
The pg driver provides a simple character device interface for
|
||||
sending ATAPI commands to a device. With the exception of the
|
||||
ATAPI reset operation, all operations are performed by a pair
|
||||
of read and write operations to the appropriate /dev/pgN device.
|
||||
A write operation delivers a command and any outbound data in
|
||||
a single buffer. Normally, the write will succeed unless the
|
||||
device is offline or malfunctioning, or there is already another
|
||||
command pending. If the write succeeds, it should be followed
|
||||
immediately by a read operation, to obtain any returned data and
|
||||
status information. A read will fail if there is no operation
|
||||
in progress.
|
||||
|
||||
As a special case, the device can be reset with a write operation,
|
||||
and in this case, no following read is expected, or permitted.
|
||||
|
||||
There are no ioctl() operations. Any single operation
|
||||
may transfer at most PG_MAX_DATA bytes. Note that the driver must
|
||||
copy the data through an internal buffer. In keeping with all
|
||||
current ATAPI devices, command packets are assumed to be exactly
|
||||
12 bytes in length.
|
||||
|
||||
To permit future changes to this interface, the headers in the
|
||||
read and write buffers contain a single character "magic" flag.
|
||||
Currently this flag must be the character "P".
|
||||
|
||||
By default, the driver will autoprobe for a single parallel
|
||||
port ATAPI device, but if their individual parameters are
|
||||
specified, the driver can handle up to 4 devices.
|
||||
|
||||
To use this device, you must have the following device
|
||||
special files defined:
|
||||
|
||||
/dev/pg0 c 97 0
|
||||
/dev/pg1 c 97 1
|
||||
/dev/pg2 c 97 2
|
||||
/dev/pg3 c 97 3
|
||||
|
||||
(You'll need to change the 97 to something else if you use
|
||||
the 'major' parameter to install the driver on a different
|
||||
major number.)
|
||||
|
||||
The behaviour of the pg driver can be altered by setting
|
||||
some parameters from the insmod command line. The following
|
||||
parameters are adjustable:
|
||||
|
||||
drive0 These four arguments can be arrays of
|
||||
drive1 1-6 integers as follows:
|
||||
drive2
|
||||
drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
|
||||
|
||||
Where,
|
||||
|
||||
<prt> is the base of the parallel port address for
|
||||
the corresponding drive. (required)
|
||||
|
||||
<pro> is the protocol number for the adapter that
|
||||
supports this drive. These numbers are
|
||||
logged by 'paride' when the protocol modules
|
||||
are initialised. (0 if not given)
|
||||
|
||||
<uni> for those adapters that support chained
|
||||
devices, this is the unit selector for the
|
||||
chain of devices on the given port. It should
|
||||
be zero for devices that don't support chaining.
|
||||
(0 if not given)
|
||||
|
||||
<mod> this can be -1 to choose the best mode, or one
|
||||
of the mode numbers supported by the adapter.
|
||||
(-1 if not given)
|
||||
|
||||
<slv> ATAPI devices can be jumpered to master or slave.
|
||||
Set this to 0 to choose the master drive, 1 to
|
||||
choose the slave, -1 (the default) to choose the
|
||||
first drive found.
|
||||
|
||||
<dly> some parallel ports require the driver to
|
||||
go more slowly. -1 sets a default value that
|
||||
should work with the chosen protocol. Otherwise,
|
||||
set this to a small integer, the larger it is
|
||||
the slower the port i/o. In some cases, setting
|
||||
this to zero will speed up the device. (default -1)
|
||||
|
||||
major You may use this parameter to overide the
|
||||
default major number (97) that this driver
|
||||
will use. Be sure to change the device
|
||||
name as well.
|
||||
|
||||
name This parameter is a character string that
|
||||
contains the name the kernel will use for this
|
||||
device (in /proc output, for instance).
|
||||
(default "pg").
|
||||
|
||||
verbose This parameter controls the amount of logging
|
||||
that is done by the driver. Set it to 0 for
|
||||
quiet operation, to 1 to enable progress
|
||||
messages while the driver probes for devices,
|
||||
or to 2 for full debug logging. (default 0)
|
||||
|
||||
If this driver is built into the kernel, you can use
|
||||
the following command line parameters, with the same values
|
||||
as the corresponding module parameters listed above:
|
||||
|
||||
pg.drive0
|
||||
pg.drive1
|
||||
pg.drive2
|
||||
pg.drive3
|
||||
|
||||
In addition, you can use the parameter pg.disable to disable
|
||||
the driver entirely.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 GRG 1998.06.16 Bug fixes
|
||||
1.02 GRG 1998.09.24 Added jumbo support
|
||||
|
||||
*/
|
||||
|
||||
#define PG_VERSION "1.02"
|
||||
#define PG_MAJOR 97
|
||||
#define PG_NAME "pg"
|
||||
#define PG_UNITS 4
|
||||
|
||||
#ifndef PI_PG
|
||||
#define PI_PG 4
|
||||
#endif
|
||||
|
||||
/* Here are things one can override from the insmod command.
|
||||
Most are autoprobed by paride unless set here. Verbose is 0
|
||||
by default.
|
||||
|
||||
*/
|
||||
|
||||
static int verbose = 0;
|
||||
static int major = PG_MAJOR;
|
||||
static char *name = PG_NAME;
|
||||
static int disable = 0;
|
||||
|
||||
static int drive0[6] = { 0, 0, 0, -1, -1, -1 };
|
||||
static int drive1[6] = { 0, 0, 0, -1, -1, -1 };
|
||||
static int drive2[6] = { 0, 0, 0, -1, -1, -1 };
|
||||
static int drive3[6] = { 0, 0, 0, -1, -1, -1 };
|
||||
|
||||
static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3};
|
||||
static int pg_drive_count;
|
||||
|
||||
enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY};
|
||||
|
||||
/* end of parameters */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mtio.h>
|
||||
#include <linux/pg.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sched.h> /* current, TASK_* */
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
module_param(verbose, bool, 0644);
|
||||
module_param(major, int, 0);
|
||||
module_param(name, charp, 0);
|
||||
module_param_array(drive0, int, NULL, 0);
|
||||
module_param_array(drive1, int, NULL, 0);
|
||||
module_param_array(drive2, int, NULL, 0);
|
||||
module_param_array(drive3, int, NULL, 0);
|
||||
|
||||
#include "paride.h"
|
||||
|
||||
#define PG_SPIN_DEL 50 /* spin delay in micro-seconds */
|
||||
#define PG_SPIN 200
|
||||
#define PG_TMO HZ
|
||||
#define PG_RESET_TMO 10*HZ
|
||||
|
||||
#define STAT_ERR 0x01
|
||||
#define STAT_INDEX 0x02
|
||||
#define STAT_ECC 0x04
|
||||
#define STAT_DRQ 0x08
|
||||
#define STAT_SEEK 0x10
|
||||
#define STAT_WRERR 0x20
|
||||
#define STAT_READY 0x40
|
||||
#define STAT_BUSY 0x80
|
||||
|
||||
#define ATAPI_IDENTIFY 0x12
|
||||
|
||||
static int pg_open(struct inode *inode, struct file *file);
|
||||
static int pg_release(struct inode *inode, struct file *file);
|
||||
static ssize_t pg_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t * ppos);
|
||||
static ssize_t pg_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t * ppos);
|
||||
static int pg_detect(void);
|
||||
|
||||
#define PG_NAMELEN 8
|
||||
|
||||
struct pg {
|
||||
struct pi_adapter pia; /* interface to paride layer */
|
||||
struct pi_adapter *pi;
|
||||
int busy; /* write done, read expected */
|
||||
int start; /* jiffies at command start */
|
||||
int dlen; /* transfer size requested */
|
||||
unsigned long timeout; /* timeout requested */
|
||||
int status; /* last sense key */
|
||||
int drive; /* drive */
|
||||
unsigned long access; /* count of active opens ... */
|
||||
int present; /* device present ? */
|
||||
char *bufptr;
|
||||
char name[PG_NAMELEN]; /* pg0, pg1, ... */
|
||||
};
|
||||
|
||||
static struct pg devices[PG_UNITS];
|
||||
|
||||
static int pg_identify(struct pg *dev, int log);
|
||||
|
||||
static char pg_scratch[512]; /* scratch block buffer */
|
||||
|
||||
static struct class *pg_class;
|
||||
|
||||
/* kernel glue structures */
|
||||
|
||||
static const struct file_operations pg_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = pg_read,
|
||||
.write = pg_write,
|
||||
.open = pg_open,
|
||||
.release = pg_release,
|
||||
};
|
||||
|
||||
static void pg_init_units(void)
|
||||
{
|
||||
int unit;
|
||||
|
||||
pg_drive_count = 0;
|
||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
||||
int *parm = *drives[unit];
|
||||
struct pg *dev = &devices[unit];
|
||||
dev->pi = &dev->pia;
|
||||
clear_bit(0, &dev->access);
|
||||
dev->busy = 0;
|
||||
dev->present = 0;
|
||||
dev->bufptr = NULL;
|
||||
dev->drive = parm[D_SLV];
|
||||
snprintf(dev->name, PG_NAMELEN, "%s%c", name, 'a'+unit);
|
||||
if (parm[D_PRT])
|
||||
pg_drive_count++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int status_reg(struct pg *dev)
|
||||
{
|
||||
return pi_read_regr(dev->pi, 1, 6);
|
||||
}
|
||||
|
||||
static inline int read_reg(struct pg *dev, int reg)
|
||||
{
|
||||
return pi_read_regr(dev->pi, 0, reg);
|
||||
}
|
||||
|
||||
static inline void write_reg(struct pg *dev, int reg, int val)
|
||||
{
|
||||
pi_write_regr(dev->pi, 0, reg, val);
|
||||
}
|
||||
|
||||
static inline u8 DRIVE(struct pg *dev)
|
||||
{
|
||||
return 0xa0+0x10*dev->drive;
|
||||
}
|
||||
|
||||
static void pg_sleep(int cs)
|
||||
{
|
||||
schedule_timeout_interruptible(cs);
|
||||
}
|
||||
|
||||
static int pg_wait(struct pg *dev, int go, int stop, unsigned long tmo, char *msg)
|
||||
{
|
||||
int j, r, e, s, p, to;
|
||||
|
||||
dev->status = 0;
|
||||
|
||||
j = 0;
|
||||
while ((((r = status_reg(dev)) & go) || (stop && (!(r & stop))))
|
||||
&& time_before(jiffies, tmo)) {
|
||||
if (j++ < PG_SPIN)
|
||||
udelay(PG_SPIN_DEL);
|
||||
else
|
||||
pg_sleep(1);
|
||||
}
|
||||
|
||||
to = time_after_eq(jiffies, tmo);
|
||||
|
||||
if ((r & (STAT_ERR & stop)) || to) {
|
||||
s = read_reg(dev, 7);
|
||||
e = read_reg(dev, 1);
|
||||
p = read_reg(dev, 2);
|
||||
if (verbose > 1)
|
||||
printk("%s: %s: stat=0x%x err=0x%x phase=%d%s\n",
|
||||
dev->name, msg, s, e, p, to ? " timeout" : "");
|
||||
if (to)
|
||||
e |= 0x100;
|
||||
dev->status = (e >> 4) & 0xff;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pg_command(struct pg *dev, char *cmd, int dlen, unsigned long tmo)
|
||||
{
|
||||
int k;
|
||||
|
||||
pi_connect(dev->pi);
|
||||
|
||||
write_reg(dev, 6, DRIVE(dev));
|
||||
|
||||
if (pg_wait(dev, STAT_BUSY | STAT_DRQ, 0, tmo, "before command"))
|
||||
goto fail;
|
||||
|
||||
write_reg(dev, 4, dlen % 256);
|
||||
write_reg(dev, 5, dlen / 256);
|
||||
write_reg(dev, 7, 0xa0); /* ATAPI packet command */
|
||||
|
||||
if (pg_wait(dev, STAT_BUSY, STAT_DRQ, tmo, "command DRQ"))
|
||||
goto fail;
|
||||
|
||||
if (read_reg(dev, 2) != 1) {
|
||||
printk("%s: command phase error\n", dev->name);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pi_write_block(dev->pi, cmd, 12);
|
||||
|
||||
if (verbose > 1) {
|
||||
printk("%s: Command sent, dlen=%d packet= ", dev->name, dlen);
|
||||
for (k = 0; k < 12; k++)
|
||||
printk("%02x ", cmd[k] & 0xff);
|
||||
printk("\n");
|
||||
}
|
||||
return 0;
|
||||
fail:
|
||||
pi_disconnect(dev->pi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pg_completion(struct pg *dev, char *buf, unsigned long tmo)
|
||||
{
|
||||
int r, d, n, p;
|
||||
|
||||
r = pg_wait(dev, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
|
||||
tmo, "completion");
|
||||
|
||||
dev->dlen = 0;
|
||||
|
||||
while (read_reg(dev, 7) & STAT_DRQ) {
|
||||
d = (read_reg(dev, 4) + 256 * read_reg(dev, 5));
|
||||
n = ((d + 3) & 0xfffc);
|
||||
p = read_reg(dev, 2) & 3;
|
||||
if (p == 0)
|
||||
pi_write_block(dev->pi, buf, n);
|
||||
if (p == 2)
|
||||
pi_read_block(dev->pi, buf, n);
|
||||
if (verbose > 1)
|
||||
printk("%s: %s %d bytes\n", dev->name,
|
||||
p ? "Read" : "Write", n);
|
||||
dev->dlen += (1 - p) * d;
|
||||
buf += d;
|
||||
r = pg_wait(dev, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
|
||||
tmo, "completion");
|
||||
}
|
||||
|
||||
pi_disconnect(dev->pi);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int pg_reset(struct pg *dev)
|
||||
{
|
||||
int i, k, err;
|
||||
int expect[5] = { 1, 1, 1, 0x14, 0xeb };
|
||||
int got[5];
|
||||
|
||||
pi_connect(dev->pi);
|
||||
write_reg(dev, 6, DRIVE(dev));
|
||||
write_reg(dev, 7, 8);
|
||||
|
||||
pg_sleep(20 * HZ / 1000);
|
||||
|
||||
k = 0;
|
||||
while ((k++ < PG_RESET_TMO) && (status_reg(dev) & STAT_BUSY))
|
||||
pg_sleep(1);
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
got[i] = read_reg(dev, i + 1);
|
||||
|
||||
err = memcmp(expect, got, sizeof(got)) ? -1 : 0;
|
||||
|
||||
if (verbose) {
|
||||
printk("%s: Reset (%d) signature = ", dev->name, k);
|
||||
for (i = 0; i < 5; i++)
|
||||
printk("%3x", got[i]);
|
||||
if (err)
|
||||
printk(" (incorrect)");
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
pi_disconnect(dev->pi);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void xs(char *buf, char *targ, int len)
|
||||
{
|
||||
char l = '\0';
|
||||
int k;
|
||||
|
||||
for (k = 0; k < len; k++) {
|
||||
char c = *buf++;
|
||||
if (c != ' ' || c != l)
|
||||
l = *targ++ = c;
|
||||
}
|
||||
if (l == ' ')
|
||||
targ--;
|
||||
*targ = '\0';
|
||||
}
|
||||
|
||||
static int pg_identify(struct pg *dev, int log)
|
||||
{
|
||||
int s;
|
||||
char *ms[2] = { "master", "slave" };
|
||||
char mf[10], id[18];
|
||||
char id_cmd[12] = { ATAPI_IDENTIFY, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
|
||||
char buf[36];
|
||||
|
||||
s = pg_command(dev, id_cmd, 36, jiffies + PG_TMO);
|
||||
if (s)
|
||||
return -1;
|
||||
s = pg_completion(dev, buf, jiffies + PG_TMO);
|
||||
if (s)
|
||||
return -1;
|
||||
|
||||
if (log) {
|
||||
xs(buf + 8, mf, 8);
|
||||
xs(buf + 16, id, 16);
|
||||
printk("%s: %s %s, %s\n", dev->name, mf, id, ms[dev->drive]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns 0, with id set if drive is detected
|
||||
* -1, if drive detection failed
|
||||
*/
|
||||
static int pg_probe(struct pg *dev)
|
||||
{
|
||||
if (dev->drive == -1) {
|
||||
for (dev->drive = 0; dev->drive <= 1; dev->drive++)
|
||||
if (!pg_reset(dev))
|
||||
return pg_identify(dev, 1);
|
||||
} else {
|
||||
if (!pg_reset(dev))
|
||||
return pg_identify(dev, 1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pg_detect(void)
|
||||
{
|
||||
struct pg *dev = &devices[0];
|
||||
int k, unit;
|
||||
|
||||
printk("%s: %s version %s, major %d\n", name, name, PG_VERSION, major);
|
||||
|
||||
k = 0;
|
||||
if (pg_drive_count == 0) {
|
||||
if (pi_init(dev->pi, 1, -1, -1, -1, -1, -1, pg_scratch,
|
||||
PI_PG, verbose, dev->name)) {
|
||||
if (!pg_probe(dev)) {
|
||||
dev->present = 1;
|
||||
k++;
|
||||
} else
|
||||
pi_release(dev->pi);
|
||||
}
|
||||
|
||||
} else
|
||||
for (unit = 0; unit < PG_UNITS; unit++, dev++) {
|
||||
int *parm = *drives[unit];
|
||||
if (!parm[D_PRT])
|
||||
continue;
|
||||
if (pi_init(dev->pi, 0, parm[D_PRT], parm[D_MOD],
|
||||
parm[D_UNI], parm[D_PRO], parm[D_DLY],
|
||||
pg_scratch, PI_PG, verbose, dev->name)) {
|
||||
if (!pg_probe(dev)) {
|
||||
dev->present = 1;
|
||||
k++;
|
||||
} else
|
||||
pi_release(dev->pi);
|
||||
}
|
||||
}
|
||||
|
||||
if (k)
|
||||
return 0;
|
||||
|
||||
printk("%s: No ATAPI device detected\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pg_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int unit = iminor(inode) & 0x7f;
|
||||
struct pg *dev = &devices[unit];
|
||||
|
||||
if ((unit >= PG_UNITS) || (!dev->present))
|
||||
return -ENODEV;
|
||||
|
||||
if (test_and_set_bit(0, &dev->access))
|
||||
return -EBUSY;
|
||||
|
||||
if (dev->busy) {
|
||||
pg_reset(dev);
|
||||
dev->busy = 0;
|
||||
}
|
||||
|
||||
pg_identify(dev, (verbose > 1));
|
||||
|
||||
dev->bufptr = kmalloc(PG_MAX_DATA, GFP_KERNEL);
|
||||
if (dev->bufptr == NULL) {
|
||||
clear_bit(0, &dev->access);
|
||||
printk("%s: buffer allocation failed\n", dev->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
file->private_data = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pg_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pg *dev = file->private_data;
|
||||
|
||||
kfree(dev->bufptr);
|
||||
dev->bufptr = NULL;
|
||||
clear_bit(0, &dev->access);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t pg_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct pg *dev = filp->private_data;
|
||||
struct pg_write_hdr hdr;
|
||||
int hs = sizeof (hdr);
|
||||
|
||||
if (dev->busy)
|
||||
return -EBUSY;
|
||||
if (count < hs)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&hdr, buf, hs))
|
||||
return -EFAULT;
|
||||
|
||||
if (hdr.magic != PG_MAGIC)
|
||||
return -EINVAL;
|
||||
if (hdr.dlen > PG_MAX_DATA)
|
||||
return -EINVAL;
|
||||
if ((count - hs) > PG_MAX_DATA)
|
||||
return -EINVAL;
|
||||
|
||||
if (hdr.func == PG_RESET) {
|
||||
if (count != hs)
|
||||
return -EINVAL;
|
||||
if (pg_reset(dev))
|
||||
return -EIO;
|
||||
return count;
|
||||
}
|
||||
|
||||
if (hdr.func != PG_COMMAND)
|
||||
return -EINVAL;
|
||||
|
||||
dev->start = jiffies;
|
||||
dev->timeout = hdr.timeout * HZ + HZ / 2 + jiffies;
|
||||
|
||||
if (pg_command(dev, hdr.packet, hdr.dlen, jiffies + PG_TMO)) {
|
||||
if (dev->status & 0x10)
|
||||
return -ETIME;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev->busy = 1;
|
||||
|
||||
if (copy_from_user(dev->bufptr, buf + hs, count - hs))
|
||||
return -EFAULT;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct pg *dev = filp->private_data;
|
||||
struct pg_read_hdr hdr;
|
||||
int hs = sizeof (hdr);
|
||||
int copy;
|
||||
|
||||
if (!dev->busy)
|
||||
return -EINVAL;
|
||||
if (count < hs)
|
||||
return -EINVAL;
|
||||
|
||||
dev->busy = 0;
|
||||
|
||||
if (pg_completion(dev, dev->bufptr, dev->timeout))
|
||||
if (dev->status & 0x10)
|
||||
return -ETIME;
|
||||
|
||||
hdr.magic = PG_MAGIC;
|
||||
hdr.dlen = dev->dlen;
|
||||
copy = 0;
|
||||
|
||||
if (hdr.dlen < 0) {
|
||||
hdr.dlen = -1 * hdr.dlen;
|
||||
copy = hdr.dlen;
|
||||
if (copy > (count - hs))
|
||||
copy = count - hs;
|
||||
}
|
||||
|
||||
hdr.duration = (jiffies - dev->start + HZ / 2) / HZ;
|
||||
hdr.scsi = dev->status & 0x0f;
|
||||
|
||||
if (copy_to_user(buf, &hdr, hs))
|
||||
return -EFAULT;
|
||||
if (copy > 0)
|
||||
if (copy_to_user(buf + hs, dev->bufptr, copy))
|
||||
return -EFAULT;
|
||||
return copy + hs;
|
||||
}
|
||||
|
||||
static int __init pg_init(void)
|
||||
{
|
||||
int unit;
|
||||
int err;
|
||||
|
||||
if (disable){
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pg_init_units();
|
||||
|
||||
if (pg_detect()) {
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = register_chrdev(major, name, &pg_fops);
|
||||
if (err < 0) {
|
||||
printk("pg_init: unable to get major number %d\n", major);
|
||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
||||
struct pg *dev = &devices[unit];
|
||||
if (dev->present)
|
||||
pi_release(dev->pi);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
major = err; /* In case the user specified `major=0' (dynamic) */
|
||||
pg_class = class_create(THIS_MODULE, "pg");
|
||||
if (IS_ERR(pg_class)) {
|
||||
err = PTR_ERR(pg_class);
|
||||
goto out_chrdev;
|
||||
}
|
||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
||||
struct pg *dev = &devices[unit];
|
||||
if (dev->present)
|
||||
class_device_create(pg_class, NULL, MKDEV(major, unit),
|
||||
NULL, "pg%u", unit);
|
||||
}
|
||||
err = 0;
|
||||
goto out;
|
||||
|
||||
out_chrdev:
|
||||
unregister_chrdev(major, "pg");
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit pg_exit(void)
|
||||
{
|
||||
int unit;
|
||||
|
||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
||||
struct pg *dev = &devices[unit];
|
||||
if (dev->present)
|
||||
class_device_destroy(pg_class, MKDEV(major, unit));
|
||||
}
|
||||
class_destroy(pg_class);
|
||||
unregister_chrdev(major, name);
|
||||
|
||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
||||
struct pg *dev = &devices[unit];
|
||||
if (dev->present)
|
||||
pi_release(dev->pi);
|
||||
}
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(pg_init)
|
||||
module_exit(pg_exit)
|
||||
726
drivers/block/paride/ppc6lnx.c
Normal file
726
drivers/block/paride/ppc6lnx.c
Normal file
@@ -0,0 +1,726 @@
|
||||
/*
|
||||
ppc6lnx.c (c) 2001 Micro Solutions Inc.
|
||||
Released under the terms of the GNU General Public license
|
||||
|
||||
ppc6lnx.c is a par of the protocol driver for the Micro Solutions
|
||||
"BACKPACK" parallel port IDE adapter
|
||||
(Works on Series 6 drives)
|
||||
|
||||
*/
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
// PPC 6 Code in C sanitized for LINUX
|
||||
// Original x86 ASM by Ron, Converted to C by Clive
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
|
||||
#define port_stb 1
|
||||
#define port_afd 2
|
||||
#define cmd_stb port_afd
|
||||
#define port_init 4
|
||||
#define data_stb port_init
|
||||
#define port_sel 8
|
||||
#define port_int 16
|
||||
#define port_dir 0x20
|
||||
|
||||
#define ECR_EPP 0x80
|
||||
#define ECR_BI 0x20
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
// 60772 Commands
|
||||
|
||||
#define ACCESS_REG 0x00
|
||||
#define ACCESS_PORT 0x40
|
||||
|
||||
#define ACCESS_READ 0x00
|
||||
#define ACCESS_WRITE 0x20
|
||||
|
||||
// 60772 Command Prefix
|
||||
|
||||
#define CMD_PREFIX_SET 0xe0 // Special command that modifies the next command's operation
|
||||
#define CMD_PREFIX_RESET 0xc0 // Resets current cmd modifier reg bits
|
||||
#define PREFIX_IO16 0x01 // perform 16-bit wide I/O
|
||||
#define PREFIX_FASTWR 0x04 // enable PPC mode fast-write
|
||||
#define PREFIX_BLK 0x08 // enable block transfer mode
|
||||
|
||||
// 60772 Registers
|
||||
|
||||
#define REG_STATUS 0x00 // status register
|
||||
#define STATUS_IRQA 0x01 // Peripheral IRQA line
|
||||
#define STATUS_EEPROM_DO 0x40 // Serial EEPROM data bit
|
||||
#define REG_VERSION 0x01 // PPC version register (read)
|
||||
#define REG_HWCFG 0x02 // Hardware Config register
|
||||
#define REG_RAMSIZE 0x03 // Size of RAM Buffer
|
||||
#define RAMSIZE_128K 0x02
|
||||
#define REG_EEPROM 0x06 // EEPROM control register
|
||||
#define EEPROM_SK 0x01 // eeprom SK bit
|
||||
#define EEPROM_DI 0x02 // eeprom DI bit
|
||||
#define EEPROM_CS 0x04 // eeprom CS bit
|
||||
#define EEPROM_EN 0x08 // eeprom output enable
|
||||
#define REG_BLKSIZE 0x08 // Block transfer len (24 bit)
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
typedef struct ppc_storage {
|
||||
u16 lpt_addr; // LPT base address
|
||||
u8 ppc_id;
|
||||
u8 mode; // operating mode
|
||||
// 0 = PPC Uni SW
|
||||
// 1 = PPC Uni FW
|
||||
// 2 = PPC Bi SW
|
||||
// 3 = PPC Bi FW
|
||||
// 4 = EPP Byte
|
||||
// 5 = EPP Word
|
||||
// 6 = EPP Dword
|
||||
u8 ppc_flags;
|
||||
u8 org_data; // original LPT data port contents
|
||||
u8 org_ctrl; // original LPT control port contents
|
||||
u8 cur_ctrl; // current control port contents
|
||||
} Interface;
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
// ppc_flags
|
||||
|
||||
#define fifo_wait 0x10
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
// DONT CHANGE THESE LEST YOU BREAK EVERYTHING - BIT FIELD DEPENDENCIES
|
||||
|
||||
#define PPCMODE_UNI_SW 0
|
||||
#define PPCMODE_UNI_FW 1
|
||||
#define PPCMODE_BI_SW 2
|
||||
#define PPCMODE_BI_FW 3
|
||||
#define PPCMODE_EPP_BYTE 4
|
||||
#define PPCMODE_EPP_WORD 5
|
||||
#define PPCMODE_EPP_DWORD 6
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static int ppc6_select(Interface *ppc);
|
||||
static void ppc6_deselect(Interface *ppc);
|
||||
static void ppc6_send_cmd(Interface *ppc, u8 cmd);
|
||||
static void ppc6_wr_data_byte(Interface *ppc, u8 data);
|
||||
static u8 ppc6_rd_data_byte(Interface *ppc);
|
||||
static u8 ppc6_rd_port(Interface *ppc, u8 port);
|
||||
static void ppc6_wr_port(Interface *ppc, u8 port, u8 data);
|
||||
static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count);
|
||||
static void ppc6_wait_for_fifo(Interface *ppc);
|
||||
static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count);
|
||||
static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
|
||||
static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length);
|
||||
static void ppc6_wr_extout(Interface *ppc, u8 regdata);
|
||||
static int ppc6_open(Interface *ppc);
|
||||
static void ppc6_close(Interface *ppc);
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static int ppc6_select(Interface *ppc)
|
||||
{
|
||||
u8 i, j, k;
|
||||
|
||||
i = inb(ppc->lpt_addr + 1);
|
||||
|
||||
if (i & 1)
|
||||
outb(i, ppc->lpt_addr + 1);
|
||||
|
||||
ppc->org_data = inb(ppc->lpt_addr);
|
||||
|
||||
ppc->org_ctrl = inb(ppc->lpt_addr + 2) & 0x5F; // readback ctrl
|
||||
|
||||
ppc->cur_ctrl = ppc->org_ctrl;
|
||||
|
||||
ppc->cur_ctrl |= port_sel;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
if (ppc->org_data == 'b')
|
||||
outb('x', ppc->lpt_addr);
|
||||
|
||||
outb('b', ppc->lpt_addr);
|
||||
outb('p', ppc->lpt_addr);
|
||||
outb(ppc->ppc_id, ppc->lpt_addr);
|
||||
outb(~ppc->ppc_id,ppc->lpt_addr);
|
||||
|
||||
ppc->cur_ctrl &= ~port_sel;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
ppc->cur_ctrl = (ppc->cur_ctrl & port_int) | port_init;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
i = ppc->mode & 0x0C;
|
||||
|
||||
if (i == 0)
|
||||
i = (ppc->mode & 2) | 1;
|
||||
|
||||
outb(i, ppc->lpt_addr);
|
||||
|
||||
ppc->cur_ctrl |= port_sel;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
// DELAY
|
||||
|
||||
ppc->cur_ctrl |= port_afd;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
j = ((i & 0x08) << 4) | ((i & 0x07) << 3);
|
||||
|
||||
k = inb(ppc->lpt_addr + 1) & 0xB8;
|
||||
|
||||
if (j == k)
|
||||
{
|
||||
ppc->cur_ctrl &= ~port_afd;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
k = (inb(ppc->lpt_addr + 1) & 0xB8) ^ 0xB8;
|
||||
|
||||
if (j == k)
|
||||
{
|
||||
if (i & 4) // EPP
|
||||
ppc->cur_ctrl &= ~(port_sel | port_init);
|
||||
else // PPC/ECP
|
||||
ppc->cur_ctrl &= ~port_sel;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
outb(ppc->org_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
outb(ppc->org_data, ppc->lpt_addr);
|
||||
|
||||
return(0); // FAIL
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_deselect(Interface *ppc)
|
||||
{
|
||||
if (ppc->mode & 4) // EPP
|
||||
ppc->cur_ctrl |= port_init;
|
||||
else // PPC/ECP
|
||||
ppc->cur_ctrl |= port_sel;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
outb(ppc->org_data, ppc->lpt_addr);
|
||||
|
||||
outb((ppc->org_ctrl | port_sel), ppc->lpt_addr + 2);
|
||||
|
||||
outb(ppc->org_ctrl, ppc->lpt_addr + 2);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_send_cmd(Interface *ppc, u8 cmd)
|
||||
{
|
||||
switch(ppc->mode)
|
||||
{
|
||||
case PPCMODE_UNI_SW :
|
||||
case PPCMODE_UNI_FW :
|
||||
case PPCMODE_BI_SW :
|
||||
case PPCMODE_BI_FW :
|
||||
{
|
||||
outb(cmd, ppc->lpt_addr);
|
||||
|
||||
ppc->cur_ctrl ^= cmd_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_EPP_BYTE :
|
||||
case PPCMODE_EPP_WORD :
|
||||
case PPCMODE_EPP_DWORD :
|
||||
{
|
||||
outb(cmd, ppc->lpt_addr + 3);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_wr_data_byte(Interface *ppc, u8 data)
|
||||
{
|
||||
switch(ppc->mode)
|
||||
{
|
||||
case PPCMODE_UNI_SW :
|
||||
case PPCMODE_UNI_FW :
|
||||
case PPCMODE_BI_SW :
|
||||
case PPCMODE_BI_FW :
|
||||
{
|
||||
outb(data, ppc->lpt_addr);
|
||||
|
||||
ppc->cur_ctrl ^= data_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_EPP_BYTE :
|
||||
case PPCMODE_EPP_WORD :
|
||||
case PPCMODE_EPP_DWORD :
|
||||
{
|
||||
outb(data, ppc->lpt_addr + 4);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static u8 ppc6_rd_data_byte(Interface *ppc)
|
||||
{
|
||||
u8 data = 0;
|
||||
|
||||
switch(ppc->mode)
|
||||
{
|
||||
case PPCMODE_UNI_SW :
|
||||
case PPCMODE_UNI_FW :
|
||||
{
|
||||
ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
// DELAY
|
||||
|
||||
data = inb(ppc->lpt_addr + 1);
|
||||
|
||||
data = ((data & 0x80) >> 1) | ((data & 0x38) >> 3);
|
||||
|
||||
ppc->cur_ctrl |= port_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
// DELAY
|
||||
|
||||
data |= inb(ppc->lpt_addr + 1) & 0xB8;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_BI_SW :
|
||||
case PPCMODE_BI_FW :
|
||||
{
|
||||
ppc->cur_ctrl |= port_dir;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
ppc->cur_ctrl = (ppc->cur_ctrl | port_stb) ^ data_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
data = inb(ppc->lpt_addr);
|
||||
|
||||
ppc->cur_ctrl &= ~port_stb;
|
||||
|
||||
outb(ppc->cur_ctrl,ppc->lpt_addr + 2);
|
||||
|
||||
ppc->cur_ctrl &= ~port_dir;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_EPP_BYTE :
|
||||
case PPCMODE_EPP_WORD :
|
||||
case PPCMODE_EPP_DWORD :
|
||||
{
|
||||
outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);
|
||||
|
||||
data = inb(ppc->lpt_addr + 4);
|
||||
|
||||
outb(ppc->cur_ctrl,ppc->lpt_addr + 2);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(data);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static u8 ppc6_rd_port(Interface *ppc, u8 port)
|
||||
{
|
||||
ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_READ));
|
||||
|
||||
return(ppc6_rd_data_byte(ppc));
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_wr_port(Interface *ppc, u8 port, u8 data)
|
||||
{
|
||||
ppc6_send_cmd(ppc,(u8)(port | ACCESS_PORT | ACCESS_WRITE));
|
||||
|
||||
ppc6_wr_data_byte(ppc, data);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_rd_data_blk(Interface *ppc, u8 *data, long count)
|
||||
{
|
||||
switch(ppc->mode)
|
||||
{
|
||||
case PPCMODE_UNI_SW :
|
||||
case PPCMODE_UNI_FW :
|
||||
{
|
||||
while(count)
|
||||
{
|
||||
u8 d;
|
||||
|
||||
ppc->cur_ctrl = (ppc->cur_ctrl & ~port_stb) ^ data_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
// DELAY
|
||||
|
||||
d = inb(ppc->lpt_addr + 1);
|
||||
|
||||
d = ((d & 0x80) >> 1) | ((d & 0x38) >> 3);
|
||||
|
||||
ppc->cur_ctrl |= port_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
// DELAY
|
||||
|
||||
d |= inb(ppc->lpt_addr + 1) & 0xB8;
|
||||
|
||||
*data++ = d;
|
||||
count--;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_BI_SW :
|
||||
case PPCMODE_BI_FW :
|
||||
{
|
||||
ppc->cur_ctrl |= port_dir;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
ppc->cur_ctrl |= port_stb;
|
||||
|
||||
while(count)
|
||||
{
|
||||
ppc->cur_ctrl ^= data_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
*data++ = inb(ppc->lpt_addr);
|
||||
count--;
|
||||
}
|
||||
|
||||
ppc->cur_ctrl &= ~port_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
ppc->cur_ctrl &= ~port_dir;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_EPP_BYTE :
|
||||
{
|
||||
outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
|
||||
|
||||
// DELAY
|
||||
|
||||
while(count)
|
||||
{
|
||||
*data++ = inb(ppc->lpt_addr + 4);
|
||||
count--;
|
||||
}
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_EPP_WORD :
|
||||
{
|
||||
outb((ppc->cur_ctrl | port_dir), ppc->lpt_addr + 2);
|
||||
|
||||
// DELAY
|
||||
|
||||
while(count > 1)
|
||||
{
|
||||
*((u16 *)data) = inw(ppc->lpt_addr + 4);
|
||||
data += 2;
|
||||
count -= 2;
|
||||
}
|
||||
|
||||
while(count)
|
||||
{
|
||||
*data++ = inb(ppc->lpt_addr + 4);
|
||||
count--;
|
||||
}
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_EPP_DWORD :
|
||||
{
|
||||
outb((ppc->cur_ctrl | port_dir),ppc->lpt_addr + 2);
|
||||
|
||||
// DELAY
|
||||
|
||||
while(count > 3)
|
||||
{
|
||||
*((u32 *)data) = inl(ppc->lpt_addr + 4);
|
||||
data += 4;
|
||||
count -= 4;
|
||||
}
|
||||
|
||||
while(count)
|
||||
{
|
||||
*data++ = inb(ppc->lpt_addr + 4);
|
||||
count--;
|
||||
}
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_wait_for_fifo(Interface *ppc)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (ppc->ppc_flags & fifo_wait)
|
||||
{
|
||||
for(i=0; i<20; i++)
|
||||
inb(ppc->lpt_addr + 1);
|
||||
}
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_wr_data_blk(Interface *ppc, u8 *data, long count)
|
||||
{
|
||||
switch(ppc->mode)
|
||||
{
|
||||
case PPCMODE_UNI_SW :
|
||||
case PPCMODE_BI_SW :
|
||||
{
|
||||
while(count--)
|
||||
{
|
||||
outb(*data++, ppc->lpt_addr);
|
||||
|
||||
ppc->cur_ctrl ^= data_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_UNI_FW :
|
||||
case PPCMODE_BI_FW :
|
||||
{
|
||||
u8 this, last;
|
||||
|
||||
ppc6_send_cmd(ppc,(CMD_PREFIX_SET | PREFIX_FASTWR));
|
||||
|
||||
ppc->cur_ctrl |= port_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
last = *data;
|
||||
|
||||
outb(last, ppc->lpt_addr);
|
||||
|
||||
while(count)
|
||||
{
|
||||
this = *data++;
|
||||
count--;
|
||||
|
||||
if (this == last)
|
||||
{
|
||||
ppc->cur_ctrl ^= data_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
outb(this, ppc->lpt_addr);
|
||||
|
||||
last = this;
|
||||
}
|
||||
}
|
||||
|
||||
ppc->cur_ctrl &= ~port_stb;
|
||||
|
||||
outb(ppc->cur_ctrl, ppc->lpt_addr + 2);
|
||||
|
||||
ppc6_send_cmd(ppc,(CMD_PREFIX_RESET | PREFIX_FASTWR));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_EPP_BYTE :
|
||||
{
|
||||
while(count)
|
||||
{
|
||||
outb(*data++,ppc->lpt_addr + 4);
|
||||
count--;
|
||||
}
|
||||
|
||||
ppc6_wait_for_fifo(ppc);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_EPP_WORD :
|
||||
{
|
||||
while(count > 1)
|
||||
{
|
||||
outw(*((u16 *)data),ppc->lpt_addr + 4);
|
||||
data += 2;
|
||||
count -= 2;
|
||||
}
|
||||
|
||||
while(count)
|
||||
{
|
||||
outb(*data++,ppc->lpt_addr + 4);
|
||||
count--;
|
||||
}
|
||||
|
||||
ppc6_wait_for_fifo(ppc);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PPCMODE_EPP_DWORD :
|
||||
{
|
||||
while(count > 3)
|
||||
{
|
||||
outl(*((u32 *)data),ppc->lpt_addr + 4);
|
||||
data += 4;
|
||||
count -= 4;
|
||||
}
|
||||
|
||||
while(count)
|
||||
{
|
||||
outb(*data++,ppc->lpt_addr + 4);
|
||||
count--;
|
||||
}
|
||||
|
||||
ppc6_wait_for_fifo(ppc);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_rd_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
|
||||
{
|
||||
length = length << 1;
|
||||
|
||||
ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
|
||||
ppc6_wr_data_byte(ppc,(u8)length);
|
||||
ppc6_wr_data_byte(ppc,(u8)(length >> 8));
|
||||
ppc6_wr_data_byte(ppc,0);
|
||||
|
||||
ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));
|
||||
|
||||
ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_READ));
|
||||
|
||||
ppc6_rd_data_blk(ppc, data, length);
|
||||
|
||||
ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_wr_port16_blk(Interface *ppc, u8 port, u8 *data, long length)
|
||||
{
|
||||
length = length << 1;
|
||||
|
||||
ppc6_send_cmd(ppc, (REG_BLKSIZE | ACCESS_REG | ACCESS_WRITE));
|
||||
ppc6_wr_data_byte(ppc,(u8)length);
|
||||
ppc6_wr_data_byte(ppc,(u8)(length >> 8));
|
||||
ppc6_wr_data_byte(ppc,0);
|
||||
|
||||
ppc6_send_cmd(ppc, (CMD_PREFIX_SET | PREFIX_IO16 | PREFIX_BLK));
|
||||
|
||||
ppc6_send_cmd(ppc, (u8)(port | ACCESS_PORT | ACCESS_WRITE));
|
||||
|
||||
ppc6_wr_data_blk(ppc, data, length);
|
||||
|
||||
ppc6_send_cmd(ppc, (CMD_PREFIX_RESET | PREFIX_IO16 | PREFIX_BLK));
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_wr_extout(Interface *ppc, u8 regdata)
|
||||
{
|
||||
ppc6_send_cmd(ppc,(REG_VERSION | ACCESS_REG | ACCESS_WRITE));
|
||||
|
||||
ppc6_wr_data_byte(ppc, (u8)((regdata & 0x03) << 6));
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static int ppc6_open(Interface *ppc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ppc6_select(ppc);
|
||||
|
||||
if (ret == 0)
|
||||
return(ret);
|
||||
|
||||
ppc->ppc_flags &= ~fifo_wait;
|
||||
|
||||
ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_WRITE | REG_RAMSIZE));
|
||||
ppc6_wr_data_byte(ppc, RAMSIZE_128K);
|
||||
|
||||
ppc6_send_cmd(ppc, (ACCESS_REG | ACCESS_READ | REG_VERSION));
|
||||
|
||||
if ((ppc6_rd_data_byte(ppc) & 0x3F) == 0x0C)
|
||||
ppc->ppc_flags |= fifo_wait;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
static void ppc6_close(Interface *ppc)
|
||||
{
|
||||
ppc6_deselect(ppc);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
|
||||
102
drivers/block/paride/pseudo.h
Normal file
102
drivers/block/paride/pseudo.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
pseudo.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||
Under the terms of the GNU General Public License.
|
||||
|
||||
This is the "pseudo-interrupt" logic for parallel port drivers.
|
||||
|
||||
This module is #included into each driver. It makes one
|
||||
function available:
|
||||
|
||||
ps_set_intr( void (*continuation)(void),
|
||||
int (*ready)(void),
|
||||
int timeout,
|
||||
int nice )
|
||||
|
||||
Which will arrange for ready() to be evaluated frequently and
|
||||
when either it returns true, or timeout jiffies have passed,
|
||||
continuation() will be invoked.
|
||||
|
||||
If nice is 1, the test will done approximately once a
|
||||
jiffy. If nice is 0, the test will also be done whenever
|
||||
the scheduler runs (by adding it to a task queue). If
|
||||
nice is greater than 1, the test will be done once every
|
||||
(nice-1) jiffies.
|
||||
|
||||
*/
|
||||
|
||||
/* Changes:
|
||||
|
||||
1.01 1998.05.03 Switched from cli()/sti() to spinlocks
|
||||
1.02 1998.12.14 Added support for nice > 1
|
||||
*/
|
||||
|
||||
#define PS_VERSION "1.02"
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
static void ps_tq_int(struct work_struct *work);
|
||||
|
||||
static void (* ps_continuation)(void);
|
||||
static int (* ps_ready)(void);
|
||||
static unsigned long ps_timeout;
|
||||
static int ps_tq_active = 0;
|
||||
static int ps_nice = 0;
|
||||
|
||||
static DEFINE_SPINLOCK(ps_spinlock __attribute__((unused)));
|
||||
|
||||
static DECLARE_DELAYED_WORK(ps_tq, ps_tq_int);
|
||||
|
||||
static void ps_set_intr(void (*continuation)(void),
|
||||
int (*ready)(void),
|
||||
int timeout, int nice)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ps_spinlock,flags);
|
||||
|
||||
ps_continuation = continuation;
|
||||
ps_ready = ready;
|
||||
ps_timeout = jiffies + timeout;
|
||||
ps_nice = nice;
|
||||
|
||||
if (!ps_tq_active) {
|
||||
ps_tq_active = 1;
|
||||
if (!ps_nice)
|
||||
schedule_delayed_work(&ps_tq, 0);
|
||||
else
|
||||
schedule_delayed_work(&ps_tq, ps_nice-1);
|
||||
}
|
||||
spin_unlock_irqrestore(&ps_spinlock,flags);
|
||||
}
|
||||
|
||||
static void ps_tq_int(struct work_struct *work)
|
||||
{
|
||||
void (*con)(void);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ps_spinlock,flags);
|
||||
|
||||
con = ps_continuation;
|
||||
ps_tq_active = 0;
|
||||
|
||||
if (!con) {
|
||||
spin_unlock_irqrestore(&ps_spinlock,flags);
|
||||
return;
|
||||
}
|
||||
if (!ps_ready || ps_ready() || time_after_eq(jiffies, ps_timeout)) {
|
||||
ps_continuation = NULL;
|
||||
spin_unlock_irqrestore(&ps_spinlock,flags);
|
||||
con();
|
||||
return;
|
||||
}
|
||||
ps_tq_active = 1;
|
||||
if (!ps_nice)
|
||||
schedule_delayed_work(&ps_tq, 0);
|
||||
else
|
||||
schedule_delayed_work(&ps_tq, ps_nice-1);
|
||||
spin_unlock_irqrestore(&ps_spinlock,flags);
|
||||
}
|
||||
|
||||
/* end of pseudo.h */
|
||||
|
||||
1005
drivers/block/paride/pt.c
Normal file
1005
drivers/block/paride/pt.c
Normal file
File diff suppressed because it is too large
Load Diff
3129
drivers/block/pktcdvd.c
Normal file
3129
drivers/block/pktcdvd.c
Normal file
File diff suppressed because it is too large
Load Diff
1079
drivers/block/ps2esdi.c
Normal file
1079
drivers/block/ps2esdi.c
Normal file
File diff suppressed because it is too large
Load Diff
510
drivers/block/rd.c
Normal file
510
drivers/block/rd.c
Normal file
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* ramdisk.c - Multiple RAM disk driver - gzip-loading version - v. 0.8 beta.
|
||||
*
|
||||
* (C) Chad Page, Theodore Ts'o, et. al, 1995.
|
||||
*
|
||||
* This RAM disk 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
|
||||
* RAM disk data. This makes it possible to dynamically allocate the RAM disk
|
||||
* 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 RAM disk 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 RAM disk interface. Theodore
|
||||
* Ts'o rewrote it so that both the compressed RAM disk loader and the
|
||||
* kernel decompressor uses the same inflate.c codebase. The RAM disk
|
||||
* loader now also loads into a dynamic (buffer cache based) RAM disk,
|
||||
* not the old static RAM disk. Support for the old static RAM disk 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 RAM disk module is removed, free the protected buffers
|
||||
* Default RAM disk size changed to 2.88 MB
|
||||
*
|
||||
* Added initrd: Werner Almesberger & Hans Lermen, Feb '96
|
||||
*
|
||||
* 4/25/96 : Made RAM disk size a parameter (default is now 4 MB)
|
||||
* - Chad Page
|
||||
*
|
||||
* Add support for fs images split across >1 disk, Paul Gortmaker, Mar '98
|
||||
*
|
||||
* Make block size and block size shift for RAM disks a global macro
|
||||
* and set blk_size for -ENOSPC, Werner Fink <werner@suse.de>, Apr '99
|
||||
*/
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/buffer_head.h> /* for invalidate_bdev() */
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/blkpg.h>
|
||||
#include <linux/writeback.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/* Various static variables go here. Most are used only in the RAM disk code.
|
||||
*/
|
||||
|
||||
static struct gendisk *rd_disks[CONFIG_BLK_DEV_RAM_COUNT];
|
||||
static struct block_device *rd_bdev[CONFIG_BLK_DEV_RAM_COUNT];/* Protected device data */
|
||||
static struct request_queue *rd_queue[CONFIG_BLK_DEV_RAM_COUNT];
|
||||
|
||||
/*
|
||||
* Parameters for the boot-loading of the RAM disk. These are set by
|
||||
* init/main.c (from arguments to the kernel command line) or from the
|
||||
* architecture-specific setup routine (from the stored boot sector
|
||||
* information).
|
||||
*/
|
||||
int rd_size = CONFIG_BLK_DEV_RAM_SIZE; /* Size of the RAM disks */
|
||||
/*
|
||||
* It would be very desirable to have a soft-blocksize (that in the case
|
||||
* of the ramdisk driver is also the hardblocksize ;) of PAGE_SIZE because
|
||||
* doing that we'll achieve a far better MM footprint. Using a rd_blocksize of
|
||||
* BLOCK_SIZE in the worst case we'll make PAGE_SIZE/BLOCK_SIZE buffer-pages
|
||||
* unfreeable. With a rd_blocksize of PAGE_SIZE instead we are sure that only
|
||||
* 1 page will be protected. Depending on the size of the ramdisk you
|
||||
* may want to change the ramdisk blocksize to achieve a better or worse MM
|
||||
* behaviour. The default is still BLOCK_SIZE (needed by rd_load_image that
|
||||
* supposes the filesystem in the image uses a BLOCK_SIZE blocksize).
|
||||
*/
|
||||
static int rd_blocksize = CONFIG_BLK_DEV_RAM_BLOCKSIZE;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2000 Linus Torvalds.
|
||||
* 2000 Transmeta Corp.
|
||||
* aops copied from ramfs.
|
||||
*/
|
||||
|
||||
/*
|
||||
* If a ramdisk page has buffers, some may be uptodate and some may be not.
|
||||
* To bring the page uptodate we zero out the non-uptodate buffers. The
|
||||
* page must be locked.
|
||||
*/
|
||||
static void make_page_uptodate(struct page *page)
|
||||
{
|
||||
if (page_has_buffers(page)) {
|
||||
struct buffer_head *bh = page_buffers(page);
|
||||
struct buffer_head *head = bh;
|
||||
|
||||
do {
|
||||
if (!buffer_uptodate(bh)) {
|
||||
memset(bh->b_data, 0, bh->b_size);
|
||||
/*
|
||||
* akpm: I'm totally undecided about this. The
|
||||
* buffer has just been magically brought "up to
|
||||
* date", but nobody should want to be reading
|
||||
* it anyway, because it hasn't been used for
|
||||
* anything yet. It is still in a "not read
|
||||
* from disk yet" state.
|
||||
*
|
||||
* But non-uptodate buffers against an uptodate
|
||||
* page are against the rules. So do it anyway.
|
||||
*/
|
||||
set_buffer_uptodate(bh);
|
||||
}
|
||||
} while ((bh = bh->b_this_page) != head);
|
||||
} else {
|
||||
memset(page_address(page), 0, PAGE_CACHE_SIZE);
|
||||
}
|
||||
flush_dcache_page(page);
|
||||
SetPageUptodate(page);
|
||||
}
|
||||
|
||||
static int ramdisk_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
if (!PageUptodate(page))
|
||||
make_page_uptodate(page);
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ramdisk_prepare_write(struct file *file, struct page *page,
|
||||
unsigned offset, unsigned to)
|
||||
{
|
||||
if (!PageUptodate(page))
|
||||
make_page_uptodate(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ramdisk_commit_write(struct file *file, struct page *page,
|
||||
unsigned offset, unsigned to)
|
||||
{
|
||||
set_page_dirty(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ->writepage to the the blockdev's mapping has to redirty the page so that the
|
||||
* VM doesn't go and steal it. We return AOP_WRITEPAGE_ACTIVATE so that the VM
|
||||
* won't try to (pointlessly) write the page again for a while.
|
||||
*
|
||||
* Really, these pages should not be on the LRU at all.
|
||||
*/
|
||||
static int ramdisk_writepage(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
if (!PageUptodate(page))
|
||||
make_page_uptodate(page);
|
||||
SetPageDirty(page);
|
||||
if (wbc->for_reclaim)
|
||||
return AOP_WRITEPAGE_ACTIVATE;
|
||||
unlock_page(page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a little speedup thing: short-circuit attempts to write back the
|
||||
* ramdisk blockdev inode to its non-existent backing store.
|
||||
*/
|
||||
static int ramdisk_writepages(struct address_space *mapping,
|
||||
struct writeback_control *wbc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ramdisk blockdev pages have their own ->set_page_dirty() because we don't
|
||||
* want them to contribute to dirty memory accounting.
|
||||
*/
|
||||
static int ramdisk_set_page_dirty(struct page *page)
|
||||
{
|
||||
if (!TestSetPageDirty(page))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct address_space_operations ramdisk_aops = {
|
||||
.readpage = ramdisk_readpage,
|
||||
.prepare_write = ramdisk_prepare_write,
|
||||
.commit_write = ramdisk_commit_write,
|
||||
.writepage = ramdisk_writepage,
|
||||
.set_page_dirty = ramdisk_set_page_dirty,
|
||||
.writepages = ramdisk_writepages,
|
||||
};
|
||||
|
||||
static int rd_blkdev_pagecache_IO(int rw, struct bio_vec *vec, sector_t sector,
|
||||
struct address_space *mapping)
|
||||
{
|
||||
pgoff_t index = sector >> (PAGE_CACHE_SHIFT - 9);
|
||||
unsigned int vec_offset = vec->bv_offset;
|
||||
int offset = (sector << 9) & ~PAGE_CACHE_MASK;
|
||||
int size = vec->bv_len;
|
||||
int err = 0;
|
||||
|
||||
do {
|
||||
int count;
|
||||
struct page *page;
|
||||
char *src;
|
||||
char *dst;
|
||||
|
||||
count = PAGE_CACHE_SIZE - offset;
|
||||
if (count > size)
|
||||
count = size;
|
||||
size -= count;
|
||||
|
||||
page = grab_cache_page(mapping, index);
|
||||
if (!page) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!PageUptodate(page))
|
||||
make_page_uptodate(page);
|
||||
|
||||
index++;
|
||||
|
||||
if (rw == READ) {
|
||||
src = kmap_atomic(page, KM_USER0) + offset;
|
||||
dst = kmap_atomic(vec->bv_page, KM_USER1) + vec_offset;
|
||||
} else {
|
||||
src = kmap_atomic(vec->bv_page, KM_USER0) + vec_offset;
|
||||
dst = kmap_atomic(page, KM_USER1) + offset;
|
||||
}
|
||||
offset = 0;
|
||||
vec_offset += count;
|
||||
|
||||
memcpy(dst, src, count);
|
||||
|
||||
kunmap_atomic(src, KM_USER0);
|
||||
kunmap_atomic(dst, KM_USER1);
|
||||
|
||||
if (rw == READ)
|
||||
flush_dcache_page(vec->bv_page);
|
||||
else
|
||||
set_page_dirty(page);
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
} while (size);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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...
|
||||
*
|
||||
* 19-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Added devfs support
|
||||
*
|
||||
*/
|
||||
static int rd_make_request(request_queue_t *q, struct bio *bio)
|
||||
{
|
||||
struct block_device *bdev = bio->bi_bdev;
|
||||
struct address_space * mapping = bdev->bd_inode->i_mapping;
|
||||
sector_t sector = bio->bi_sector;
|
||||
unsigned long len = bio->bi_size >> 9;
|
||||
int rw = bio_data_dir(bio);
|
||||
struct bio_vec *bvec;
|
||||
int ret = 0, i;
|
||||
|
||||
if (sector + len > get_capacity(bdev->bd_disk))
|
||||
goto fail;
|
||||
|
||||
if (rw==READA)
|
||||
rw=READ;
|
||||
|
||||
bio_for_each_segment(bvec, bio, i) {
|
||||
ret |= rd_blkdev_pagecache_IO(rw, bvec, sector, mapping);
|
||||
sector += bvec->bv_len >> 9;
|
||||
}
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
bio_endio(bio, bio->bi_size, 0);
|
||||
return 0;
|
||||
fail:
|
||||
bio_io_error(bio, bio->bi_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rd_ioctl(struct inode *inode, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int error;
|
||||
struct block_device *bdev = inode->i_bdev;
|
||||
|
||||
if (cmd != BLKFLSBUF)
|
||||
return -ENOTTY;
|
||||
|
||||
/*
|
||||
* special: we want to release the ramdisk memory, it's not like with
|
||||
* the other blockdevices where this ioctl only flushes away the buffer
|
||||
* cache
|
||||
*/
|
||||
error = -EBUSY;
|
||||
mutex_lock(&bdev->bd_mutex);
|
||||
if (bdev->bd_openers <= 2) {
|
||||
truncate_inode_pages(bdev->bd_inode->i_mapping, 0);
|
||||
error = 0;
|
||||
}
|
||||
mutex_unlock(&bdev->bd_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the backing_dev_info for the blockdev inode itself. It doesn't need
|
||||
* writeback and it does not contribute to dirty memory accounting.
|
||||
*/
|
||||
static struct backing_dev_info rd_backing_dev_info = {
|
||||
.ra_pages = 0, /* No readahead */
|
||||
.capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK | BDI_CAP_MAP_COPY,
|
||||
.unplug_io_fn = default_unplug_io_fn,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is the backing_dev_info for the files which live atop the ramdisk
|
||||
* "device". These files do need writeback and they do contribute to dirty
|
||||
* memory accounting.
|
||||
*/
|
||||
static struct backing_dev_info rd_file_backing_dev_info = {
|
||||
.ra_pages = 0, /* No readahead */
|
||||
.capabilities = BDI_CAP_MAP_COPY, /* Does contribute to dirty memory */
|
||||
.unplug_io_fn = default_unplug_io_fn,
|
||||
};
|
||||
|
||||
static int rd_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
unsigned unit = iminor(inode);
|
||||
|
||||
if (rd_bdev[unit] == NULL) {
|
||||
struct block_device *bdev = inode->i_bdev;
|
||||
struct address_space *mapping;
|
||||
unsigned bsize;
|
||||
gfp_t gfp_mask;
|
||||
|
||||
inode = igrab(bdev->bd_inode);
|
||||
rd_bdev[unit] = bdev;
|
||||
bdev->bd_openers++;
|
||||
bsize = bdev_hardsect_size(bdev);
|
||||
bdev->bd_block_size = bsize;
|
||||
inode->i_blkbits = blksize_bits(bsize);
|
||||
inode->i_size = get_capacity(bdev->bd_disk)<<9;
|
||||
|
||||
mapping = inode->i_mapping;
|
||||
mapping->a_ops = &ramdisk_aops;
|
||||
mapping->backing_dev_info = &rd_backing_dev_info;
|
||||
bdev->bd_inode_backing_dev_info = &rd_file_backing_dev_info;
|
||||
|
||||
/*
|
||||
* Deep badness. rd_blkdev_pagecache_IO() needs to allocate
|
||||
* pagecache pages within a request_fn. We cannot recur back
|
||||
* into the filesytem which is mounted atop the ramdisk, because
|
||||
* that would deadlock on fs locks. And we really don't want
|
||||
* to reenter rd_blkdev_pagecache_IO when we're already within
|
||||
* that function.
|
||||
*
|
||||
* So we turn off __GFP_FS and __GFP_IO.
|
||||
*
|
||||
* And to give this thing a hope of working, turn on __GFP_HIGH.
|
||||
* Hopefully, there's enough regular memory allocation going on
|
||||
* for the page allocator emergency pools to keep the ramdisk
|
||||
* driver happy.
|
||||
*/
|
||||
gfp_mask = mapping_gfp_mask(mapping);
|
||||
gfp_mask &= ~(__GFP_FS|__GFP_IO);
|
||||
gfp_mask |= __GFP_HIGH;
|
||||
mapping_set_gfp_mask(mapping, gfp_mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct block_device_operations rd_bd_op = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = rd_open,
|
||||
.ioctl = rd_ioctl,
|
||||
};
|
||||
|
||||
/*
|
||||
* Before freeing the module, invalidate all of the protected buffers!
|
||||
*/
|
||||
static void __exit rd_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_BLK_DEV_RAM_COUNT; i++) {
|
||||
struct block_device *bdev = rd_bdev[i];
|
||||
rd_bdev[i] = NULL;
|
||||
if (bdev) {
|
||||
invalidate_bdev(bdev, 1);
|
||||
blkdev_put(bdev);
|
||||
}
|
||||
del_gendisk(rd_disks[i]);
|
||||
put_disk(rd_disks[i]);
|
||||
blk_cleanup_queue(rd_queue[i]);
|
||||
}
|
||||
unregister_blkdev(RAMDISK_MAJOR, "ramdisk");
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the registration and initialization section of the RAM disk driver
|
||||
*/
|
||||
static int __init rd_init(void)
|
||||
{
|
||||
int i;
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (rd_blocksize > PAGE_SIZE || rd_blocksize < 512 ||
|
||||
(rd_blocksize & (rd_blocksize-1))) {
|
||||
printk("RAMDISK: wrong blocksize %d, reverting to defaults\n",
|
||||
rd_blocksize);
|
||||
rd_blocksize = BLOCK_SIZE;
|
||||
}
|
||||
|
||||
for (i = 0; i < CONFIG_BLK_DEV_RAM_COUNT; i++) {
|
||||
rd_disks[i] = alloc_disk(1);
|
||||
if (!rd_disks[i])
|
||||
goto out;
|
||||
|
||||
rd_queue[i] = blk_alloc_queue(GFP_KERNEL);
|
||||
if (!rd_queue[i]) {
|
||||
put_disk(rd_disks[i]);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (register_blkdev(RAMDISK_MAJOR, "ramdisk")) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < CONFIG_BLK_DEV_RAM_COUNT; i++) {
|
||||
struct gendisk *disk = rd_disks[i];
|
||||
|
||||
blk_queue_make_request(rd_queue[i], &rd_make_request);
|
||||
blk_queue_hardsect_size(rd_queue[i], rd_blocksize);
|
||||
|
||||
/* rd_size is given in kB */
|
||||
disk->major = RAMDISK_MAJOR;
|
||||
disk->first_minor = i;
|
||||
disk->fops = &rd_bd_op;
|
||||
disk->queue = rd_queue[i];
|
||||
disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
|
||||
sprintf(disk->disk_name, "ram%d", i);
|
||||
set_capacity(disk, rd_size * 2);
|
||||
add_disk(rd_disks[i]);
|
||||
}
|
||||
|
||||
/* rd_size is given in kB */
|
||||
printk("RAMDISK driver initialized: "
|
||||
"%d RAM disks of %dK size %d blocksize\n",
|
||||
CONFIG_BLK_DEV_RAM_COUNT, rd_size, rd_blocksize);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
while (i--) {
|
||||
put_disk(rd_disks[i]);
|
||||
blk_cleanup_queue(rd_queue[i]);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(rd_init);
|
||||
module_exit(rd_cleanup);
|
||||
|
||||
/* options - nonmodular */
|
||||
#ifndef MODULE
|
||||
static int __init ramdisk_size(char *str)
|
||||
{
|
||||
rd_size = simple_strtol(str,NULL,0);
|
||||
return 1;
|
||||
}
|
||||
static int __init ramdisk_size2(char *str) /* kludge */
|
||||
{
|
||||
return ramdisk_size(str);
|
||||
}
|
||||
static int __init ramdisk_blocksize(char *str)
|
||||
{
|
||||
rd_blocksize = simple_strtol(str,NULL,0);
|
||||
return 1;
|
||||
}
|
||||
__setup("ramdisk=", ramdisk_size);
|
||||
__setup("ramdisk_size=", ramdisk_size2);
|
||||
__setup("ramdisk_blocksize=", ramdisk_blocksize);
|
||||
#endif
|
||||
|
||||
/* options - modular */
|
||||
module_param(rd_size, int, 0);
|
||||
MODULE_PARM_DESC(rd_size, "Size of each RAM disk in kbytes.");
|
||||
module_param(rd_blocksize, int, 0);
|
||||
MODULE_PARM_DESC(rd_blocksize, "Blocksize of each RAM disk in bytes.");
|
||||
MODULE_ALIAS_BLOCKDEV_MAJOR(RAMDISK_MAJOR);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
278
drivers/block/smart1,2.h
Normal file
278
drivers/block/smart1,2.h
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Disk Array driver for Compaq SMART2 Controllers
|
||||
* Copyright 1998 Compaq Computer Corporation
|
||||
*
|
||||
* 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, GOOD TITLE or
|
||||
* NON INFRINGEMENT. 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.
|
||||
*
|
||||
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
|
||||
*
|
||||
* If you want to make changes, improve or add functionality to this
|
||||
* driver, you'll probably need the Compaq Array Controller Interface
|
||||
* Specificiation (Document number ECG086/1198)
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains the controller communication implementation for
|
||||
* Compaq SMART-1 and SMART-2 controllers. To the best of my knowledge,
|
||||
* this should support:
|
||||
*
|
||||
* PCI:
|
||||
* SMART-2/P, SMART-2DH, SMART-2SL, SMART-221, SMART-3100ES, SMART-3200
|
||||
* Integerated SMART Array Controller, SMART-4200, SMART-4250ES
|
||||
*
|
||||
* EISA:
|
||||
* SMART-2/E, SMART, IAES, IDA-2, IDA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Memory mapped FIFO interface (SMART 42xx cards)
|
||||
*/
|
||||
static void smart4_submit_command(ctlr_info_t *h, cmdlist_t *c)
|
||||
{
|
||||
writel(c->busaddr, h->vaddr + S42XX_REQUEST_PORT_OFFSET);
|
||||
}
|
||||
|
||||
/*
|
||||
* This card is the opposite of the other cards.
|
||||
* 0 turns interrupts on...
|
||||
* 0x08 turns them off...
|
||||
*/
|
||||
static void smart4_intr_mask(ctlr_info_t *h, unsigned long val)
|
||||
{
|
||||
if (val)
|
||||
{ /* Turn interrupts on */
|
||||
writel(0, h->vaddr + S42XX_REPLY_INTR_MASK_OFFSET);
|
||||
} else /* Turn them off */
|
||||
{
|
||||
writel( S42XX_INTR_OFF,
|
||||
h->vaddr + S42XX_REPLY_INTR_MASK_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For older cards FIFO Full = 0.
|
||||
* On this card 0 means there is room, anything else FIFO Full.
|
||||
*
|
||||
*/
|
||||
static unsigned long smart4_fifo_full(ctlr_info_t *h)
|
||||
{
|
||||
|
||||
return (!readl(h->vaddr + S42XX_REQUEST_PORT_OFFSET));
|
||||
}
|
||||
|
||||
/* This type of controller returns -1 if the fifo is empty,
|
||||
* Not 0 like the others.
|
||||
* And we need to let it know we read a value out
|
||||
*/
|
||||
static unsigned long smart4_completed(ctlr_info_t *h)
|
||||
{
|
||||
long register_value
|
||||
= readl(h->vaddr + S42XX_REPLY_PORT_OFFSET);
|
||||
|
||||
/* Fifo is empty */
|
||||
if( register_value == 0xffffffff)
|
||||
return 0;
|
||||
|
||||
/* Need to let it know we got the reply */
|
||||
/* We do this by writing a 0 to the port we just read from */
|
||||
writel(0, h->vaddr + S42XX_REPLY_PORT_OFFSET);
|
||||
|
||||
return ((unsigned long) register_value);
|
||||
}
|
||||
|
||||
/*
|
||||
* This hardware returns interrupt pending at a different place and
|
||||
* it does not tell us if the fifo is empty, we will have check
|
||||
* that by getting a 0 back from the comamnd_completed call.
|
||||
*/
|
||||
static unsigned long smart4_intr_pending(ctlr_info_t *h)
|
||||
{
|
||||
unsigned long register_value =
|
||||
readl(h->vaddr + S42XX_INTR_STATUS);
|
||||
|
||||
if( register_value & S42XX_INTR_PENDING)
|
||||
return FIFO_NOT_EMPTY;
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
static struct access_method smart4_access = {
|
||||
smart4_submit_command,
|
||||
smart4_intr_mask,
|
||||
smart4_fifo_full,
|
||||
smart4_intr_pending,
|
||||
smart4_completed,
|
||||
};
|
||||
|
||||
/*
|
||||
* Memory mapped FIFO interface (PCI SMART2 and SMART 3xxx cards)
|
||||
*/
|
||||
static void smart2_submit_command(ctlr_info_t *h, cmdlist_t *c)
|
||||
{
|
||||
writel(c->busaddr, h->vaddr + COMMAND_FIFO);
|
||||
}
|
||||
|
||||
static void smart2_intr_mask(ctlr_info_t *h, unsigned long val)
|
||||
{
|
||||
writel(val, h->vaddr + INTR_MASK);
|
||||
}
|
||||
|
||||
static unsigned long smart2_fifo_full(ctlr_info_t *h)
|
||||
{
|
||||
return readl(h->vaddr + COMMAND_FIFO);
|
||||
}
|
||||
|
||||
static unsigned long smart2_completed(ctlr_info_t *h)
|
||||
{
|
||||
return readl(h->vaddr + COMMAND_COMPLETE_FIFO);
|
||||
}
|
||||
|
||||
static unsigned long smart2_intr_pending(ctlr_info_t *h)
|
||||
{
|
||||
return readl(h->vaddr + INTR_PENDING);
|
||||
}
|
||||
|
||||
static struct access_method smart2_access = {
|
||||
smart2_submit_command,
|
||||
smart2_intr_mask,
|
||||
smart2_fifo_full,
|
||||
smart2_intr_pending,
|
||||
smart2_completed,
|
||||
};
|
||||
|
||||
/*
|
||||
* IO access for SMART-2/E cards
|
||||
*/
|
||||
static void smart2e_submit_command(ctlr_info_t *h, cmdlist_t *c)
|
||||
{
|
||||
outl(c->busaddr, h->io_mem_addr + COMMAND_FIFO);
|
||||
}
|
||||
|
||||
static void smart2e_intr_mask(ctlr_info_t *h, unsigned long val)
|
||||
{
|
||||
outl(val, h->io_mem_addr + INTR_MASK);
|
||||
}
|
||||
|
||||
static unsigned long smart2e_fifo_full(ctlr_info_t *h)
|
||||
{
|
||||
return inl(h->io_mem_addr + COMMAND_FIFO);
|
||||
}
|
||||
|
||||
static unsigned long smart2e_completed(ctlr_info_t *h)
|
||||
{
|
||||
return inl(h->io_mem_addr + COMMAND_COMPLETE_FIFO);
|
||||
}
|
||||
|
||||
static unsigned long smart2e_intr_pending(ctlr_info_t *h)
|
||||
{
|
||||
return inl(h->io_mem_addr + INTR_PENDING);
|
||||
}
|
||||
|
||||
static struct access_method smart2e_access = {
|
||||
smart2e_submit_command,
|
||||
smart2e_intr_mask,
|
||||
smart2e_fifo_full,
|
||||
smart2e_intr_pending,
|
||||
smart2e_completed,
|
||||
};
|
||||
|
||||
/*
|
||||
* IO access for older SMART-1 type cards
|
||||
*/
|
||||
#define SMART1_SYSTEM_MASK 0xC8E
|
||||
#define SMART1_SYSTEM_DOORBELL 0xC8F
|
||||
#define SMART1_LOCAL_MASK 0xC8C
|
||||
#define SMART1_LOCAL_DOORBELL 0xC8D
|
||||
#define SMART1_INTR_MASK 0xC89
|
||||
#define SMART1_LISTADDR 0xC90
|
||||
#define SMART1_LISTLEN 0xC94
|
||||
#define SMART1_TAG 0xC97
|
||||
#define SMART1_COMPLETE_ADDR 0xC98
|
||||
#define SMART1_LISTSTATUS 0xC9E
|
||||
|
||||
#define CHANNEL_BUSY 0x01
|
||||
#define CHANNEL_CLEAR 0x02
|
||||
|
||||
static void smart1_submit_command(ctlr_info_t *h, cmdlist_t *c)
|
||||
{
|
||||
/*
|
||||
* This __u16 is actually a bunch of control flags on SMART
|
||||
* and below. We want them all to be zero.
|
||||
*/
|
||||
c->hdr.size = 0;
|
||||
|
||||
outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_SYSTEM_DOORBELL);
|
||||
|
||||
outl(c->busaddr, h->io_mem_addr + SMART1_LISTADDR);
|
||||
outw(c->size, h->io_mem_addr + SMART1_LISTLEN);
|
||||
|
||||
outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL);
|
||||
}
|
||||
|
||||
static void smart1_intr_mask(ctlr_info_t *h, unsigned long val)
|
||||
{
|
||||
if (val == 1) {
|
||||
outb(0xFD, h->io_mem_addr + SMART1_SYSTEM_DOORBELL);
|
||||
outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL);
|
||||
outb(0x01, h->io_mem_addr + SMART1_INTR_MASK);
|
||||
outb(0x01, h->io_mem_addr + SMART1_SYSTEM_MASK);
|
||||
} else {
|
||||
outb(0, h->io_mem_addr + 0xC8E);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long smart1_fifo_full(ctlr_info_t *h)
|
||||
{
|
||||
unsigned char chan;
|
||||
chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_CLEAR;
|
||||
return chan;
|
||||
}
|
||||
|
||||
static unsigned long smart1_completed(ctlr_info_t *h)
|
||||
{
|
||||
unsigned char status;
|
||||
unsigned long cmd;
|
||||
|
||||
if (inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY) {
|
||||
outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_SYSTEM_DOORBELL);
|
||||
|
||||
cmd = inl(h->io_mem_addr + SMART1_COMPLETE_ADDR);
|
||||
status = inb(h->io_mem_addr + SMART1_LISTSTATUS);
|
||||
|
||||
outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_LOCAL_DOORBELL);
|
||||
|
||||
/*
|
||||
* this is x86 (actually compaq x86) only, so it's ok
|
||||
*/
|
||||
if (cmd) ((cmdlist_t*)bus_to_virt(cmd))->req.hdr.rcode = status;
|
||||
} else {
|
||||
cmd = 0;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static unsigned long smart1_intr_pending(ctlr_info_t *h)
|
||||
{
|
||||
unsigned char chan;
|
||||
chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY;
|
||||
return chan;
|
||||
}
|
||||
|
||||
static struct access_method smart1_access = {
|
||||
smart1_submit_command,
|
||||
smart1_intr_mask,
|
||||
smart1_fifo_full,
|
||||
smart1_intr_pending,
|
||||
smart1_completed,
|
||||
};
|
||||
1192
drivers/block/swim3.c
Normal file
1192
drivers/block/swim3.c
Normal file
File diff suppressed because it is too large
Load Diff
1766
drivers/block/sx8.c
Normal file
1766
drivers/block/sx8.c
Normal file
File diff suppressed because it is too large
Load Diff
2481
drivers/block/ub.c
Normal file
2481
drivers/block/ub.c
Normal file
File diff suppressed because it is too large
Load Diff
1244
drivers/block/umem.c
Normal file
1244
drivers/block/umem.c
Normal file
File diff suppressed because it is too large
Load Diff
847
drivers/block/viodasd.c
Normal file
847
drivers/block/viodasd.c
Normal file
@@ -0,0 +1,847 @@
|
||||
/* -*- linux-c -*-
|
||||
* viodasd.c
|
||||
* Authors: Dave Boutcher <boutcher@us.ibm.com>
|
||||
* Ryan Arnold <ryanarn@us.ibm.com>
|
||||
* Colin Devilbiss <devilbis@us.ibm.com>
|
||||
* Stephen Rothwell <sfr@au1.ibm.com>
|
||||
*
|
||||
* (C) Copyright 2000-2004 IBM Corporation
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* This routine provides access to disk space (termed "DASD" in historical
|
||||
* IBM terms) owned and managed by an OS/400 partition running on the
|
||||
* same box as this Linux partition.
|
||||
*
|
||||
* All disk operations are performed by sending messages back and forth to
|
||||
* the OS/400 partition.
|
||||
*/
|
||||
#include <linux/major.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/vio.h>
|
||||
#include <asm/iseries/hv_types.h>
|
||||
#include <asm/iseries/hv_lp_event.h>
|
||||
#include <asm/iseries/hv_lp_config.h>
|
||||
#include <asm/iseries/vio.h>
|
||||
#include <asm/firmware.h>
|
||||
|
||||
MODULE_DESCRIPTION("iSeries Virtual DASD");
|
||||
MODULE_AUTHOR("Dave Boutcher");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* We only support 7 partitions per physical disk....so with minor
|
||||
* numbers 0-255 we get a maximum of 32 disks.
|
||||
*/
|
||||
#define VIOD_GENHD_NAME "iseries/vd"
|
||||
|
||||
#define VIOD_VERS "1.64"
|
||||
|
||||
#define VIOD_KERN_WARNING KERN_WARNING "viod: "
|
||||
#define VIOD_KERN_INFO KERN_INFO "viod: "
|
||||
|
||||
enum {
|
||||
PARTITION_SHIFT = 3,
|
||||
MAX_DISKNO = HVMAXARCHITECTEDVIRTUALDISKS,
|
||||
MAX_DISK_NAME = sizeof(((struct gendisk *)0)->disk_name)
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(viodasd_spinlock);
|
||||
|
||||
#define VIOMAXREQ 16
|
||||
#define VIOMAXBLOCKDMA 12
|
||||
|
||||
#define DEVICE_NO(cell) ((struct viodasd_device *)(cell) - &viodasd_devices[0])
|
||||
|
||||
struct open_data {
|
||||
u64 disk_size;
|
||||
u16 max_disk;
|
||||
u16 cylinders;
|
||||
u16 tracks;
|
||||
u16 sectors;
|
||||
u16 bytes_per_sector;
|
||||
};
|
||||
|
||||
struct rw_data {
|
||||
u64 offset;
|
||||
struct {
|
||||
u32 token;
|
||||
u32 reserved;
|
||||
u64 len;
|
||||
} dma_info[VIOMAXBLOCKDMA];
|
||||
};
|
||||
|
||||
struct vioblocklpevent {
|
||||
struct HvLpEvent event;
|
||||
u32 reserved;
|
||||
u16 version;
|
||||
u16 sub_result;
|
||||
u16 disk;
|
||||
u16 flags;
|
||||
union {
|
||||
struct open_data open_data;
|
||||
struct rw_data rw_data;
|
||||
u64 changed;
|
||||
} u;
|
||||
};
|
||||
|
||||
#define vioblockflags_ro 0x0001
|
||||
|
||||
enum vioblocksubtype {
|
||||
vioblockopen = 0x0001,
|
||||
vioblockclose = 0x0002,
|
||||
vioblockread = 0x0003,
|
||||
vioblockwrite = 0x0004,
|
||||
vioblockflush = 0x0005,
|
||||
vioblockcheck = 0x0007
|
||||
};
|
||||
|
||||
struct viodasd_waitevent {
|
||||
struct completion com;
|
||||
int rc;
|
||||
u16 sub_result;
|
||||
int max_disk; /* open */
|
||||
};
|
||||
|
||||
static const struct vio_error_entry viodasd_err_table[] = {
|
||||
{ 0x0201, EINVAL, "Invalid Range" },
|
||||
{ 0x0202, EINVAL, "Invalid Token" },
|
||||
{ 0x0203, EIO, "DMA Error" },
|
||||
{ 0x0204, EIO, "Use Error" },
|
||||
{ 0x0205, EIO, "Release Error" },
|
||||
{ 0x0206, EINVAL, "Invalid Disk" },
|
||||
{ 0x0207, EBUSY, "Cant Lock" },
|
||||
{ 0x0208, EIO, "Already Locked" },
|
||||
{ 0x0209, EIO, "Already Unlocked" },
|
||||
{ 0x020A, EIO, "Invalid Arg" },
|
||||
{ 0x020B, EIO, "Bad IFS File" },
|
||||
{ 0x020C, EROFS, "Read Only Device" },
|
||||
{ 0x02FF, EIO, "Internal Error" },
|
||||
{ 0x0000, 0, NULL },
|
||||
};
|
||||
|
||||
/*
|
||||
* Figure out the biggest I/O request (in sectors) we can accept
|
||||
*/
|
||||
#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA)
|
||||
|
||||
/*
|
||||
* Number of disk I/O requests we've sent to OS/400
|
||||
*/
|
||||
static int num_req_outstanding;
|
||||
|
||||
/*
|
||||
* This is our internal structure for keeping track of disk devices
|
||||
*/
|
||||
struct viodasd_device {
|
||||
u16 cylinders;
|
||||
u16 tracks;
|
||||
u16 sectors;
|
||||
u16 bytes_per_sector;
|
||||
u64 size;
|
||||
int read_only;
|
||||
spinlock_t q_lock;
|
||||
struct gendisk *disk;
|
||||
struct device *dev;
|
||||
} viodasd_devices[MAX_DISKNO];
|
||||
|
||||
/*
|
||||
* External open entry point.
|
||||
*/
|
||||
static int viodasd_open(struct inode *ino, struct file *fil)
|
||||
{
|
||||
struct viodasd_device *d = ino->i_bdev->bd_disk->private_data;
|
||||
HvLpEvent_Rc hvrc;
|
||||
struct viodasd_waitevent we;
|
||||
u16 flags = 0;
|
||||
|
||||
if (d->read_only) {
|
||||
if ((fil != NULL) && (fil->f_mode & FMODE_WRITE))
|
||||
return -EROFS;
|
||||
flags = vioblockflags_ro;
|
||||
}
|
||||
|
||||
init_completion(&we.com);
|
||||
|
||||
/* Send the open event to OS/400 */
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_blockio | vioblockopen,
|
||||
HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
(u64)(unsigned long)&we, VIOVERSION << 16,
|
||||
((u64)DEVICE_NO(d) << 48) | ((u64)flags << 32),
|
||||
0, 0, 0);
|
||||
if (hvrc != 0) {
|
||||
printk(VIOD_KERN_WARNING "HV open failed %d\n", (int)hvrc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
wait_for_completion(&we.com);
|
||||
|
||||
/* Check the return code */
|
||||
if (we.rc != 0) {
|
||||
const struct vio_error_entry *err =
|
||||
vio_lookup_rc(viodasd_err_table, we.sub_result);
|
||||
|
||||
printk(VIOD_KERN_WARNING
|
||||
"bad rc opening disk: %d:0x%04x (%s)\n",
|
||||
(int)we.rc, we.sub_result, err->msg);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* External release entry point.
|
||||
*/
|
||||
static int viodasd_release(struct inode *ino, struct file *fil)
|
||||
{
|
||||
struct viodasd_device *d = ino->i_bdev->bd_disk->private_data;
|
||||
HvLpEvent_Rc hvrc;
|
||||
|
||||
/* Send the event to OS/400. We DON'T expect a response */
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_blockio | vioblockclose,
|
||||
HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
0, VIOVERSION << 16,
|
||||
((u64)DEVICE_NO(d) << 48) /* | ((u64)flags << 32) */,
|
||||
0, 0, 0);
|
||||
if (hvrc != 0)
|
||||
printk(VIOD_KERN_WARNING "HV close call failed %d\n",
|
||||
(int)hvrc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* External ioctl entry point.
|
||||
*/
|
||||
static int viodasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
||||
{
|
||||
struct gendisk *disk = bdev->bd_disk;
|
||||
struct viodasd_device *d = disk->private_data;
|
||||
|
||||
geo->sectors = d->sectors ? d->sectors : 0;
|
||||
geo->heads = d->tracks ? d->tracks : 64;
|
||||
geo->cylinders = d->cylinders ? d->cylinders :
|
||||
get_capacity(disk) / (geo->cylinders * geo->heads);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Our file operations table
|
||||
*/
|
||||
static struct block_device_operations viodasd_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = viodasd_open,
|
||||
.release = viodasd_release,
|
||||
.getgeo = viodasd_getgeo,
|
||||
};
|
||||
|
||||
/*
|
||||
* End a request
|
||||
*/
|
||||
static void viodasd_end_request(struct request *req, int uptodate,
|
||||
int num_sectors)
|
||||
{
|
||||
if (end_that_request_first(req, uptodate, num_sectors))
|
||||
return;
|
||||
add_disk_randomness(req->rq_disk);
|
||||
end_that_request_last(req, uptodate);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send an actual I/O request to OS/400
|
||||
*/
|
||||
static int send_request(struct request *req)
|
||||
{
|
||||
u64 start;
|
||||
int direction;
|
||||
int nsg;
|
||||
u16 viocmd;
|
||||
HvLpEvent_Rc hvrc;
|
||||
struct vioblocklpevent *bevent;
|
||||
struct HvLpEvent *hev;
|
||||
struct scatterlist sg[VIOMAXBLOCKDMA];
|
||||
int sgindex;
|
||||
int statindex;
|
||||
struct viodasd_device *d;
|
||||
unsigned long flags;
|
||||
|
||||
start = (u64)req->sector << 9;
|
||||
|
||||
if (rq_data_dir(req) == READ) {
|
||||
direction = DMA_FROM_DEVICE;
|
||||
viocmd = viomajorsubtype_blockio | vioblockread;
|
||||
statindex = 0;
|
||||
} else {
|
||||
direction = DMA_TO_DEVICE;
|
||||
viocmd = viomajorsubtype_blockio | vioblockwrite;
|
||||
statindex = 1;
|
||||
}
|
||||
|
||||
d = req->rq_disk->private_data;
|
||||
|
||||
/* Now build the scatter-gather list */
|
||||
nsg = blk_rq_map_sg(req->q, req, sg);
|
||||
nsg = dma_map_sg(d->dev, sg, nsg, direction);
|
||||
|
||||
spin_lock_irqsave(&viodasd_spinlock, flags);
|
||||
num_req_outstanding++;
|
||||
|
||||
/* This optimization handles a single DMA block */
|
||||
if (nsg == 1)
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo, viocmd,
|
||||
HvLpEvent_AckInd_DoAck,
|
||||
HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
(u64)(unsigned long)req, VIOVERSION << 16,
|
||||
((u64)DEVICE_NO(d) << 48), start,
|
||||
((u64)sg_dma_address(&sg[0])) << 32,
|
||||
sg_dma_len(&sg[0]));
|
||||
else {
|
||||
bevent = (struct vioblocklpevent *)
|
||||
vio_get_event_buffer(viomajorsubtype_blockio);
|
||||
if (bevent == NULL) {
|
||||
printk(VIOD_KERN_WARNING
|
||||
"error allocating disk event buffer\n");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now build up the actual request. Note that we store
|
||||
* the pointer to the request in the correlation
|
||||
* token so we can match the response up later
|
||||
*/
|
||||
memset(bevent, 0, sizeof(struct vioblocklpevent));
|
||||
hev = &bevent->event;
|
||||
hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DO_ACK |
|
||||
HV_LP_EVENT_INT;
|
||||
hev->xType = HvLpEvent_Type_VirtualIo;
|
||||
hev->xSubtype = viocmd;
|
||||
hev->xSourceLp = HvLpConfig_getLpIndex();
|
||||
hev->xTargetLp = viopath_hostLp;
|
||||
hev->xSizeMinus1 =
|
||||
offsetof(struct vioblocklpevent, u.rw_data.dma_info) +
|
||||
(sizeof(bevent->u.rw_data.dma_info[0]) * nsg) - 1;
|
||||
hev->xSourceInstanceId = viopath_sourceinst(viopath_hostLp);
|
||||
hev->xTargetInstanceId = viopath_targetinst(viopath_hostLp);
|
||||
hev->xCorrelationToken = (u64)req;
|
||||
bevent->version = VIOVERSION;
|
||||
bevent->disk = DEVICE_NO(d);
|
||||
bevent->u.rw_data.offset = start;
|
||||
|
||||
/*
|
||||
* Copy just the dma information from the sg list
|
||||
* into the request
|
||||
*/
|
||||
for (sgindex = 0; sgindex < nsg; sgindex++) {
|
||||
bevent->u.rw_data.dma_info[sgindex].token =
|
||||
sg_dma_address(&sg[sgindex]);
|
||||
bevent->u.rw_data.dma_info[sgindex].len =
|
||||
sg_dma_len(&sg[sgindex]);
|
||||
}
|
||||
|
||||
/* Send the request */
|
||||
hvrc = HvCallEvent_signalLpEvent(&bevent->event);
|
||||
vio_free_event_buffer(viomajorsubtype_blockio, bevent);
|
||||
}
|
||||
|
||||
if (hvrc != HvLpEvent_Rc_Good) {
|
||||
printk(VIOD_KERN_WARNING
|
||||
"error sending disk event to OS/400 (rc %d)\n",
|
||||
(int)hvrc);
|
||||
goto error_ret;
|
||||
}
|
||||
spin_unlock_irqrestore(&viodasd_spinlock, flags);
|
||||
return 0;
|
||||
|
||||
error_ret:
|
||||
num_req_outstanding--;
|
||||
spin_unlock_irqrestore(&viodasd_spinlock, flags);
|
||||
dma_unmap_sg(d->dev, sg, nsg, direction);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the external request processing routine
|
||||
*/
|
||||
static void do_viodasd_request(request_queue_t *q)
|
||||
{
|
||||
struct request *req;
|
||||
|
||||
/*
|
||||
* If we already have the maximum number of requests
|
||||
* outstanding to OS/400 just bail out. We'll come
|
||||
* back later.
|
||||
*/
|
||||
while (num_req_outstanding < VIOMAXREQ) {
|
||||
req = elv_next_request(q);
|
||||
if (req == NULL)
|
||||
return;
|
||||
/* dequeue the current request from the queue */
|
||||
blkdev_dequeue_request(req);
|
||||
/* check that request contains a valid command */
|
||||
if (!blk_fs_request(req)) {
|
||||
viodasd_end_request(req, 0, req->hard_nr_sectors);
|
||||
continue;
|
||||
}
|
||||
/* Try sending the request */
|
||||
if (send_request(req) != 0)
|
||||
viodasd_end_request(req, 0, req->hard_nr_sectors);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe a single disk and fill in the viodasd_device structure
|
||||
* for it.
|
||||
*/
|
||||
static void probe_disk(struct viodasd_device *d)
|
||||
{
|
||||
HvLpEvent_Rc hvrc;
|
||||
struct viodasd_waitevent we;
|
||||
int dev_no = DEVICE_NO(d);
|
||||
struct gendisk *g;
|
||||
struct request_queue *q;
|
||||
u16 flags = 0;
|
||||
|
||||
retry:
|
||||
init_completion(&we.com);
|
||||
|
||||
/* Send the open event to OS/400 */
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_blockio | vioblockopen,
|
||||
HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
(u64)(unsigned long)&we, VIOVERSION << 16,
|
||||
((u64)dev_no << 48) | ((u64)flags<< 32),
|
||||
0, 0, 0);
|
||||
if (hvrc != 0) {
|
||||
printk(VIOD_KERN_WARNING "bad rc on HV open %d\n", (int)hvrc);
|
||||
return;
|
||||
}
|
||||
|
||||
wait_for_completion(&we.com);
|
||||
|
||||
if (we.rc != 0) {
|
||||
if (flags != 0)
|
||||
return;
|
||||
/* try again with read only flag set */
|
||||
flags = vioblockflags_ro;
|
||||
goto retry;
|
||||
}
|
||||
if (we.max_disk > (MAX_DISKNO - 1)) {
|
||||
static int warned;
|
||||
|
||||
if (warned == 0) {
|
||||
warned++;
|
||||
printk(VIOD_KERN_INFO
|
||||
"Only examining the first %d "
|
||||
"of %d disks connected\n",
|
||||
MAX_DISKNO, we.max_disk + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the close event to OS/400. We DON'T expect a response */
|
||||
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
|
||||
HvLpEvent_Type_VirtualIo,
|
||||
viomajorsubtype_blockio | vioblockclose,
|
||||
HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
|
||||
viopath_sourceinst(viopath_hostLp),
|
||||
viopath_targetinst(viopath_hostLp),
|
||||
0, VIOVERSION << 16,
|
||||
((u64)dev_no << 48) | ((u64)flags << 32),
|
||||
0, 0, 0);
|
||||
if (hvrc != 0) {
|
||||
printk(VIOD_KERN_WARNING
|
||||
"bad rc sending event to OS/400 %d\n", (int)hvrc);
|
||||
return;
|
||||
}
|
||||
/* create the request queue for the disk */
|
||||
spin_lock_init(&d->q_lock);
|
||||
q = blk_init_queue(do_viodasd_request, &d->q_lock);
|
||||
if (q == NULL) {
|
||||
printk(VIOD_KERN_WARNING "cannot allocate queue for disk %d\n",
|
||||
dev_no);
|
||||
return;
|
||||
}
|
||||
g = alloc_disk(1 << PARTITION_SHIFT);
|
||||
if (g == NULL) {
|
||||
printk(VIOD_KERN_WARNING
|
||||
"cannot allocate disk structure for disk %d\n",
|
||||
dev_no);
|
||||
blk_cleanup_queue(q);
|
||||
return;
|
||||
}
|
||||
|
||||
d->disk = g;
|
||||
blk_queue_max_hw_segments(q, VIOMAXBLOCKDMA);
|
||||
blk_queue_max_phys_segments(q, VIOMAXBLOCKDMA);
|
||||
blk_queue_max_sectors(q, VIODASD_MAXSECTORS);
|
||||
g->major = VIODASD_MAJOR;
|
||||
g->first_minor = dev_no << PARTITION_SHIFT;
|
||||
if (dev_no >= 26)
|
||||
snprintf(g->disk_name, sizeof(g->disk_name),
|
||||
VIOD_GENHD_NAME "%c%c",
|
||||
'a' + (dev_no / 26) - 1, 'a' + (dev_no % 26));
|
||||
else
|
||||
snprintf(g->disk_name, sizeof(g->disk_name),
|
||||
VIOD_GENHD_NAME "%c", 'a' + (dev_no % 26));
|
||||
g->fops = &viodasd_fops;
|
||||
g->queue = q;
|
||||
g->private_data = d;
|
||||
g->driverfs_dev = d->dev;
|
||||
set_capacity(g, d->size >> 9);
|
||||
|
||||
printk(VIOD_KERN_INFO "disk %d: %lu sectors (%lu MB) "
|
||||
"CHS=%d/%d/%d sector size %d%s\n",
|
||||
dev_no, (unsigned long)(d->size >> 9),
|
||||
(unsigned long)(d->size >> 20),
|
||||
(int)d->cylinders, (int)d->tracks,
|
||||
(int)d->sectors, (int)d->bytes_per_sector,
|
||||
d->read_only ? " (RO)" : "");
|
||||
|
||||
/* register us in the global list */
|
||||
add_disk(g);
|
||||
}
|
||||
|
||||
/* returns the total number of scatterlist elements converted */
|
||||
static int block_event_to_scatterlist(const struct vioblocklpevent *bevent,
|
||||
struct scatterlist *sg, int *total_len)
|
||||
{
|
||||
int i, numsg;
|
||||
const struct rw_data *rw_data = &bevent->u.rw_data;
|
||||
static const int offset =
|
||||
offsetof(struct vioblocklpevent, u.rw_data.dma_info);
|
||||
static const int element_size = sizeof(rw_data->dma_info[0]);
|
||||
|
||||
numsg = ((bevent->event.xSizeMinus1 + 1) - offset) / element_size;
|
||||
if (numsg > VIOMAXBLOCKDMA)
|
||||
numsg = VIOMAXBLOCKDMA;
|
||||
|
||||
*total_len = 0;
|
||||
memset(sg, 0, sizeof(sg[0]) * VIOMAXBLOCKDMA);
|
||||
|
||||
for (i = 0; (i < numsg) && (rw_data->dma_info[i].len > 0); ++i) {
|
||||
sg_dma_address(&sg[i]) = rw_data->dma_info[i].token;
|
||||
sg_dma_len(&sg[i]) = rw_data->dma_info[i].len;
|
||||
*total_len += rw_data->dma_info[i].len;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restart all queues, starting with the one _after_ the disk given,
|
||||
* thus reducing the chance of starvation of higher numbered disks.
|
||||
*/
|
||||
static void viodasd_restart_all_queues_starting_from(int first_index)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = first_index + 1; i < MAX_DISKNO; ++i)
|
||||
if (viodasd_devices[i].disk)
|
||||
blk_run_queue(viodasd_devices[i].disk->queue);
|
||||
for (i = 0; i <= first_index; ++i)
|
||||
if (viodasd_devices[i].disk)
|
||||
blk_run_queue(viodasd_devices[i].disk->queue);
|
||||
}
|
||||
|
||||
/*
|
||||
* For read and write requests, decrement the number of outstanding requests,
|
||||
* Free the DMA buffers we allocated.
|
||||
*/
|
||||
static int viodasd_handle_read_write(struct vioblocklpevent *bevent)
|
||||
{
|
||||
int num_sg, num_sect, pci_direction, total_len;
|
||||
struct request *req;
|
||||
struct scatterlist sg[VIOMAXBLOCKDMA];
|
||||
struct HvLpEvent *event = &bevent->event;
|
||||
unsigned long irq_flags;
|
||||
struct viodasd_device *d;
|
||||
int error;
|
||||
spinlock_t *qlock;
|
||||
|
||||
num_sg = block_event_to_scatterlist(bevent, sg, &total_len);
|
||||
num_sect = total_len >> 9;
|
||||
if (event->xSubtype == (viomajorsubtype_blockio | vioblockread))
|
||||
pci_direction = DMA_FROM_DEVICE;
|
||||
else
|
||||
pci_direction = DMA_TO_DEVICE;
|
||||
req = (struct request *)bevent->event.xCorrelationToken;
|
||||
d = req->rq_disk->private_data;
|
||||
|
||||
dma_unmap_sg(d->dev, sg, num_sg, pci_direction);
|
||||
|
||||
/*
|
||||
* Since this is running in interrupt mode, we need to make sure
|
||||
* we're not stepping on any global I/O operations
|
||||
*/
|
||||
spin_lock_irqsave(&viodasd_spinlock, irq_flags);
|
||||
num_req_outstanding--;
|
||||
spin_unlock_irqrestore(&viodasd_spinlock, irq_flags);
|
||||
|
||||
error = event->xRc != HvLpEvent_Rc_Good;
|
||||
if (error) {
|
||||
const struct vio_error_entry *err;
|
||||
err = vio_lookup_rc(viodasd_err_table, bevent->sub_result);
|
||||
printk(VIOD_KERN_WARNING "read/write error %d:0x%04x (%s)\n",
|
||||
event->xRc, bevent->sub_result, err->msg);
|
||||
num_sect = req->hard_nr_sectors;
|
||||
}
|
||||
qlock = req->q->queue_lock;
|
||||
spin_lock_irqsave(qlock, irq_flags);
|
||||
viodasd_end_request(req, !error, num_sect);
|
||||
spin_unlock_irqrestore(qlock, irq_flags);
|
||||
|
||||
/* Finally, try to get more requests off of this device's queue */
|
||||
viodasd_restart_all_queues_starting_from(DEVICE_NO(d));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This routine handles incoming block LP events */
|
||||
static void handle_block_event(struct HvLpEvent *event)
|
||||
{
|
||||
struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
|
||||
struct viodasd_waitevent *pwe;
|
||||
|
||||
if (event == NULL)
|
||||
/* Notification that a partition went away! */
|
||||
return;
|
||||
/* First, we should NEVER get an int here...only acks */
|
||||
if (hvlpevent_is_int(event)) {
|
||||
printk(VIOD_KERN_WARNING
|
||||
"Yikes! got an int in viodasd event handler!\n");
|
||||
if (hvlpevent_need_ack(event)) {
|
||||
event->xRc = HvLpEvent_Rc_InvalidSubtype;
|
||||
HvCallEvent_ackLpEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
|
||||
case vioblockopen:
|
||||
/*
|
||||
* Handle a response to an open request. We get all the
|
||||
* disk information in the response, so update it. The
|
||||
* correlation token contains a pointer to a waitevent
|
||||
* structure that has a completion in it. update the
|
||||
* return code in the waitevent structure and post the
|
||||
* completion to wake up the guy who sent the request
|
||||
*/
|
||||
pwe = (struct viodasd_waitevent *)event->xCorrelationToken;
|
||||
pwe->rc = event->xRc;
|
||||
pwe->sub_result = bevent->sub_result;
|
||||
if (event->xRc == HvLpEvent_Rc_Good) {
|
||||
const struct open_data *data = &bevent->u.open_data;
|
||||
struct viodasd_device *device =
|
||||
&viodasd_devices[bevent->disk];
|
||||
device->read_only =
|
||||
bevent->flags & vioblockflags_ro;
|
||||
device->size = data->disk_size;
|
||||
device->cylinders = data->cylinders;
|
||||
device->tracks = data->tracks;
|
||||
device->sectors = data->sectors;
|
||||
device->bytes_per_sector = data->bytes_per_sector;
|
||||
pwe->max_disk = data->max_disk;
|
||||
}
|
||||
complete(&pwe->com);
|
||||
break;
|
||||
case vioblockclose:
|
||||
break;
|
||||
case vioblockread:
|
||||
case vioblockwrite:
|
||||
viodasd_handle_read_write(bevent);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(VIOD_KERN_WARNING "invalid subtype!");
|
||||
if (hvlpevent_need_ack(event)) {
|
||||
event->xRc = HvLpEvent_Rc_InvalidSubtype;
|
||||
HvCallEvent_ackLpEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the driver to reprobe for more disks.
|
||||
*/
|
||||
static ssize_t probe_disks(struct device_driver *drv, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct viodasd_device *d;
|
||||
|
||||
for (d = viodasd_devices; d < &viodasd_devices[MAX_DISKNO]; d++) {
|
||||
if (d->disk == NULL)
|
||||
probe_disk(d);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
static DRIVER_ATTR(probe, S_IWUSR, NULL, probe_disks);
|
||||
|
||||
static int viodasd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
|
||||
{
|
||||
struct viodasd_device *d = &viodasd_devices[vdev->unit_address];
|
||||
|
||||
d->dev = &vdev->dev;
|
||||
probe_disk(d);
|
||||
if (d->disk == NULL)
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int viodasd_remove(struct vio_dev *vdev)
|
||||
{
|
||||
struct viodasd_device *d;
|
||||
|
||||
d = &viodasd_devices[vdev->unit_address];
|
||||
if (d->disk) {
|
||||
del_gendisk(d->disk);
|
||||
blk_cleanup_queue(d->disk->queue);
|
||||
put_disk(d->disk);
|
||||
d->disk = NULL;
|
||||
}
|
||||
d->dev = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* viodasd_device_table: Used by vio.c to match devices that we
|
||||
* support.
|
||||
*/
|
||||
static struct vio_device_id viodasd_device_table[] __devinitdata = {
|
||||
{ "block", "IBM,iSeries-viodasd" },
|
||||
{ "", "" }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(vio, viodasd_device_table);
|
||||
|
||||
static struct vio_driver viodasd_driver = {
|
||||
.id_table = viodasd_device_table,
|
||||
.probe = viodasd_probe,
|
||||
.remove = viodasd_remove,
|
||||
.driver = {
|
||||
.name = "viodasd",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
|
||||
static int need_delete_probe;
|
||||
|
||||
/*
|
||||
* Initialize the whole device driver. Handle module and non-module
|
||||
* versions
|
||||
*/
|
||||
static int __init viodasd_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!firmware_has_feature(FW_FEATURE_ISERIES)) {
|
||||
rc = -ENODEV;
|
||||
goto early_fail;
|
||||
}
|
||||
|
||||
/* Try to open to our host lp */
|
||||
if (viopath_hostLp == HvLpIndexInvalid)
|
||||
vio_set_hostlp();
|
||||
|
||||
if (viopath_hostLp == HvLpIndexInvalid) {
|
||||
printk(VIOD_KERN_WARNING "invalid hosting partition\n");
|
||||
rc = -EIO;
|
||||
goto early_fail;
|
||||
}
|
||||
|
||||
printk(VIOD_KERN_INFO "vers " VIOD_VERS ", hosting partition %d\n",
|
||||
viopath_hostLp);
|
||||
|
||||
/* register the block device */
|
||||
rc = register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
|
||||
if (rc) {
|
||||
printk(VIOD_KERN_WARNING
|
||||
"Unable to get major number %d for %s\n",
|
||||
VIODASD_MAJOR, VIOD_GENHD_NAME);
|
||||
goto early_fail;
|
||||
}
|
||||
/* Actually open the path to the hosting partition */
|
||||
rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio,
|
||||
VIOMAXREQ + 2);
|
||||
if (rc) {
|
||||
printk(VIOD_KERN_WARNING
|
||||
"error opening path to host partition %d\n",
|
||||
viopath_hostLp);
|
||||
goto unregister_blk;
|
||||
}
|
||||
|
||||
/* Initialize our request handler */
|
||||
vio_setHandler(viomajorsubtype_blockio, handle_block_event);
|
||||
|
||||
rc = vio_register_driver(&viodasd_driver);
|
||||
if (rc) {
|
||||
printk(VIOD_KERN_WARNING "vio_register_driver failed\n");
|
||||
goto unset_handler;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this call fails, it just means that we cannot dynamically
|
||||
* add virtual disks, but the driver will still work fine for
|
||||
* all existing disk, so ignore the failure.
|
||||
*/
|
||||
if (!driver_create_file(&viodasd_driver.driver, &driver_attr_probe))
|
||||
need_delete_probe = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
unset_handler:
|
||||
vio_clearHandler(viomajorsubtype_blockio);
|
||||
viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2);
|
||||
unregister_blk:
|
||||
unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
|
||||
early_fail:
|
||||
return rc;
|
||||
}
|
||||
module_init(viodasd_init);
|
||||
|
||||
void __exit viodasd_exit(void)
|
||||
{
|
||||
if (need_delete_probe)
|
||||
driver_remove_file(&viodasd_driver.driver, &driver_attr_probe);
|
||||
vio_unregister_driver(&viodasd_driver);
|
||||
vio_clearHandler(viomajorsubtype_blockio);
|
||||
viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2);
|
||||
unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
|
||||
}
|
||||
module_exit(viodasd_exit);
|
||||
1106
drivers/block/xd.c
Normal file
1106
drivers/block/xd.c
Normal file
File diff suppressed because it is too large
Load Diff
134
drivers/block/xd.h
Normal file
134
drivers/block/xd.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#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.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
/* 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 & CX 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?) */
|
||||
#define CMD_XBSETPARAM 0x0C /* set drive parameters (XEBEC 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;
|
||||
int unit;
|
||||
} XD_INFO;
|
||||
|
||||
/* 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;
|
||||
|
||||
#ifndef MODULE
|
||||
static int xd_manual_geo_init (char *command);
|
||||
#endif /* MODULE */
|
||||
static u_char xd_detect (u_char *controller, unsigned int *address);
|
||||
static u_char xd_initdrives (void (*init_drive)(u_char drive));
|
||||
|
||||
static void do_xd_request (request_queue_t * q);
|
||||
static int xd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg);
|
||||
static int xd_readwrite (u_char operation,XD_INFO *disk,char *buffer,u_int block,u_int count);
|
||||
static void xd_recalibrate (u_char drive);
|
||||
|
||||
static irqreturn_t xd_interrupt_handler(int irq, void *dev_id);
|
||||
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 void xd_watchdog (unsigned long unused);
|
||||
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_dtc5150cx_init_drive (u_char drive);
|
||||
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_xebec_init_controller (unsigned int address);
|
||||
static void xd_xebec_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 */
|
||||
412
drivers/block/z2ram.c
Normal file
412
drivers/block/z2ram.c
Normal file
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
** z2ram - Amiga pseudo-driver to access 16bit-RAM in ZorroII space
|
||||
** as a block device, to be used as a RAM disk or swap space
|
||||
**
|
||||
** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
|
||||
**
|
||||
** ++Geert: support for zorro_unused_z2ram, better range checking
|
||||
** ++roman: translate accesses via an array
|
||||
** ++Milan: support for ChipRAM usage
|
||||
** ++yambo: converted to 2.0 kernel
|
||||
** ++yambo: modularized and support added for 3 minor devices including:
|
||||
** MAJOR MINOR DESCRIPTION
|
||||
** ----- ----- ----------------------------------------------
|
||||
** 37 0 Use Zorro II and Chip ram
|
||||
** 37 1 Use only Zorro II ram
|
||||
** 37 2 Use only Chip ram
|
||||
** 37 4-7 Use memory list entry 1-4 (first is 0)
|
||||
** ++jskov: support for 1-4th memory list entry.
|
||||
**
|
||||
** Permission to use, copy, modify, and distribute this software and its
|
||||
** documentation for any purpose and without fee is hereby granted, provided
|
||||
** that the above copyright notice appear in all copies and that both that
|
||||
** copyright notice and this permission notice appear in supporting
|
||||
** documentation. This software is provided "as is" without express or
|
||||
** implied warranty.
|
||||
*/
|
||||
|
||||
#define DEVICE_NAME "Z2RAM"
|
||||
|
||||
#include <linux/major.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/amigahw.h>
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
#include <linux/zorro.h>
|
||||
|
||||
|
||||
extern int m68k_realnum_memory;
|
||||
extern struct mem_info m68k_memory[NUM_MEMINFO];
|
||||
|
||||
#define TRUE (1)
|
||||
#define FALSE (0)
|
||||
|
||||
#define Z2MINOR_COMBINED (0)
|
||||
#define Z2MINOR_Z2ONLY (1)
|
||||
#define Z2MINOR_CHIPONLY (2)
|
||||
#define Z2MINOR_MEMLIST1 (4)
|
||||
#define Z2MINOR_MEMLIST2 (5)
|
||||
#define Z2MINOR_MEMLIST3 (6)
|
||||
#define Z2MINOR_MEMLIST4 (7)
|
||||
#define Z2MINOR_COUNT (8) /* Move this down when adding a new minor */
|
||||
|
||||
#define Z2RAM_CHUNK1024 ( Z2RAM_CHUNKSIZE >> 10 )
|
||||
|
||||
static u_long *z2ram_map = NULL;
|
||||
static u_long z2ram_size = 0;
|
||||
static int z2_count = 0;
|
||||
static int chip_count = 0;
|
||||
static int list_count = 0;
|
||||
static int current_device = -1;
|
||||
|
||||
static DEFINE_SPINLOCK(z2ram_lock);
|
||||
|
||||
static struct block_device_operations z2_fops;
|
||||
static struct gendisk *z2ram_gendisk;
|
||||
|
||||
static void do_z2_request(request_queue_t *q)
|
||||
{
|
||||
struct request *req;
|
||||
while ((req = elv_next_request(q)) != NULL) {
|
||||
unsigned long start = req->sector << 9;
|
||||
unsigned long len = req->current_nr_sectors << 9;
|
||||
|
||||
if (start + len > z2ram_size) {
|
||||
printk( KERN_ERR DEVICE_NAME ": bad access: block=%lu, count=%u\n",
|
||||
req->sector, req->current_nr_sectors);
|
||||
end_request(req, 0);
|
||||
continue;
|
||||
}
|
||||
while (len) {
|
||||
unsigned long addr = start & Z2RAM_CHUNKMASK;
|
||||
unsigned long size = Z2RAM_CHUNKSIZE - addr;
|
||||
if (len < size)
|
||||
size = len;
|
||||
addr += z2ram_map[ start >> Z2RAM_CHUNKSHIFT ];
|
||||
if (rq_data_dir(req) == READ)
|
||||
memcpy(req->buffer, (char *)addr, size);
|
||||
else
|
||||
memcpy((char *)addr, req->buffer, size);
|
||||
start += size;
|
||||
len -= size;
|
||||
}
|
||||
end_request(req, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_z2ram( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < Z2RAM_SIZE / Z2RAM_CHUNKSIZE; i++ )
|
||||
{
|
||||
if ( test_bit( i, zorro_unused_z2ram ) )
|
||||
{
|
||||
z2_count++;
|
||||
z2ram_map[ z2ram_size++ ] =
|
||||
ZTWO_VADDR( Z2RAM_START ) + ( i << Z2RAM_CHUNKSHIFT );
|
||||
clear_bit( i, zorro_unused_z2ram );
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
get_chipram( void )
|
||||
{
|
||||
|
||||
while ( amiga_chip_avail() > ( Z2RAM_CHUNKSIZE * 4 ) )
|
||||
{
|
||||
chip_count++;
|
||||
z2ram_map[ z2ram_size ] =
|
||||
(u_long)amiga_chip_alloc( Z2RAM_CHUNKSIZE, "z2ram" );
|
||||
|
||||
if ( z2ram_map[ z2ram_size ] == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
z2ram_size++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
z2_open( struct inode *inode, struct file *filp )
|
||||
{
|
||||
int device;
|
||||
int max_z2_map = ( Z2RAM_SIZE / Z2RAM_CHUNKSIZE ) *
|
||||
sizeof( z2ram_map[0] );
|
||||
int max_chip_map = ( amiga_chip_size / Z2RAM_CHUNKSIZE ) *
|
||||
sizeof( z2ram_map[0] );
|
||||
int rc = -ENOMEM;
|
||||
|
||||
device = iminor(inode);
|
||||
|
||||
if ( current_device != -1 && current_device != device )
|
||||
{
|
||||
rc = -EBUSY;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if ( current_device == -1 )
|
||||
{
|
||||
z2_count = 0;
|
||||
chip_count = 0;
|
||||
list_count = 0;
|
||||
z2ram_size = 0;
|
||||
|
||||
/* Use a specific list entry. */
|
||||
if (device >= Z2MINOR_MEMLIST1 && device <= Z2MINOR_MEMLIST4) {
|
||||
int index = device - Z2MINOR_MEMLIST1 + 1;
|
||||
unsigned long size, paddr, vaddr;
|
||||
|
||||
if (index >= m68k_realnum_memory) {
|
||||
printk( KERN_ERR DEVICE_NAME
|
||||
": no such entry in z2ram_map\n" );
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
paddr = m68k_memory[index].addr;
|
||||
size = m68k_memory[index].size & ~(Z2RAM_CHUNKSIZE-1);
|
||||
|
||||
#ifdef __powerpc__
|
||||
/* FIXME: ioremap doesn't build correct memory tables. */
|
||||
{
|
||||
vfree(vmalloc (size));
|
||||
}
|
||||
|
||||
vaddr = (unsigned long) __ioremap (paddr, size,
|
||||
_PAGE_WRITETHRU);
|
||||
|
||||
#else
|
||||
vaddr = (unsigned long)z_remap_nocache_nonser(paddr, size);
|
||||
#endif
|
||||
z2ram_map =
|
||||
kmalloc((size/Z2RAM_CHUNKSIZE)*sizeof(z2ram_map[0]),
|
||||
GFP_KERNEL);
|
||||
if ( z2ram_map == NULL )
|
||||
{
|
||||
printk( KERN_ERR DEVICE_NAME
|
||||
": cannot get mem for z2ram_map\n" );
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
while (size) {
|
||||
z2ram_map[ z2ram_size++ ] = vaddr;
|
||||
size -= Z2RAM_CHUNKSIZE;
|
||||
vaddr += Z2RAM_CHUNKSIZE;
|
||||
list_count++;
|
||||
}
|
||||
|
||||
if ( z2ram_size != 0 )
|
||||
printk( KERN_INFO DEVICE_NAME
|
||||
": using %iK List Entry %d Memory\n",
|
||||
list_count * Z2RAM_CHUNK1024, index );
|
||||
} else
|
||||
|
||||
switch ( device )
|
||||
{
|
||||
case Z2MINOR_COMBINED:
|
||||
|
||||
z2ram_map = kmalloc( max_z2_map + max_chip_map, GFP_KERNEL );
|
||||
if ( z2ram_map == NULL )
|
||||
{
|
||||
printk( KERN_ERR DEVICE_NAME
|
||||
": cannot get mem for z2ram_map\n" );
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
get_z2ram();
|
||||
get_chipram();
|
||||
|
||||
if ( z2ram_size != 0 )
|
||||
printk( KERN_INFO DEVICE_NAME
|
||||
": using %iK Zorro II RAM and %iK Chip RAM (Total %dK)\n",
|
||||
z2_count * Z2RAM_CHUNK1024,
|
||||
chip_count * Z2RAM_CHUNK1024,
|
||||
( z2_count + chip_count ) * Z2RAM_CHUNK1024 );
|
||||
|
||||
break;
|
||||
|
||||
case Z2MINOR_Z2ONLY:
|
||||
z2ram_map = kmalloc( max_z2_map, GFP_KERNEL );
|
||||
if ( z2ram_map == NULL )
|
||||
{
|
||||
printk( KERN_ERR DEVICE_NAME
|
||||
": cannot get mem for z2ram_map\n" );
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
get_z2ram();
|
||||
|
||||
if ( z2ram_size != 0 )
|
||||
printk( KERN_INFO DEVICE_NAME
|
||||
": using %iK of Zorro II RAM\n",
|
||||
z2_count * Z2RAM_CHUNK1024 );
|
||||
|
||||
break;
|
||||
|
||||
case Z2MINOR_CHIPONLY:
|
||||
z2ram_map = kmalloc( max_chip_map, GFP_KERNEL );
|
||||
if ( z2ram_map == NULL )
|
||||
{
|
||||
printk( KERN_ERR DEVICE_NAME
|
||||
": cannot get mem for z2ram_map\n" );
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
get_chipram();
|
||||
|
||||
if ( z2ram_size != 0 )
|
||||
printk( KERN_INFO DEVICE_NAME
|
||||
": using %iK Chip RAM\n",
|
||||
chip_count * Z2RAM_CHUNK1024 );
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
rc = -ENODEV;
|
||||
goto err_out;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ( z2ram_size == 0 )
|
||||
{
|
||||
printk( KERN_NOTICE DEVICE_NAME
|
||||
": no unused ZII/Chip RAM found\n" );
|
||||
goto err_out_kfree;
|
||||
}
|
||||
|
||||
current_device = device;
|
||||
z2ram_size <<= Z2RAM_CHUNKSHIFT;
|
||||
set_capacity(z2ram_gendisk, z2ram_size >> 9);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out_kfree:
|
||||
kfree(z2ram_map);
|
||||
err_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
z2_release( struct inode *inode, struct file *filp )
|
||||
{
|
||||
if ( current_device == -1 )
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* FIXME: unmap memory
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct block_device_operations z2_fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.open = z2_open,
|
||||
.release = z2_release,
|
||||
};
|
||||
|
||||
static struct kobject *z2_find(dev_t dev, int *part, void *data)
|
||||
{
|
||||
*part = 0;
|
||||
return get_disk(z2ram_gendisk);
|
||||
}
|
||||
|
||||
static struct request_queue *z2_queue;
|
||||
|
||||
static int __init
|
||||
z2_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!MACH_IS_AMIGA)
|
||||
return -ENXIO;
|
||||
|
||||
ret = -EBUSY;
|
||||
if (register_blkdev(Z2RAM_MAJOR, DEVICE_NAME))
|
||||
goto err;
|
||||
|
||||
ret = -ENOMEM;
|
||||
z2ram_gendisk = alloc_disk(1);
|
||||
if (!z2ram_gendisk)
|
||||
goto out_disk;
|
||||
|
||||
z2_queue = blk_init_queue(do_z2_request, &z2ram_lock);
|
||||
if (!z2_queue)
|
||||
goto out_queue;
|
||||
|
||||
z2ram_gendisk->major = Z2RAM_MAJOR;
|
||||
z2ram_gendisk->first_minor = 0;
|
||||
z2ram_gendisk->fops = &z2_fops;
|
||||
sprintf(z2ram_gendisk->disk_name, "z2ram");
|
||||
|
||||
z2ram_gendisk->queue = z2_queue;
|
||||
add_disk(z2ram_gendisk);
|
||||
blk_register_region(MKDEV(Z2RAM_MAJOR, 0), Z2MINOR_COUNT, THIS_MODULE,
|
||||
z2_find, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
|
||||
out_queue:
|
||||
put_disk(z2ram_gendisk);
|
||||
out_disk:
|
||||
unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit z2_exit(void)
|
||||
{
|
||||
int i, j;
|
||||
blk_unregister_region(MKDEV(Z2RAM_MAJOR, 0), 256);
|
||||
if ( unregister_blkdev( Z2RAM_MAJOR, DEVICE_NAME ) != 0 )
|
||||
printk( KERN_ERR DEVICE_NAME ": unregister of device failed\n");
|
||||
|
||||
del_gendisk(z2ram_gendisk);
|
||||
put_disk(z2ram_gendisk);
|
||||
blk_cleanup_queue(z2_queue);
|
||||
|
||||
if ( current_device != -1 )
|
||||
{
|
||||
i = 0;
|
||||
|
||||
for ( j = 0 ; j < z2_count; j++ )
|
||||
{
|
||||
set_bit( i++, zorro_unused_z2ram );
|
||||
}
|
||||
|
||||
for ( j = 0 ; j < chip_count; j++ )
|
||||
{
|
||||
if ( z2ram_map[ i ] )
|
||||
{
|
||||
amiga_chip_free( (void *) z2ram_map[ i++ ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( z2ram_map != NULL )
|
||||
{
|
||||
kfree( z2ram_map );
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(z2_init);
|
||||
module_exit(z2_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user