Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
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
Reference in New Issue
Block a user