Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

7204
drivers/block/DAC960.c Normal file

File diff suppressed because it is too large Load Diff

4422
drivers/block/DAC960.h Normal file

File diff suppressed because it is too large Load Diff

458
drivers/block/Kconfig Normal file
View File

@@ -0,0 +1,458 @@
#
# Block device driver configuration
#
if BLOCK
menu "Block devices"
config BLK_DEV_FD
tristate "Normal floppy disk support"
depends on ARCH_MAY_HAVE_PC_FDC
---help---
If you want to use the floppy disk drive(s) of your PC under Linux,
say Y. Information about this driver, especially important for IBM
Thinkpad users, is contained in <file:Documentation/floppy.txt>.
That file also contains the location of the Floppy driver FAQ as
well as location of the fdutils package used to configure additional
parameters of the driver at run time.
To compile this driver as a module, choose M here: the
module will be called floppy.
config AMIGA_FLOPPY
tristate "Amiga floppy support"
depends on AMIGA
config ATARI_FLOPPY
tristate "Atari floppy support"
depends on ATARI
config MAC_FLOPPY
tristate "Support for PowerMac floppy"
depends on PPC_PMAC && !PPC_PMAC64
help
If you have a SWIM-3 (Super Woz Integrated Machine 3; from Apple)
floppy controller, say Y here. Most commonly found in PowerMacs.
config BLK_DEV_PS2
tristate "PS/2 ESDI hard disk support"
depends on MCA && MCA_LEGACY && BROKEN
help
Say Y here if you have a PS/2 machine with a MCA bus and an ESDI
hard disk.
To compile this driver as a module, choose M here: the
module will be called ps2esdi.
config AMIGA_Z2RAM
tristate "Amiga Zorro II ramdisk support"
depends on ZORRO
help
This enables support for using Chip RAM and Zorro II RAM as a
ramdisk or as a swap partition. Say Y if you want to include this
driver in the kernel.
To compile this driver as a module, choose M here: the
module will be called z2ram.
config ATARI_ACSI
tristate "Atari ACSI support"
depends on ATARI && BROKEN
---help---
This enables support for the Atari ACSI interface. The driver
supports hard disks and CD-ROMs, which have 512-byte sectors, or can
be switched to that mode. Due to the ACSI command format, only disks
up to 1 GB are supported. Special support for certain ACSI to SCSI
adapters, which could relax that, isn't included yet. The ACSI
driver is also the basis for certain other drivers for devices
attached to the ACSI bus: Atari SLM laser printer, BioNet-100
Ethernet, and PAMsNet Ethernet. If you want to use one of these
devices, you need ACSI support, too.
To compile this driver as a module, choose M here: the
module will be called acsi.
comment "Some devices (e.g. CD jukebox) support multiple LUNs"
depends on ATARI && ATARI_ACSI
config ACSI_MULTI_LUN
bool "Probe all LUNs on each ACSI device"
depends on ATARI_ACSI
help
If you have a ACSI device that supports more than one LUN (Logical
Unit Number), e.g. a CD jukebox, you should say Y here so that all
will be found by the ACSI driver. An ACSI device with multiple LUNs
acts logically like multiple ACSI devices. The vast majority of ACSI
devices have only one LUN, and so most people can say N here and
should in fact do so, because it is safer.
config ATARI_SLM
tristate "Atari SLM laser printer support"
depends on ATARI && ATARI_ACSI!=n
help
If you have an Atari SLM laser printer, say Y to include support for
it in the kernel. Otherwise, say N. This driver is also available as
a module ( = code which can be inserted in and removed from the
running kernel whenever you want). The module will be called
acsi_slm. Be warned: the driver needs much ST-RAM and can cause
problems due to that fact!
config BLK_DEV_XD
tristate "XT hard disk support"
depends on ISA && ISA_DMA_API
help
Very old 8 bit hard disk controllers used in the IBM XT computer
will be supported if you say Y here.
To compile this driver as a module, choose M here: the
module will be called xd.
It's pretty unlikely that you have one of these: say N.
config PARIDE
tristate "Parallel port IDE device support"
depends on PARPORT_PC
---help---
There are many external CD-ROM and disk devices that connect through
your computer's parallel port. Most of them are actually IDE devices
using a parallel port IDE adapter. This option enables the PARIDE
subsystem which contains drivers for many of these external drives.
Read <file:Documentation/paride.txt> for more information.
If you have said Y to the "Parallel-port support" configuration
option, you may share a single port between your printer and other
parallel port devices. Answer Y to build PARIDE support into your
kernel, or M if you would like to build it as a loadable module. If
your parallel port support is in a loadable module, you must build
PARIDE as a module. If you built PARIDE support into your kernel,
you may still build the individual protocol modules and high-level
drivers as loadable modules. If you build this support as a module,
it will be called paride.
To use the PARIDE support, you must say Y or M here and also to at
least one high-level driver (e.g. "Parallel port IDE disks",
"Parallel port ATAPI CD-ROMs", "Parallel port ATAPI disks" etc.) and
to at least one protocol driver (e.g. "ATEN EH-100 protocol",
"MicroSolutions backpack protocol", "DataStor Commuter protocol"
etc.).
source "drivers/block/paride/Kconfig"
config BLK_CPQ_DA
tristate "Compaq SMART2 support"
depends on PCI
help
This is the driver for Compaq Smart Array controllers. Everyone
using these boards should say Y here. See the file
<file:Documentation/cpqarray.txt> for the current list of boards
supported by this driver, and for further information on the use of
this driver.
config BLK_CPQ_CISS_DA
tristate "Compaq Smart Array 5xxx support"
depends on PCI
help
This is the driver for Compaq Smart Array 5xxx controllers.
Everyone using these boards should say Y here.
See <file:Documentation/cciss.txt> for the current list of
boards supported by this driver, and for further information
on the use of this driver.
config CISS_SCSI_TAPE
bool "SCSI tape drive support for Smart Array 5xxx"
depends on BLK_CPQ_CISS_DA && PROC_FS
depends on SCSI=y || SCSI=BLK_CPQ_CISS_DA
help
When enabled (Y), this option allows SCSI tape drives and SCSI medium
changers (tape robots) to be accessed via a Compaq 5xxx array
controller. (See <file:Documentation/cciss.txt> for more details.)
"SCSI support" and "SCSI tape support" must also be enabled for this
option to work.
When this option is disabled (N), the SCSI portion of the driver
is not compiled.
config BLK_DEV_DAC960
tristate "Mylex DAC960/DAC1100 PCI RAID Controller support"
depends on PCI
help
This driver adds support for the Mylex DAC960, AcceleRAID, and
eXtremeRAID PCI RAID controllers. See the file
<file:Documentation/README.DAC960> for further information about
this driver.
To compile this driver as a module, choose M here: the
module will be called DAC960.
config BLK_DEV_UMEM
tristate "Micro Memory MM5415 Battery Backed RAM support (EXPERIMENTAL)"
depends on PCI && EXPERIMENTAL
---help---
Saying Y here will include support for the MM5415 family of
battery backed (Non-volatile) RAM cards.
<http://www.umem.com/>
The cards appear as block devices that can be partitioned into
as many as 15 partitions.
To compile this driver as a module, choose M here: the
module will be called umem.
The umem driver has not yet been allocated a MAJOR number, so
one is chosen dynamically.
config BLK_DEV_UBD
bool "Virtual block device"
depends on UML
---help---
The User-Mode Linux port includes a driver called UBD which will let
you access arbitrary files on the host computer as block devices.
Unless you know that you do not need such virtual block devices say
Y here.
config BLK_DEV_UBD_SYNC
bool "Always do synchronous disk IO for UBD"
depends on BLK_DEV_UBD
---help---
Writes to the virtual block device are not immediately written to the
host's disk; this may cause problems if, for example, the User-Mode
Linux 'Virtual Machine' uses a journalling filesystem and the host
computer crashes.
Synchronous operation (i.e. always writing data to the host's disk
immediately) is configurable on a per-UBD basis by using a special
kernel command line option. Alternatively, you can say Y here to
turn on synchronous operation by default for all block devices.
If you're running a journalling file system (like reiserfs, for
example) in your virtual machine, you will want to say Y here. If
you care for the safety of the data in your virtual machine, Y is a
wise choice too. In all other cases (for example, if you're just
playing around with User-Mode Linux) you can choose N.
config BLK_DEV_COW_COMMON
bool
default BLK_DEV_UBD
config MMAPPER
tristate "Example IO memory driver (BROKEN)"
depends on UML && BROKEN
---help---
The User-Mode Linux port can provide support for IO Memory
emulation with this option. This allows a host file to be
specified as an I/O region on the kernel command line. That file
will be mapped into UML's kernel address space where a driver can
locate it and do whatever it wants with the memory, including
providing an interface to it for UML processes to use.
For more information, see
<http://user-mode-linux.sourceforge.net/iomem.html>.
If you'd like to be able to provide a simulated IO port space for
User-Mode Linux processes, say Y. If unsure, say N.
config BLK_DEV_LOOP
tristate "Loopback device support"
---help---
Saying Y here will allow you to use a regular file as a block
device; you can then create a file system on that block device and
mount it just as you would mount other block devices such as hard
drive partitions, CD-ROM drives or floppy drives. The loop devices
are block special device files with major number 7 and typically
called /dev/loop0, /dev/loop1 etc.
This is useful if you want to check an ISO 9660 file system before
burning the CD, or if you want to use floppy images without first
writing them to floppy. Furthermore, some Linux distributions avoid
the need for a dedicated Linux partition by keeping their complete
root file system inside a DOS FAT file using this loop device
driver.
To use the loop device, you need the losetup utility, found in the
util-linux package, see
<ftp://ftp.kernel.org/pub/linux/utils/util-linux/>.
The loop device driver can also be used to "hide" a file system in
a disk partition, floppy, or regular file, either using encryption
(scrambling the data) or steganography (hiding the data in the low
bits of, say, a sound file). This is also safe if the file resides
on a remote file server.
There are several ways of encrypting disks. Some of these require
kernel patches. The vanilla kernel offers the cryptoloop option
and a Device Mapper target (which is superior, as it supports all
file systems). If you want to use the cryptoloop, say Y to both
LOOP and CRYPTOLOOP, and make sure you have a recent (version 2.12
or later) version of util-linux. Additionally, be aware that
the cryptoloop is not safe for storing journaled filesystems.
Note that this loop device has nothing to do with the loopback
device used for network connections from the machine to itself.
To compile this driver as a module, choose M here: the
module will be called loop.
Most users will answer N here.
config BLK_DEV_CRYPTOLOOP
tristate "Cryptoloop Support"
select CRYPTO
select CRYPTO_CBC
depends on BLK_DEV_LOOP
---help---
Say Y here if you want to be able to use the ciphers that are
provided by the CryptoAPI as loop transformation. This might be
used as hard disk encryption.
WARNING: This device is not safe for journaled file systems like
ext3 or Reiserfs. Please use the Device Mapper crypto module
instead, which can be configured to be on-disk compatible with the
cryptoloop device.
config BLK_DEV_NBD
tristate "Network block device support"
depends on NET
---help---
Saying Y here will allow your computer to be a client for network
block devices, i.e. it will be able to use block devices exported by
servers (mount file systems on them etc.). Communication between
client and server works over TCP/IP networking, but to the client
program this is hidden: it looks like a regular local file access to
a block device special file such as /dev/nd0.
Network block devices also allows you to run a block-device in
userland (making server and client physically the same computer,
communicating using the loopback network device).
Read <file:Documentation/nbd.txt> for more information, especially
about where to find the server code, which runs in user space and
does not need special kernel support.
Note that this has nothing to do with the network file systems NFS
or Coda; you can say N here even if you intend to use NFS or Coda.
To compile this driver as a module, choose M here: the
module will be called nbd.
If unsure, say N.
config BLK_DEV_SX8
tristate "Promise SATA SX8 support"
depends on PCI
---help---
Saying Y or M here will enable support for the
Promise SATA SX8 controllers.
Use devices /dev/sx8/$N and /dev/sx8/$Np$M.
config BLK_DEV_UB
tristate "Low Performance USB Block driver"
depends on USB
help
This driver supports certain USB attached storage devices
such as flash keys.
If you enable this driver, it is recommended to avoid conflicts
with usb-storage by enabling USB_LIBUSUAL.
If unsure, say N.
config BLK_DEV_RAM
tristate "RAM disk support"
---help---
Saying Y here will allow you to use a portion of your RAM memory as
a block device, so that you can make file systems on it, read and
write to it and do all the other things that you can do with normal
block devices (such as hard drives). It is usually used to load and
store a copy of a minimal root file system off of a floppy into RAM
during the initial install of Linux.
Note that the kernel command line option "ramdisk=XX" is now
obsolete. For details, read <file:Documentation/ramdisk.txt>.
To compile this driver as a module, choose M here: the
module will be called rd.
Most normal users won't need the RAM disk functionality, and can
thus say N here.
config BLK_DEV_RAM_COUNT
int "Default number of RAM disks"
default "16"
depends on BLK_DEV_RAM
help
The default value is 16 RAM disks. Change this if you know what
are doing. If you boot from a filesystem that needs to be extracted
in memory, you will need at least one RAM disk (e.g. root on cramfs).
config BLK_DEV_RAM_SIZE
int "Default RAM disk size (kbytes)"
depends on BLK_DEV_RAM
default "4096"
help
The default value is 4096 kilobytes. Only change this if you know
what are you doing. If you are using IBM S/390, then set this to
8192.
config BLK_DEV_RAM_BLOCKSIZE
int "Default RAM disk block size (bytes)"
depends on BLK_DEV_RAM
default "1024"
help
The default value is 1024 bytes. PAGE_SIZE is a much more
efficient choice however. The default is kept to ensure initrd
setups function - apparently needed by the rd_load_image routine
that supposes the filesystem in the image uses a 1024 blocksize.
config CDROM_PKTCDVD
tristate "Packet writing on CD/DVD media"
depends on !UML
help
If you have a CDROM/DVD drive that supports packet writing, say
Y to include support. It should work with any MMC/Mt Fuji
compliant ATAPI or SCSI drive, which is just about any newer
DVD/CD writer.
Currently only writing to CD-RW, DVD-RW, DVD+RW and DVDRAM discs
is possible.
DVD-RW disks must be in restricted overwrite mode.
See the file <file:Documentation/cdrom/packet-writing.txt>
for further information on the use of this driver.
To compile this driver as a module, choose M here: the
module will be called pktcdvd.
config CDROM_PKTCDVD_BUFFERS
int "Free buffers for data gathering"
depends on CDROM_PKTCDVD
default "8"
help
This controls the maximum number of active concurrent packets. More
concurrent packets can increase write performance, but also require
more memory. Each concurrent packet will require approximately 64Kb
of non-swappable kernel memory, memory which will be allocated when
a disc is opened for writing.
config CDROM_PKTCDVD_WCACHE
bool "Enable write caching (EXPERIMENTAL)"
depends on CDROM_PKTCDVD && EXPERIMENTAL
help
If enabled, write caching will be set for the CD-R/W device. For now
this option is dangerous unless the CD-RW media is known good, as we
don't do deferred write error handling yet.
source "drivers/s390/block/Kconfig"
config ATA_OVER_ETH
tristate "ATA over Ethernet support"
depends on NET
help
This driver provides Support for ATA over Ethernet block
devices like the Coraid EtherDrive (R) Storage Blade.
endmenu
endif

31
drivers/block/Makefile Normal file
View File

@@ -0,0 +1,31 @@
#
# Makefile for the kernel block device drivers.
#
# 12 June 2000, Christoph Hellwig <hch@infradead.org>
# Rewritten to use lists instead of if-statements.
#
obj-$(CONFIG_MAC_FLOPPY) += swim3.o
obj-$(CONFIG_BLK_DEV_FD) += floppy.o
obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o
obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
obj-$(CONFIG_ATARI_ACSI) += acsi.o
obj-$(CONFIG_ATARI_SLM) += acsi_slm.o
obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
obj-$(CONFIG_BLK_DEV_RAM) += rd.o
obj-$(CONFIG_BLK_DEV_LOOP) += loop.o
obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o
obj-$(CONFIG_BLK_DEV_XD) += xd.o
obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o
obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o
obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o
obj-$(CONFIG_BLK_DEV_UMEM) += umem.o
obj-$(CONFIG_BLK_DEV_NBD) += nbd.o
obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) += cryptoloop.o
obj-$(CONFIG_VIODASD) += viodasd.o
obj-$(CONFIG_BLK_DEV_SX8) += sx8.o
obj-$(CONFIG_BLK_DEV_UB) += ub.o

1825
drivers/block/acsi.c Normal file

File diff suppressed because it is too large Load Diff

1033
drivers/block/acsi_slm.c Normal file

File diff suppressed because it is too large Load Diff

1855
drivers/block/amiflop.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
#
# Makefile for ATA over Ethernet
#
obj-$(CONFIG_ATA_OVER_ETH) += aoe.o
aoe-objs := aoeblk.o aoechr.o aoecmd.o aoedev.o aoemain.o aoenet.o

178
drivers/block/aoe/aoe.h Normal file
View File

@@ -0,0 +1,178 @@
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
#define VERSION "32"
#define AOE_MAJOR 152
#define DEVICE_NAME "aoe"
/* set AOE_PARTITIONS to 1 to use whole-disks only
* default is 16, which is 15 partitions plus the whole disk
*/
#ifndef AOE_PARTITIONS
#define AOE_PARTITIONS (16)
#endif
#define SYSMINOR(aoemajor, aoeminor) ((aoemajor) * NPERSHELF + (aoeminor))
#define AOEMAJOR(sysminor) ((sysminor) / NPERSHELF)
#define AOEMINOR(sysminor) ((sysminor) % NPERSHELF)
#define WHITESPACE " \t\v\f\n"
enum {
AOECMD_ATA,
AOECMD_CFG,
AOEFL_RSP = (1<<3),
AOEFL_ERR = (1<<2),
AOEAFL_EXT = (1<<6),
AOEAFL_DEV = (1<<4),
AOEAFL_ASYNC = (1<<1),
AOEAFL_WRITE = (1<<0),
AOECCMD_READ = 0,
AOECCMD_TEST,
AOECCMD_PTEST,
AOECCMD_SET,
AOECCMD_FSET,
AOE_HVER = 0x10,
};
struct aoe_hdr {
unsigned char dst[6];
unsigned char src[6];
__be16 type;
unsigned char verfl;
unsigned char err;
__be16 major;
unsigned char minor;
unsigned char cmd;
__be32 tag;
};
struct aoe_atahdr {
unsigned char aflags;
unsigned char errfeat;
unsigned char scnt;
unsigned char cmdstat;
unsigned char lba0;
unsigned char lba1;
unsigned char lba2;
unsigned char lba3;
unsigned char lba4;
unsigned char lba5;
unsigned char res[2];
};
struct aoe_cfghdr {
__be16 bufcnt;
__be16 fwver;
unsigned char scnt;
unsigned char aoeccmd;
unsigned char cslen[2];
};
enum {
DEVFL_UP = 1, /* device is installed in system and ready for AoE->ATA commands */
DEVFL_TKILL = (1<<1), /* flag for timer to know when to kill self */
DEVFL_EXT = (1<<2), /* device accepts lba48 commands */
DEVFL_CLOSEWAIT = (1<<3), /* device is waiting for all closes to revalidate */
DEVFL_GDALLOC = (1<<4), /* need to alloc gendisk */
DEVFL_PAUSE = (1<<5),
DEVFL_NEWSIZE = (1<<6), /* need to update dev size in block layer */
DEVFL_MAXBCNT = (1<<7), /* d->maxbcnt is not changeable */
DEVFL_KICKME = (1<<8),
BUFFL_FAIL = 1,
};
enum {
DEFAULTBCNT = 2 * 512, /* 2 sectors */
NPERSHELF = 16, /* number of slots per shelf address */
FREETAG = -1,
MIN_BUFS = 8,
};
struct buf {
struct list_head bufs;
ulong start_time; /* for disk stats */
ulong flags;
ulong nframesout;
char *bufaddr;
ulong resid;
ulong bv_resid;
sector_t sector;
struct bio *bio;
struct bio_vec *bv;
};
struct frame {
int tag;
ulong waited;
struct buf *buf;
char *bufaddr;
ulong bcnt;
sector_t lba;
struct sk_buff *skb;
};
struct aoedev {
struct aoedev *next;
unsigned char addr[6]; /* remote mac addr */
ushort flags;
ulong sysminor;
ulong aoemajor;
ulong aoeminor;
u16 nopen; /* (bd_openers isn't available without sleeping) */
u16 lasttag; /* last tag sent */
u16 rttavg; /* round trip average of requests/responses */
u16 mintimer;
u16 fw_ver; /* version of blade's firmware */
u16 maxbcnt;
struct work_struct work;/* disk create work struct */
struct gendisk *gd;
request_queue_t blkq;
struct hd_geometry geo;
sector_t ssize;
struct timer_list timer;
spinlock_t lock;
struct net_device *ifp; /* interface ed is attached to */
struct sk_buff *sendq_hd; /* packets needing to be sent, list head */
struct sk_buff *sendq_tl;
mempool_t *bufpool; /* for deadlock-free Buf allocation */
struct list_head bufq; /* queue of bios to work on */
struct buf *inprocess; /* the one we're currently working on */
ushort lostjumbo;
ushort nframes; /* number of frames below */
struct frame *frames;
};
int aoeblk_init(void);
void aoeblk_exit(void);
void aoeblk_gdalloc(void *);
void aoedisk_rm_sysfs(struct aoedev *d);
int aoechr_init(void);
void aoechr_exit(void);
void aoechr_error(char *);
void aoecmd_work(struct aoedev *d);
void aoecmd_cfg(ushort aoemajor, unsigned char aoeminor);
void aoecmd_ata_rsp(struct sk_buff *);
void aoecmd_cfg_rsp(struct sk_buff *);
void aoecmd_sleepwork(struct work_struct *);
struct sk_buff *new_skb(ulong);
int aoedev_init(void);
void aoedev_exit(void);
struct aoedev *aoedev_by_aoeaddr(int maj, int min);
struct aoedev *aoedev_by_sysminor_m(ulong sysminor, ulong bufcnt);
void aoedev_downdev(struct aoedev *d);
int aoedev_isbusy(struct aoedev *d);
int aoenet_init(void);
void aoenet_exit(void);
void aoenet_xmit(struct sk_buff *);
int is_aoe_netif(struct net_device *ifp);
int set_aoe_iflist(const char __user *str, size_t size);
u64 mac_addr(char addr[6]);

268
drivers/block/aoe/aoeblk.c Normal file
View File

@@ -0,0 +1,268 @@
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoeblk.c
* block device routines
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/genhd.h>
#include <linux/netdevice.h>
#include "aoe.h"
static struct kmem_cache *buf_pool_cache;
static ssize_t aoedisk_show_state(struct gendisk * disk, char *page)
{
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE,
"%s%s\n",
(d->flags & DEVFL_UP) ? "up" : "down",
(d->flags & DEVFL_PAUSE) ? ",paused" :
(d->nopen && !(d->flags & DEVFL_UP)) ? ",closewait" : "");
/* I'd rather see nopen exported so we can ditch closewait */
}
static ssize_t aoedisk_show_mac(struct gendisk * disk, char *page)
{
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE, "%012llx\n",
(unsigned long long)mac_addr(d->addr));
}
static ssize_t aoedisk_show_netif(struct gendisk * disk, char *page)
{
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE, "%s\n", d->ifp->name);
}
/* firmware version */
static ssize_t aoedisk_show_fwver(struct gendisk * disk, char *page)
{
struct aoedev *d = disk->private_data;
return snprintf(page, PAGE_SIZE, "0x%04x\n", (unsigned int) d->fw_ver);
}
static struct disk_attribute disk_attr_state = {
.attr = {.name = "state", .mode = S_IRUGO },
.show = aoedisk_show_state
};
static struct disk_attribute disk_attr_mac = {
.attr = {.name = "mac", .mode = S_IRUGO },
.show = aoedisk_show_mac
};
static struct disk_attribute disk_attr_netif = {
.attr = {.name = "netif", .mode = S_IRUGO },
.show = aoedisk_show_netif
};
static struct disk_attribute disk_attr_fwver = {
.attr = {.name = "firmware-version", .mode = S_IRUGO },
.show = aoedisk_show_fwver
};
static struct attribute *aoe_attrs[] = {
&disk_attr_state.attr,
&disk_attr_mac.attr,
&disk_attr_netif.attr,
&disk_attr_fwver.attr,
NULL
};
static const struct attribute_group attr_group = {
.attrs = aoe_attrs,
};
static int
aoedisk_add_sysfs(struct aoedev *d)
{
return sysfs_create_group(&d->gd->kobj, &attr_group);
}
void
aoedisk_rm_sysfs(struct aoedev *d)
{
sysfs_remove_group(&d->gd->kobj, &attr_group);
}
static int
aoeblk_open(struct inode *inode, struct file *filp)
{
struct aoedev *d;
ulong flags;
d = inode->i_bdev->bd_disk->private_data;
spin_lock_irqsave(&d->lock, flags);
if (d->flags & DEVFL_UP) {
d->nopen++;
spin_unlock_irqrestore(&d->lock, flags);
return 0;
}
spin_unlock_irqrestore(&d->lock, flags);
return -ENODEV;
}
static int
aoeblk_release(struct inode *inode, struct file *filp)
{
struct aoedev *d;
ulong flags;
d = inode->i_bdev->bd_disk->private_data;
spin_lock_irqsave(&d->lock, flags);
if (--d->nopen == 0) {
spin_unlock_irqrestore(&d->lock, flags);
aoecmd_cfg(d->aoemajor, d->aoeminor);
return 0;
}
spin_unlock_irqrestore(&d->lock, flags);
return 0;
}
static int
aoeblk_make_request(request_queue_t *q, struct bio *bio)
{
struct aoedev *d;
struct buf *buf;
struct sk_buff *sl;
ulong flags;
blk_queue_bounce(q, &bio);
d = bio->bi_bdev->bd_disk->private_data;
buf = mempool_alloc(d->bufpool, GFP_NOIO);
if (buf == NULL) {
printk(KERN_INFO "aoe: buf allocation failure\n");
bio_endio(bio, bio->bi_size, -ENOMEM);
return 0;
}
memset(buf, 0, sizeof(*buf));
INIT_LIST_HEAD(&buf->bufs);
buf->start_time = jiffies;
buf->bio = bio;
buf->resid = bio->bi_size;
buf->sector = bio->bi_sector;
buf->bv = &bio->bi_io_vec[bio->bi_idx];
WARN_ON(buf->bv->bv_len == 0);
buf->bv_resid = buf->bv->bv_len;
buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
spin_lock_irqsave(&d->lock, flags);
if ((d->flags & DEVFL_UP) == 0) {
printk(KERN_INFO "aoe: device %ld.%ld is not up\n",
d->aoemajor, d->aoeminor);
spin_unlock_irqrestore(&d->lock, flags);
mempool_free(buf, d->bufpool);
bio_endio(bio, bio->bi_size, -ENXIO);
return 0;
}
list_add_tail(&buf->bufs, &d->bufq);
aoecmd_work(d);
sl = d->sendq_hd;
d->sendq_hd = d->sendq_tl = NULL;
spin_unlock_irqrestore(&d->lock, flags);
aoenet_xmit(sl);
return 0;
}
static int
aoeblk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
struct aoedev *d = bdev->bd_disk->private_data;
if ((d->flags & DEVFL_UP) == 0) {
printk(KERN_ERR "aoe: disk not up\n");
return -ENODEV;
}
geo->cylinders = d->geo.cylinders;
geo->heads = d->geo.heads;
geo->sectors = d->geo.sectors;
return 0;
}
static struct block_device_operations aoe_bdops = {
.open = aoeblk_open,
.release = aoeblk_release,
.getgeo = aoeblk_getgeo,
.owner = THIS_MODULE,
};
/* alloc_disk and add_disk can sleep */
void
aoeblk_gdalloc(void *vp)
{
struct aoedev *d = vp;
struct gendisk *gd;
ulong flags;
gd = alloc_disk(AOE_PARTITIONS);
if (gd == NULL) {
printk(KERN_ERR "aoe: cannot allocate disk structure for %ld.%ld\n",
d->aoemajor, d->aoeminor);
spin_lock_irqsave(&d->lock, flags);
d->flags &= ~DEVFL_GDALLOC;
spin_unlock_irqrestore(&d->lock, flags);
return;
}
d->bufpool = mempool_create_slab_pool(MIN_BUFS, buf_pool_cache);
if (d->bufpool == NULL) {
printk(KERN_ERR "aoe: cannot allocate bufpool for %ld.%ld\n",
d->aoemajor, d->aoeminor);
put_disk(gd);
spin_lock_irqsave(&d->lock, flags);
d->flags &= ~DEVFL_GDALLOC;
spin_unlock_irqrestore(&d->lock, flags);
return;
}
spin_lock_irqsave(&d->lock, flags);
blk_queue_make_request(&d->blkq, aoeblk_make_request);
gd->major = AOE_MAJOR;
gd->first_minor = d->sysminor * AOE_PARTITIONS;
gd->fops = &aoe_bdops;
gd->private_data = d;
gd->capacity = d->ssize;
snprintf(gd->disk_name, sizeof gd->disk_name, "etherd/e%ld.%ld",
d->aoemajor, d->aoeminor);
gd->queue = &d->blkq;
d->gd = gd;
d->flags &= ~DEVFL_GDALLOC;
d->flags |= DEVFL_UP;
spin_unlock_irqrestore(&d->lock, flags);
add_disk(gd);
aoedisk_add_sysfs(d);
}
void
aoeblk_exit(void)
{
kmem_cache_destroy(buf_pool_cache);
}
int __init
aoeblk_init(void)
{
buf_pool_cache = kmem_cache_create("aoe_bufs",
sizeof(struct buf),
0, 0, NULL, NULL);
if (buf_pool_cache == NULL)
return -ENOMEM;
return 0;
}

279
drivers/block/aoe/aoechr.c Normal file
View File

@@ -0,0 +1,279 @@
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoechr.c
* AoE character device driver
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include "aoe.h"
enum {
//MINOR_STAT = 1, (moved to sysfs)
MINOR_ERR = 2,
MINOR_DISCOVER,
MINOR_INTERFACES,
MINOR_REVALIDATE,
MSGSZ = 2048,
NMSG = 100, /* message backlog to retain */
};
struct aoe_chardev {
ulong minor;
char name[32];
};
enum { EMFL_VALID = 1 };
struct ErrMsg {
short flags;
short len;
char *msg;
};
static struct ErrMsg emsgs[NMSG];
static int emsgs_head_idx, emsgs_tail_idx;
static struct semaphore emsgs_sema;
static spinlock_t emsgs_lock;
static int nblocked_emsgs_readers;
static struct class *aoe_class;
static struct aoe_chardev chardevs[] = {
{ MINOR_ERR, "err" },
{ MINOR_DISCOVER, "discover" },
{ MINOR_INTERFACES, "interfaces" },
{ MINOR_REVALIDATE, "revalidate" },
};
static int
discover(void)
{
aoecmd_cfg(0xffff, 0xff);
return 0;
}
static int
interfaces(const char __user *str, size_t size)
{
if (set_aoe_iflist(str, size)) {
printk(KERN_ERR
"aoe: could not set interface list: too many interfaces\n");
return -EINVAL;
}
return 0;
}
static int
revalidate(const char __user *str, size_t size)
{
int major, minor, n;
ulong flags;
struct aoedev *d;
char buf[16];
if (size >= sizeof buf)
return -EINVAL;
buf[sizeof buf - 1] = '\0';
if (copy_from_user(buf, str, size))
return -EFAULT;
/* should be e%d.%d format */
n = sscanf(buf, "e%d.%d", &major, &minor);
if (n != 2) {
printk(KERN_ERR "aoe: invalid device specification\n");
return -EINVAL;
}
d = aoedev_by_aoeaddr(major, minor);
if (!d)
return -EINVAL;
spin_lock_irqsave(&d->lock, flags);
d->flags &= ~DEVFL_MAXBCNT;
d->flags |= DEVFL_PAUSE;
spin_unlock_irqrestore(&d->lock, flags);
aoecmd_cfg(major, minor);
return 0;
}
void
aoechr_error(char *msg)
{
struct ErrMsg *em;
char *mp;
ulong flags, n;
n = strlen(msg);
spin_lock_irqsave(&emsgs_lock, flags);
em = emsgs + emsgs_tail_idx;
if ((em->flags & EMFL_VALID)) {
bail: spin_unlock_irqrestore(&emsgs_lock, flags);
return;
}
mp = kmalloc(n, GFP_ATOMIC);
if (mp == NULL) {
printk(KERN_ERR "aoe: allocation failure, len=%ld\n", n);
goto bail;
}
memcpy(mp, msg, n);
em->msg = mp;
em->flags |= EMFL_VALID;
em->len = n;
emsgs_tail_idx++;
emsgs_tail_idx %= ARRAY_SIZE(emsgs);
spin_unlock_irqrestore(&emsgs_lock, flags);
if (nblocked_emsgs_readers)
up(&emsgs_sema);
}
static ssize_t
aoechr_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offp)
{
int ret = -EINVAL;
switch ((unsigned long) filp->private_data) {
default:
printk(KERN_INFO "aoe: can't write to that file.\n");
break;
case MINOR_DISCOVER:
ret = discover();
break;
case MINOR_INTERFACES:
ret = interfaces(buf, cnt);
break;
case MINOR_REVALIDATE:
ret = revalidate(buf, cnt);
}
if (ret == 0)
ret = cnt;
return ret;
}
static int
aoechr_open(struct inode *inode, struct file *filp)
{
int n, i;
n = iminor(inode);
filp->private_data = (void *) (unsigned long) n;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
if (chardevs[i].minor == n)
return 0;
return -EINVAL;
}
static int
aoechr_rel(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t
aoechr_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
unsigned long n;
char *mp;
struct ErrMsg *em;
ssize_t len;
ulong flags;
n = (unsigned long) filp->private_data;
switch (n) {
case MINOR_ERR:
spin_lock_irqsave(&emsgs_lock, flags);
loop:
em = emsgs + emsgs_head_idx;
if ((em->flags & EMFL_VALID) == 0) {
if (filp->f_flags & O_NDELAY) {
spin_unlock_irqrestore(&emsgs_lock, flags);
return -EAGAIN;
}
nblocked_emsgs_readers++;
spin_unlock_irqrestore(&emsgs_lock, flags);
n = down_interruptible(&emsgs_sema);
spin_lock_irqsave(&emsgs_lock, flags);
nblocked_emsgs_readers--;
if (n) {
spin_unlock_irqrestore(&emsgs_lock, flags);
return -ERESTARTSYS;
}
goto loop;
}
if (em->len > cnt) {
spin_unlock_irqrestore(&emsgs_lock, flags);
return -EAGAIN;
}
mp = em->msg;
len = em->len;
em->msg = NULL;
em->flags &= ~EMFL_VALID;
emsgs_head_idx++;
emsgs_head_idx %= ARRAY_SIZE(emsgs);
spin_unlock_irqrestore(&emsgs_lock, flags);
n = copy_to_user(buf, mp, len);
kfree(mp);
return n == 0 ? len : -EFAULT;
default:
return -EFAULT;
}
}
static const struct file_operations aoe_fops = {
.write = aoechr_write,
.read = aoechr_read,
.open = aoechr_open,
.release = aoechr_rel,
.owner = THIS_MODULE,
};
int __init
aoechr_init(void)
{
int n, i;
n = register_chrdev(AOE_MAJOR, "aoechr", &aoe_fops);
if (n < 0) {
printk(KERN_ERR "aoe: can't register char device\n");
return n;
}
sema_init(&emsgs_sema, 0);
spin_lock_init(&emsgs_lock);
aoe_class = class_create(THIS_MODULE, "aoe");
if (IS_ERR(aoe_class)) {
unregister_chrdev(AOE_MAJOR, "aoechr");
return PTR_ERR(aoe_class);
}
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
class_device_create(aoe_class, NULL,
MKDEV(AOE_MAJOR, chardevs[i].minor),
NULL, chardevs[i].name);
return 0;
}
void
aoechr_exit(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
class_device_destroy(aoe_class, MKDEV(AOE_MAJOR, chardevs[i].minor));
class_destroy(aoe_class);
unregister_chrdev(AOE_MAJOR, "aoechr");
}

796
drivers/block/aoe/aoecmd.c Normal file
View File

@@ -0,0 +1,796 @@
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoecmd.c
* Filesystem request handling methods
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/genhd.h>
#include <asm/unaligned.h>
#include "aoe.h"
#define TIMERTICK (HZ / 10)
#define MINTIMER (2 * TIMERTICK)
#define MAXTIMER (HZ << 1)
static int aoe_deadsecs = 60 * 3;
module_param(aoe_deadsecs, int, 0644);
MODULE_PARM_DESC(aoe_deadsecs, "After aoe_deadsecs seconds, give up and fail dev.");
struct sk_buff *
new_skb(ulong len)
{
struct sk_buff *skb;
skb = alloc_skb(len, GFP_ATOMIC);
if (skb) {
skb->nh.raw = skb->mac.raw = skb->data;
skb->protocol = __constant_htons(ETH_P_AOE);
skb->priority = 0;
skb->next = skb->prev = NULL;
/* tell the network layer not to perform IP checksums
* or to get the NIC to do it
*/
skb->ip_summed = CHECKSUM_NONE;
}
return skb;
}
static struct frame *
getframe(struct aoedev *d, int tag)
{
struct frame *f, *e;
f = d->frames;
e = f + d->nframes;
for (; f<e; f++)
if (f->tag == tag)
return f;
return NULL;
}
/*
* Leave the top bit clear so we have tagspace for userland.
* The bottom 16 bits are the xmit tick for rexmit/rttavg processing.
* This driver reserves tag -1 to mean "unused frame."
*/
static int
newtag(struct aoedev *d)
{
register ulong n;
n = jiffies & 0xffff;
return n |= (++d->lasttag & 0x7fff) << 16;
}
static int
aoehdr_atainit(struct aoedev *d, struct aoe_hdr *h)
{
u32 host_tag = newtag(d);
memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
memcpy(h->dst, d->addr, sizeof h->dst);
h->type = __constant_cpu_to_be16(ETH_P_AOE);
h->verfl = AOE_HVER;
h->major = cpu_to_be16(d->aoemajor);
h->minor = d->aoeminor;
h->cmd = AOECMD_ATA;
h->tag = cpu_to_be32(host_tag);
return host_tag;
}
static inline void
put_lba(struct aoe_atahdr *ah, sector_t lba)
{
ah->lba0 = lba;
ah->lba1 = lba >>= 8;
ah->lba2 = lba >>= 8;
ah->lba3 = lba >>= 8;
ah->lba4 = lba >>= 8;
ah->lba5 = lba >>= 8;
}
static void
aoecmd_ata_rw(struct aoedev *d, struct frame *f)
{
struct aoe_hdr *h;
struct aoe_atahdr *ah;
struct buf *buf;
struct sk_buff *skb;
ulong bcnt;
register sector_t sector;
char writebit, extbit;
writebit = 0x10;
extbit = 0x4;
buf = d->inprocess;
sector = buf->sector;
bcnt = buf->bv_resid;
if (bcnt > d->maxbcnt)
bcnt = d->maxbcnt;
/* initialize the headers & frame */
skb = f->skb;
h = (struct aoe_hdr *) skb->mac.raw;
ah = (struct aoe_atahdr *) (h+1);
skb_put(skb, sizeof *h + sizeof *ah);
memset(h, 0, skb->len);
f->tag = aoehdr_atainit(d, h);
f->waited = 0;
f->buf = buf;
f->bufaddr = buf->bufaddr;
f->bcnt = bcnt;
f->lba = sector;
/* set up ata header */
ah->scnt = bcnt >> 9;
put_lba(ah, sector);
if (d->flags & DEVFL_EXT) {
ah->aflags |= AOEAFL_EXT;
} else {
extbit = 0;
ah->lba3 &= 0x0f;
ah->lba3 |= 0xe0; /* LBA bit + obsolete 0xa0 */
}
if (bio_data_dir(buf->bio) == WRITE) {
skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr),
offset_in_page(f->bufaddr), bcnt);
ah->aflags |= AOEAFL_WRITE;
skb->len += bcnt;
skb->data_len = bcnt;
} else {
writebit = 0;
}
ah->cmdstat = WIN_READ | writebit | extbit;
/* mark all tracking fields and load out */
buf->nframesout += 1;
buf->bufaddr += bcnt;
buf->bv_resid -= bcnt;
/* printk(KERN_DEBUG "aoe: bv_resid=%ld\n", buf->bv_resid); */
buf->resid -= bcnt;
buf->sector += bcnt >> 9;
if (buf->resid == 0) {
d->inprocess = NULL;
} else if (buf->bv_resid == 0) {
buf->bv++;
WARN_ON(buf->bv->bv_len == 0);
buf->bv_resid = buf->bv->bv_len;
buf->bufaddr = page_address(buf->bv->bv_page) + buf->bv->bv_offset;
}
skb->dev = d->ifp;
skb = skb_clone(skb, GFP_ATOMIC);
if (skb == NULL)
return;
if (d->sendq_hd)
d->sendq_tl->next = skb;
else
d->sendq_hd = skb;
d->sendq_tl = skb;
}
/* some callers cannot sleep, and they can call this function,
* transmitting the packets later, when interrupts are on
*/
static struct sk_buff *
aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff **tail)
{
struct aoe_hdr *h;
struct aoe_cfghdr *ch;
struct sk_buff *skb, *sl, *sl_tail;
struct net_device *ifp;
sl = sl_tail = NULL;
read_lock(&dev_base_lock);
for (ifp = dev_base; ifp; dev_put(ifp), ifp = ifp->next) {
dev_hold(ifp);
if (!is_aoe_netif(ifp))
continue;
skb = new_skb(sizeof *h + sizeof *ch);
if (skb == NULL) {
printk(KERN_INFO "aoe: skb alloc failure\n");
continue;
}
skb_put(skb, sizeof *h + sizeof *ch);
skb->dev = ifp;
if (sl_tail == NULL)
sl_tail = skb;
h = (struct aoe_hdr *) skb->mac.raw;
memset(h, 0, sizeof *h + sizeof *ch);
memset(h->dst, 0xff, sizeof h->dst);
memcpy(h->src, ifp->dev_addr, sizeof h->src);
h->type = __constant_cpu_to_be16(ETH_P_AOE);
h->verfl = AOE_HVER;
h->major = cpu_to_be16(aoemajor);
h->minor = aoeminor;
h->cmd = AOECMD_CFG;
skb->next = sl;
sl = skb;
}
read_unlock(&dev_base_lock);
if (tail != NULL)
*tail = sl_tail;
return sl;
}
static struct frame *
freeframe(struct aoedev *d)
{
struct frame *f, *e;
int n = 0;
f = d->frames;
e = f + d->nframes;
for (; f<e; f++) {
if (f->tag != FREETAG)
continue;
if (atomic_read(&skb_shinfo(f->skb)->dataref) == 1) {
skb_shinfo(f->skb)->nr_frags = f->skb->data_len = 0;
skb_trim(f->skb, 0);
return f;
}
n++;
}
if (n == d->nframes) /* wait for network layer */
d->flags |= DEVFL_KICKME;
return NULL;
}
/* enters with d->lock held */
void
aoecmd_work(struct aoedev *d)
{
struct frame *f;
struct buf *buf;
if (d->flags & DEVFL_PAUSE) {
if (!aoedev_isbusy(d))
d->sendq_hd = aoecmd_cfg_pkts(d->aoemajor,
d->aoeminor, &d->sendq_tl);
return;
}
loop:
f = freeframe(d);
if (f == NULL)
return;
if (d->inprocess == NULL) {
if (list_empty(&d->bufq))
return;
buf = container_of(d->bufq.next, struct buf, bufs);
list_del(d->bufq.next);
/*printk(KERN_DEBUG "aoe: bi_size=%ld\n", buf->bio->bi_size); */
d->inprocess = buf;
}
aoecmd_ata_rw(d, f);
goto loop;
}
static void
rexmit(struct aoedev *d, struct frame *f)
{
struct sk_buff *skb;
struct aoe_hdr *h;
struct aoe_atahdr *ah;
char buf[128];
u32 n;
n = newtag(d);
snprintf(buf, sizeof buf,
"%15s e%ld.%ld oldtag=%08x@%08lx newtag=%08x\n",
"retransmit",
d->aoemajor, d->aoeminor, f->tag, jiffies, n);
aoechr_error(buf);
skb = f->skb;
h = (struct aoe_hdr *) skb->mac.raw;
ah = (struct aoe_atahdr *) (h+1);
f->tag = n;
h->tag = cpu_to_be32(n);
memcpy(h->dst, d->addr, sizeof h->dst);
memcpy(h->src, d->ifp->dev_addr, sizeof h->src);
n = DEFAULTBCNT / 512;
if (ah->scnt > n) {
ah->scnt = n;
if (ah->aflags & AOEAFL_WRITE) {
skb_fill_page_desc(skb, 0, virt_to_page(f->bufaddr),
offset_in_page(f->bufaddr), DEFAULTBCNT);
skb->len = sizeof *h + sizeof *ah + DEFAULTBCNT;
skb->data_len = DEFAULTBCNT;
}
if (++d->lostjumbo > (d->nframes << 1))
if (d->maxbcnt != DEFAULTBCNT) {
printk(KERN_INFO "aoe: e%ld.%ld: too many lost jumbo on %s - using 1KB frames.\n",
d->aoemajor, d->aoeminor, d->ifp->name);
d->maxbcnt = DEFAULTBCNT;
d->flags |= DEVFL_MAXBCNT;
}
}
skb->dev = d->ifp;
skb = skb_clone(skb, GFP_ATOMIC);
if (skb == NULL)
return;
if (d->sendq_hd)
d->sendq_tl->next = skb;
else
d->sendq_hd = skb;
d->sendq_tl = skb;
}
static int
tsince(int tag)
{
int n;
n = jiffies & 0xffff;
n -= tag & 0xffff;
if (n < 0)
n += 1<<16;
return n;
}
static void
rexmit_timer(ulong vp)
{
struct aoedev *d;
struct frame *f, *e;
struct sk_buff *sl;
register long timeout;
ulong flags, n;
d = (struct aoedev *) vp;
sl = NULL;
/* timeout is always ~150% of the moving average */
timeout = d->rttavg;
timeout += timeout >> 1;
spin_lock_irqsave(&d->lock, flags);
if (d->flags & DEVFL_TKILL) {
spin_unlock_irqrestore(&d->lock, flags);
return;
}
f = d->frames;
e = f + d->nframes;
for (; f<e; f++) {
if (f->tag != FREETAG && tsince(f->tag) >= timeout) {
n = f->waited += timeout;
n /= HZ;
if (n > aoe_deadsecs) { /* waited too long for response */
aoedev_downdev(d);
break;
}
rexmit(d, f);
}
}
if (d->flags & DEVFL_KICKME) {
d->flags &= ~DEVFL_KICKME;
aoecmd_work(d);
}
sl = d->sendq_hd;
d->sendq_hd = d->sendq_tl = NULL;
if (sl) {
n = d->rttavg <<= 1;
if (n > MAXTIMER)
d->rttavg = MAXTIMER;
}
d->timer.expires = jiffies + TIMERTICK;
add_timer(&d->timer);
spin_unlock_irqrestore(&d->lock, flags);
aoenet_xmit(sl);
}
/* this function performs work that has been deferred until sleeping is OK
*/
void
aoecmd_sleepwork(struct work_struct *work)
{
struct aoedev *d = container_of(work, struct aoedev, work);
if (d->flags & DEVFL_GDALLOC)
aoeblk_gdalloc(d);
if (d->flags & DEVFL_NEWSIZE) {
struct block_device *bd;
unsigned long flags;
u64 ssize;
ssize = d->gd->capacity;
bd = bdget_disk(d->gd, 0);
if (bd) {
mutex_lock(&bd->bd_inode->i_mutex);
i_size_write(bd->bd_inode, (loff_t)ssize<<9);
mutex_unlock(&bd->bd_inode->i_mutex);
bdput(bd);
}
spin_lock_irqsave(&d->lock, flags);
d->flags |= DEVFL_UP;
d->flags &= ~DEVFL_NEWSIZE;
spin_unlock_irqrestore(&d->lock, flags);
}
}
static void
ataid_complete(struct aoedev *d, unsigned char *id)
{
u64 ssize;
u16 n;
/* word 83: command set supported */
n = le16_to_cpu(get_unaligned((__le16 *) &id[83<<1]));
/* word 86: command set/feature enabled */
n |= le16_to_cpu(get_unaligned((__le16 *) &id[86<<1]));
if (n & (1<<10)) { /* bit 10: LBA 48 */
d->flags |= DEVFL_EXT;
/* word 100: number lba48 sectors */
ssize = le64_to_cpu(get_unaligned((__le64 *) &id[100<<1]));
/* set as in ide-disk.c:init_idedisk_capacity */
d->geo.cylinders = ssize;
d->geo.cylinders /= (255 * 63);
d->geo.heads = 255;
d->geo.sectors = 63;
} else {
d->flags &= ~DEVFL_EXT;
/* number lba28 sectors */
ssize = le32_to_cpu(get_unaligned((__le32 *) &id[60<<1]));
/* NOTE: obsolete in ATA 6 */
d->geo.cylinders = le16_to_cpu(get_unaligned((__le16 *) &id[54<<1]));
d->geo.heads = le16_to_cpu(get_unaligned((__le16 *) &id[55<<1]));
d->geo.sectors = le16_to_cpu(get_unaligned((__le16 *) &id[56<<1]));
}
if (d->ssize != ssize)
printk(KERN_INFO "aoe: %012llx e%lu.%lu v%04x has %llu sectors\n",
(unsigned long long)mac_addr(d->addr),
d->aoemajor, d->aoeminor,
d->fw_ver, (long long)ssize);
d->ssize = ssize;
d->geo.start = 0;
if (d->gd != NULL) {
d->gd->capacity = ssize;
d->flags |= DEVFL_NEWSIZE;
} else {
if (d->flags & DEVFL_GDALLOC) {
printk(KERN_ERR "aoe: can't schedule work for e%lu.%lu, %s\n",
d->aoemajor, d->aoeminor,
"it's already on! This shouldn't happen.\n");
return;
}
d->flags |= DEVFL_GDALLOC;
}
schedule_work(&d->work);
}
static void
calc_rttavg(struct aoedev *d, int rtt)
{
register long n;
n = rtt;
if (n < 0) {
n = -rtt;
if (n < MINTIMER)
n = MINTIMER;
else if (n > MAXTIMER)
n = MAXTIMER;
d->mintimer += (n - d->mintimer) >> 1;
} else if (n < d->mintimer)
n = d->mintimer;
else if (n > MAXTIMER)
n = MAXTIMER;
/* g == .25; cf. Congestion Avoidance and Control, Jacobson & Karels; 1988 */
n -= d->rttavg;
d->rttavg += n >> 2;
}
void
aoecmd_ata_rsp(struct sk_buff *skb)
{
struct aoedev *d;
struct aoe_hdr *hin, *hout;
struct aoe_atahdr *ahin, *ahout;
struct frame *f;
struct buf *buf;
struct sk_buff *sl;
register long n;
ulong flags;
char ebuf[128];
u16 aoemajor;
hin = (struct aoe_hdr *) skb->mac.raw;
aoemajor = be16_to_cpu(get_unaligned(&hin->major));
d = aoedev_by_aoeaddr(aoemajor, hin->minor);
if (d == NULL) {
snprintf(ebuf, sizeof ebuf, "aoecmd_ata_rsp: ata response "
"for unknown device %d.%d\n",
aoemajor, hin->minor);
aoechr_error(ebuf);
return;
}
spin_lock_irqsave(&d->lock, flags);
n = be32_to_cpu(get_unaligned(&hin->tag));
f = getframe(d, n);
if (f == NULL) {
calc_rttavg(d, -tsince(n));
spin_unlock_irqrestore(&d->lock, flags);
snprintf(ebuf, sizeof ebuf,
"%15s e%d.%d tag=%08x@%08lx\n",
"unexpected rsp",
be16_to_cpu(get_unaligned(&hin->major)),
hin->minor,
be32_to_cpu(get_unaligned(&hin->tag)),
jiffies);
aoechr_error(ebuf);
return;
}
calc_rttavg(d, tsince(f->tag));
ahin = (struct aoe_atahdr *) (hin+1);
hout = (struct aoe_hdr *) f->skb->mac.raw;
ahout = (struct aoe_atahdr *) (hout+1);
buf = f->buf;
if (ahout->cmdstat == WIN_IDENTIFY)
d->flags &= ~DEVFL_PAUSE;
if (ahin->cmdstat & 0xa9) { /* these bits cleared on success */
printk(KERN_ERR
"aoe: ata error cmd=%2.2Xh stat=%2.2Xh from e%ld.%ld\n",
ahout->cmdstat, ahin->cmdstat,
d->aoemajor, d->aoeminor);
if (buf)
buf->flags |= BUFFL_FAIL;
} else {
n = ahout->scnt << 9;
switch (ahout->cmdstat) {
case WIN_READ:
case WIN_READ_EXT:
if (skb->len - sizeof *hin - sizeof *ahin < n) {
printk(KERN_ERR
"aoe: runt data size in read. skb->len=%d\n",
skb->len);
/* fail frame f? just returning will rexmit. */
spin_unlock_irqrestore(&d->lock, flags);
return;
}
memcpy(f->bufaddr, ahin+1, n);
case WIN_WRITE:
case WIN_WRITE_EXT:
if (f->bcnt -= n) {
skb = f->skb;
f->bufaddr += n;
put_lba(ahout, f->lba += ahout->scnt);
n = f->bcnt;
if (n > DEFAULTBCNT)
n = DEFAULTBCNT;
ahout->scnt = n >> 9;
if (ahout->aflags & AOEAFL_WRITE) {
skb_fill_page_desc(skb, 0,
virt_to_page(f->bufaddr),
offset_in_page(f->bufaddr), n);
skb->len = sizeof *hout + sizeof *ahout + n;
skb->data_len = n;
}
f->tag = newtag(d);
hout->tag = cpu_to_be32(f->tag);
skb->dev = d->ifp;
skb = skb_clone(skb, GFP_ATOMIC);
spin_unlock_irqrestore(&d->lock, flags);
if (skb)
aoenet_xmit(skb);
return;
}
if (n > DEFAULTBCNT)
d->lostjumbo = 0;
break;
case WIN_IDENTIFY:
if (skb->len - sizeof *hin - sizeof *ahin < 512) {
printk(KERN_INFO
"aoe: runt data size in ataid. skb->len=%d\n",
skb->len);
spin_unlock_irqrestore(&d->lock, flags);
return;
}
ataid_complete(d, (char *) (ahin+1));
break;
default:
printk(KERN_INFO
"aoe: unrecognized ata command %2.2Xh for %d.%d\n",
ahout->cmdstat,
be16_to_cpu(get_unaligned(&hin->major)),
hin->minor);
}
}
if (buf) {
buf->nframesout -= 1;
if (buf->nframesout == 0 && buf->resid == 0) {
unsigned long duration = jiffies - buf->start_time;
unsigned long n_sect = buf->bio->bi_size >> 9;
struct gendisk *disk = d->gd;
const int rw = bio_data_dir(buf->bio);
disk_stat_inc(disk, ios[rw]);
disk_stat_add(disk, ticks[rw], duration);
disk_stat_add(disk, sectors[rw], n_sect);
disk_stat_add(disk, io_ticks, duration);
n = (buf->flags & BUFFL_FAIL) ? -EIO : 0;
bio_endio(buf->bio, buf->bio->bi_size, n);
mempool_free(buf, d->bufpool);
}
}
f->buf = NULL;
f->tag = FREETAG;
aoecmd_work(d);
sl = d->sendq_hd;
d->sendq_hd = d->sendq_tl = NULL;
spin_unlock_irqrestore(&d->lock, flags);
aoenet_xmit(sl);
}
void
aoecmd_cfg(ushort aoemajor, unsigned char aoeminor)
{
struct sk_buff *sl;
sl = aoecmd_cfg_pkts(aoemajor, aoeminor, NULL);
aoenet_xmit(sl);
}
/*
* Since we only call this in one place (and it only prepares one frame)
* we just return the skb. Usually we'd chain it up to the aoedev sendq.
*/
static struct sk_buff *
aoecmd_ata_id(struct aoedev *d)
{
struct aoe_hdr *h;
struct aoe_atahdr *ah;
struct frame *f;
struct sk_buff *skb;
f = freeframe(d);
if (f == NULL) {
printk(KERN_ERR "aoe: can't get a frame. This shouldn't happen.\n");
return NULL;
}
/* initialize the headers & frame */
skb = f->skb;
h = (struct aoe_hdr *) skb->mac.raw;
ah = (struct aoe_atahdr *) (h+1);
skb_put(skb, sizeof *h + sizeof *ah);
memset(h, 0, skb->len);
f->tag = aoehdr_atainit(d, h);
f->waited = 0;
/* set up ata header */
ah->scnt = 1;
ah->cmdstat = WIN_IDENTIFY;
ah->lba3 = 0xa0;
skb->dev = d->ifp;
d->rttavg = MAXTIMER;
d->timer.function = rexmit_timer;
return skb_clone(skb, GFP_ATOMIC);
}
void
aoecmd_cfg_rsp(struct sk_buff *skb)
{
struct aoedev *d;
struct aoe_hdr *h;
struct aoe_cfghdr *ch;
ulong flags, sysminor, aoemajor;
struct sk_buff *sl;
enum { MAXFRAMES = 16 };
u16 n;
h = (struct aoe_hdr *) skb->mac.raw;
ch = (struct aoe_cfghdr *) (h+1);
/*
* Enough people have their dip switches set backwards to
* warrant a loud message for this special case.
*/
aoemajor = be16_to_cpu(get_unaligned(&h->major));
if (aoemajor == 0xfff) {
printk(KERN_ERR "aoe: Warning: shelf address is all ones. "
"Check shelf dip switches.\n");
return;
}
sysminor = SYSMINOR(aoemajor, h->minor);
if (sysminor * AOE_PARTITIONS + AOE_PARTITIONS > MINORMASK) {
printk(KERN_INFO "aoe: e%ld.%d: minor number too large\n",
aoemajor, (int) h->minor);
return;
}
n = be16_to_cpu(ch->bufcnt);
if (n > MAXFRAMES) /* keep it reasonable */
n = MAXFRAMES;
d = aoedev_by_sysminor_m(sysminor, n);
if (d == NULL) {
printk(KERN_INFO "aoe: device sysminor_m failure\n");
return;
}
spin_lock_irqsave(&d->lock, flags);
/* permit device to migrate mac and network interface */
d->ifp = skb->dev;
memcpy(d->addr, h->src, sizeof d->addr);
if (!(d->flags & DEVFL_MAXBCNT)) {
n = d->ifp->mtu;
n -= sizeof (struct aoe_hdr) + sizeof (struct aoe_atahdr);
n /= 512;
if (n > ch->scnt)
n = ch->scnt;
n = n ? n * 512 : DEFAULTBCNT;
if (n != d->maxbcnt) {
printk(KERN_INFO
"aoe: e%ld.%ld: setting %d byte data frames on %s\n",
d->aoemajor, d->aoeminor, n, d->ifp->name);
d->maxbcnt = n;
}
}
/* don't change users' perspective */
if (d->nopen && !(d->flags & DEVFL_PAUSE)) {
spin_unlock_irqrestore(&d->lock, flags);
return;
}
d->flags |= DEVFL_PAUSE; /* force pause */
d->mintimer = MINTIMER;
d->fw_ver = be16_to_cpu(ch->fwver);
/* check for already outstanding ataid */
sl = aoedev_isbusy(d) == 0 ? aoecmd_ata_id(d) : NULL;
spin_unlock_irqrestore(&d->lock, flags);
aoenet_xmit(sl);
}

220
drivers/block/aoe/aoedev.c Normal file
View File

@@ -0,0 +1,220 @@
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoedev.c
* AoE device utility functions; maintains device list.
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/netdevice.h>
#include "aoe.h"
static struct aoedev *devlist;
static spinlock_t devlist_lock;
int
aoedev_isbusy(struct aoedev *d)
{
struct frame *f, *e;
f = d->frames;
e = f + d->nframes;
do {
if (f->tag != FREETAG)
return 1;
} while (++f < e);
return 0;
}
struct aoedev *
aoedev_by_aoeaddr(int maj, int min)
{
struct aoedev *d;
ulong flags;
spin_lock_irqsave(&devlist_lock, flags);
for (d=devlist; d; d=d->next)
if (d->aoemajor == maj && d->aoeminor == min)
break;
spin_unlock_irqrestore(&devlist_lock, flags);
return d;
}
static void
dummy_timer(ulong vp)
{
struct aoedev *d;
d = (struct aoedev *)vp;
if (d->flags & DEVFL_TKILL)
return;
d->timer.expires = jiffies + HZ;
add_timer(&d->timer);
}
/* called with devlist lock held */
static struct aoedev *
aoedev_newdev(ulong nframes)
{
struct aoedev *d;
struct frame *f, *e;
d = kzalloc(sizeof *d, GFP_ATOMIC);
f = kcalloc(nframes, sizeof *f, GFP_ATOMIC);
switch (!d || !f) {
case 0:
d->nframes = nframes;
d->frames = f;
e = f + nframes;
for (; f<e; f++) {
f->tag = FREETAG;
f->skb = new_skb(ETH_ZLEN);
if (!f->skb)
break;
}
if (f == e)
break;
while (f > d->frames) {
f--;
dev_kfree_skb(f->skb);
}
default:
if (f)
kfree(f);
if (d)
kfree(d);
return NULL;
}
INIT_WORK(&d->work, aoecmd_sleepwork);
spin_lock_init(&d->lock);
init_timer(&d->timer);
d->timer.data = (ulong) d;
d->timer.function = dummy_timer;
d->timer.expires = jiffies + HZ;
add_timer(&d->timer);
d->bufpool = NULL; /* defer to aoeblk_gdalloc */
INIT_LIST_HEAD(&d->bufq);
d->next = devlist;
devlist = d;
return d;
}
void
aoedev_downdev(struct aoedev *d)
{
struct frame *f, *e;
struct buf *buf;
struct bio *bio;
f = d->frames;
e = f + d->nframes;
for (; f<e; f->tag = FREETAG, f->buf = NULL, f++) {
if (f->tag == FREETAG || f->buf == NULL)
continue;
buf = f->buf;
bio = buf->bio;
if (--buf->nframesout == 0) {
mempool_free(buf, d->bufpool);
bio_endio(bio, bio->bi_size, -EIO);
}
skb_shinfo(f->skb)->nr_frags = f->skb->data_len = 0;
}
d->inprocess = NULL;
while (!list_empty(&d->bufq)) {
buf = container_of(d->bufq.next, struct buf, bufs);
list_del(d->bufq.next);
bio = buf->bio;
mempool_free(buf, d->bufpool);
bio_endio(bio, bio->bi_size, -EIO);
}
if (d->gd)
d->gd->capacity = 0;
d->flags &= ~(DEVFL_UP | DEVFL_PAUSE);
}
/* find it or malloc it */
struct aoedev *
aoedev_by_sysminor_m(ulong sysminor, ulong bufcnt)
{
struct aoedev *d;
ulong flags;
spin_lock_irqsave(&devlist_lock, flags);
for (d=devlist; d; d=d->next)
if (d->sysminor == sysminor)
break;
if (d == NULL) {
d = aoedev_newdev(bufcnt);
if (d == NULL) {
spin_unlock_irqrestore(&devlist_lock, flags);
printk(KERN_INFO "aoe: aoedev_newdev failure.\n");
return NULL;
}
d->sysminor = sysminor;
d->aoemajor = AOEMAJOR(sysminor);
d->aoeminor = AOEMINOR(sysminor);
}
spin_unlock_irqrestore(&devlist_lock, flags);
return d;
}
static void
aoedev_freedev(struct aoedev *d)
{
struct frame *f, *e;
if (d->gd) {
aoedisk_rm_sysfs(d);
del_gendisk(d->gd);
put_disk(d->gd);
}
f = d->frames;
e = f + d->nframes;
for (; f<e; f++) {
skb_shinfo(f->skb)->nr_frags = 0;
dev_kfree_skb(f->skb);
}
kfree(d->frames);
if (d->bufpool)
mempool_destroy(d->bufpool);
kfree(d);
}
void
aoedev_exit(void)
{
struct aoedev *d;
ulong flags;
flush_scheduled_work();
while ((d = devlist)) {
devlist = d->next;
spin_lock_irqsave(&d->lock, flags);
aoedev_downdev(d);
d->flags |= DEVFL_TKILL;
spin_unlock_irqrestore(&d->lock, flags);
del_timer_sync(&d->timer);
aoedev_freedev(d);
}
}
int __init
aoedev_init(void)
{
spin_lock_init(&devlist_lock);
return 0;
}

110
drivers/block/aoe/aoemain.c Normal file
View File

@@ -0,0 +1,110 @@
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoemain.c
* Module initialization routines, discover timer
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/module.h>
#include "aoe.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sam Hopkins <sah@coraid.com>");
MODULE_DESCRIPTION("AoE block/char driver for 2.6.2 and newer 2.6 kernels");
MODULE_VERSION(VERSION);
enum { TINIT, TRUN, TKILL };
static void
discover_timer(ulong vp)
{
static struct timer_list t;
static volatile ulong die;
static spinlock_t lock;
ulong flags;
enum { DTIMERTICK = HZ * 60 }; /* one minute */
switch (vp) {
case TINIT:
init_timer(&t);
spin_lock_init(&lock);
t.data = TRUN;
t.function = discover_timer;
die = 0;
case TRUN:
spin_lock_irqsave(&lock, flags);
if (!die) {
t.expires = jiffies + DTIMERTICK;
add_timer(&t);
}
spin_unlock_irqrestore(&lock, flags);
aoecmd_cfg(0xffff, 0xff);
return;
case TKILL:
spin_lock_irqsave(&lock, flags);
die = 1;
spin_unlock_irqrestore(&lock, flags);
del_timer_sync(&t);
default:
return;
}
}
static void
aoe_exit(void)
{
discover_timer(TKILL);
aoenet_exit();
unregister_blkdev(AOE_MAJOR, DEVICE_NAME);
aoechr_exit();
aoedev_exit();
aoeblk_exit(); /* free cache after de-allocating bufs */
}
static int __init
aoe_init(void)
{
int ret;
ret = aoedev_init();
if (ret)
return ret;
ret = aoechr_init();
if (ret)
goto chr_fail;
ret = aoeblk_init();
if (ret)
goto blk_fail;
ret = aoenet_init();
if (ret)
goto net_fail;
ret = register_blkdev(AOE_MAJOR, DEVICE_NAME);
if (ret < 0) {
printk(KERN_ERR "aoe: can't register major\n");
goto blkreg_fail;
}
printk(KERN_INFO "aoe: AoE v%s initialised.\n", VERSION);
discover_timer(TINIT);
return 0;
blkreg_fail:
aoenet_exit();
net_fail:
aoeblk_exit();
blk_fail:
aoechr_exit();
chr_fail:
aoedev_exit();
printk(KERN_INFO "aoe: initialisation failure.\n");
return ret;
}
module_init(aoe_init);
module_exit(aoe_exit);

174
drivers/block/aoe/aoenet.c Normal file
View File

@@ -0,0 +1,174 @@
/* Copyright (c) 2006 Coraid, Inc. See COPYING for GPL terms. */
/*
* aoenet.c
* Ethernet portion of AoE driver
*/
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/netdevice.h>
#include <linux/moduleparam.h>
#include <asm/unaligned.h>
#include "aoe.h"
#define NECODES 5
static char *aoe_errlist[] =
{
"no such error",
"unrecognized command code",
"bad argument parameter",
"device unavailable",
"config string present",
"unsupported version"
};
enum {
IFLISTSZ = 1024,
};
static char aoe_iflist[IFLISTSZ];
module_param_string(aoe_iflist, aoe_iflist, IFLISTSZ, 0600);
MODULE_PARM_DESC(aoe_iflist, "aoe_iflist=\"dev1 [dev2 ...]\"\n");
#ifndef MODULE
static int __init aoe_iflist_setup(char *str)
{
strncpy(aoe_iflist, str, IFLISTSZ);
aoe_iflist[IFLISTSZ - 1] = '\0';
return 1;
}
__setup("aoe_iflist=", aoe_iflist_setup);
#endif
int
is_aoe_netif(struct net_device *ifp)
{
register char *p, *q;
register int len;
if (aoe_iflist[0] == '\0')
return 1;
p = aoe_iflist + strspn(aoe_iflist, WHITESPACE);
for (; *p; p = q + strspn(q, WHITESPACE)) {
q = p + strcspn(p, WHITESPACE);
if (q != p)
len = q - p;
else
len = strlen(p); /* last token in aoe_iflist */
if (strlen(ifp->name) == len && !strncmp(ifp->name, p, len))
return 1;
if (q == p)
break;
}
return 0;
}
int
set_aoe_iflist(const char __user *user_str, size_t size)
{
if (size >= IFLISTSZ)
return -EINVAL;
if (copy_from_user(aoe_iflist, user_str, size)) {
printk(KERN_INFO "aoe: copy from user failed\n");
return -EFAULT;
}
aoe_iflist[size] = 0x00;
return 0;
}
u64
mac_addr(char addr[6])
{
__be64 n = 0;
char *p = (char *) &n;
memcpy(p + 2, addr, 6); /* (sizeof addr != 6) */
return __be64_to_cpu(n);
}
void
aoenet_xmit(struct sk_buff *sl)
{
struct sk_buff *skb;
while ((skb = sl)) {
sl = sl->next;
skb->next = skb->prev = NULL;
dev_queue_xmit(skb);
}
}
/*
* (1) len doesn't include the header by default. I want this.
*/
static int
aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, struct net_device *orig_dev)
{
struct aoe_hdr *h;
u32 n;
skb = skb_share_check(skb, GFP_ATOMIC);
if (skb == NULL)
return 0;
if (skb_linearize(skb))
goto exit;
if (!is_aoe_netif(ifp))
goto exit;
skb_push(skb, ETH_HLEN); /* (1) */
h = (struct aoe_hdr *) skb->mac.raw;
n = be32_to_cpu(get_unaligned(&h->tag));
if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31))
goto exit;
if (h->verfl & AOEFL_ERR) {
n = h->err;
if (n > NECODES)
n = 0;
if (net_ratelimit())
printk(KERN_ERR "aoe: error packet from %d.%d; ecode=%d '%s'\n",
be16_to_cpu(get_unaligned(&h->major)), h->minor,
h->err, aoe_errlist[n]);
goto exit;
}
switch (h->cmd) {
case AOECMD_ATA:
aoecmd_ata_rsp(skb);
break;
case AOECMD_CFG:
aoecmd_cfg_rsp(skb);
break;
default:
printk(KERN_INFO "aoe: unknown cmd %d\n", h->cmd);
}
exit:
dev_kfree_skb(skb);
return 0;
}
static struct packet_type aoe_pt = {
.type = __constant_htons(ETH_P_AOE),
.func = aoenet_rcv,
};
int __init
aoenet_init(void)
{
dev_add_pack(&aoe_pt);
return 0;
}
void
aoenet_exit(void)
{
dev_remove_pack(&aoe_pt);
}

2011
drivers/block/ataflop.c Normal file

File diff suppressed because it is too large Load Diff

3574
drivers/block/cciss.c Normal file

File diff suppressed because it is too large Load Diff

292
drivers/block/cciss.h Normal file
View File

@@ -0,0 +1,292 @@
#ifndef CCISS_H
#define CCISS_H
#include <linux/genhd.h>
#include "cciss_cmd.h"
#define NWD_SHIFT 4
#define MAX_PART (1 << NWD_SHIFT)
#define IO_OK 0
#define IO_ERROR 1
struct ctlr_info;
typedef struct ctlr_info ctlr_info_t;
struct access_method {
void (*submit_command)(ctlr_info_t *h, CommandList_struct *c);
void (*set_intr_mask)(ctlr_info_t *h, unsigned long val);
unsigned long (*fifo_full)(ctlr_info_t *h);
unsigned long (*intr_pending)(ctlr_info_t *h);
unsigned long (*command_completed)(ctlr_info_t *h);
};
typedef struct _drive_info_struct
{
__u32 LunID;
int usage_count;
struct request_queue *queue;
sector_t nr_blocks;
int block_size;
int heads;
int sectors;
int cylinders;
int raid_level; /* set to -1 to indicate that
* the drive is not in use/configured
*/
int busy_configuring; /*This is set when the drive is being removed
*to prevent it from being opened or it's queue
*from being started.
*/
} drive_info_struct;
#ifdef CONFIG_CISS_SCSI_TAPE
struct sendcmd_reject_list {
int ncompletions;
unsigned long *complete; /* array of NR_CMDS tags */
};
#endif
struct ctlr_info
{
int ctlr;
char devname[8];
char *product_name;
char firm_ver[4]; // Firmware version
struct pci_dev *pdev;
__u32 board_id;
void __iomem *vaddr;
unsigned long paddr;
int nr_cmds; /* Number of commands allowed on this controller */
CfgTable_struct __iomem *cfgtable;
int interrupts_enabled;
int major;
int max_commands;
int commands_outstanding;
int max_outstanding; /* Debug */
int num_luns;
int highest_lun;
int usage_count; /* number of opens all all minor devices */
# define DOORBELL_INT 0
# define PERF_MODE_INT 1
# define SIMPLE_MODE_INT 2
# define MEMQ_MODE_INT 3
unsigned int intr[4];
unsigned int msix_vector;
unsigned int msi_vector;
int cciss_max_sectors;
BYTE cciss_read;
BYTE cciss_write;
BYTE cciss_read_capacity;
// information about each logical volume
drive_info_struct drv[CISS_MAX_LUN];
struct access_method access;
/* queue and queue Info */
CommandList_struct *reqQ;
CommandList_struct *cmpQ;
unsigned int Qdepth;
unsigned int maxQsinceinit;
unsigned int maxSG;
spinlock_t lock;
//* pointers to command and error info pool */
CommandList_struct *cmd_pool;
dma_addr_t cmd_pool_dhandle;
ErrorInfo_struct *errinfo_pool;
dma_addr_t errinfo_pool_dhandle;
unsigned long *cmd_pool_bits;
int nr_allocs;
int nr_frees;
int busy_configuring;
int busy_initializing;
/* This element holds the zero based queue number of the last
* queue to be started. It is used for fairness.
*/
int next_to_run;
// Disk structures we need to pass back
struct gendisk *gendisk[CISS_MAX_LUN];
#ifdef CONFIG_CISS_SCSI_TAPE
void *scsi_ctlr; /* ptr to structure containing scsi related stuff */
/* list of block side commands the scsi error handling sucked up */
/* and saved for later processing */
struct sendcmd_reject_list scsi_rejects;
#endif
unsigned char alive;
};
/* Defining the diffent access_menthods */
/*
* Memory mapped FIFO interface (SMART 53xx cards)
*/
#define SA5_DOORBELL 0x20
#define SA5_REQUEST_PORT_OFFSET 0x40
#define SA5_REPLY_INTR_MASK_OFFSET 0x34
#define SA5_REPLY_PORT_OFFSET 0x44
#define SA5_INTR_STATUS 0x30
#define SA5_SCRATCHPAD_OFFSET 0xB0
#define SA5_CTCFG_OFFSET 0xB4
#define SA5_CTMEM_OFFSET 0xB8
#define SA5_INTR_OFF 0x08
#define SA5B_INTR_OFF 0x04
#define SA5_INTR_PENDING 0x08
#define SA5B_INTR_PENDING 0x04
#define FIFO_EMPTY 0xffffffff
#define CCISS_FIRMWARE_READY 0xffff0000 /* value in scratchpad register */
#define CISS_ERROR_BIT 0x02
#define CCISS_INTR_ON 1
#define CCISS_INTR_OFF 0
/*
Send the command to the hardware
*/
static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c)
{
#ifdef CCISS_DEBUG
printk("Sending %x - down to controller\n", c->busaddr );
#endif /* CCISS_DEBUG */
writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET);
h->commands_outstanding++;
if ( h->commands_outstanding > h->max_outstanding)
h->max_outstanding = h->commands_outstanding;
}
/*
* This card is the opposite of the other cards.
* 0 turns interrupts on...
* 0x08 turns them off...
*/
static void SA5_intr_mask(ctlr_info_t *h, unsigned long val)
{
if (val)
{ /* Turn interrupts on */
h->interrupts_enabled = 1;
writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
} else /* Turn them off */
{
h->interrupts_enabled = 0;
writel( SA5_INTR_OFF,
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
}
}
/*
* This card is the opposite of the other cards.
* 0 turns interrupts on...
* 0x04 turns them off...
*/
static void SA5B_intr_mask(ctlr_info_t *h, unsigned long val)
{
if (val)
{ /* Turn interrupts on */
h->interrupts_enabled = 1;
writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
} else /* Turn them off */
{
h->interrupts_enabled = 0;
writel( SA5B_INTR_OFF,
h->vaddr + SA5_REPLY_INTR_MASK_OFFSET);
}
}
/*
* Returns true if fifo is full.
*
*/
static unsigned long SA5_fifo_full(ctlr_info_t *h)
{
if( h->commands_outstanding >= h->max_commands)
return(1);
else
return(0);
}
/*
* returns value read from hardware.
* returns FIFO_EMPTY if there is nothing to read
*/
static unsigned long SA5_completed(ctlr_info_t *h)
{
unsigned long register_value
= readl(h->vaddr + SA5_REPLY_PORT_OFFSET);
if(register_value != FIFO_EMPTY)
{
h->commands_outstanding--;
#ifdef CCISS_DEBUG
printk("cciss: Read %lx back from board\n", register_value);
#endif /* CCISS_DEBUG */
}
#ifdef CCISS_DEBUG
else
{
printk("cciss: FIFO Empty read\n");
}
#endif
return ( register_value);
}
/*
* Returns true if an interrupt is pending..
*/
static unsigned long SA5_intr_pending(ctlr_info_t *h)
{
unsigned long register_value =
readl(h->vaddr + SA5_INTR_STATUS);
#ifdef CCISS_DEBUG
printk("cciss: intr_pending %lx\n", register_value);
#endif /* CCISS_DEBUG */
if( register_value & SA5_INTR_PENDING)
return 1;
return 0 ;
}
/*
* Returns true if an interrupt is pending..
*/
static unsigned long SA5B_intr_pending(ctlr_info_t *h)
{
unsigned long register_value =
readl(h->vaddr + SA5_INTR_STATUS);
#ifdef CCISS_DEBUG
printk("cciss: intr_pending %lx\n", register_value);
#endif /* CCISS_DEBUG */
if( register_value & SA5B_INTR_PENDING)
return 1;
return 0 ;
}
static struct access_method SA5_access = {
SA5_submit_command,
SA5_intr_mask,
SA5_fifo_full,
SA5_intr_pending,
SA5_completed,
};
static struct access_method SA5B_access = {
SA5_submit_command,
SA5B_intr_mask,
SA5_fifo_full,
SA5B_intr_pending,
SA5_completed,
};
struct board_type {
__u32 board_id;
char *product_name;
struct access_method *access;
int nr_cmds; /* Max cmds this kind of ctlr can handle. */
};
#define CCISS_LOCK(i) (&hba[i]->lock)
#endif /* CCISS_H */

299
drivers/block/cciss_cmd.h Normal file
View File

@@ -0,0 +1,299 @@
#ifndef CCISS_CMD_H
#define CCISS_CMD_H
//###########################################################################
//DEFINES
//###########################################################################
#define CISS_VERSION "1.00"
//general boundary defintions
#define SENSEINFOBYTES 32//note that this value may vary between host implementations
#define MAXSGENTRIES 31
#define MAXREPLYQS 256
//Command Status value
#define CMD_SUCCESS 0x0000
#define CMD_TARGET_STATUS 0x0001
#define CMD_DATA_UNDERRUN 0x0002
#define CMD_DATA_OVERRUN 0x0003
#define CMD_INVALID 0x0004
#define CMD_PROTOCOL_ERR 0x0005
#define CMD_HARDWARE_ERR 0x0006
#define CMD_CONNECTION_LOST 0x0007
#define CMD_ABORTED 0x0008
#define CMD_ABORT_FAILED 0x0009
#define CMD_UNSOLICITED_ABORT 0x000A
#define CMD_TIMEOUT 0x000B
#define CMD_UNABORTABLE 0x000C
//transfer direction
#define XFER_NONE 0x00
#define XFER_WRITE 0x01
#define XFER_READ 0x02
#define XFER_RSVD 0x03
//task attribute
#define ATTR_UNTAGGED 0x00
#define ATTR_SIMPLE 0x04
#define ATTR_HEADOFQUEUE 0x05
#define ATTR_ORDERED 0x06
#define ATTR_ACA 0x07
//cdb type
#define TYPE_CMD 0x00
#define TYPE_MSG 0x01
//config space register offsets
#define CFG_VENDORID 0x00
#define CFG_DEVICEID 0x02
#define CFG_I2OBAR 0x10
#define CFG_MEM1BAR 0x14
//i2o space register offsets
#define I2O_IBDB_SET 0x20
#define I2O_IBDB_CLEAR 0x70
#define I2O_INT_STATUS 0x30
#define I2O_INT_MASK 0x34
#define I2O_IBPOST_Q 0x40
#define I2O_OBPOST_Q 0x44
#define I2O_DMA1_CFG 0x214
//Configuration Table
#define CFGTBL_ChangeReq 0x00000001l
#define CFGTBL_AccCmds 0x00000001l
#define CFGTBL_Trans_Simple 0x00000002l
#define CFGTBL_BusType_Ultra2 0x00000001l
#define CFGTBL_BusType_Ultra3 0x00000002l
#define CFGTBL_BusType_Fibre1G 0x00000100l
#define CFGTBL_BusType_Fibre2G 0x00000200l
typedef struct _vals32
{
__u32 lower;
__u32 upper;
} vals32;
typedef union _u64bit
{
vals32 val32;
__u64 val;
} u64bit;
// Type defs used in the following structs
#define BYTE __u8
#define WORD __u16
#define HWORD __u16
#define DWORD __u32
#define QWORD vals32
//###########################################################################
//STRUCTURES
//###########################################################################
#define CISS_MAX_LUN 1024
#define CISS_MAX_PHYS_LUN 1024
// SCSI-3 Cmmands
#pragma pack(1)
#define CISS_INQUIRY 0x12
//Date returned
typedef struct _InquiryData_struct
{
BYTE data_byte[36];
} InquiryData_struct;
#define CISS_REPORT_LOG 0xc2 /* Report Logical LUNs */
#define CISS_REPORT_PHYS 0xc3 /* Report Physical LUNs */
// Data returned
typedef struct _ReportLUNdata_struct
{
BYTE LUNListLength[4];
DWORD reserved;
BYTE LUN[CISS_MAX_LUN][8];
} ReportLunData_struct;
#define CCISS_READ_CAPACITY 0x25 /* Read Capacity */
typedef struct _ReadCapdata_struct
{
BYTE total_size[4]; // Total size in blocks
BYTE block_size[4]; // Size of blocks in bytes
} ReadCapdata_struct;
#define CCISS_READ_CAPACITY_16 0x9e /* Read Capacity 16 */
/* service action to differentiate a 16 byte read capacity from
other commands that use the 0x9e SCSI op code */
#define CCISS_READ_CAPACITY_16_SERVICE_ACT 0x10
typedef struct _ReadCapdata_struct_16
{
BYTE total_size[8]; /* Total size in blocks */
BYTE block_size[4]; /* Size of blocks in bytes */
BYTE prot_en:1; /* protection enable bit */
BYTE rto_en:1; /* reference tag own enable bit */
BYTE reserved:6; /* reserved bits */
BYTE reserved2[18]; /* reserved bytes per spec */
} ReadCapdata_struct_16;
/* Define the supported read/write commands for cciss based controllers */
#define CCISS_READ_10 0x28 /* Read(10) */
#define CCISS_WRITE_10 0x2a /* Write(10) */
#define CCISS_READ_16 0x88 /* Read(16) */
#define CCISS_WRITE_16 0x8a /* Write(16) */
/* Define the CDB lengths supported by cciss based controllers */
#define CDB_LEN10 10
#define CDB_LEN16 16
// BMIC commands
#define BMIC_READ 0x26
#define BMIC_WRITE 0x27
#define BMIC_CACHE_FLUSH 0xc2
#define CCISS_CACHE_FLUSH 0x01 //C2 was already being used by CCISS
//Command List Structure
typedef union _SCSI3Addr_struct {
struct {
BYTE Dev;
BYTE Bus:6;
BYTE Mode:2; // b00
} PeripDev;
struct {
BYTE DevLSB;
BYTE DevMSB:6;
BYTE Mode:2; // b01
} LogDev;
struct {
BYTE Dev:5;
BYTE Bus:3;
BYTE Targ:6;
BYTE Mode:2; // b10
} LogUnit;
} SCSI3Addr_struct;
typedef struct _PhysDevAddr_struct {
DWORD TargetId:24;
DWORD Bus:6;
DWORD Mode:2;
SCSI3Addr_struct Target[2]; //2 level target device addr
} PhysDevAddr_struct;
typedef struct _LogDevAddr_struct {
DWORD VolId:30;
DWORD Mode:2;
BYTE reserved[4];
} LogDevAddr_struct;
typedef union _LUNAddr_struct {
BYTE LunAddrBytes[8];
SCSI3Addr_struct SCSI3Lun[4];
PhysDevAddr_struct PhysDev;
LogDevAddr_struct LogDev;
} LUNAddr_struct;
typedef struct _CommandListHeader_struct {
BYTE ReplyQueue;
BYTE SGList;
HWORD SGTotal;
QWORD Tag;
LUNAddr_struct LUN;
} CommandListHeader_struct;
typedef struct _RequestBlock_struct {
BYTE CDBLen;
struct {
BYTE Type:3;
BYTE Attribute:3;
BYTE Direction:2;
} Type;
HWORD Timeout;
BYTE CDB[16];
} RequestBlock_struct;
typedef struct _ErrDescriptor_struct {
QWORD Addr;
DWORD Len;
} ErrDescriptor_struct;
typedef struct _SGDescriptor_struct {
QWORD Addr;
DWORD Len;
DWORD Ext;
} SGDescriptor_struct;
typedef union _MoreErrInfo_struct{
struct {
BYTE Reserved[3];
BYTE Type;
DWORD ErrorInfo;
}Common_Info;
struct{
BYTE Reserved[2];
BYTE offense_size;//size of offending entry
BYTE offense_num; //byte # of offense 0-base
DWORD offense_value;
}Invalid_Cmd;
}MoreErrInfo_struct;
typedef struct _ErrorInfo_struct {
BYTE ScsiStatus;
BYTE SenseLen;
HWORD CommandStatus;
DWORD ResidualCnt;
MoreErrInfo_struct MoreErrInfo;
BYTE SenseInfo[SENSEINFOBYTES];
} ErrorInfo_struct;
/* Command types */
#define CMD_RWREQ 0x00
#define CMD_IOCTL_PEND 0x01
#define CMD_SCSI 0x03
#define CMD_MSG_DONE 0x04
#define CMD_MSG_TIMEOUT 0x05
/* This structure needs to be divisible by 8 for new
* indexing method.
*/
#define PADSIZE (sizeof(long) - 4)
typedef struct _CommandList_struct {
CommandListHeader_struct Header;
RequestBlock_struct Request;
ErrDescriptor_struct ErrDesc;
SGDescriptor_struct SG[MAXSGENTRIES];
/* information associated with the command */
__u32 busaddr; /* physical address of this record */
ErrorInfo_struct * err_info; /* pointer to the allocated mem */
int ctlr;
int cmd_type;
long cmdindex;
struct _CommandList_struct *prev;
struct _CommandList_struct *next;
struct request * rq;
struct completion *waiting;
int retry_count;
void * scsi_cmd;
char pad[PADSIZE];
} CommandList_struct;
//Configuration Table Structure
typedef struct _HostWrite_struct {
DWORD TransportRequest;
DWORD Reserved;
DWORD CoalIntDelay;
DWORD CoalIntCount;
} HostWrite_struct;
typedef struct _CfgTable_struct {
BYTE Signature[4];
DWORD SpecValence;
DWORD TransportSupport;
DWORD TransportActive;
HostWrite_struct HostWrite;
DWORD CmdsOutMax;
DWORD BusTypes;
DWORD Reserved;
BYTE ServerName[16];
DWORD HeartBeat;
DWORD SCSI_Prefetch;
} CfgTable_struct;
#pragma pack()
#endif // CCISS_CMD_H

1535
drivers/block/cciss_scsi.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
/*
* Disk Array driver for Compaq SA53xx Controllers, SCSI Tape module
* Copyright 2001 Compaq Computer Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
*
*/
#ifdef CONFIG_CISS_SCSI_TAPE
#ifndef _CCISS_SCSI_H_
#define _CCISS_SCSI_H_
#include <scsi/scsicam.h> /* possibly irrelevant, since we don't show disks */
// the scsi id of the adapter...
#define SELF_SCSI_ID 15
// 15 is somewhat arbitrary, since the scsi-2 bus
// that's presented by the driver to the OS is
// fabricated. The "real" scsi-3 bus the
// hardware presents is fabricated too.
// The actual, honest-to-goodness physical
// bus that the devices are attached to is not
// addressible natively, and may in fact turn
// out to be not scsi at all.
#define SCSI_CCISS_CAN_QUEUE 2
/*
Note, cmd_per_lun could give us some trouble, so I'm setting it very low.
Likewise, SCSI_CCISS_CAN_QUEUE is set very conservatively.
If the upper scsi layer tries to track how many commands we have
outstanding, it will be operating under the misapprehension that it is
the only one sending us requests. We also have the block interface,
which is where most requests must surely come from, so the upper layer's
notion of how many requests we have outstanding will be wrong most or
all of the time.
Note, the normal SCSI mid-layer error handling doesn't work well
for this driver because 1) it takes the io_request_lock before
calling error handlers and uses a local variable to store flags,
so the io_request_lock cannot be released and interrupts enabled
inside the error handlers, and, the error handlers cannot poll
for command completion because they might get commands from the
block half of the driver completing, and not know what to do
with them. That's what we get for making a hybrid scsi/block
driver, I suppose.
*/
struct cciss_scsi_dev_t {
int devtype;
int bus, target, lun; /* as presented to the OS */
unsigned char scsi3addr[8]; /* as presented to the HW */
};
struct cciss_scsi_hba_t {
char *name;
int ndevices;
#define CCISS_MAX_SCSI_DEVS_PER_HBA 16
struct cciss_scsi_dev_t dev[CCISS_MAX_SCSI_DEVS_PER_HBA];
};
#endif /* _CCISS_SCSI_H_ */
#endif /* CONFIG_CISS_SCSI_TAPE */

1850
drivers/block/cpqarray.c Normal file

File diff suppressed because it is too large Load Diff

126
drivers/block/cpqarray.h Normal file
View File

@@ -0,0 +1,126 @@
/*
* Disk Array driver for Compaq SMART2 Controllers
* Copyright 1998 Compaq Computer Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
*
* If you want to make changes, improve or add functionality to this
* driver, you'll probably need the Compaq Array Controller Interface
* Specificiation (Document number ECG086/1198)
*/
#ifndef CPQARRAY_H
#define CPQARRAY_H
#ifdef __KERNEL__
#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/timer.h>
#endif
#include "ida_cmd.h"
#define IO_OK 0
#define IO_ERROR 1
#define NWD 16
#define NWD_SHIFT 4
#define IDA_TIMER (5*HZ)
#define IDA_TIMEOUT (10*HZ)
#define MISC_NONFATAL_WARN 0x01
typedef struct {
unsigned blk_size;
unsigned nr_blks;
unsigned cylinders;
unsigned heads;
unsigned sectors;
int usage_count;
} drv_info_t;
#ifdef __KERNEL__
struct ctlr_info;
typedef struct ctlr_info ctlr_info_t;
struct access_method {
void (*submit_command)(ctlr_info_t *h, cmdlist_t *c);
void (*set_intr_mask)(ctlr_info_t *h, unsigned long val);
unsigned long (*fifo_full)(ctlr_info_t *h);
unsigned long (*intr_pending)(ctlr_info_t *h);
unsigned long (*command_completed)(ctlr_info_t *h);
};
struct board_type {
__u32 board_id;
char *product_name;
struct access_method *access;
};
struct ctlr_info {
int ctlr;
char devname[8];
__u32 log_drv_map;
__u32 drv_assign_map;
__u32 drv_spare_map;
__u32 mp_failed_drv_map;
char firm_rev[4];
int ctlr_sig;
int log_drives;
int phys_drives;
struct pci_dev *pci_dev; /* NULL if EISA */
__u32 board_id;
char *product_name;
void __iomem *vaddr;
unsigned long paddr;
unsigned long io_mem_addr;
unsigned long io_mem_length;
int intr;
int usage_count;
drv_info_t drv[NWD];
struct proc_dir_entry *proc;
struct access_method access;
cmdlist_t *reqQ;
cmdlist_t *cmpQ;
cmdlist_t *cmd_pool;
dma_addr_t cmd_pool_dhandle;
unsigned long *cmd_pool_bits;
struct request_queue *queue;
spinlock_t lock;
unsigned int Qdepth;
unsigned int maxQsinceinit;
unsigned int nr_requests;
unsigned int nr_allocs;
unsigned int nr_frees;
struct timer_list timer;
unsigned int misc_tflags;
};
#define IDA_LOCK(i) (&hba[i]->lock)
#endif
#endif /* CPQARRAY_H */

218
drivers/block/cryptoloop.c Normal file
View File

@@ -0,0 +1,218 @@
/*
Linux loop encryption enabling module
Copyright (C) 2002 Herbert Valerio Riedel <hvr@gnu.org>
Copyright (C) 2003 Fruhwirth Clemens <clemens@endorphin.org>
This module is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This module is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this module; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/crypto.h>
#include <linux/blkdev.h>
#include <linux/loop.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("loop blockdevice transferfunction adaptor / CryptoAPI");
MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
#define LOOP_IV_SECTOR_BITS 9
#define LOOP_IV_SECTOR_SIZE (1 << LOOP_IV_SECTOR_BITS)
static int
cryptoloop_init(struct loop_device *lo, const struct loop_info64 *info)
{
int err = -EINVAL;
int cipher_len;
int mode_len;
char cms[LO_NAME_SIZE]; /* cipher-mode string */
char *cipher;
char *mode;
char *cmsp = cms; /* c-m string pointer */
struct crypto_blkcipher *tfm;
/* encryption breaks for non sector aligned offsets */
if (info->lo_offset % LOOP_IV_SECTOR_SIZE)
goto out;
strncpy(cms, info->lo_crypt_name, LO_NAME_SIZE);
cms[LO_NAME_SIZE - 1] = 0;
cipher = cmsp;
cipher_len = strcspn(cmsp, "-");
mode = cmsp + cipher_len;
mode_len = 0;
if (*mode) {
mode++;
mode_len = strcspn(mode, "-");
}
if (!mode_len) {
mode = "cbc";
mode_len = 3;
}
if (cipher_len + mode_len + 3 > LO_NAME_SIZE)
return -EINVAL;
memmove(cms, mode, mode_len);
cmsp = cms + mode_len;
*cmsp++ = '(';
memcpy(cmsp, info->lo_crypt_name, cipher_len);
cmsp += cipher_len;
*cmsp++ = ')';
*cmsp = 0;
tfm = crypto_alloc_blkcipher(cms, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
err = crypto_blkcipher_setkey(tfm, info->lo_encrypt_key,
info->lo_encrypt_key_size);
if (err != 0)
goto out_free_tfm;
lo->key_data = tfm;
return 0;
out_free_tfm:
crypto_free_blkcipher(tfm);
out:
return err;
}
typedef int (*encdec_cbc_t)(struct blkcipher_desc *desc,
struct scatterlist *sg_out,
struct scatterlist *sg_in,
unsigned int nsg);
static int
cryptoloop_transfer(struct loop_device *lo, int cmd,
struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off,
int size, sector_t IV)
{
struct crypto_blkcipher *tfm = lo->key_data;
struct blkcipher_desc desc = {
.tfm = tfm,
.flags = CRYPTO_TFM_REQ_MAY_SLEEP,
};
struct scatterlist sg_out = { NULL, };
struct scatterlist sg_in = { NULL, };
encdec_cbc_t encdecfunc;
struct page *in_page, *out_page;
unsigned in_offs, out_offs;
int err;
if (cmd == READ) {
in_page = raw_page;
in_offs = raw_off;
out_page = loop_page;
out_offs = loop_off;
encdecfunc = crypto_blkcipher_crt(tfm)->decrypt;
} else {
in_page = loop_page;
in_offs = loop_off;
out_page = raw_page;
out_offs = raw_off;
encdecfunc = crypto_blkcipher_crt(tfm)->encrypt;
}
while (size > 0) {
const int sz = min(size, LOOP_IV_SECTOR_SIZE);
u32 iv[4] = { 0, };
iv[0] = cpu_to_le32(IV & 0xffffffff);
sg_in.page = in_page;
sg_in.offset = in_offs;
sg_in.length = sz;
sg_out.page = out_page;
sg_out.offset = out_offs;
sg_out.length = sz;
desc.info = iv;
err = encdecfunc(&desc, &sg_out, &sg_in, sz);
if (err)
return err;
IV++;
size -= sz;
in_offs += sz;
out_offs += sz;
}
return 0;
}
static int
cryptoloop_ioctl(struct loop_device *lo, int cmd, unsigned long arg)
{
return -EINVAL;
}
static int
cryptoloop_release(struct loop_device *lo)
{
struct crypto_blkcipher *tfm = lo->key_data;
if (tfm != NULL) {
crypto_free_blkcipher(tfm);
lo->key_data = NULL;
return 0;
}
printk(KERN_ERR "cryptoloop_release(): tfm == NULL?\n");
return -EINVAL;
}
static struct loop_func_table cryptoloop_funcs = {
.number = LO_CRYPT_CRYPTOAPI,
.init = cryptoloop_init,
.ioctl = cryptoloop_ioctl,
.transfer = cryptoloop_transfer,
.release = cryptoloop_release,
.owner = THIS_MODULE
};
static int __init
init_cryptoloop(void)
{
int rc = loop_register_transfer(&cryptoloop_funcs);
if (rc)
printk(KERN_ERR "cryptoloop: loop_register_transfer failed\n");
return rc;
}
static void __exit
cleanup_cryptoloop(void)
{
if (loop_unregister_transfer(LO_CRYPT_CRYPTOAPI))
printk(KERN_ERR
"cryptoloop: loop_unregister_transfer failed\n");
}
module_init(init_cryptoloop);
module_exit(cleanup_cryptoloop);

4585
drivers/block/floppy.c Normal file

File diff suppressed because it is too large Load Diff

349
drivers/block/ida_cmd.h Normal file
View File

@@ -0,0 +1,349 @@
/*
* Disk Array driver for Compaq SMART2 Controllers
* Copyright 1998 Compaq Computer Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
*
*/
#ifndef ARRAYCMD_H
#define ARRAYCMD_H
#include <asm/types.h>
#if 0
#include <linux/blkdev.h>
#endif
/* for the Smart Array 42XX cards */
#define S42XX_REQUEST_PORT_OFFSET 0x40
#define S42XX_REPLY_INTR_MASK_OFFSET 0x34
#define S42XX_REPLY_PORT_OFFSET 0x44
#define S42XX_INTR_STATUS 0x30
#define S42XX_INTR_OFF 0x08
#define S42XX_INTR_PENDING 0x08
#define COMMAND_FIFO 0x04
#define COMMAND_COMPLETE_FIFO 0x08
#define INTR_MASK 0x0C
#define INTR_STATUS 0x10
#define INTR_PENDING 0x14
#define FIFO_NOT_EMPTY 0x01
#define FIFO_NOT_FULL 0x02
#define BIG_PROBLEM 0x40
#define LOG_NOT_CONF 2
#pragma pack(1)
typedef struct {
__u32 size;
__u32 addr;
} sg_t;
#define RCODE_NONFATAL 0x02
#define RCODE_FATAL 0x04
#define RCODE_INVREQ 0x10
typedef struct {
__u16 next;
__u8 cmd;
__u8 rcode;
__u32 blk;
__u16 blk_cnt;
__u8 sg_cnt;
__u8 reserved;
} rhdr_t;
#define SG_MAX 32
typedef struct {
rhdr_t hdr;
sg_t sg[SG_MAX];
__u32 bp;
} rblk_t;
typedef struct {
__u8 unit;
__u8 prio;
__u16 size;
} chdr_t;
#define CMD_RWREQ 0x00
#define CMD_IOCTL_PEND 0x01
#define CMD_IOCTL_DONE 0x02
typedef struct cmdlist {
chdr_t hdr;
rblk_t req;
__u32 size;
int retry_cnt;
__u32 busaddr;
int ctlr;
struct cmdlist *prev;
struct cmdlist *next;
struct request *rq;
int type;
} cmdlist_t;
#define ID_CTLR 0x11
typedef struct {
__u8 nr_drvs;
__u32 cfg_sig;
__u8 firm_rev[4];
__u8 rom_rev[4];
__u8 hw_rev;
__u32 bb_rev;
__u32 drv_present_map;
__u32 ext_drv_map;
__u32 board_id;
__u8 cfg_error;
__u32 non_disk_bits;
__u8 bad_ram_addr;
__u8 cpu_rev;
__u8 pdpi_rev;
__u8 epic_rev;
__u8 wcxc_rev;
__u8 marketing_rev;
__u8 ctlr_flags;
__u8 host_flags;
__u8 expand_dis;
__u8 scsi_chips;
__u32 max_req_blocks;
__u32 ctlr_clock;
__u8 drvs_per_bus;
__u16 big_drv_present_map[8];
__u16 big_ext_drv_map[8];
__u16 big_non_disk_map[8];
__u16 task_flags;
__u8 icl_bus;
__u8 red_modes;
__u8 cur_red_mode;
__u8 red_ctlr_stat;
__u8 red_fail_reason;
__u8 reserved[403];
} id_ctlr_t;
typedef struct {
__u16 cyl;
__u8 heads;
__u8 xsig;
__u8 psectors;
__u16 wpre;
__u8 maxecc;
__u8 drv_ctrl;
__u16 pcyls;
__u8 pheads;
__u16 landz;
__u8 sect_per_track;
__u8 cksum;
} drv_param_t;
#define ID_LOG_DRV 0x10
typedef struct {
__u16 blk_size;
__u32 nr_blks;
drv_param_t drv;
__u8 fault_tol;
__u8 reserved;
__u8 bios_disable;
} id_log_drv_t;
#define ID_LOG_DRV_EXT 0x18
typedef struct {
__u32 log_drv_id;
__u8 log_drv_label[64];
__u8 reserved[418];
} id_log_drv_ext_t;
#define SENSE_LOG_DRV_STAT 0x12
typedef struct {
__u8 status;
__u32 fail_map;
__u16 read_err[32];
__u16 write_err[32];
__u8 drv_err_data[256];
__u8 drq_timeout[32];
__u32 blks_to_recover;
__u8 drv_recovering;
__u16 remap_cnt[32];
__u32 replace_drv_map;
__u32 act_spare_map;
__u8 spare_stat;
__u8 spare_repl_map[32];
__u32 repl_ok_map;
__u8 media_exch;
__u8 cache_fail;
__u8 expn_fail;
__u8 unit_flags;
__u16 big_fail_map[8];
__u16 big_remap_map[128];
__u16 big_repl_map[8];
__u16 big_act_spare_map[8];
__u8 big_spar_repl_map[128];
__u16 big_repl_ok_map[8];
__u8 big_drv_rebuild;
__u8 reserved[36];
} sense_log_drv_stat_t;
#define START_RECOVER 0x13
#define ID_PHYS_DRV 0x15
typedef struct {
__u8 scsi_bus;
__u8 scsi_id;
__u16 blk_size;
__u32 nr_blks;
__u32 rsvd_blks;
__u8 drv_model[40];
__u8 drv_sn[40];
__u8 drv_fw[8];
__u8 scsi_iq_bits;
__u8 compaq_drv_stmp;
__u8 last_fail;
__u8 phys_drv_flags;
__u8 phys_drv_flags1;
__u8 scsi_lun;
__u8 phys_drv_flags2;
__u8 reserved;
__u32 spi_speed_rules;
__u8 phys_connector[2];
__u8 phys_box_on_bus;
__u8 phys_bay_in_box;
} id_phys_drv_t;
#define BLINK_DRV_LEDS 0x16
typedef struct {
__u32 blink_duration;
__u32 reserved;
__u8 blink[256];
__u8 reserved1[248];
} blink_drv_leds_t;
#define SENSE_BLINK_LEDS 0x17
typedef struct {
__u32 blink_duration;
__u32 btime_elap;
__u8 blink[256];
__u8 reserved1[248];
} sense_blink_leds_t;
#define IDA_READ 0x20
#define IDA_WRITE 0x30
#define IDA_WRITE_MEDIA 0x31
#define RESET_TO_DIAG 0x40
#define DIAG_PASS_THRU 0x41
#define SENSE_CONFIG 0x50
#define SET_CONFIG 0x51
typedef struct {
__u32 cfg_sig;
__u16 compat_port;
__u8 data_dist_mode;
__u8 surf_an_ctrl;
__u16 ctlr_phys_drv;
__u16 log_unit_phys_drv;
__u16 fault_tol_mode;
__u8 phys_drv_param[16];
drv_param_t drv;
__u32 drv_asgn_map;
__u16 dist_factor;
__u32 spare_asgn_map;
__u8 reserved[6];
__u16 os;
__u8 ctlr_order;
__u8 extra_info;
__u32 data_offs;
__u8 parity_backedout_write_drvs;
__u8 parity_dist_mode;
__u8 parity_shift_fact;
__u8 bios_disable_flag;
__u32 blks_on_vol;
__u32 blks_per_drv;
__u8 scratch[16];
__u16 big_drv_map[8];
__u16 big_spare_map[8];
__u8 ss_source_vol;
__u8 mix_drv_cap_range;
struct {
__u16 big_drv_map[8];
__u32 blks_per_drv;
__u16 fault_tol_mode;
__u16 dist_factor;
} MDC_range[4];
__u8 reserved1[248];
} config_t;
#define BYPASS_VOL_STATE 0x52
#define SS_CREATE_VOL 0x53
#define CHANGE_CONFIG 0x54
#define SENSE_ORIG_CONF 0x55
#define REORDER_LOG_DRV 0x56
typedef struct {
__u8 old_units[32];
} reorder_log_drv_t;
#define LABEL_LOG_DRV 0x57
typedef struct {
__u8 log_drv_label[64];
} label_log_drv_t;
#define SS_TO_VOL 0x58
#define SET_SURF_DELAY 0x60
typedef struct {
__u16 delay;
__u8 reserved[510];
} surf_delay_t;
#define SET_OVERHEAT_DELAY 0x61
typedef struct {
__u16 delay;
} overhead_delay_t;
#define SET_MP_DELAY
typedef struct {
__u16 delay;
__u8 reserved[510];
} mp_delay_t;
#define PASSTHRU_A 0x91
typedef struct {
__u8 target;
__u8 bus;
__u8 lun;
__u32 timeout;
__u32 flags;
__u8 status;
__u8 error;
__u8 cdb_len;
__u8 sense_error;
__u8 sense_key;
__u32 sense_info;
__u8 sense_code;
__u8 sense_qual;
__u32 residual;
__u8 reserved[4];
__u8 cdb[12];
} scsi_param_t;
#define RESUME_BACKGROUND_ACTIVITY 0x99
#define SENSE_CONTROLLER_PERFORMANCE 0xa8
#define FLUSH_CACHE 0xc2
#define COLLECT_BUFFER 0xd2
#define READ_FLASH_ROM 0xf6
#define WRITE_FLASH_ROM 0xf7
#pragma pack()
#endif /* ARRAYCMD_H */

87
drivers/block/ida_ioctl.h Normal file
View File

@@ -0,0 +1,87 @@
/*
* Disk Array driver for Compaq SMART2 Controllers
* Copyright 1998 Compaq Computer Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
*
*/
#ifndef IDA_IOCTL_H
#define IDA_IOCTL_H
#include "ida_cmd.h"
#include "cpqarray.h"
#define IDAGETDRVINFO 0x27272828
#define IDAPASSTHRU 0x28282929
#define IDAGETCTLRSIG 0x29293030
#define IDAREVALIDATEVOLS 0x30303131
#define IDADRIVERVERSION 0x31313232
#define IDAGETPCIINFO 0x32323333
typedef struct _ida_pci_info_struct
{
unsigned char bus;
unsigned char dev_fn;
__u32 board_id;
} ida_pci_info_struct;
/*
* Normally, the ioctl determines the logical unit for this command by
* the major,minor number of the fd passed to ioctl. If you need to send
* a command to a different/nonexistant unit (such as during config), you
* can override the normal behavior by setting the unit valid bit. (Normally,
* it should be zero) The controller the command is sent to is still
* determined by the major number of the open device.
*/
#define UNITVALID 0x80
typedef struct {
__u8 cmd;
__u8 rcode;
__u8 unit;
__u32 blk;
__u16 blk_cnt;
/* currently, sg_cnt is assumed to be 1: only the 0th element of sg is used */
struct {
void __user *addr;
size_t size;
} sg[SG_MAX];
int sg_cnt;
union ctlr_cmds {
drv_info_t drv;
unsigned char buf[1024];
id_ctlr_t id_ctlr;
drv_param_t drv_param;
id_log_drv_t id_log_drv;
id_log_drv_ext_t id_log_drv_ext;
sense_log_drv_stat_t sense_log_drv_stat;
id_phys_drv_t id_phys_drv;
blink_drv_leds_t blink_drv_leds;
sense_blink_leds_t sense_blink_leds;
config_t config;
reorder_log_drv_t reorder_log_drv;
label_log_drv_t label_log_drv;
surf_delay_t surf_delay;
overhead_delay_t overhead_delay;
mp_delay_t mp_delay;
scsi_param_t scsi_param;
} c;
} ida_ioctl_t;
#endif /* IDA_IOCTL_H */

1499
drivers/block/loop.c Normal file

File diff suppressed because it is too large Load Diff

719
drivers/block/nbd.c Normal file
View File

@@ -0,0 +1,719 @@
/*
* Network block device - make block devices work over TCP
*
* Note that you can not swap over this thing, yet. Seems to work but
* deadlocks sometimes - you can not swap over TCP in general.
*
* Copyright 1997-2000 Pavel Machek <pavel@ucw.cz>
* Parts copyright 2001 Steven Whitehouse <steve@chygwyn.com>
*
* This file is released under GPLv2 or later.
*
* (part of code stolen from loop.c)
*/
#include <linux/major.h>
#include <linux/blkdev.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/bio.h>
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/file.h>
#include <linux/ioctl.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/types.h>
#include <linux/nbd.h>
#define LO_MAGIC 0x68797548
#ifdef NDEBUG
#define dprintk(flags, fmt...)
#else /* NDEBUG */
#define dprintk(flags, fmt...) do { \
if (debugflags & (flags)) printk(KERN_DEBUG fmt); \
} while (0)
#define DBG_IOCTL 0x0004
#define DBG_INIT 0x0010
#define DBG_EXIT 0x0020
#define DBG_BLKDEV 0x0100
#define DBG_RX 0x0200
#define DBG_TX 0x0400
static unsigned int debugflags;
#endif /* NDEBUG */
static unsigned int nbds_max = 16;
static struct nbd_device nbd_dev[MAX_NBD];
/*
* Use just one lock (or at most 1 per NIC). Two arguments for this:
* 1. Each NIC is essentially a synchronization point for all servers
* accessed through that NIC so there's no need to have more locks
* than NICs anyway.
* 2. More locks lead to more "Dirty cache line bouncing" which will slow
* down each lock to the point where they're actually slower than just
* a single lock.
* Thanks go to Jens Axboe and Al Viro for their LKML emails explaining this!
*/
static DEFINE_SPINLOCK(nbd_lock);
#ifndef NDEBUG
static const char *ioctl_cmd_to_ascii(int cmd)
{
switch (cmd) {
case NBD_SET_SOCK: return "set-sock";
case NBD_SET_BLKSIZE: return "set-blksize";
case NBD_SET_SIZE: return "set-size";
case NBD_DO_IT: return "do-it";
case NBD_CLEAR_SOCK: return "clear-sock";
case NBD_CLEAR_QUE: return "clear-que";
case NBD_PRINT_DEBUG: return "print-debug";
case NBD_SET_SIZE_BLOCKS: return "set-size-blocks";
case NBD_DISCONNECT: return "disconnect";
case BLKROSET: return "set-read-only";
case BLKFLSBUF: return "flush-buffer-cache";
}
return "unknown";
}
static const char *nbdcmd_to_ascii(int cmd)
{
switch (cmd) {
case NBD_CMD_READ: return "read";
case NBD_CMD_WRITE: return "write";
case NBD_CMD_DISC: return "disconnect";
}
return "invalid";
}
#endif /* NDEBUG */
static void nbd_end_request(struct request *req)
{
int uptodate = (req->errors == 0) ? 1 : 0;
request_queue_t *q = req->q;
unsigned long flags;
dprintk(DBG_BLKDEV, "%s: request %p: %s\n", req->rq_disk->disk_name,
req, uptodate? "done": "failed");
spin_lock_irqsave(q->queue_lock, flags);
if (!end_that_request_first(req, uptodate, req->nr_sectors)) {
end_that_request_last(req, uptodate);
}
spin_unlock_irqrestore(q->queue_lock, flags);
}
/*
* Send or receive packet.
*/
static int sock_xmit(struct socket *sock, int send, void *buf, int size,
int msg_flags)
{
int result;
struct msghdr msg;
struct kvec iov;
unsigned long flags;
sigset_t oldset;
/* Allow interception of SIGKILL only
* Don't allow other signals to interrupt the transmission */
spin_lock_irqsave(&current->sighand->siglock, flags);
oldset = current->blocked;
sigfillset(&current->blocked);
sigdelsetmask(&current->blocked, sigmask(SIGKILL));
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, flags);
do {
sock->sk->sk_allocation = GFP_NOIO;
iov.iov_base = buf;
iov.iov_len = size;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = msg_flags | MSG_NOSIGNAL;
if (send)
result = kernel_sendmsg(sock, &msg, &iov, 1, size);
else
result = kernel_recvmsg(sock, &msg, &iov, 1, size, 0);
if (signal_pending(current)) {
siginfo_t info;
spin_lock_irqsave(&current->sighand->siglock, flags);
printk(KERN_WARNING "nbd (pid %d: %s) got signal %d\n",
current->pid, current->comm,
dequeue_signal(current, &current->blocked, &info));
spin_unlock_irqrestore(&current->sighand->siglock, flags);
result = -EINTR;
break;
}
if (result <= 0) {
if (result == 0)
result = -EPIPE; /* short read */
break;
}
size -= result;
buf += result;
} while (size > 0);
spin_lock_irqsave(&current->sighand->siglock, flags);
current->blocked = oldset;
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, flags);
return result;
}
static inline int sock_send_bvec(struct socket *sock, struct bio_vec *bvec,
int flags)
{
int result;
void *kaddr = kmap(bvec->bv_page);
result = sock_xmit(sock, 1, kaddr + bvec->bv_offset, bvec->bv_len,
flags);
kunmap(bvec->bv_page);
return result;
}
static int nbd_send_req(struct nbd_device *lo, struct request *req)
{
int result, i, flags;
struct nbd_request request;
unsigned long size = req->nr_sectors << 9;
struct socket *sock = lo->sock;
request.magic = htonl(NBD_REQUEST_MAGIC);
request.type = htonl(nbd_cmd(req));
request.from = cpu_to_be64((u64) req->sector << 9);
request.len = htonl(size);
memcpy(request.handle, &req, sizeof(req));
dprintk(DBG_TX, "%s: request %p: sending control (%s@%llu,%luB)\n",
lo->disk->disk_name, req,
nbdcmd_to_ascii(nbd_cmd(req)),
(unsigned long long)req->sector << 9,
req->nr_sectors << 9);
result = sock_xmit(sock, 1, &request, sizeof(request),
(nbd_cmd(req) == NBD_CMD_WRITE)? MSG_MORE: 0);
if (result <= 0) {
printk(KERN_ERR "%s: Send control failed (result %d)\n",
lo->disk->disk_name, result);
goto error_out;
}
if (nbd_cmd(req) == NBD_CMD_WRITE) {
struct bio *bio;
/*
* we are really probing at internals to determine
* whether to set MSG_MORE or not...
*/
rq_for_each_bio(bio, req) {
struct bio_vec *bvec;
bio_for_each_segment(bvec, bio, i) {
flags = 0;
if ((i < (bio->bi_vcnt - 1)) || bio->bi_next)
flags = MSG_MORE;
dprintk(DBG_TX, "%s: request %p: sending %d bytes data\n",
lo->disk->disk_name, req,
bvec->bv_len);
result = sock_send_bvec(sock, bvec, flags);
if (result <= 0) {
printk(KERN_ERR "%s: Send data failed (result %d)\n",
lo->disk->disk_name,
result);
goto error_out;
}
}
}
}
return 0;
error_out:
return 1;
}
static struct request *nbd_find_request(struct nbd_device *lo, char *handle)
{
struct request *req;
struct list_head *tmp;
struct request *xreq;
int err;
memcpy(&xreq, handle, sizeof(xreq));
err = wait_event_interruptible(lo->active_wq, lo->active_req != xreq);
if (unlikely(err))
goto out;
spin_lock(&lo->queue_lock);
list_for_each(tmp, &lo->queue_head) {
req = list_entry(tmp, struct request, queuelist);
if (req != xreq)
continue;
list_del_init(&req->queuelist);
spin_unlock(&lo->queue_lock);
return req;
}
spin_unlock(&lo->queue_lock);
err = -ENOENT;
out:
return ERR_PTR(err);
}
static inline int sock_recv_bvec(struct socket *sock, struct bio_vec *bvec)
{
int result;
void *kaddr = kmap(bvec->bv_page);
result = sock_xmit(sock, 0, kaddr + bvec->bv_offset, bvec->bv_len,
MSG_WAITALL);
kunmap(bvec->bv_page);
return result;
}
/* NULL returned = something went wrong, inform userspace */
static struct request *nbd_read_stat(struct nbd_device *lo)
{
int result;
struct nbd_reply reply;
struct request *req;
struct socket *sock = lo->sock;
reply.magic = 0;
result = sock_xmit(sock, 0, &reply, sizeof(reply), MSG_WAITALL);
if (result <= 0) {
printk(KERN_ERR "%s: Receive control failed (result %d)\n",
lo->disk->disk_name, result);
goto harderror;
}
if (ntohl(reply.magic) != NBD_REPLY_MAGIC) {
printk(KERN_ERR "%s: Wrong magic (0x%lx)\n",
lo->disk->disk_name,
(unsigned long)ntohl(reply.magic));
result = -EPROTO;
goto harderror;
}
req = nbd_find_request(lo, reply.handle);
if (unlikely(IS_ERR(req))) {
result = PTR_ERR(req);
if (result != -ENOENT)
goto harderror;
printk(KERN_ERR "%s: Unexpected reply (%p)\n",
lo->disk->disk_name, reply.handle);
result = -EBADR;
goto harderror;
}
if (ntohl(reply.error)) {
printk(KERN_ERR "%s: Other side returned error (%d)\n",
lo->disk->disk_name, ntohl(reply.error));
req->errors++;
return req;
}
dprintk(DBG_RX, "%s: request %p: got reply\n",
lo->disk->disk_name, req);
if (nbd_cmd(req) == NBD_CMD_READ) {
int i;
struct bio *bio;
rq_for_each_bio(bio, req) {
struct bio_vec *bvec;
bio_for_each_segment(bvec, bio, i) {
result = sock_recv_bvec(sock, bvec);
if (result <= 0) {
printk(KERN_ERR "%s: Receive data failed (result %d)\n",
lo->disk->disk_name,
result);
req->errors++;
return req;
}
dprintk(DBG_RX, "%s: request %p: got %d bytes data\n",
lo->disk->disk_name, req, bvec->bv_len);
}
}
}
return req;
harderror:
lo->harderror = result;
return NULL;
}
static ssize_t pid_show(struct gendisk *disk, char *page)
{
return sprintf(page, "%ld\n",
(long) ((struct nbd_device *)disk->private_data)->pid);
}
static struct disk_attribute pid_attr = {
.attr = { .name = "pid", .mode = S_IRUGO },
.show = pid_show,
};
static void nbd_do_it(struct nbd_device *lo)
{
struct request *req;
BUG_ON(lo->magic != LO_MAGIC);
lo->pid = current->pid;
sysfs_create_file(&lo->disk->kobj, &pid_attr.attr);
while ((req = nbd_read_stat(lo)) != NULL)
nbd_end_request(req);
sysfs_remove_file(&lo->disk->kobj, &pid_attr.attr);
return;
}
static void nbd_clear_que(struct nbd_device *lo)
{
struct request *req;
BUG_ON(lo->magic != LO_MAGIC);
/*
* Because we have set lo->sock to NULL under the tx_lock, all
* modifications to the list must have completed by now. For
* the same reason, the active_req must be NULL.
*
* As a consequence, we don't need to take the spin lock while
* purging the list here.
*/
BUG_ON(lo->sock);
BUG_ON(lo->active_req);
while (!list_empty(&lo->queue_head)) {
req = list_entry(lo->queue_head.next, struct request,
queuelist);
list_del_init(&req->queuelist);
req->errors++;
nbd_end_request(req);
}
}
/*
* We always wait for result of write, for now. It would be nice to make it optional
* in future
* if ((req->cmd == WRITE) && (lo->flags & NBD_WRITE_NOCHK))
* { printk( "Warning: Ignoring result!\n"); nbd_end_request( req ); }
*/
static void do_nbd_request(request_queue_t * q)
{
struct request *req;
while ((req = elv_next_request(q)) != NULL) {
struct nbd_device *lo;
blkdev_dequeue_request(req);
dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%x)\n",
req->rq_disk->disk_name, req, req->cmd_type);
if (!blk_fs_request(req))
goto error_out;
lo = req->rq_disk->private_data;
BUG_ON(lo->magic != LO_MAGIC);
nbd_cmd(req) = NBD_CMD_READ;
if (rq_data_dir(req) == WRITE) {
nbd_cmd(req) = NBD_CMD_WRITE;
if (lo->flags & NBD_READ_ONLY) {
printk(KERN_ERR "%s: Write on read-only\n",
lo->disk->disk_name);
goto error_out;
}
}
req->errors = 0;
spin_unlock_irq(q->queue_lock);
mutex_lock(&lo->tx_lock);
if (unlikely(!lo->sock)) {
mutex_unlock(&lo->tx_lock);
printk(KERN_ERR "%s: Attempted send on closed socket\n",
lo->disk->disk_name);
req->errors++;
nbd_end_request(req);
spin_lock_irq(q->queue_lock);
continue;
}
lo->active_req = req;
if (nbd_send_req(lo, req) != 0) {
printk(KERN_ERR "%s: Request send failed\n",
lo->disk->disk_name);
req->errors++;
nbd_end_request(req);
} else {
spin_lock(&lo->queue_lock);
list_add(&req->queuelist, &lo->queue_head);
spin_unlock(&lo->queue_lock);
}
lo->active_req = NULL;
mutex_unlock(&lo->tx_lock);
wake_up_all(&lo->active_wq);
spin_lock_irq(q->queue_lock);
continue;
error_out:
req->errors++;
spin_unlock(q->queue_lock);
nbd_end_request(req);
spin_lock(q->queue_lock);
}
return;
}
static int nbd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct nbd_device *lo = inode->i_bdev->bd_disk->private_data;
int error;
struct request sreq ;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
BUG_ON(lo->magic != LO_MAGIC);
/* Anyone capable of this syscall can do *real bad* things */
dprintk(DBG_IOCTL, "%s: nbd_ioctl cmd=%s(0x%x) arg=%lu\n",
lo->disk->disk_name, ioctl_cmd_to_ascii(cmd), cmd, arg);
switch (cmd) {
case NBD_DISCONNECT:
printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name);
sreq.cmd_type = REQ_TYPE_SPECIAL;
nbd_cmd(&sreq) = NBD_CMD_DISC;
/*
* Set these to sane values in case server implementation
* fails to check the request type first and also to keep
* debugging output cleaner.
*/
sreq.sector = 0;
sreq.nr_sectors = 0;
if (!lo->sock)
return -EINVAL;
nbd_send_req(lo, &sreq);
return 0;
case NBD_CLEAR_SOCK:
error = 0;
mutex_lock(&lo->tx_lock);
lo->sock = NULL;
mutex_unlock(&lo->tx_lock);
file = lo->file;
lo->file = NULL;
nbd_clear_que(lo);
BUG_ON(!list_empty(&lo->queue_head));
if (file)
fput(file);
return error;
case NBD_SET_SOCK:
if (lo->file)
return -EBUSY;
error = -EINVAL;
file = fget(arg);
if (file) {
inode = file->f_path.dentry->d_inode;
if (S_ISSOCK(inode->i_mode)) {
lo->file = file;
lo->sock = SOCKET_I(inode);
error = 0;
} else {
fput(file);
}
}
return error;
case NBD_SET_BLKSIZE:
lo->blksize = arg;
lo->bytesize &= ~(lo->blksize-1);
inode->i_bdev->bd_inode->i_size = lo->bytesize;
set_blocksize(inode->i_bdev, lo->blksize);
set_capacity(lo->disk, lo->bytesize >> 9);
return 0;
case NBD_SET_SIZE:
lo->bytesize = arg & ~(lo->blksize-1);
inode->i_bdev->bd_inode->i_size = lo->bytesize;
set_blocksize(inode->i_bdev, lo->blksize);
set_capacity(lo->disk, lo->bytesize >> 9);
return 0;
case NBD_SET_SIZE_BLOCKS:
lo->bytesize = ((u64) arg) * lo->blksize;
inode->i_bdev->bd_inode->i_size = lo->bytesize;
set_blocksize(inode->i_bdev, lo->blksize);
set_capacity(lo->disk, lo->bytesize >> 9);
return 0;
case NBD_DO_IT:
if (!lo->file)
return -EINVAL;
nbd_do_it(lo);
/* on return tidy up in case we have a signal */
/* Forcibly shutdown the socket causing all listeners
* to error
*
* FIXME: This code is duplicated from sys_shutdown, but
* there should be a more generic interface rather than
* calling socket ops directly here */
mutex_lock(&lo->tx_lock);
if (lo->sock) {
printk(KERN_WARNING "%s: shutting down socket\n",
lo->disk->disk_name);
lo->sock->ops->shutdown(lo->sock,
SEND_SHUTDOWN|RCV_SHUTDOWN);
lo->sock = NULL;
}
mutex_unlock(&lo->tx_lock);
file = lo->file;
lo->file = NULL;
nbd_clear_que(lo);
printk(KERN_WARNING "%s: queue cleared\n", lo->disk->disk_name);
if (file)
fput(file);
return lo->harderror;
case NBD_CLEAR_QUE:
/*
* This is for compatibility only. The queue is always cleared
* by NBD_DO_IT or NBD_CLEAR_SOCK.
*/
BUG_ON(!lo->sock && !list_empty(&lo->queue_head));
return 0;
case NBD_PRINT_DEBUG:
printk(KERN_INFO "%s: next = %p, prev = %p, head = %p\n",
inode->i_bdev->bd_disk->disk_name,
lo->queue_head.next, lo->queue_head.prev,
&lo->queue_head);
return 0;
}
return -EINVAL;
}
static struct block_device_operations nbd_fops =
{
.owner = THIS_MODULE,
.ioctl = nbd_ioctl,
};
/*
* And here should be modules and kernel interface
* (Just smiley confuses emacs :-)
*/
static int __init nbd_init(void)
{
int err = -ENOMEM;
int i;
BUILD_BUG_ON(sizeof(struct nbd_request) != 28);
if (nbds_max > MAX_NBD) {
printk(KERN_CRIT "nbd: cannot allocate more than %u nbds; %u requested.\n", MAX_NBD,
nbds_max);
return -EINVAL;
}
for (i = 0; i < nbds_max; i++) {
struct gendisk *disk = alloc_disk(1);
if (!disk)
goto out;
nbd_dev[i].disk = disk;
/*
* The new linux 2.5 block layer implementation requires
* every gendisk to have its very own request_queue struct.
* These structs are big so we dynamically allocate them.
*/
disk->queue = blk_init_queue(do_nbd_request, &nbd_lock);
if (!disk->queue) {
put_disk(disk);
goto out;
}
}
if (register_blkdev(NBD_MAJOR, "nbd")) {
err = -EIO;
goto out;
}
printk(KERN_INFO "nbd: registered device at major %d\n", NBD_MAJOR);
dprintk(DBG_INIT, "nbd: debugflags=0x%x\n", debugflags);
for (i = 0; i < nbds_max; i++) {
struct gendisk *disk = nbd_dev[i].disk;
nbd_dev[i].file = NULL;
nbd_dev[i].magic = LO_MAGIC;
nbd_dev[i].flags = 0;
spin_lock_init(&nbd_dev[i].queue_lock);
INIT_LIST_HEAD(&nbd_dev[i].queue_head);
mutex_init(&nbd_dev[i].tx_lock);
init_waitqueue_head(&nbd_dev[i].active_wq);
nbd_dev[i].blksize = 1024;
nbd_dev[i].bytesize = 0x7ffffc00ULL << 10; /* 2TB */
disk->major = NBD_MAJOR;
disk->first_minor = i;
disk->fops = &nbd_fops;
disk->private_data = &nbd_dev[i];
disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
sprintf(disk->disk_name, "nbd%d", i);
set_capacity(disk, 0x7ffffc00ULL << 1); /* 2 TB */
add_disk(disk);
}
return 0;
out:
while (i--) {
blk_cleanup_queue(nbd_dev[i].disk->queue);
put_disk(nbd_dev[i].disk);
}
return err;
}
static void __exit nbd_cleanup(void)
{
int i;
for (i = 0; i < nbds_max; i++) {
struct gendisk *disk = nbd_dev[i].disk;
nbd_dev[i].magic = 0;
if (disk) {
del_gendisk(disk);
blk_cleanup_queue(disk->queue);
put_disk(disk);
}
}
unregister_blkdev(NBD_MAJOR, "nbd");
printk(KERN_INFO "nbd: unregistered device at major %d\n", NBD_MAJOR);
}
module_init(nbd_init);
module_exit(nbd_cleanup);
MODULE_DESCRIPTION("Network Block Device");
MODULE_LICENSE("GPL");
module_param(nbds_max, int, 0444);
MODULE_PARM_DESC(nbds_max, "How many network block devices to initialize.");
#ifndef NDEBUG
module_param(debugflags, int, 0644);
MODULE_PARM_DESC(debugflags, "flags for controlling debug output");
#endif

View 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.
#

View 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

View 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
View 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
View 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)

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)

View 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);

View 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
View 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
View 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
View 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
View 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)

View 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);
}
//***************************************************************************

View 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

File diff suppressed because it is too large Load Diff

3129
drivers/block/pktcdvd.c Normal file

File diff suppressed because it is too large Load Diff

1079
drivers/block/ps2esdi.c Normal file

File diff suppressed because it is too large Load Diff

510
drivers/block/rd.c Normal file
View File

@@ -0,0 +1,510 @@
/*
* ramdisk.c - Multiple RAM disk driver - gzip-loading version - v. 0.8 beta.
*
* (C) Chad Page, Theodore Ts'o, et. al, 1995.
*
* This RAM disk is designed to have filesystems created on it and mounted
* just like a regular floppy disk.
*
* It also does something suggested by Linus: use the buffer cache as the
* RAM disk data. This makes it possible to dynamically allocate the RAM disk
* buffer - with some consequences I have to deal with as I write this.
*
* This code is based on the original ramdisk.c, written mostly by
* Theodore Ts'o (TYT) in 1991. The code was largely rewritten by
* Chad Page to use the buffer cache to store the RAM disk data in
* 1995; Theodore then took over the driver again, and cleaned it up
* for inclusion in the mainline kernel.
*
* The original CRAMDISK code was written by Richard Lyons, and
* adapted by Chad Page to use the new RAM disk interface. Theodore
* Ts'o rewrote it so that both the compressed RAM disk loader and the
* kernel decompressor uses the same inflate.c codebase. The RAM disk
* loader now also loads into a dynamic (buffer cache based) RAM disk,
* not the old static RAM disk. Support for the old static RAM disk has
* been completely removed.
*
* Loadable module support added by Tom Dyas.
*
* Further cleanups by Chad Page (page0588@sundance.sjsu.edu):
* Cosmetic changes in #ifdef MODULE, code movement, etc.
* When the RAM disk module is removed, free the protected buffers
* Default RAM disk size changed to 2.88 MB
*
* Added initrd: Werner Almesberger & Hans Lermen, Feb '96
*
* 4/25/96 : Made RAM disk size a parameter (default is now 4 MB)
* - Chad Page
*
* Add support for fs images split across >1 disk, Paul Gortmaker, Mar '98
*
* Make block size and block size shift for RAM disks a global macro
* and set blk_size for -ENOSPC, Werner Fink <werner@suse.de>, Apr '99
*/
#include <linux/string.h>
#include <linux/slab.h>
#include <asm/atomic.h>
#include <linux/bio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <linux/buffer_head.h> /* for invalidate_bdev() */
#include <linux/backing-dev.h>
#include <linux/blkpg.h>
#include <linux/writeback.h>
#include <asm/uaccess.h>
/* Various static variables go here. Most are used only in the RAM disk code.
*/
static struct gendisk *rd_disks[CONFIG_BLK_DEV_RAM_COUNT];
static struct block_device *rd_bdev[CONFIG_BLK_DEV_RAM_COUNT];/* Protected device data */
static struct request_queue *rd_queue[CONFIG_BLK_DEV_RAM_COUNT];
/*
* Parameters for the boot-loading of the RAM disk. These are set by
* init/main.c (from arguments to the kernel command line) or from the
* architecture-specific setup routine (from the stored boot sector
* information).
*/
int rd_size = CONFIG_BLK_DEV_RAM_SIZE; /* Size of the RAM disks */
/*
* It would be very desirable to have a soft-blocksize (that in the case
* of the ramdisk driver is also the hardblocksize ;) of PAGE_SIZE because
* doing that we'll achieve a far better MM footprint. Using a rd_blocksize of
* BLOCK_SIZE in the worst case we'll make PAGE_SIZE/BLOCK_SIZE buffer-pages
* unfreeable. With a rd_blocksize of PAGE_SIZE instead we are sure that only
* 1 page will be protected. Depending on the size of the ramdisk you
* may want to change the ramdisk blocksize to achieve a better or worse MM
* behaviour. The default is still BLOCK_SIZE (needed by rd_load_image that
* supposes the filesystem in the image uses a BLOCK_SIZE blocksize).
*/
static int rd_blocksize = CONFIG_BLK_DEV_RAM_BLOCKSIZE;
/*
* Copyright (C) 2000 Linus Torvalds.
* 2000 Transmeta Corp.
* aops copied from ramfs.
*/
/*
* If a ramdisk page has buffers, some may be uptodate and some may be not.
* To bring the page uptodate we zero out the non-uptodate buffers. The
* page must be locked.
*/
static void make_page_uptodate(struct page *page)
{
if (page_has_buffers(page)) {
struct buffer_head *bh = page_buffers(page);
struct buffer_head *head = bh;
do {
if (!buffer_uptodate(bh)) {
memset(bh->b_data, 0, bh->b_size);
/*
* akpm: I'm totally undecided about this. The
* buffer has just been magically brought "up to
* date", but nobody should want to be reading
* it anyway, because it hasn't been used for
* anything yet. It is still in a "not read
* from disk yet" state.
*
* But non-uptodate buffers against an uptodate
* page are against the rules. So do it anyway.
*/
set_buffer_uptodate(bh);
}
} while ((bh = bh->b_this_page) != head);
} else {
memset(page_address(page), 0, PAGE_CACHE_SIZE);
}
flush_dcache_page(page);
SetPageUptodate(page);
}
static int ramdisk_readpage(struct file *file, struct page *page)
{
if (!PageUptodate(page))
make_page_uptodate(page);
unlock_page(page);
return 0;
}
static int ramdisk_prepare_write(struct file *file, struct page *page,
unsigned offset, unsigned to)
{
if (!PageUptodate(page))
make_page_uptodate(page);
return 0;
}
static int ramdisk_commit_write(struct file *file, struct page *page,
unsigned offset, unsigned to)
{
set_page_dirty(page);
return 0;
}
/*
* ->writepage to the the blockdev's mapping has to redirty the page so that the
* VM doesn't go and steal it. We return AOP_WRITEPAGE_ACTIVATE so that the VM
* won't try to (pointlessly) write the page again for a while.
*
* Really, these pages should not be on the LRU at all.
*/
static int ramdisk_writepage(struct page *page, struct writeback_control *wbc)
{
if (!PageUptodate(page))
make_page_uptodate(page);
SetPageDirty(page);
if (wbc->for_reclaim)
return AOP_WRITEPAGE_ACTIVATE;
unlock_page(page);
return 0;
}
/*
* This is a little speedup thing: short-circuit attempts to write back the
* ramdisk blockdev inode to its non-existent backing store.
*/
static int ramdisk_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
return 0;
}
/*
* ramdisk blockdev pages have their own ->set_page_dirty() because we don't
* want them to contribute to dirty memory accounting.
*/
static int ramdisk_set_page_dirty(struct page *page)
{
if (!TestSetPageDirty(page))
return 1;
return 0;
}
static const struct address_space_operations ramdisk_aops = {
.readpage = ramdisk_readpage,
.prepare_write = ramdisk_prepare_write,
.commit_write = ramdisk_commit_write,
.writepage = ramdisk_writepage,
.set_page_dirty = ramdisk_set_page_dirty,
.writepages = ramdisk_writepages,
};
static int rd_blkdev_pagecache_IO(int rw, struct bio_vec *vec, sector_t sector,
struct address_space *mapping)
{
pgoff_t index = sector >> (PAGE_CACHE_SHIFT - 9);
unsigned int vec_offset = vec->bv_offset;
int offset = (sector << 9) & ~PAGE_CACHE_MASK;
int size = vec->bv_len;
int err = 0;
do {
int count;
struct page *page;
char *src;
char *dst;
count = PAGE_CACHE_SIZE - offset;
if (count > size)
count = size;
size -= count;
page = grab_cache_page(mapping, index);
if (!page) {
err = -ENOMEM;
goto out;
}
if (!PageUptodate(page))
make_page_uptodate(page);
index++;
if (rw == READ) {
src = kmap_atomic(page, KM_USER0) + offset;
dst = kmap_atomic(vec->bv_page, KM_USER1) + vec_offset;
} else {
src = kmap_atomic(vec->bv_page, KM_USER0) + vec_offset;
dst = kmap_atomic(page, KM_USER1) + offset;
}
offset = 0;
vec_offset += count;
memcpy(dst, src, count);
kunmap_atomic(src, KM_USER0);
kunmap_atomic(dst, KM_USER1);
if (rw == READ)
flush_dcache_page(vec->bv_page);
else
set_page_dirty(page);
unlock_page(page);
put_page(page);
} while (size);
out:
return err;
}
/*
* Basically, my strategy here is to set up a buffer-head which can't be
* deleted, and make that my Ramdisk. If the request is outside of the
* allocated size, we must get rid of it...
*
* 19-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Added devfs support
*
*/
static int rd_make_request(request_queue_t *q, struct bio *bio)
{
struct block_device *bdev = bio->bi_bdev;
struct address_space * mapping = bdev->bd_inode->i_mapping;
sector_t sector = bio->bi_sector;
unsigned long len = bio->bi_size >> 9;
int rw = bio_data_dir(bio);
struct bio_vec *bvec;
int ret = 0, i;
if (sector + len > get_capacity(bdev->bd_disk))
goto fail;
if (rw==READA)
rw=READ;
bio_for_each_segment(bvec, bio, i) {
ret |= rd_blkdev_pagecache_IO(rw, bvec, sector, mapping);
sector += bvec->bv_len >> 9;
}
if (ret)
goto fail;
bio_endio(bio, bio->bi_size, 0);
return 0;
fail:
bio_io_error(bio, bio->bi_size);
return 0;
}
static int rd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int error;
struct block_device *bdev = inode->i_bdev;
if (cmd != BLKFLSBUF)
return -ENOTTY;
/*
* special: we want to release the ramdisk memory, it's not like with
* the other blockdevices where this ioctl only flushes away the buffer
* cache
*/
error = -EBUSY;
mutex_lock(&bdev->bd_mutex);
if (bdev->bd_openers <= 2) {
truncate_inode_pages(bdev->bd_inode->i_mapping, 0);
error = 0;
}
mutex_unlock(&bdev->bd_mutex);
return error;
}
/*
* This is the backing_dev_info for the blockdev inode itself. It doesn't need
* writeback and it does not contribute to dirty memory accounting.
*/
static struct backing_dev_info rd_backing_dev_info = {
.ra_pages = 0, /* No readahead */
.capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK | BDI_CAP_MAP_COPY,
.unplug_io_fn = default_unplug_io_fn,
};
/*
* This is the backing_dev_info for the files which live atop the ramdisk
* "device". These files do need writeback and they do contribute to dirty
* memory accounting.
*/
static struct backing_dev_info rd_file_backing_dev_info = {
.ra_pages = 0, /* No readahead */
.capabilities = BDI_CAP_MAP_COPY, /* Does contribute to dirty memory */
.unplug_io_fn = default_unplug_io_fn,
};
static int rd_open(struct inode *inode, struct file *filp)
{
unsigned unit = iminor(inode);
if (rd_bdev[unit] == NULL) {
struct block_device *bdev = inode->i_bdev;
struct address_space *mapping;
unsigned bsize;
gfp_t gfp_mask;
inode = igrab(bdev->bd_inode);
rd_bdev[unit] = bdev;
bdev->bd_openers++;
bsize = bdev_hardsect_size(bdev);
bdev->bd_block_size = bsize;
inode->i_blkbits = blksize_bits(bsize);
inode->i_size = get_capacity(bdev->bd_disk)<<9;
mapping = inode->i_mapping;
mapping->a_ops = &ramdisk_aops;
mapping->backing_dev_info = &rd_backing_dev_info;
bdev->bd_inode_backing_dev_info = &rd_file_backing_dev_info;
/*
* Deep badness. rd_blkdev_pagecache_IO() needs to allocate
* pagecache pages within a request_fn. We cannot recur back
* into the filesytem which is mounted atop the ramdisk, because
* that would deadlock on fs locks. And we really don't want
* to reenter rd_blkdev_pagecache_IO when we're already within
* that function.
*
* So we turn off __GFP_FS and __GFP_IO.
*
* And to give this thing a hope of working, turn on __GFP_HIGH.
* Hopefully, there's enough regular memory allocation going on
* for the page allocator emergency pools to keep the ramdisk
* driver happy.
*/
gfp_mask = mapping_gfp_mask(mapping);
gfp_mask &= ~(__GFP_FS|__GFP_IO);
gfp_mask |= __GFP_HIGH;
mapping_set_gfp_mask(mapping, gfp_mask);
}
return 0;
}
static struct block_device_operations rd_bd_op = {
.owner = THIS_MODULE,
.open = rd_open,
.ioctl = rd_ioctl,
};
/*
* Before freeing the module, invalidate all of the protected buffers!
*/
static void __exit rd_cleanup(void)
{
int i;
for (i = 0; i < CONFIG_BLK_DEV_RAM_COUNT; i++) {
struct block_device *bdev = rd_bdev[i];
rd_bdev[i] = NULL;
if (bdev) {
invalidate_bdev(bdev, 1);
blkdev_put(bdev);
}
del_gendisk(rd_disks[i]);
put_disk(rd_disks[i]);
blk_cleanup_queue(rd_queue[i]);
}
unregister_blkdev(RAMDISK_MAJOR, "ramdisk");
}
/*
* This is the registration and initialization section of the RAM disk driver
*/
static int __init rd_init(void)
{
int i;
int err = -ENOMEM;
if (rd_blocksize > PAGE_SIZE || rd_blocksize < 512 ||
(rd_blocksize & (rd_blocksize-1))) {
printk("RAMDISK: wrong blocksize %d, reverting to defaults\n",
rd_blocksize);
rd_blocksize = BLOCK_SIZE;
}
for (i = 0; i < CONFIG_BLK_DEV_RAM_COUNT; i++) {
rd_disks[i] = alloc_disk(1);
if (!rd_disks[i])
goto out;
rd_queue[i] = blk_alloc_queue(GFP_KERNEL);
if (!rd_queue[i]) {
put_disk(rd_disks[i]);
goto out;
}
}
if (register_blkdev(RAMDISK_MAJOR, "ramdisk")) {
err = -EIO;
goto out;
}
for (i = 0; i < CONFIG_BLK_DEV_RAM_COUNT; i++) {
struct gendisk *disk = rd_disks[i];
blk_queue_make_request(rd_queue[i], &rd_make_request);
blk_queue_hardsect_size(rd_queue[i], rd_blocksize);
/* rd_size is given in kB */
disk->major = RAMDISK_MAJOR;
disk->first_minor = i;
disk->fops = &rd_bd_op;
disk->queue = rd_queue[i];
disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
sprintf(disk->disk_name, "ram%d", i);
set_capacity(disk, rd_size * 2);
add_disk(rd_disks[i]);
}
/* rd_size is given in kB */
printk("RAMDISK driver initialized: "
"%d RAM disks of %dK size %d blocksize\n",
CONFIG_BLK_DEV_RAM_COUNT, rd_size, rd_blocksize);
return 0;
out:
while (i--) {
put_disk(rd_disks[i]);
blk_cleanup_queue(rd_queue[i]);
}
return err;
}
module_init(rd_init);
module_exit(rd_cleanup);
/* options - nonmodular */
#ifndef MODULE
static int __init ramdisk_size(char *str)
{
rd_size = simple_strtol(str,NULL,0);
return 1;
}
static int __init ramdisk_size2(char *str) /* kludge */
{
return ramdisk_size(str);
}
static int __init ramdisk_blocksize(char *str)
{
rd_blocksize = simple_strtol(str,NULL,0);
return 1;
}
__setup("ramdisk=", ramdisk_size);
__setup("ramdisk_size=", ramdisk_size2);
__setup("ramdisk_blocksize=", ramdisk_blocksize);
#endif
/* options - modular */
module_param(rd_size, int, 0);
MODULE_PARM_DESC(rd_size, "Size of each RAM disk in kbytes.");
module_param(rd_blocksize, int, 0);
MODULE_PARM_DESC(rd_blocksize, "Blocksize of each RAM disk in bytes.");
MODULE_ALIAS_BLOCKDEV_MAJOR(RAMDISK_MAJOR);
MODULE_LICENSE("GPL");

278
drivers/block/smart1,2.h Normal file
View File

@@ -0,0 +1,278 @@
/*
* Disk Array driver for Compaq SMART2 Controllers
* Copyright 1998 Compaq Computer Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Questions/Comments/Bugfixes to iss_storagedev@hp.com
*
* If you want to make changes, improve or add functionality to this
* driver, you'll probably need the Compaq Array Controller Interface
* Specificiation (Document number ECG086/1198)
*/
/*
* This file contains the controller communication implementation for
* Compaq SMART-1 and SMART-2 controllers. To the best of my knowledge,
* this should support:
*
* PCI:
* SMART-2/P, SMART-2DH, SMART-2SL, SMART-221, SMART-3100ES, SMART-3200
* Integerated SMART Array Controller, SMART-4200, SMART-4250ES
*
* EISA:
* SMART-2/E, SMART, IAES, IDA-2, IDA
*/
/*
* Memory mapped FIFO interface (SMART 42xx cards)
*/
static void smart4_submit_command(ctlr_info_t *h, cmdlist_t *c)
{
writel(c->busaddr, h->vaddr + S42XX_REQUEST_PORT_OFFSET);
}
/*
* This card is the opposite of the other cards.
* 0 turns interrupts on...
* 0x08 turns them off...
*/
static void smart4_intr_mask(ctlr_info_t *h, unsigned long val)
{
if (val)
{ /* Turn interrupts on */
writel(0, h->vaddr + S42XX_REPLY_INTR_MASK_OFFSET);
} else /* Turn them off */
{
writel( S42XX_INTR_OFF,
h->vaddr + S42XX_REPLY_INTR_MASK_OFFSET);
}
}
/*
* For older cards FIFO Full = 0.
* On this card 0 means there is room, anything else FIFO Full.
*
*/
static unsigned long smart4_fifo_full(ctlr_info_t *h)
{
return (!readl(h->vaddr + S42XX_REQUEST_PORT_OFFSET));
}
/* This type of controller returns -1 if the fifo is empty,
* Not 0 like the others.
* And we need to let it know we read a value out
*/
static unsigned long smart4_completed(ctlr_info_t *h)
{
long register_value
= readl(h->vaddr + S42XX_REPLY_PORT_OFFSET);
/* Fifo is empty */
if( register_value == 0xffffffff)
return 0;
/* Need to let it know we got the reply */
/* We do this by writing a 0 to the port we just read from */
writel(0, h->vaddr + S42XX_REPLY_PORT_OFFSET);
return ((unsigned long) register_value);
}
/*
* This hardware returns interrupt pending at a different place and
* it does not tell us if the fifo is empty, we will have check
* that by getting a 0 back from the comamnd_completed call.
*/
static unsigned long smart4_intr_pending(ctlr_info_t *h)
{
unsigned long register_value =
readl(h->vaddr + S42XX_INTR_STATUS);
if( register_value & S42XX_INTR_PENDING)
return FIFO_NOT_EMPTY;
return 0 ;
}
static struct access_method smart4_access = {
smart4_submit_command,
smart4_intr_mask,
smart4_fifo_full,
smart4_intr_pending,
smart4_completed,
};
/*
* Memory mapped FIFO interface (PCI SMART2 and SMART 3xxx cards)
*/
static void smart2_submit_command(ctlr_info_t *h, cmdlist_t *c)
{
writel(c->busaddr, h->vaddr + COMMAND_FIFO);
}
static void smart2_intr_mask(ctlr_info_t *h, unsigned long val)
{
writel(val, h->vaddr + INTR_MASK);
}
static unsigned long smart2_fifo_full(ctlr_info_t *h)
{
return readl(h->vaddr + COMMAND_FIFO);
}
static unsigned long smart2_completed(ctlr_info_t *h)
{
return readl(h->vaddr + COMMAND_COMPLETE_FIFO);
}
static unsigned long smart2_intr_pending(ctlr_info_t *h)
{
return readl(h->vaddr + INTR_PENDING);
}
static struct access_method smart2_access = {
smart2_submit_command,
smart2_intr_mask,
smart2_fifo_full,
smart2_intr_pending,
smart2_completed,
};
/*
* IO access for SMART-2/E cards
*/
static void smart2e_submit_command(ctlr_info_t *h, cmdlist_t *c)
{
outl(c->busaddr, h->io_mem_addr + COMMAND_FIFO);
}
static void smart2e_intr_mask(ctlr_info_t *h, unsigned long val)
{
outl(val, h->io_mem_addr + INTR_MASK);
}
static unsigned long smart2e_fifo_full(ctlr_info_t *h)
{
return inl(h->io_mem_addr + COMMAND_FIFO);
}
static unsigned long smart2e_completed(ctlr_info_t *h)
{
return inl(h->io_mem_addr + COMMAND_COMPLETE_FIFO);
}
static unsigned long smart2e_intr_pending(ctlr_info_t *h)
{
return inl(h->io_mem_addr + INTR_PENDING);
}
static struct access_method smart2e_access = {
smart2e_submit_command,
smart2e_intr_mask,
smart2e_fifo_full,
smart2e_intr_pending,
smart2e_completed,
};
/*
* IO access for older SMART-1 type cards
*/
#define SMART1_SYSTEM_MASK 0xC8E
#define SMART1_SYSTEM_DOORBELL 0xC8F
#define SMART1_LOCAL_MASK 0xC8C
#define SMART1_LOCAL_DOORBELL 0xC8D
#define SMART1_INTR_MASK 0xC89
#define SMART1_LISTADDR 0xC90
#define SMART1_LISTLEN 0xC94
#define SMART1_TAG 0xC97
#define SMART1_COMPLETE_ADDR 0xC98
#define SMART1_LISTSTATUS 0xC9E
#define CHANNEL_BUSY 0x01
#define CHANNEL_CLEAR 0x02
static void smart1_submit_command(ctlr_info_t *h, cmdlist_t *c)
{
/*
* This __u16 is actually a bunch of control flags on SMART
* and below. We want them all to be zero.
*/
c->hdr.size = 0;
outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_SYSTEM_DOORBELL);
outl(c->busaddr, h->io_mem_addr + SMART1_LISTADDR);
outw(c->size, h->io_mem_addr + SMART1_LISTLEN);
outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL);
}
static void smart1_intr_mask(ctlr_info_t *h, unsigned long val)
{
if (val == 1) {
outb(0xFD, h->io_mem_addr + SMART1_SYSTEM_DOORBELL);
outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_LOCAL_DOORBELL);
outb(0x01, h->io_mem_addr + SMART1_INTR_MASK);
outb(0x01, h->io_mem_addr + SMART1_SYSTEM_MASK);
} else {
outb(0, h->io_mem_addr + 0xC8E);
}
}
static unsigned long smart1_fifo_full(ctlr_info_t *h)
{
unsigned char chan;
chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_CLEAR;
return chan;
}
static unsigned long smart1_completed(ctlr_info_t *h)
{
unsigned char status;
unsigned long cmd;
if (inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY) {
outb(CHANNEL_BUSY, h->io_mem_addr + SMART1_SYSTEM_DOORBELL);
cmd = inl(h->io_mem_addr + SMART1_COMPLETE_ADDR);
status = inb(h->io_mem_addr + SMART1_LISTSTATUS);
outb(CHANNEL_CLEAR, h->io_mem_addr + SMART1_LOCAL_DOORBELL);
/*
* this is x86 (actually compaq x86) only, so it's ok
*/
if (cmd) ((cmdlist_t*)bus_to_virt(cmd))->req.hdr.rcode = status;
} else {
cmd = 0;
}
return cmd;
}
static unsigned long smart1_intr_pending(ctlr_info_t *h)
{
unsigned char chan;
chan = inb(h->io_mem_addr + SMART1_SYSTEM_DOORBELL) & CHANNEL_BUSY;
return chan;
}
static struct access_method smart1_access = {
smart1_submit_command,
smart1_intr_mask,
smart1_fifo_full,
smart1_intr_pending,
smart1_completed,
};

1192
drivers/block/swim3.c Normal file

File diff suppressed because it is too large Load Diff

1766
drivers/block/sx8.c Normal file

File diff suppressed because it is too large Load Diff

2481
drivers/block/ub.c Normal file

File diff suppressed because it is too large Load Diff

1244
drivers/block/umem.c Normal file

File diff suppressed because it is too large Load Diff

847
drivers/block/viodasd.c Normal file
View File

@@ -0,0 +1,847 @@
/* -*- linux-c -*-
* viodasd.c
* Authors: Dave Boutcher <boutcher@us.ibm.com>
* Ryan Arnold <ryanarn@us.ibm.com>
* Colin Devilbiss <devilbis@us.ibm.com>
* Stephen Rothwell <sfr@au1.ibm.com>
*
* (C) Copyright 2000-2004 IBM Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* This routine provides access to disk space (termed "DASD" in historical
* IBM terms) owned and managed by an OS/400 partition running on the
* same box as this Linux partition.
*
* All disk operations are performed by sending messages back and forth to
* the OS/400 partition.
*/
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/dma-mapping.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <asm/vio.h>
#include <asm/iseries/hv_types.h>
#include <asm/iseries/hv_lp_event.h>
#include <asm/iseries/hv_lp_config.h>
#include <asm/iseries/vio.h>
#include <asm/firmware.h>
MODULE_DESCRIPTION("iSeries Virtual DASD");
MODULE_AUTHOR("Dave Boutcher");
MODULE_LICENSE("GPL");
/*
* We only support 7 partitions per physical disk....so with minor
* numbers 0-255 we get a maximum of 32 disks.
*/
#define VIOD_GENHD_NAME "iseries/vd"
#define VIOD_VERS "1.64"
#define VIOD_KERN_WARNING KERN_WARNING "viod: "
#define VIOD_KERN_INFO KERN_INFO "viod: "
enum {
PARTITION_SHIFT = 3,
MAX_DISKNO = HVMAXARCHITECTEDVIRTUALDISKS,
MAX_DISK_NAME = sizeof(((struct gendisk *)0)->disk_name)
};
static DEFINE_SPINLOCK(viodasd_spinlock);
#define VIOMAXREQ 16
#define VIOMAXBLOCKDMA 12
#define DEVICE_NO(cell) ((struct viodasd_device *)(cell) - &viodasd_devices[0])
struct open_data {
u64 disk_size;
u16 max_disk;
u16 cylinders;
u16 tracks;
u16 sectors;
u16 bytes_per_sector;
};
struct rw_data {
u64 offset;
struct {
u32 token;
u32 reserved;
u64 len;
} dma_info[VIOMAXBLOCKDMA];
};
struct vioblocklpevent {
struct HvLpEvent event;
u32 reserved;
u16 version;
u16 sub_result;
u16 disk;
u16 flags;
union {
struct open_data open_data;
struct rw_data rw_data;
u64 changed;
} u;
};
#define vioblockflags_ro 0x0001
enum vioblocksubtype {
vioblockopen = 0x0001,
vioblockclose = 0x0002,
vioblockread = 0x0003,
vioblockwrite = 0x0004,
vioblockflush = 0x0005,
vioblockcheck = 0x0007
};
struct viodasd_waitevent {
struct completion com;
int rc;
u16 sub_result;
int max_disk; /* open */
};
static const struct vio_error_entry viodasd_err_table[] = {
{ 0x0201, EINVAL, "Invalid Range" },
{ 0x0202, EINVAL, "Invalid Token" },
{ 0x0203, EIO, "DMA Error" },
{ 0x0204, EIO, "Use Error" },
{ 0x0205, EIO, "Release Error" },
{ 0x0206, EINVAL, "Invalid Disk" },
{ 0x0207, EBUSY, "Cant Lock" },
{ 0x0208, EIO, "Already Locked" },
{ 0x0209, EIO, "Already Unlocked" },
{ 0x020A, EIO, "Invalid Arg" },
{ 0x020B, EIO, "Bad IFS File" },
{ 0x020C, EROFS, "Read Only Device" },
{ 0x02FF, EIO, "Internal Error" },
{ 0x0000, 0, NULL },
};
/*
* Figure out the biggest I/O request (in sectors) we can accept
*/
#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA)
/*
* Number of disk I/O requests we've sent to OS/400
*/
static int num_req_outstanding;
/*
* This is our internal structure for keeping track of disk devices
*/
struct viodasd_device {
u16 cylinders;
u16 tracks;
u16 sectors;
u16 bytes_per_sector;
u64 size;
int read_only;
spinlock_t q_lock;
struct gendisk *disk;
struct device *dev;
} viodasd_devices[MAX_DISKNO];
/*
* External open entry point.
*/
static int viodasd_open(struct inode *ino, struct file *fil)
{
struct viodasd_device *d = ino->i_bdev->bd_disk->private_data;
HvLpEvent_Rc hvrc;
struct viodasd_waitevent we;
u16 flags = 0;
if (d->read_only) {
if ((fil != NULL) && (fil->f_mode & FMODE_WRITE))
return -EROFS;
flags = vioblockflags_ro;
}
init_completion(&we.com);
/* Send the open event to OS/400 */
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
HvLpEvent_Type_VirtualIo,
viomajorsubtype_blockio | vioblockopen,
HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
viopath_sourceinst(viopath_hostLp),
viopath_targetinst(viopath_hostLp),
(u64)(unsigned long)&we, VIOVERSION << 16,
((u64)DEVICE_NO(d) << 48) | ((u64)flags << 32),
0, 0, 0);
if (hvrc != 0) {
printk(VIOD_KERN_WARNING "HV open failed %d\n", (int)hvrc);
return -EIO;
}
wait_for_completion(&we.com);
/* Check the return code */
if (we.rc != 0) {
const struct vio_error_entry *err =
vio_lookup_rc(viodasd_err_table, we.sub_result);
printk(VIOD_KERN_WARNING
"bad rc opening disk: %d:0x%04x (%s)\n",
(int)we.rc, we.sub_result, err->msg);
return -EIO;
}
return 0;
}
/*
* External release entry point.
*/
static int viodasd_release(struct inode *ino, struct file *fil)
{
struct viodasd_device *d = ino->i_bdev->bd_disk->private_data;
HvLpEvent_Rc hvrc;
/* Send the event to OS/400. We DON'T expect a response */
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
HvLpEvent_Type_VirtualIo,
viomajorsubtype_blockio | vioblockclose,
HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
viopath_sourceinst(viopath_hostLp),
viopath_targetinst(viopath_hostLp),
0, VIOVERSION << 16,
((u64)DEVICE_NO(d) << 48) /* | ((u64)flags << 32) */,
0, 0, 0);
if (hvrc != 0)
printk(VIOD_KERN_WARNING "HV close call failed %d\n",
(int)hvrc);
return 0;
}
/* External ioctl entry point.
*/
static int viodasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
struct gendisk *disk = bdev->bd_disk;
struct viodasd_device *d = disk->private_data;
geo->sectors = d->sectors ? d->sectors : 0;
geo->heads = d->tracks ? d->tracks : 64;
geo->cylinders = d->cylinders ? d->cylinders :
get_capacity(disk) / (geo->cylinders * geo->heads);
return 0;
}
/*
* Our file operations table
*/
static struct block_device_operations viodasd_fops = {
.owner = THIS_MODULE,
.open = viodasd_open,
.release = viodasd_release,
.getgeo = viodasd_getgeo,
};
/*
* End a request
*/
static void viodasd_end_request(struct request *req, int uptodate,
int num_sectors)
{
if (end_that_request_first(req, uptodate, num_sectors))
return;
add_disk_randomness(req->rq_disk);
end_that_request_last(req, uptodate);
}
/*
* Send an actual I/O request to OS/400
*/
static int send_request(struct request *req)
{
u64 start;
int direction;
int nsg;
u16 viocmd;
HvLpEvent_Rc hvrc;
struct vioblocklpevent *bevent;
struct HvLpEvent *hev;
struct scatterlist sg[VIOMAXBLOCKDMA];
int sgindex;
int statindex;
struct viodasd_device *d;
unsigned long flags;
start = (u64)req->sector << 9;
if (rq_data_dir(req) == READ) {
direction = DMA_FROM_DEVICE;
viocmd = viomajorsubtype_blockio | vioblockread;
statindex = 0;
} else {
direction = DMA_TO_DEVICE;
viocmd = viomajorsubtype_blockio | vioblockwrite;
statindex = 1;
}
d = req->rq_disk->private_data;
/* Now build the scatter-gather list */
nsg = blk_rq_map_sg(req->q, req, sg);
nsg = dma_map_sg(d->dev, sg, nsg, direction);
spin_lock_irqsave(&viodasd_spinlock, flags);
num_req_outstanding++;
/* This optimization handles a single DMA block */
if (nsg == 1)
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
HvLpEvent_Type_VirtualIo, viocmd,
HvLpEvent_AckInd_DoAck,
HvLpEvent_AckType_ImmediateAck,
viopath_sourceinst(viopath_hostLp),
viopath_targetinst(viopath_hostLp),
(u64)(unsigned long)req, VIOVERSION << 16,
((u64)DEVICE_NO(d) << 48), start,
((u64)sg_dma_address(&sg[0])) << 32,
sg_dma_len(&sg[0]));
else {
bevent = (struct vioblocklpevent *)
vio_get_event_buffer(viomajorsubtype_blockio);
if (bevent == NULL) {
printk(VIOD_KERN_WARNING
"error allocating disk event buffer\n");
goto error_ret;
}
/*
* Now build up the actual request. Note that we store
* the pointer to the request in the correlation
* token so we can match the response up later
*/
memset(bevent, 0, sizeof(struct vioblocklpevent));
hev = &bevent->event;
hev->flags = HV_LP_EVENT_VALID | HV_LP_EVENT_DO_ACK |
HV_LP_EVENT_INT;
hev->xType = HvLpEvent_Type_VirtualIo;
hev->xSubtype = viocmd;
hev->xSourceLp = HvLpConfig_getLpIndex();
hev->xTargetLp = viopath_hostLp;
hev->xSizeMinus1 =
offsetof(struct vioblocklpevent, u.rw_data.dma_info) +
(sizeof(bevent->u.rw_data.dma_info[0]) * nsg) - 1;
hev->xSourceInstanceId = viopath_sourceinst(viopath_hostLp);
hev->xTargetInstanceId = viopath_targetinst(viopath_hostLp);
hev->xCorrelationToken = (u64)req;
bevent->version = VIOVERSION;
bevent->disk = DEVICE_NO(d);
bevent->u.rw_data.offset = start;
/*
* Copy just the dma information from the sg list
* into the request
*/
for (sgindex = 0; sgindex < nsg; sgindex++) {
bevent->u.rw_data.dma_info[sgindex].token =
sg_dma_address(&sg[sgindex]);
bevent->u.rw_data.dma_info[sgindex].len =
sg_dma_len(&sg[sgindex]);
}
/* Send the request */
hvrc = HvCallEvent_signalLpEvent(&bevent->event);
vio_free_event_buffer(viomajorsubtype_blockio, bevent);
}
if (hvrc != HvLpEvent_Rc_Good) {
printk(VIOD_KERN_WARNING
"error sending disk event to OS/400 (rc %d)\n",
(int)hvrc);
goto error_ret;
}
spin_unlock_irqrestore(&viodasd_spinlock, flags);
return 0;
error_ret:
num_req_outstanding--;
spin_unlock_irqrestore(&viodasd_spinlock, flags);
dma_unmap_sg(d->dev, sg, nsg, direction);
return -1;
}
/*
* This is the external request processing routine
*/
static void do_viodasd_request(request_queue_t *q)
{
struct request *req;
/*
* If we already have the maximum number of requests
* outstanding to OS/400 just bail out. We'll come
* back later.
*/
while (num_req_outstanding < VIOMAXREQ) {
req = elv_next_request(q);
if (req == NULL)
return;
/* dequeue the current request from the queue */
blkdev_dequeue_request(req);
/* check that request contains a valid command */
if (!blk_fs_request(req)) {
viodasd_end_request(req, 0, req->hard_nr_sectors);
continue;
}
/* Try sending the request */
if (send_request(req) != 0)
viodasd_end_request(req, 0, req->hard_nr_sectors);
}
}
/*
* Probe a single disk and fill in the viodasd_device structure
* for it.
*/
static void probe_disk(struct viodasd_device *d)
{
HvLpEvent_Rc hvrc;
struct viodasd_waitevent we;
int dev_no = DEVICE_NO(d);
struct gendisk *g;
struct request_queue *q;
u16 flags = 0;
retry:
init_completion(&we.com);
/* Send the open event to OS/400 */
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
HvLpEvent_Type_VirtualIo,
viomajorsubtype_blockio | vioblockopen,
HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck,
viopath_sourceinst(viopath_hostLp),
viopath_targetinst(viopath_hostLp),
(u64)(unsigned long)&we, VIOVERSION << 16,
((u64)dev_no << 48) | ((u64)flags<< 32),
0, 0, 0);
if (hvrc != 0) {
printk(VIOD_KERN_WARNING "bad rc on HV open %d\n", (int)hvrc);
return;
}
wait_for_completion(&we.com);
if (we.rc != 0) {
if (flags != 0)
return;
/* try again with read only flag set */
flags = vioblockflags_ro;
goto retry;
}
if (we.max_disk > (MAX_DISKNO - 1)) {
static int warned;
if (warned == 0) {
warned++;
printk(VIOD_KERN_INFO
"Only examining the first %d "
"of %d disks connected\n",
MAX_DISKNO, we.max_disk + 1);
}
}
/* Send the close event to OS/400. We DON'T expect a response */
hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp,
HvLpEvent_Type_VirtualIo,
viomajorsubtype_blockio | vioblockclose,
HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck,
viopath_sourceinst(viopath_hostLp),
viopath_targetinst(viopath_hostLp),
0, VIOVERSION << 16,
((u64)dev_no << 48) | ((u64)flags << 32),
0, 0, 0);
if (hvrc != 0) {
printk(VIOD_KERN_WARNING
"bad rc sending event to OS/400 %d\n", (int)hvrc);
return;
}
/* create the request queue for the disk */
spin_lock_init(&d->q_lock);
q = blk_init_queue(do_viodasd_request, &d->q_lock);
if (q == NULL) {
printk(VIOD_KERN_WARNING "cannot allocate queue for disk %d\n",
dev_no);
return;
}
g = alloc_disk(1 << PARTITION_SHIFT);
if (g == NULL) {
printk(VIOD_KERN_WARNING
"cannot allocate disk structure for disk %d\n",
dev_no);
blk_cleanup_queue(q);
return;
}
d->disk = g;
blk_queue_max_hw_segments(q, VIOMAXBLOCKDMA);
blk_queue_max_phys_segments(q, VIOMAXBLOCKDMA);
blk_queue_max_sectors(q, VIODASD_MAXSECTORS);
g->major = VIODASD_MAJOR;
g->first_minor = dev_no << PARTITION_SHIFT;
if (dev_no >= 26)
snprintf(g->disk_name, sizeof(g->disk_name),
VIOD_GENHD_NAME "%c%c",
'a' + (dev_no / 26) - 1, 'a' + (dev_no % 26));
else
snprintf(g->disk_name, sizeof(g->disk_name),
VIOD_GENHD_NAME "%c", 'a' + (dev_no % 26));
g->fops = &viodasd_fops;
g->queue = q;
g->private_data = d;
g->driverfs_dev = d->dev;
set_capacity(g, d->size >> 9);
printk(VIOD_KERN_INFO "disk %d: %lu sectors (%lu MB) "
"CHS=%d/%d/%d sector size %d%s\n",
dev_no, (unsigned long)(d->size >> 9),
(unsigned long)(d->size >> 20),
(int)d->cylinders, (int)d->tracks,
(int)d->sectors, (int)d->bytes_per_sector,
d->read_only ? " (RO)" : "");
/* register us in the global list */
add_disk(g);
}
/* returns the total number of scatterlist elements converted */
static int block_event_to_scatterlist(const struct vioblocklpevent *bevent,
struct scatterlist *sg, int *total_len)
{
int i, numsg;
const struct rw_data *rw_data = &bevent->u.rw_data;
static const int offset =
offsetof(struct vioblocklpevent, u.rw_data.dma_info);
static const int element_size = sizeof(rw_data->dma_info[0]);
numsg = ((bevent->event.xSizeMinus1 + 1) - offset) / element_size;
if (numsg > VIOMAXBLOCKDMA)
numsg = VIOMAXBLOCKDMA;
*total_len = 0;
memset(sg, 0, sizeof(sg[0]) * VIOMAXBLOCKDMA);
for (i = 0; (i < numsg) && (rw_data->dma_info[i].len > 0); ++i) {
sg_dma_address(&sg[i]) = rw_data->dma_info[i].token;
sg_dma_len(&sg[i]) = rw_data->dma_info[i].len;
*total_len += rw_data->dma_info[i].len;
}
return i;
}
/*
* Restart all queues, starting with the one _after_ the disk given,
* thus reducing the chance of starvation of higher numbered disks.
*/
static void viodasd_restart_all_queues_starting_from(int first_index)
{
int i;
for (i = first_index + 1; i < MAX_DISKNO; ++i)
if (viodasd_devices[i].disk)
blk_run_queue(viodasd_devices[i].disk->queue);
for (i = 0; i <= first_index; ++i)
if (viodasd_devices[i].disk)
blk_run_queue(viodasd_devices[i].disk->queue);
}
/*
* For read and write requests, decrement the number of outstanding requests,
* Free the DMA buffers we allocated.
*/
static int viodasd_handle_read_write(struct vioblocklpevent *bevent)
{
int num_sg, num_sect, pci_direction, total_len;
struct request *req;
struct scatterlist sg[VIOMAXBLOCKDMA];
struct HvLpEvent *event = &bevent->event;
unsigned long irq_flags;
struct viodasd_device *d;
int error;
spinlock_t *qlock;
num_sg = block_event_to_scatterlist(bevent, sg, &total_len);
num_sect = total_len >> 9;
if (event->xSubtype == (viomajorsubtype_blockio | vioblockread))
pci_direction = DMA_FROM_DEVICE;
else
pci_direction = DMA_TO_DEVICE;
req = (struct request *)bevent->event.xCorrelationToken;
d = req->rq_disk->private_data;
dma_unmap_sg(d->dev, sg, num_sg, pci_direction);
/*
* Since this is running in interrupt mode, we need to make sure
* we're not stepping on any global I/O operations
*/
spin_lock_irqsave(&viodasd_spinlock, irq_flags);
num_req_outstanding--;
spin_unlock_irqrestore(&viodasd_spinlock, irq_flags);
error = event->xRc != HvLpEvent_Rc_Good;
if (error) {
const struct vio_error_entry *err;
err = vio_lookup_rc(viodasd_err_table, bevent->sub_result);
printk(VIOD_KERN_WARNING "read/write error %d:0x%04x (%s)\n",
event->xRc, bevent->sub_result, err->msg);
num_sect = req->hard_nr_sectors;
}
qlock = req->q->queue_lock;
spin_lock_irqsave(qlock, irq_flags);
viodasd_end_request(req, !error, num_sect);
spin_unlock_irqrestore(qlock, irq_flags);
/* Finally, try to get more requests off of this device's queue */
viodasd_restart_all_queues_starting_from(DEVICE_NO(d));
return 0;
}
/* This routine handles incoming block LP events */
static void handle_block_event(struct HvLpEvent *event)
{
struct vioblocklpevent *bevent = (struct vioblocklpevent *)event;
struct viodasd_waitevent *pwe;
if (event == NULL)
/* Notification that a partition went away! */
return;
/* First, we should NEVER get an int here...only acks */
if (hvlpevent_is_int(event)) {
printk(VIOD_KERN_WARNING
"Yikes! got an int in viodasd event handler!\n");
if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
}
}
switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) {
case vioblockopen:
/*
* Handle a response to an open request. We get all the
* disk information in the response, so update it. The
* correlation token contains a pointer to a waitevent
* structure that has a completion in it. update the
* return code in the waitevent structure and post the
* completion to wake up the guy who sent the request
*/
pwe = (struct viodasd_waitevent *)event->xCorrelationToken;
pwe->rc = event->xRc;
pwe->sub_result = bevent->sub_result;
if (event->xRc == HvLpEvent_Rc_Good) {
const struct open_data *data = &bevent->u.open_data;
struct viodasd_device *device =
&viodasd_devices[bevent->disk];
device->read_only =
bevent->flags & vioblockflags_ro;
device->size = data->disk_size;
device->cylinders = data->cylinders;
device->tracks = data->tracks;
device->sectors = data->sectors;
device->bytes_per_sector = data->bytes_per_sector;
pwe->max_disk = data->max_disk;
}
complete(&pwe->com);
break;
case vioblockclose:
break;
case vioblockread:
case vioblockwrite:
viodasd_handle_read_write(bevent);
break;
default:
printk(VIOD_KERN_WARNING "invalid subtype!");
if (hvlpevent_need_ack(event)) {
event->xRc = HvLpEvent_Rc_InvalidSubtype;
HvCallEvent_ackLpEvent(event);
}
}
}
/*
* Get the driver to reprobe for more disks.
*/
static ssize_t probe_disks(struct device_driver *drv, const char *buf,
size_t count)
{
struct viodasd_device *d;
for (d = viodasd_devices; d < &viodasd_devices[MAX_DISKNO]; d++) {
if (d->disk == NULL)
probe_disk(d);
}
return count;
}
static DRIVER_ATTR(probe, S_IWUSR, NULL, probe_disks);
static int viodasd_probe(struct vio_dev *vdev, const struct vio_device_id *id)
{
struct viodasd_device *d = &viodasd_devices[vdev->unit_address];
d->dev = &vdev->dev;
probe_disk(d);
if (d->disk == NULL)
return -ENODEV;
return 0;
}
static int viodasd_remove(struct vio_dev *vdev)
{
struct viodasd_device *d;
d = &viodasd_devices[vdev->unit_address];
if (d->disk) {
del_gendisk(d->disk);
blk_cleanup_queue(d->disk->queue);
put_disk(d->disk);
d->disk = NULL;
}
d->dev = NULL;
return 0;
}
/**
* viodasd_device_table: Used by vio.c to match devices that we
* support.
*/
static struct vio_device_id viodasd_device_table[] __devinitdata = {
{ "block", "IBM,iSeries-viodasd" },
{ "", "" }
};
MODULE_DEVICE_TABLE(vio, viodasd_device_table);
static struct vio_driver viodasd_driver = {
.id_table = viodasd_device_table,
.probe = viodasd_probe,
.remove = viodasd_remove,
.driver = {
.name = "viodasd",
.owner = THIS_MODULE,
}
};
static int need_delete_probe;
/*
* Initialize the whole device driver. Handle module and non-module
* versions
*/
static int __init viodasd_init(void)
{
int rc;
if (!firmware_has_feature(FW_FEATURE_ISERIES)) {
rc = -ENODEV;
goto early_fail;
}
/* Try to open to our host lp */
if (viopath_hostLp == HvLpIndexInvalid)
vio_set_hostlp();
if (viopath_hostLp == HvLpIndexInvalid) {
printk(VIOD_KERN_WARNING "invalid hosting partition\n");
rc = -EIO;
goto early_fail;
}
printk(VIOD_KERN_INFO "vers " VIOD_VERS ", hosting partition %d\n",
viopath_hostLp);
/* register the block device */
rc = register_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
if (rc) {
printk(VIOD_KERN_WARNING
"Unable to get major number %d for %s\n",
VIODASD_MAJOR, VIOD_GENHD_NAME);
goto early_fail;
}
/* Actually open the path to the hosting partition */
rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio,
VIOMAXREQ + 2);
if (rc) {
printk(VIOD_KERN_WARNING
"error opening path to host partition %d\n",
viopath_hostLp);
goto unregister_blk;
}
/* Initialize our request handler */
vio_setHandler(viomajorsubtype_blockio, handle_block_event);
rc = vio_register_driver(&viodasd_driver);
if (rc) {
printk(VIOD_KERN_WARNING "vio_register_driver failed\n");
goto unset_handler;
}
/*
* If this call fails, it just means that we cannot dynamically
* add virtual disks, but the driver will still work fine for
* all existing disk, so ignore the failure.
*/
if (!driver_create_file(&viodasd_driver.driver, &driver_attr_probe))
need_delete_probe = 1;
return 0;
unset_handler:
vio_clearHandler(viomajorsubtype_blockio);
viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2);
unregister_blk:
unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
early_fail:
return rc;
}
module_init(viodasd_init);
void __exit viodasd_exit(void)
{
if (need_delete_probe)
driver_remove_file(&viodasd_driver.driver, &driver_attr_probe);
vio_unregister_driver(&viodasd_driver);
vio_clearHandler(viomajorsubtype_blockio);
viopath_close(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ + 2);
unregister_blkdev(VIODASD_MAJOR, VIOD_GENHD_NAME);
}
module_exit(viodasd_exit);

1106
drivers/block/xd.c Normal file

File diff suppressed because it is too large Load Diff

134
drivers/block/xd.h Normal file
View File

@@ -0,0 +1,134 @@
#ifndef _LINUX_XD_H
#define _LINUX_XD_H
/*
* This file contains the definitions for the IO ports and errors etc. for XT hard disk controllers (at least the DTC 5150X).
*
* Author: Pat Mackinlay, pat@it.com.au
* Date: 29/09/92
*
* Revised: 01/01/93, ...
*
* Ref: DTC 5150X Controller Specification (thanks to Kevin Fowler, kevinf@agora.rain.com)
* Also thanks to: Salvador Abreu, Dave Thaler, Risto Kankkunen and Wim Van Dorst.
*/
#include <linux/interrupt.h>
/* XT hard disk controller registers */
#define XD_DATA (xd_iobase + 0x00) /* data RW register */
#define XD_RESET (xd_iobase + 0x01) /* reset WO register */
#define XD_STATUS (xd_iobase + 0x01) /* status RO register */
#define XD_SELECT (xd_iobase + 0x02) /* select WO register */
#define XD_JUMPER (xd_iobase + 0x02) /* jumper RO register */
#define XD_CONTROL (xd_iobase + 0x03) /* DMAE/INTE WO register */
#define XD_RESERVED (xd_iobase + 0x03) /* reserved */
/* XT hard disk controller commands (incomplete list) */
#define CMD_TESTREADY 0x00 /* test drive ready */
#define CMD_RECALIBRATE 0x01 /* recalibrate drive */
#define CMD_SENSE 0x03 /* request sense */
#define CMD_FORMATDRV 0x04 /* format drive */
#define CMD_VERIFY 0x05 /* read verify */
#define CMD_FORMATTRK 0x06 /* format track */
#define CMD_FORMATBAD 0x07 /* format bad track */
#define CMD_READ 0x08 /* read */
#define CMD_WRITE 0x0A /* write */
#define CMD_SEEK 0x0B /* seek */
/* Controller specific commands */
#define CMD_DTCSETPARAM 0x0C /* set drive parameters (DTC 5150X & CX only?) */
#define CMD_DTCGETECC 0x0D /* get ecc error length (DTC 5150X only?) */
#define CMD_DTCREADBUF 0x0E /* read sector buffer (DTC 5150X only?) */
#define CMD_DTCWRITEBUF 0x0F /* write sector buffer (DTC 5150X only?) */
#define CMD_DTCREMAPTRK 0x11 /* assign alternate track (DTC 5150X only?) */
#define CMD_DTCGETPARAM 0xFB /* get drive parameters (DTC 5150X only?) */
#define CMD_DTCSETSTEP 0xFC /* set step rate (DTC 5150X only?) */
#define CMD_DTCSETGEOM 0xFE /* set geometry data (DTC 5150X only?) */
#define CMD_DTCGETGEOM 0xFF /* get geometry data (DTC 5150X only?) */
#define CMD_ST11GETGEOM 0xF8 /* get geometry data (Seagate ST11R/M only?) */
#define CMD_WDSETPARAM 0x0C /* set drive parameters (WD 1004A27X only?) */
#define CMD_XBSETPARAM 0x0C /* set drive parameters (XEBEC only?) */
/* Bits for command status byte */
#define CSB_ERROR 0x02 /* error */
#define CSB_LUN 0x20 /* logical Unit Number */
/* XT hard disk controller status bits */
#define STAT_READY 0x01 /* controller is ready */
#define STAT_INPUT 0x02 /* data flowing from controller to host */
#define STAT_COMMAND 0x04 /* controller in command phase */
#define STAT_SELECT 0x08 /* controller is selected */
#define STAT_REQUEST 0x10 /* controller requesting data */
#define STAT_INTERRUPT 0x20 /* controller requesting interrupt */
/* XT hard disk controller control bits */
#define PIO_MODE 0x00 /* control bits to set for PIO */
#define DMA_MODE 0x03 /* control bits to set for DMA & interrupt */
#define XD_MAXDRIVES 2 /* maximum 2 drives */
#define XD_TIMEOUT HZ /* 1 second timeout */
#define XD_RETRIES 4 /* maximum 4 retries */
#undef DEBUG /* define for debugging output */
#ifdef DEBUG
#define DEBUG_STARTUP /* debug driver initialisation */
#define DEBUG_OVERRIDE /* debug override geometry detection */
#define DEBUG_READWRITE /* debug each read/write command */
#define DEBUG_OTHER /* debug misc. interrupt/DMA stuff */
#define DEBUG_COMMAND /* debug each controller command */
#endif /* DEBUG */
/* this structure defines the XT drives and their types */
typedef struct {
u_char heads;
u_short cylinders;
u_char sectors;
u_char control;
int unit;
} XD_INFO;
/* this structure defines a ROM BIOS signature */
typedef struct {
unsigned int offset;
const char *string;
void (*init_controller)(unsigned int address);
void (*init_drive)(u_char drive);
const char *name;
} XD_SIGNATURE;
#ifndef MODULE
static int xd_manual_geo_init (char *command);
#endif /* MODULE */
static u_char xd_detect (u_char *controller, unsigned int *address);
static u_char xd_initdrives (void (*init_drive)(u_char drive));
static void do_xd_request (request_queue_t * q);
static int xd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg);
static int xd_readwrite (u_char operation,XD_INFO *disk,char *buffer,u_int block,u_int count);
static void xd_recalibrate (u_char drive);
static irqreturn_t xd_interrupt_handler(int irq, void *dev_id);
static u_char xd_setup_dma (u_char opcode,u_char *buffer,u_int count);
static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control);
static void xd_watchdog (unsigned long unused);
static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout);
static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout);
/* card specific setup and geometry gathering code */
static void xd_dtc_init_controller (unsigned int address);
static void xd_dtc5150cx_init_drive (u_char drive);
static void xd_dtc_init_drive (u_char drive);
static void xd_wd_init_controller (unsigned int address);
static void xd_wd_init_drive (u_char drive);
static void xd_seagate_init_controller (unsigned int address);
static void xd_seagate_init_drive (u_char drive);
static void xd_omti_init_controller (unsigned int address);
static void xd_omti_init_drive (u_char drive);
static void xd_xebec_init_controller (unsigned int address);
static void xd_xebec_init_drive (u_char drive);
static void xd_setparam (u_char command,u_char drive,u_char heads,u_short cylinders,u_short rwrite,u_short wprecomp,u_char ecc);
static void xd_override_init_drive (u_char drive);
#endif /* _LINUX_XD_H */

412
drivers/block/z2ram.c Normal file
View File

@@ -0,0 +1,412 @@
/*
** z2ram - Amiga pseudo-driver to access 16bit-RAM in ZorroII space
** as a block device, to be used as a RAM disk or swap space
**
** Copyright (C) 1994 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
**
** ++Geert: support for zorro_unused_z2ram, better range checking
** ++roman: translate accesses via an array
** ++Milan: support for ChipRAM usage
** ++yambo: converted to 2.0 kernel
** ++yambo: modularized and support added for 3 minor devices including:
** MAJOR MINOR DESCRIPTION
** ----- ----- ----------------------------------------------
** 37 0 Use Zorro II and Chip ram
** 37 1 Use only Zorro II ram
** 37 2 Use only Chip ram
** 37 4-7 Use memory list entry 1-4 (first is 0)
** ++jskov: support for 1-4th memory list entry.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
*/
#define DEVICE_NAME "Z2RAM"
#include <linux/major.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/bitops.h>
#include <asm/setup.h>
#include <asm/amigahw.h>
#include <asm/pgtable.h>
#include <linux/zorro.h>
extern int m68k_realnum_memory;
extern struct mem_info m68k_memory[NUM_MEMINFO];
#define TRUE (1)
#define FALSE (0)
#define Z2MINOR_COMBINED (0)
#define Z2MINOR_Z2ONLY (1)
#define Z2MINOR_CHIPONLY (2)
#define Z2MINOR_MEMLIST1 (4)
#define Z2MINOR_MEMLIST2 (5)
#define Z2MINOR_MEMLIST3 (6)
#define Z2MINOR_MEMLIST4 (7)
#define Z2MINOR_COUNT (8) /* Move this down when adding a new minor */
#define Z2RAM_CHUNK1024 ( Z2RAM_CHUNKSIZE >> 10 )
static u_long *z2ram_map = NULL;
static u_long z2ram_size = 0;
static int z2_count = 0;
static int chip_count = 0;
static int list_count = 0;
static int current_device = -1;
static DEFINE_SPINLOCK(z2ram_lock);
static struct block_device_operations z2_fops;
static struct gendisk *z2ram_gendisk;
static void do_z2_request(request_queue_t *q)
{
struct request *req;
while ((req = elv_next_request(q)) != NULL) {
unsigned long start = req->sector << 9;
unsigned long len = req->current_nr_sectors << 9;
if (start + len > z2ram_size) {
printk( KERN_ERR DEVICE_NAME ": bad access: block=%lu, count=%u\n",
req->sector, req->current_nr_sectors);
end_request(req, 0);
continue;
}
while (len) {
unsigned long addr = start & Z2RAM_CHUNKMASK;
unsigned long size = Z2RAM_CHUNKSIZE - addr;
if (len < size)
size = len;
addr += z2ram_map[ start >> Z2RAM_CHUNKSHIFT ];
if (rq_data_dir(req) == READ)
memcpy(req->buffer, (char *)addr, size);
else
memcpy((char *)addr, req->buffer, size);
start += size;
len -= size;
}
end_request(req, 1);
}
}
static void
get_z2ram( void )
{
int i;
for ( i = 0; i < Z2RAM_SIZE / Z2RAM_CHUNKSIZE; i++ )
{
if ( test_bit( i, zorro_unused_z2ram ) )
{
z2_count++;
z2ram_map[ z2ram_size++ ] =
ZTWO_VADDR( Z2RAM_START ) + ( i << Z2RAM_CHUNKSHIFT );
clear_bit( i, zorro_unused_z2ram );
}
}
return;
}
static void
get_chipram( void )
{
while ( amiga_chip_avail() > ( Z2RAM_CHUNKSIZE * 4 ) )
{
chip_count++;
z2ram_map[ z2ram_size ] =
(u_long)amiga_chip_alloc( Z2RAM_CHUNKSIZE, "z2ram" );
if ( z2ram_map[ z2ram_size ] == 0 )
{
break;
}
z2ram_size++;
}
return;
}
static int
z2_open( struct inode *inode, struct file *filp )
{
int device;
int max_z2_map = ( Z2RAM_SIZE / Z2RAM_CHUNKSIZE ) *
sizeof( z2ram_map[0] );
int max_chip_map = ( amiga_chip_size / Z2RAM_CHUNKSIZE ) *
sizeof( z2ram_map[0] );
int rc = -ENOMEM;
device = iminor(inode);
if ( current_device != -1 && current_device != device )
{
rc = -EBUSY;
goto err_out;
}
if ( current_device == -1 )
{
z2_count = 0;
chip_count = 0;
list_count = 0;
z2ram_size = 0;
/* Use a specific list entry. */
if (device >= Z2MINOR_MEMLIST1 && device <= Z2MINOR_MEMLIST4) {
int index = device - Z2MINOR_MEMLIST1 + 1;
unsigned long size, paddr, vaddr;
if (index >= m68k_realnum_memory) {
printk( KERN_ERR DEVICE_NAME
": no such entry in z2ram_map\n" );
goto err_out;
}
paddr = m68k_memory[index].addr;
size = m68k_memory[index].size & ~(Z2RAM_CHUNKSIZE-1);
#ifdef __powerpc__
/* FIXME: ioremap doesn't build correct memory tables. */
{
vfree(vmalloc (size));
}
vaddr = (unsigned long) __ioremap (paddr, size,
_PAGE_WRITETHRU);
#else
vaddr = (unsigned long)z_remap_nocache_nonser(paddr, size);
#endif
z2ram_map =
kmalloc((size/Z2RAM_CHUNKSIZE)*sizeof(z2ram_map[0]),
GFP_KERNEL);
if ( z2ram_map == NULL )
{
printk( KERN_ERR DEVICE_NAME
": cannot get mem for z2ram_map\n" );
goto err_out;
}
while (size) {
z2ram_map[ z2ram_size++ ] = vaddr;
size -= Z2RAM_CHUNKSIZE;
vaddr += Z2RAM_CHUNKSIZE;
list_count++;
}
if ( z2ram_size != 0 )
printk( KERN_INFO DEVICE_NAME
": using %iK List Entry %d Memory\n",
list_count * Z2RAM_CHUNK1024, index );
} else
switch ( device )
{
case Z2MINOR_COMBINED:
z2ram_map = kmalloc( max_z2_map + max_chip_map, GFP_KERNEL );
if ( z2ram_map == NULL )
{
printk( KERN_ERR DEVICE_NAME
": cannot get mem for z2ram_map\n" );
goto err_out;
}
get_z2ram();
get_chipram();
if ( z2ram_size != 0 )
printk( KERN_INFO DEVICE_NAME
": using %iK Zorro II RAM and %iK Chip RAM (Total %dK)\n",
z2_count * Z2RAM_CHUNK1024,
chip_count * Z2RAM_CHUNK1024,
( z2_count + chip_count ) * Z2RAM_CHUNK1024 );
break;
case Z2MINOR_Z2ONLY:
z2ram_map = kmalloc( max_z2_map, GFP_KERNEL );
if ( z2ram_map == NULL )
{
printk( KERN_ERR DEVICE_NAME
": cannot get mem for z2ram_map\n" );
goto err_out;
}
get_z2ram();
if ( z2ram_size != 0 )
printk( KERN_INFO DEVICE_NAME
": using %iK of Zorro II RAM\n",
z2_count * Z2RAM_CHUNK1024 );
break;
case Z2MINOR_CHIPONLY:
z2ram_map = kmalloc( max_chip_map, GFP_KERNEL );
if ( z2ram_map == NULL )
{
printk( KERN_ERR DEVICE_NAME
": cannot get mem for z2ram_map\n" );
goto err_out;
}
get_chipram();
if ( z2ram_size != 0 )
printk( KERN_INFO DEVICE_NAME
": using %iK Chip RAM\n",
chip_count * Z2RAM_CHUNK1024 );
break;
default:
rc = -ENODEV;
goto err_out;
break;
}
if ( z2ram_size == 0 )
{
printk( KERN_NOTICE DEVICE_NAME
": no unused ZII/Chip RAM found\n" );
goto err_out_kfree;
}
current_device = device;
z2ram_size <<= Z2RAM_CHUNKSHIFT;
set_capacity(z2ram_gendisk, z2ram_size >> 9);
}
return 0;
err_out_kfree:
kfree(z2ram_map);
err_out:
return rc;
}
static int
z2_release( struct inode *inode, struct file *filp )
{
if ( current_device == -1 )
return 0;
/*
* FIXME: unmap memory
*/
return 0;
}
static struct block_device_operations z2_fops =
{
.owner = THIS_MODULE,
.open = z2_open,
.release = z2_release,
};
static struct kobject *z2_find(dev_t dev, int *part, void *data)
{
*part = 0;
return get_disk(z2ram_gendisk);
}
static struct request_queue *z2_queue;
static int __init
z2_init(void)
{
int ret;
if (!MACH_IS_AMIGA)
return -ENXIO;
ret = -EBUSY;
if (register_blkdev(Z2RAM_MAJOR, DEVICE_NAME))
goto err;
ret = -ENOMEM;
z2ram_gendisk = alloc_disk(1);
if (!z2ram_gendisk)
goto out_disk;
z2_queue = blk_init_queue(do_z2_request, &z2ram_lock);
if (!z2_queue)
goto out_queue;
z2ram_gendisk->major = Z2RAM_MAJOR;
z2ram_gendisk->first_minor = 0;
z2ram_gendisk->fops = &z2_fops;
sprintf(z2ram_gendisk->disk_name, "z2ram");
z2ram_gendisk->queue = z2_queue;
add_disk(z2ram_gendisk);
blk_register_region(MKDEV(Z2RAM_MAJOR, 0), Z2MINOR_COUNT, THIS_MODULE,
z2_find, NULL, NULL);
return 0;
out_queue:
put_disk(z2ram_gendisk);
out_disk:
unregister_blkdev(Z2RAM_MAJOR, DEVICE_NAME);
err:
return ret;
}
static void __exit z2_exit(void)
{
int i, j;
blk_unregister_region(MKDEV(Z2RAM_MAJOR, 0), 256);
if ( unregister_blkdev( Z2RAM_MAJOR, DEVICE_NAME ) != 0 )
printk( KERN_ERR DEVICE_NAME ": unregister of device failed\n");
del_gendisk(z2ram_gendisk);
put_disk(z2ram_gendisk);
blk_cleanup_queue(z2_queue);
if ( current_device != -1 )
{
i = 0;
for ( j = 0 ; j < z2_count; j++ )
{
set_bit( i++, zorro_unused_z2ram );
}
for ( j = 0 ; j < chip_count; j++ )
{
if ( z2ram_map[ i ] )
{
amiga_chip_free( (void *) z2ram_map[ i++ ] );
}
}
if ( z2ram_map != NULL )
{
kfree( z2ram_map );
}
}
return;
}
module_init(z2_init);
module_exit(z2_exit);
MODULE_LICENSE("GPL");