summaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/.cvsignore1
-rw-r--r--drivers/block/Config.in24
-rw-r--r--drivers/block/Makefile34
-rw-r--r--drivers/block/README.fd18
-rw-r--r--drivers/block/acsi.c66
-rw-r--r--drivers/block/acsi_slm.c28
-rw-r--r--drivers/block/amiflop.c53
-rw-r--r--drivers/block/ataflop.c51
-rw-r--r--drivers/block/ez.c1022
-rw-r--r--drivers/block/floppy.c164
-rw-r--r--drivers/block/genhd.c62
-rw-r--r--drivers/block/ide-cd.c96
-rw-r--r--drivers/block/ide-disk.c194
-rw-r--r--drivers/block/ide-dma.c27
-rw-r--r--drivers/block/ide-floppy.c39
-rw-r--r--drivers/block/ide-pci.c54
-rw-r--r--drivers/block/ide-probe.c16
-rw-r--r--drivers/block/ide-proc.c440
-rw-r--r--drivers/block/ide-tape.c92
-rw-r--r--drivers/block/ide.c563
-rw-r--r--drivers/block/ide.h96
-rw-r--r--drivers/block/ide_modes.h4
-rw-r--r--drivers/block/ll_rw_blk.c57
-rw-r--r--drivers/block/md.c120
-rw-r--r--drivers/block/paride/Config.in20
-rw-r--r--drivers/block/paride/Makefile141
-rw-r--r--drivers/block/paride/aten.c166
-rw-r--r--drivers/block/paride/bpck.c476
-rw-r--r--drivers/block/paride/comm.c222
-rw-r--r--drivers/block/paride/dstr.c237
-rw-r--r--drivers/block/paride/epat.c315
-rw-r--r--drivers/block/paride/epia.c318
-rw-r--r--drivers/block/paride/frpw.c256
-rw-r--r--drivers/block/paride/kbic.c305
-rw-r--r--drivers/block/paride/on20.c157
-rw-r--r--drivers/block/paride/on26.c261
-rw-r--r--drivers/block/paride/paride.c485
-rw-r--r--drivers/block/paride/paride.h157
-rw-r--r--drivers/block/paride/pcd.c816
-rw-r--r--drivers/block/paride/pd.c1088
-rw-r--r--drivers/block/paride/pf.c1072
-rw-r--r--drivers/block/paride/pseudo.h138
-rw-r--r--drivers/block/paride/pt.c959
-rw-r--r--drivers/block/paride/setup.h56
-rw-r--r--drivers/block/ps2esdi.c34
-rw-r--r--drivers/block/raid5.c4
-rw-r--r--drivers/block/rz1000.c1
-rw-r--r--drivers/block/swim3.c4
-rw-r--r--drivers/block/trm290.c8
-rw-r--r--drivers/block/xd.c143
-rw-r--r--drivers/block/xd.h3
51 files changed, 9485 insertions, 1678 deletions
diff --git a/drivers/block/.cvsignore b/drivers/block/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/drivers/block/.cvsignore
+++ b/drivers/block/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/drivers/block/Config.in b/drivers/block/Config.in
index b76e9af08..99226e40a 100644
--- a/drivers/block/Config.in
+++ b/drivers/block/Config.in
@@ -27,9 +27,11 @@ else
if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621
- bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
+ if [ "$CONFIG_BLK_DEV_IDEDMA" = "y" ]; then
+ bool ' Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290
+ bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415
+ fi
fi
fi
fi
@@ -63,12 +65,28 @@ if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING
tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5
fi
+if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then
+ bool ' Boot support (linear, striped)' CONFIG_MD_BOOT
+fi
tristate 'RAM disk support' CONFIG_BLK_DEV_RAM
if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then
bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD
fi
tristate 'XT harddisk support' CONFIG_BLK_DEV_XD
-tristate 'SyQuest EZ parallel port disk support' CONFIG_BLK_DEV_EZ
+
+# PARIDE doesn't need PARPORT, but if PARPORT is configured as a module,
+# PARIDE must also be a module. The bogus CONFIG_PARIDE_PARPORT option
+# controls the choices given to the user ...
+
+if [ "$CONFIG_PARPORT" = "y" -o "$CONFIG_PARPORT" = "n" ] ; then
+ define_bool CONFIG_PARIDE_PARPORT y
+else
+ define_bool CONFIG_PARIDE_PARPORT m
+fi
+dep_tristate 'Parallel port IDE device support' CONFIG_PARIDE $CONFIG_PARIDE_PARPORT
+if [ "$CONFIG_PARIDE" = "y" -o "$CONFIG_PARIDE" = "m" ]; then
+ source drivers/block/paride/Config.in
+fi
if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then
define_bool CONFIG_BLK_DEV_HD y
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index f992a98a5..39786eb97 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -14,12 +14,16 @@
# In the future, some of these should be built conditionally.
#
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS) paride
+
L_TARGET := block.a
-L_OBJS := ll_rw_blk.o genhd.o
+L_OBJS := genhd.o
M_OBJS :=
MOD_LIST_NAME := BLOCK_MODULES
-LX_OBJS :=
+LX_OBJS := ll_rw_blk.o
MX_OBJS :=
ifeq ($(CONFIG_MAC_FLOPPY),y)
@@ -86,11 +90,12 @@ ifeq ($(CONFIG_BLK_DEV_IDE),y)
L_OBJS += ide-probe.o
else
ifeq ($(CONFIG_BLK_DEV_IDE),m)
- MX_OBJS += ide.o
+ MIX_OBJS += ide.o
ifeq ($(CONFIG_PROC_FS),y)
M_OBJS += ide-proc.o
endif
- M_OBJS += ide-probe.o
+ M_OBJS += ide-mod.o
+ MX_OBJS += ide-probe.o
endif
endif
@@ -114,7 +119,6 @@ ifeq ($(CONFIG_BLK_DEV_PS2),y)
L_OBJS += ps2esdi.o
endif
-
ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
L_OBJS += dtc2278.o
endif
@@ -191,14 +195,6 @@ else
endif
endif
-ifeq ($(CONFIG_BLK_DEV_EZ),y)
-L_OBJS += ez.o
-else
- ifeq ($(CONFIG_BLK_DEV_EZ),m)
- M_OBJS += ez.o
- endif
-endif
-
ifeq ($(CONFIG_BLK_DEV_MD),y)
LX_OBJS += md.o
@@ -244,4 +240,16 @@ else
endif
endif
+ifeq ($(CONFIG_PARIDE),y)
+SUB_DIRS += paride
+MOD_SUB_DIRS += paride
+else
+ ifeq ($(CONFIG_PARIDE),m)
+ MOD_SUB_DIRS += paride
+ endif
+endif
+
include $(TOPDIR)/Rules.make
+
+ide-mod.o: ide.o ide-proc.o
+ $(LD) $(LD_RFLAG) -r -o $@ ide.o ide-proc.o
diff --git a/drivers/block/README.fd b/drivers/block/README.fd
index 045dbe63e..e34fa9f86 100644
--- a/drivers/block/README.fd
+++ b/drivers/block/README.fd
@@ -85,16 +85,20 @@ isn't, use the old method using environment variables.
This is needed on HP Omnibooks, which don't have a workable
DMA channel for the floppy driver. This option is also useful
if you frequently get "Unable to allocate DMA memory" messages.
- Indeed, dma memory needs to be continuous in physical, and is
- thus harder to find, whereas non-dma buffers may be allocated
- in virtual memory. However, I advise against this if you have
- an FDC without a FIFO (8272A or 82072). 82072A and later are
- OK. You also need at least a 486 to use nodma.
+ Indeed, dma memory needs to be continuous in physical memory,
+ and is thus harder to find, whereas non-dma buffers may be
+ allocated in virtual memory. However, I advise against this if
+ you have an FDC without a FIFO (8272A or 82072). 82072A and
+ later are OK). You also need at least a 486 to use nodma.
If you use nodma mode, I suggest you also set the FIFO
threshold to 10 or lower, in order to limit the number of data
transfer interrupts.
-
- floppy=dma
+
+ If you have a FIFO-able FDC, the floppy driver automatically
+ falls back on non DMA mode if no DMA-able memory can be found.
+ If you want to avoid this, explicitely ask for 'yesdma'.
+
+ floppy=yesdma
Tells the floppy driver that a workable DMA channel is available
(the default).
diff --git a/drivers/block/acsi.c b/drivers/block/acsi.c
index e14cf09c7..f04c0347f 100644
--- a/drivers/block/acsi.c
+++ b/drivers/block/acsi.c
@@ -59,8 +59,11 @@
#include <linux/major.h>
#include <linux/blk.h>
#include <linux/malloc.h>
-
#include <linux/interrupt.h>
+#include <scsi/scsi.h> /* for SCSI_IOCTL_GET_IDLUN */
+typedef void Scsi_Device; /* hack to avoid including scsi.h */
+#include <scsi/scsi_ioctl.h>
+#include <linux/hdreg.h> /* for HDIO_GETGEO */
#include <asm/setup.h>
#include <asm/pgtable.h>
@@ -70,7 +73,7 @@
#include <asm/atariints.h>
#include <asm/atari_acsi.h>
#include <asm/atari_stdma.h>
-#include <asm/atari_rootsec.h>
+#include <asm/atari_stram.h>
#define DEBUG
@@ -386,7 +389,7 @@ struct timer_list acsi_timer = { NULL, NULL, 0, 0, acsi_times_out };
#ifdef CONFIG_ATARI_SLM
-extern void attach_slm( int target, int lun );
+extern int attach_slm( int target, int lun );
extern int slm_init( void );
#endif
@@ -1121,12 +1124,30 @@ static int acsi_ioctl( struct inode *inode, struct file *file,
if (dev >= NDevices)
return -EINVAL;
switch (cmd) {
- /* I left out the GETGEO cmd; This doesn't make much sense for
- * ACSI disks...
- */
+ case HDIO_GETGEO:
+ /* HDIO_GETGEO is supported more for getting the partition's start
+ * sector... */
+ { struct hd_geometry *geo = (struct hd_geometry *)arg;
+ /* just fake some geometry here, it's nonsense anyway; to make it
+ * easy, use Adaptec's usual 64/32 mapping */
+ put_user( 64, &geo->heads );
+ put_user( 32, &geo->sectors );
+ put_user( acsi_info[dev].size >> 11, &geo->cylinders );
+ put_user( acsi_part[MINOR(inode->i_rdev)].start_sect, &geo->start );
+ return 0;
+ }
+
+ case SCSI_IOCTL_GET_IDLUN:
+ /* SCSI compatible GET_IDLUN call to get target's ID and LUN number */
+ put_user( acsi_info[dev].target | (acsi_info[dev].lun << 8),
+ &((Scsi_Idlun *) arg)->dev_id );
+ put_user( 0, &((Scsi_Idlun *) arg)->host_unique_id );
+ return 0;
+
case BLKGETSIZE: /* Return device size */
return put_user(acsi_part[MINOR(inode->i_rdev)].nr_sects,
(long *) arg);
+
case BLKFLSBUF:
if(!suser()) return -EACCES;
if(!inode->i_rdev) return -EINVAL;
@@ -1784,8 +1805,8 @@ int acsi_init( void )
return -EBUSY;
}
- if (!(acsi_buffer = (char *)__get_free_pages(GFP_KERNEL,
- ACSI_BUFFER_ORDER, 1))) {
+ if (!(acsi_buffer =
+ (char *)atari_stram_alloc( ACSI_BUFFER_SIZE, NULL, "acsi" ))) {
printk( KERN_ERR "Unable to get ACSI ST-Ram buffer.\n" );
unregister_blkdev( MAJOR_NR, "ad" );
return -ENOMEM;
@@ -1820,12 +1841,22 @@ int init_module(void)
void cleanup_module(void)
{
+ struct gendisk ** gdp;
+
del_timer( &acsi_timer );
blk_dev[MAJOR_NR].request_fn = 0;
- free_pages( (unsigned long)acsi_buffer, ACSI_BUFFER_ORDER );
+ atari_stram_free( acsi_buffer );
if (unregister_blkdev( MAJOR_NR, "ad" ) != 0)
printk( KERN_ERR "acsi: cleanup_module failed\n");
+
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+ if (*gdp == &acsi_gendisk)
+ break;
+ if (!*gdp)
+ printk( KERN_ERR "acsi: entry in disk chain missing!\n" );
+ else
+ *gdp = (*gdp)->next;
}
#endif
@@ -1854,7 +1885,7 @@ void cleanup_module(void)
static int revalidate_acsidisk( int dev, int maxusage )
{
- int device, major;
+ int device;
struct gendisk * gdev;
int max_p, start, i;
struct acsi_info_struct *aip;
@@ -1873,14 +1904,19 @@ static int revalidate_acsidisk( int dev, int maxusage )
max_p = gdev->max_p;
start = device << gdev->minor_shift;
- major = MAJOR_NR << 8;
for( i = max_p - 1; i >= 0 ; i-- ) {
- sync_dev( major | start | i );
- invalidate_inodes( major | start | i );
- invalidate_buffers( major | start | i );
+ if (gdev->part[start + i].nr_sects != 0) {
+ kdev_t devp = MKDEV(MAJOR_NR, start + i);
+ struct super_block *sb = get_super(devp);
+
+ fsync_dev(devp);
+ if (sb)
+ invalidate_inodes(sb);
+ invalidate_buffers(devp);
+ gdev->part[start + i].nr_sects = 0;
+ }
gdev->part[start+i].start_sect = 0;
- gdev->part[start+i].nr_sects = 0;
};
stdma_lock( NULL, NULL );
diff --git a/drivers/block/acsi_slm.c b/drivers/block/acsi_slm.c
index 64a401140..4b301cb2a 100644
--- a/drivers/block/acsi_slm.c
+++ b/drivers/block/acsi_slm.c
@@ -73,6 +73,7 @@ not be guaranteed. There are several ways to assure this:
#include <asm/atariints.h>
#include <asm/atari_acsi.h>
#include <asm/atari_stdma.h>
+#include <asm/atari_stram.h>
#include <asm/atari_SLM.h>
@@ -252,15 +253,15 @@ static struct {
static char *slm_errstr( int stat );
static int slm_getstats( char *buffer, int device );
-static long slm_read( struct inode *node, struct file* file, char *buf,
- unsigned long count );
+static ssize_t slm_read( struct file* file, char *buf, size_t count, loff_t
+ *ppos );
static void start_print( int device );
static void slm_interrupt(int irc, void *data, struct pt_regs *fp);
static void slm_test_ready( unsigned long dummy );
static void set_dma_addr( unsigned long paddr );
static unsigned long get_dma_addr( void );
-static long slm_write( struct inode *node, struct file *file, const char *buf,
- unsigned long count );
+static ssize_t slm_write( struct file *file, const char *buf, size_t count,
+ loff_t *ppos );
static int slm_ioctl( struct inode *inode, struct file *file, unsigned int
cmd, unsigned long arg );
static int slm_open( struct inode *inode, struct file *file );
@@ -372,10 +373,12 @@ static int slm_getstats( char *buffer, int device )
}
-static long slm_read( struct inode *node, struct file* file,
- char *buf, unsigned long count )
+static ssize_t slm_read( struct file *file, char *buf, size_t count,
+ loff_t *ppos )
-{ unsigned long page;
+{
+ struct inode *node = file->f_dentry->d_inode;
+ unsigned long page;
int length;
int end;
@@ -625,10 +628,12 @@ static unsigned long get_dma_addr( void )
}
-static long slm_write( struct inode *node, struct file *file,
- const char *buf, unsigned long count )
+static ssize_t slm_write( struct file *file, const char *buf, size_t count,
+ loff_t *ppos )
-{ int device = MINOR( node->i_rdev );
+{
+ struct inode *node = file->f_dentry->d_inode;
+ int device = MINOR( node->i_rdev );
int n, filled, w, h;
while( SLMState == PRINTING ||
@@ -1005,7 +1010,7 @@ int slm_init( void )
return -EBUSY;
}
- if (!(SLMBuffer = kmalloc( SLM_BUFFER_SIZE, GFP_KERNEL | GFP_DMA))) {
+ if (!(SLMBuffer = atari_stram_alloc( SLM_BUFFER_SIZE, NULL, "SLM" ))) {
printk( KERN_ERR "Unable to get SLM ST-Ram buffer.\n" );
unregister_chrdev( MAJOR_NR, "slm" );
return -ENOMEM;
@@ -1037,5 +1042,6 @@ void cleanup_module(void)
{
if (unregister_chrdev( MAJOR_NR, "slm" ) != 0)
printk( KERN_ERR "acsi_slm: cleanup_module failed\n");
+ atari_stram_free( SLMBuffer );
}
#endif
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
index ab11b62a8..60bcb042d 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -219,8 +219,8 @@ static struct wait_queue *ms_wait = NULL;
static int probing = 0;
/* Prevent "aliased" accesses. */
-static fd_ref[4] = { 0,0,0,0 };
-static fd_device[4] = { 0,0,0,0 };
+static int fd_ref[4] = { 0,0,0,0 };
+static int fd_device[4] = { 0,0,0,0 };
/*
* Current device number. Taken either from the block header or from the
@@ -678,7 +678,7 @@ static unsigned long *putsec(int disk, unsigned long *raw, int track, int cnt,
static void amiga_write(int disk, unsigned long raw, unsigned char *data,
int track)
{
- int cnt;
+ unsigned int cnt;
unsigned long *ptr = (unsigned long *)raw;
disk&=3;
@@ -1376,8 +1376,8 @@ unsigned long flags;
static void redo_fd_request(void)
{
- unsigned int block, track, sector;
- int device, drive, cnt;
+ unsigned int cnt, block, track, sector;
+ int device, drive;
struct amiga_floppy_struct *floppy;
char *data;
unsigned long flags;
@@ -1511,7 +1511,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp,
{
int drive = inode->i_rdev & 3;
static struct floppy_struct getprm;
- int error;
+ struct super_block * sb;
unsigned long flags;
switch(cmd){
@@ -1522,9 +1522,9 @@ static int fd_ioctl(struct inode *inode, struct file *filp,
loc.sectors = unit[drive].sects;
loc.cylinders = unit[drive].type->tracks;
loc.start = 0;
- if ((error = copy_to_user((void *)param, (void *)&loc,
- sizeof(struct hd_geometry))))
- return error;
+ if (copy_to_user((void *)param, (void *)&loc,
+ sizeof(struct hd_geometry)))
+ return -EFAULT;
break;
}
case FDFMTBEG:
@@ -1566,7 +1566,9 @@ static int fd_ioctl(struct inode *inode, struct file *filp,
break;
case FDFMTEND:
floppy_off(drive);
- invalidate_inodes(inode->i_rdev);
+ sb = get_super(inode->i_rdev);
+ if (sb)
+ invalidate_inodes(sb);
invalidate_buffers(inode->i_rdev);
break;
case FDGETPRM:
@@ -1575,15 +1577,13 @@ static int fd_ioctl(struct inode *inode, struct file *filp,
getprm.head=unit[drive].type->heads;
getprm.sect=unit[drive].sects;
getprm.size=unit[drive].blocks;
- if ((error = copy_to_user((void *)param,
- (void *)&getprm,
- sizeof(struct floppy_struct))))
- return error;
+ if (copy_to_user((void *)param,
+ (void *)&getprm,
+ sizeof(struct floppy_struct)))
+ return -EFAULT;
break;
case BLKGETSIZE:
- if (put_user(unit[drive].blocks,(long *)param))
- return -EFAULT;
- break;
+ return put_user(unit[drive].blocks,(long *)param);
case FDSETPRM:
case FDDEFPRM:
return -EINVAL;
@@ -1600,10 +1600,9 @@ static int fd_ioctl(struct inode *inode, struct file *filp,
break;
#ifdef RAW_IOCTL
case IOCTL_RAW_TRACK:
- error = copy_to_user((void *)param, raw_buf,
- unit[drive].type->read_size);
- if (error)
- return error;
+ if (copy_to_user((void *)param, raw_buf,
+ unit[drive].type->read_size))
+ return -EFAULT;
else
return unit[drive].type->read_size;
#endif
@@ -1691,6 +1690,7 @@ static void fd_probe(int dev)
if (type >= num_dr_types) {
printk(KERN_WARNING "fd_probe: unsupported drive type %08lx found\n",
code);
+ unit[drive].type = &drive_types[num_dr_types-1]; /* FD_NODRIVE */
return;
}
@@ -1795,9 +1795,12 @@ printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,unit[drive].ty
static int floppy_release(struct inode * inode, struct file * filp)
{
unsigned long flags;
+ struct super_block * sb;
fsync_dev(inode->i_rdev);
- invalidate_inodes(inode->i_rdev);
+ sb = get_super(inode->i_rdev);
+ if (sb)
+ invalidate_inodes(sb);
invalidate_buffers(inode->i_rdev);
save_flags (flags);
cli();
@@ -1819,10 +1822,10 @@ static int floppy_release(struct inode * inode, struct file * filp)
return 0;
}
-void amiga_floppy_setup (char *str, int *ints)
+__initfunc(void amiga_floppy_setup (char *str, int *ints))
{
-printk ("amiflop: Setting default df0 to %x\n", ints[1]);
-fd_def_df0 = ints[1];
+ printk ("amiflop: Setting default df0 to %x\n", ints[1]);
+ fd_def_df0 = ints[1];
}
static struct file_operations floppy_fops = {
diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c
index 3e0c28379..445e90fca 100644
--- a/drivers/block/ataflop.c
+++ b/drivers/block/ataflop.c
@@ -89,6 +89,7 @@
#include <asm/atarihw.h>
#include <asm/atariints.h>
#include <asm/atari_stdma.h>
+#include <asm/atari_stram.h>
#define MAJOR_NR FLOPPY_MAJOR
#include <linux/blk.h>
@@ -278,7 +279,6 @@ static int BufferSize[] = {
15*512, 30*512, 60*512
};
-#define MAX_SECTORS (MaxSectors[DriveType])
#define BUFFER_SIZE (BufferSize[DriveType])
unsigned char *DMABuffer; /* buffer for writes */
@@ -395,6 +395,7 @@ static void fd_seek_done( int status );
static void fd_rwsec( void );
static void fd_readtrack_check( unsigned long dummy );
static void fd_rwsec_done( int status );
+static void fd_rwsec_done1(int status);
static void fd_writetrack( void );
static void fd_writetrack_done( int status );
static void fd_times_out( unsigned long dummy );
@@ -459,6 +460,7 @@ static void fd_select_drive( int drive )
sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */
tmp = sound_ym.rd_data_reg_sel;
sound_ym.wd_data = (tmp | DSKDRVNONE) & ~(drive == 0 ? DSKDRV0 : DSKDRV1);
+ atari_dont_touch_floppy_select = 1;
restore_flags(flags);
/* restore track register to saved value */
@@ -482,8 +484,12 @@ static void fd_deselect( void )
save_flags(flags);
cli(); /* protect against various other ints mucking around with the PSG */
+ atari_dont_touch_floppy_select = 0;
sound_ym.rd_data_reg_sel=14; /* Select PSG Port A */
- sound_ym.wd_data = sound_ym.rd_data_reg_sel | 7; /* no drives selected */
+ sound_ym.wd_data = (sound_ym.rd_data_reg_sel |
+ (MACH_IS_FALCON ? 3 : 7)); /* no drives selected */
+ /* On Falcon, the drive B select line is used on the printer port, so
+ * leave it alone... */
SelectedDrive = -1;
restore_flags(flags);
}
@@ -977,11 +983,12 @@ static void fd_rwsec( void )
* search for the first non-existent sector and need 1 sec to
* recognise that it isn't present :-(
*/
+ del_timer (&readtrack_timer);
readtrack_timer.expires =
jiffies + HZ/5 + (old_motoron ? 0 : HZ);
/* 1 rot. + 5 rot.s if motor was off */
- add_timer( &readtrack_timer );
MultReadInProgress = 1;
+ add_timer( &readtrack_timer );
}
START_TIMEOUT();
}
@@ -1028,6 +1035,7 @@ static void fd_readtrack_check( unsigned long dummy )
* the read operation
*/
SET_IRQ_HANDLER( NULL );
+ MultReadInProgress = 0;
restore_flags(flags);
DPRINT(("fd_readtrack_check(): done\n"));
FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
@@ -1036,7 +1044,7 @@ static void fd_readtrack_check( unsigned long dummy )
/* No error until now -- the FDC would have interrupted
* otherwise!
*/
- fd_rwsec_done( 0 );
+ fd_rwsec_done1(0);
}
else {
/* not yet finished, wait another tenth rotation */
@@ -1050,19 +1058,23 @@ static void fd_readtrack_check( unsigned long dummy )
static void fd_rwsec_done( int status )
{
- unsigned int track;
-
DPRINT(("fd_rwsec_done()\n"));
- STOP_TIMEOUT();
-
if (read_track) {
+ del_timer(&readtrack_timer);
if (!MultReadInProgress)
return;
MultReadInProgress = 0;
- del_timer( &readtrack_timer );
}
+ fd_rwsec_done1(status);
+}
+static void fd_rwsec_done1(int status)
+{
+ unsigned int track;
+
+ STOP_TIMEOUT();
+
/* Correct the track if stretch != 0 */
if (SUDT->stretch) {
track = FDC_READ( FDCREG_TRACK);
@@ -1147,7 +1159,7 @@ static void fd_rwsec_done( int status )
if (!ATARIHW_PRESENT( EXTD_DMA ))
copy_buffer (addr, ReqData);
} else {
- dma_cache_maintenance( PhysTrackBuffer, MAX_SECTORS * 512, 0 );
+ dma_cache_maintenance( PhysTrackBuffer, MaxSectors[DriveType] * 512, 0 );
BufferDrive = SelectedDrive;
BufferSide = ReqSide;
BufferTrack = ReqTrack;
@@ -1802,7 +1814,7 @@ __initfunc(static void fd_probe( int drive ))
UD.steprate = FDCSTEP_12;
break;
default: /* should be -1 for "not set by user" */
- if (ATARIHW_PRESENT( FDCSPEED ) || is_medusa)
+ if (ATARIHW_PRESENT( FDCSPEED ) || MACH_IS_MEDUSA)
UD.steprate = FDCSTEP_3;
else
UD.steprate = FDCSTEP_6;
@@ -1827,7 +1839,7 @@ __initfunc(static int fd_test_drive_present( int drive ))
unsigned char status;
int ok;
- if (drive > 1) return( 0 );
+ if (drive >= (MACH_IS_FALCON ? 1 : 2)) return( 0 );
fd_select_drive( drive );
/* disable interrupt temporarily */
@@ -2019,6 +2031,14 @@ __initfunc(int atari_floppy_init (void))
{
int i;
+ if (!MACH_IS_ATARI)
+ /* Amiga, Mac, ... don't have Atari-compatible floppy :-) */
+ return -ENXIO;
+
+ if (MACH_IS_HADES)
+ /* Hades doesn't have Atari-compatible floppy */
+ return -ENXIO;
+
if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
printk(KERN_ERR "Unable to get major %d for floppy\n",MAJOR_NR);
return -EBUSY;
@@ -2029,7 +2049,7 @@ __initfunc(int atari_floppy_init (void))
track buffering off for all Medusas, though it
could be used with ones that have a counter
card. But the test is too hard :-( */
- UseTrackbuffer = !is_medusa;
+ UseTrackbuffer = !MACH_IS_MEDUSA;
/* initialize variables */
SelectedDrive = -1;
@@ -2039,7 +2059,7 @@ __initfunc(int atari_floppy_init (void))
timer_table[FLOPPY_TIMER].fn = check_change;
timer_active &= ~(1 << FLOPPY_TIMER);
- DMABuffer = kmalloc(BUFFER_SIZE + 512, GFP_KERNEL | GFP_DMA);
+ DMABuffer = atari_stram_alloc( BUFFER_SIZE+512, NULL, "ataflop" );
if (!DMABuffer) {
printk(KERN_ERR "atari_floppy_init: cannot get dma buffer\n");
unregister_blkdev(MAJOR_NR, "fd");
@@ -2071,6 +2091,7 @@ __initfunc(int atari_floppy_init (void))
UseTrackbuffer ? "" : "no ");
config_types();
+ (void)do_floppy; /* avoid warning about unused variable */
return 0;
}
@@ -2118,7 +2139,7 @@ void cleanup_module (void)
blk_dev[MAJOR_NR].request_fn = 0;
timer_active &= ~(1 << FLOPPY_TIMER);
timer_table[FLOPPY_TIMER].fn = 0;
- kfree (DMABuffer);
+ atari_stram_free( DMABuffer );
}
#endif
diff --git a/drivers/block/ez.c b/drivers/block/ez.c
deleted file mode 100644
index 6c2960a5a..000000000
--- a/drivers/block/ez.c
+++ /dev/null
@@ -1,1022 +0,0 @@
-/*
- ez.c (c) 1996 Grant R. Guenther <grant@torque.net>
- Under the terms of the GNU public license.
-
- This is a driver for the parallel port versions of SyQuest's
- EZ135 and EZ230 removable media disk drives.
-
- Special thanks go to Pedro Soria-Rodriguez for his help testing
- the EZFlyer 230 support.
-
- The drive is actually SyQuest's IDE product with a
- ShuttleTech IDE <-> parallel converter chip built in.
-
- To compile the driver, ensure that /usr/include/linux and
- /usr/include/asm are links to the correct include files for
- the target system. Then compile the driver with
-
- cc -D__KERNEL__ -DMODULE -O2 -c ez.c
-
- If you are using MODVERSIONS, add the following to the cc command:
-
- -DMODVERSIONS -I /usr/include/linux/modversions.h
-
- You must then load it with insmod.
-
- Before attempting to access the new driver, you will need to
- create some device special files. The following commands will
- do that for you:
-
- mknod /dev/eza b 40 0
- mknod /dev/eza1 b 40 1
- mknod /dev/eza2 b 40 2
- mknod /dev/eza3 b 40 3
- mknod /dev/eza4 b 40 4
- chown root:disk /dev/ez*
- chmod 660 /dev/ez*
-
- You can make devices for more partitions (up to 15) if you need to.
-
- You can alter the port used by the driver in two ways: either
- change the definition of EZ_BASE or modify the ez_base variable
- on the insmod command line, for example:
-
- insmod ez ez_base=0x3bc
-
- The driver can detect if the parallel port supports 8-bit
- transfers. If so, it will use them. You can force it to use
- 4-bit (nybble) mode by setting the variable ez_nybble to 1.
-
- The driver can be used with or without interrupts. If an IRQ
- is specified in the variable ez_irq, the driver will use it.
- If ez_irq is set to 0, an alternative, polling-based, strategy
- will be used.
-
- If you experience timeout errors while using this driver - and
- you have enabled interrupts - try disabling the interrupt. I
- have heard reports of some parallel ports having exceptionally
- unreliable interrupts. This could happen on misconfigured
- systems in which an inactive sound card shares the same IRQ with
- the parallel port. (Remember that most people do not use the
- parallel port interrupt for printing.)
-
- It would be advantageous to use multiple mode transfers,
- but ShuttleTech's driver does not appear to use them, so I'm not
- sure that the converter can handle it.
-
- It is not currently possible to connect a printer to the chained
- port on the EZ135p and expect Linux to use both devices at once.
-
- When the EZ230 powers on, the "standby timer" is set to about 6
- minutes: if the drive is idle for that length of time, it will
- put itself into a low power standby mode. It takes a couple of
- seconds for the drive to come out of standby mode. So, if you
- load this driver while it is in standby mode, you will notice
- a "freeze" of a second or two as the driver waits for the EZ230
- to come back to life. Once loaded, this driver disables the
- standby timer (until you next power up the EZ230 ...)
-
- Keep an eye on http://www.torque.net/ez135.html for news and
- other information about the driver. If you have any problems
- with this driver, please send me, grant@torque.net, some mail
- directly before posting into the newsgroups or mailing lists.
-
-*/
-
-#define EZ_VERSION "0.11"
-
-#define EZ_BASE 0x378
-#define EZ_IRQ 7
-#define EZ_REP 4
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/fs.h>
-#include <linux/kernel.h>
-#include <linux/tqueue.h>
-#include <linux/timer.h>
-#include <linux/delay.h>
-#include <linux/genhd.h>
-#include <linux/hdreg.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-#define EZ_BITS 4 /* compatible with SCSI version */
-#define EZ_MAJOR 40 /* as assigned by hpa */
-
-#define MAJOR_NR EZ_MAJOR
-
-/* set up defines for blk.h, why don't all drivers do it this way ? */
-
-#define DEVICE_NAME "ez"
-#define DEVICE_REQUEST do_ez_request
-#define DEVICE_NR(device) (MINOR(device)>>EZ_BITS)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#include <linux/blk.h>
-
-#define EZ_PARTNS (1<<EZ_BITS)
-
-#define EZ_LOG_HEADS 64
-#define EZ_LOG_SECTS 32 /* SCSI compatible logical geometry */
-
-#define EZ_SIGOFF 54
-#define EZ_SIG "ySuQse tZE"
-#define EZ_SIGLEN 10
-#define EZ_ID_LEN 14
-
-#define EZ_TMO 250 /* interrupt timeout in jiffies */
-
-#define EZ_SPIN_DEL 50 /* spin delay in micro-seconds */
-
-#define EZ_SPIN (10000/EZ_SPIN_DEL)*EZ_TMO
-#define EZ_ISPIN (10000/EZ_SPIN_DEL)*20
-#define EZ_DELAY udelay(EZ_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_STANDBY 0x96
-#define IDE_DOORLOCK 0xde
-#define IDE_DOORUNLOCK 0xdf
-#define IDE_ACKCHANGE 0xdb
-#define IDE_IDENTIFY 0xec
-
-int ez_init(void);
-void ez_setup(char * str, int * ints);
-#ifdef MODULE
-void cleanup_module( void );
-#endif
-static void ez_geninit(struct gendisk *ignored);
-static int ez_open(struct inode *inode, struct file *file);
-static void do_ez_request(void);
-static int ez_ioctl(struct inode *inode,struct file *file,
- unsigned int cmd, unsigned long arg);
-static int ez_release (struct inode *inode, struct file *file);
-static int ez_revalidate(kdev_t dev);
-static int ez_check_media(kdev_t dev);
-static void ez_get_capacity( void );
-static int ez_detect(void);
-static void do_ez_read(void);
-static void do_ez_write(void);
-static void ez_media_check(void);
-static void ez_doorlock(int func);
-static void ez_interrupt( int irq, void * dev_id, struct pt_regs * regs);
-static void ez_pseudo( void *data);
-static void ez_timer_int( unsigned long data);
-static void do_ez_read_drq( void );
-static void do_ez_write_done( void );
-
-static struct hd_struct ez[EZ_PARTNS];
-static int ez_sizes[EZ_PARTNS];
-static int ez_blocksizes[EZ_PARTNS];
-
-static int ez_base = EZ_BASE;
-static int ez_irq = EZ_IRQ;
-static int ez_rep = EZ_REP;
-static int ez_nybble = 0; /* force 4-bit mode ? */
-
-static int ez_valid = 0; /* OK to open */
-static int ez_access = 0; /* count of active opens ... */
-static int ez_changed = 0; /* Did we see new media on open ? */
-static int ez_capacity = 512*16*32; /* Size of this volume in sectors */
-static int ez_heads = 16; /* physical geometry */
-static int ez_sectors = 32;
-static int ez_mode = 1; /* 4- or 8-bit mode */
-static int ez_loops = 0; /* counter for pseudo-interrupts */
-static int ez_timeout = 0; /* did the interrupt time out ? */
-static int ez_int_seen = 0; /* have we ever seen an interrupt ? */
-static int ez_busy = 0; /* request being processed ? */
-static int ez_block; /* address of next requested block */
-static int ez_count; /* number of blocks still to do */
-static char * ez_buf; /* buffer for request in progress */
-static char ez_scratch[512]; /* scratch block buffer */
-static void (*ez_continuation)(void); /* i/o completion handler */
-
-char *ez_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR",
- "READY","BUSY","AMNF","TK0NF","ABRT","MCR",
- "IDNF","MC","UNC","???","TMO"};
-
-static struct tq_struct ez_tq = {0,0,ez_pseudo,NULL};
-static struct timer_list ez_timer = {0,0,0,0,ez_timer_int};
-static struct wait_queue *ez_wait_open = NULL;
-
-/* kernel glue structures */
-
-static struct gendisk ez_gendisk = {
- MAJOR_NR, /* Major number */
- "ez", /* Major name */
- EZ_BITS, /* Bits to shift to get real from partition */
- EZ_PARTNS, /* Number of partitions per real */
- 1, /* maximum number of real */
- ez_geninit, /* init function */
- ez, /* hd struct */
- ez_sizes, /* block sizes */
- 0, /* number */
- NULL, /* internal */
- NULL /* next */
-};
-
-static struct file_operations ez_fops = {
- NULL, /* lseek - default */
- block_read, /* read - general block-dev read */
- block_write, /* write - general block-dev write */
- NULL, /* readdir - bad */
- NULL, /* select */
- ez_ioctl, /* ioctl */
- NULL, /* mmap */
- ez_open, /* open */
- ez_release, /* release */
- block_fsync, /* fsync */
- NULL, /* fasync */
- ez_check_media, /* media change ? */
- ez_revalidate /* revalidate new media */
-};
-
-__initfunc(int ez_init (void)) /* preliminary initialisation */
-
-{
- if (register_blkdev(MAJOR_NR,"ez",&ez_fops)) {
- printk("ez_init: unable to get major number %d\n",MAJOR_NR);
- return -1;
- }
- blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
- read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
- ez_gendisk.next = gendisk_head;
- gendisk_head = &ez_gendisk;
-
- return 0;
-}
-
-__initfunc(static void ez_geninit (struct gendisk *ignored)) /* real init */
-
-{ int i;
-
- ez_gendisk.nr_real = 0;
-
- if (ez_detect()) {
- ez_busy = 0;
- ez_valid = 1;
- ez_gendisk.nr_real = 1;
- ez[0].nr_sects = ez_capacity;
- for(i=0;i<EZ_PARTNS;i++) ez_blocksizes[i] = 1024;
- blksize_size[MAJOR_NR] = ez_blocksizes;
- }
-#ifdef MODULE
- else cleanup_module();
-#endif
-}
-
-static int ez_open (struct inode *inode, struct file *file)
-
-{ int dev = DEVICE_NR(inode->i_rdev);
-
- if (dev >= ez_gendisk.nr_real) return -ENODEV;
-
- MOD_INC_USE_COUNT;
-
- while (!ez_valid) sleep_on(&ez_wait_open);
- ez_access++;
- ez_media_check();
- ez_doorlock(IDE_DOORLOCK);
- return 0;
-}
-
-static void do_ez_request (void)
-
-{ int dev;
-
- if (ez_busy) return;
-repeat:
- if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
- INIT_REQUEST;
-
- dev = MINOR(CURRENT->rq_dev);
- ez_block = CURRENT->sector;
- ez_count = CURRENT->nr_sectors;
-
- if ((dev >= EZ_PARTNS) || ((ez_block+ez_count) > ez[dev].nr_sects)) {
- end_request(0);
- goto repeat;
- }
-
- ez_block += ez[dev].start_sect;
- ez_buf = CURRENT->buffer;
-
- if (CURRENT->cmd == READ) do_ez_read();
- else if (CURRENT->cmd == WRITE) do_ez_write();
- else { end_request(0);
- goto repeat;
- }
-}
-
-static int ez_ioctl(struct inode *inode,struct file *file,
- unsigned int cmd, unsigned long arg)
-
-{ struct hd_geometry *geo = (struct hd_geometry *) arg;
- int dev, err;
-
- if ((!inode) || (!inode->i_rdev)) return -EINVAL;
- dev = MINOR(inode->i_rdev);
- if (dev >= EZ_PARTNS) return -EINVAL;
-
- switch (cmd) {
- case HDIO_GETGEO:
- if (!geo) return -EINVAL;
- err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
- if (err) return err;
- put_user(ez_capacity/(EZ_LOG_HEADS*EZ_LOG_SECTS),
- (short *) &geo->cylinders);
- put_user(EZ_LOG_HEADS, (char *) &geo->heads);
- put_user(EZ_LOG_SECTS, (char *) &geo->sectors);
- put_user(ez[dev].start_sect,(long *)&geo->start);
- return 0;
- case BLKRASET:
- if(!suser()) return -EACCES;
- if(!(inode->i_rdev)) return -EINVAL;
- if(arg > 0xff) return -EINVAL;
- read_ahead[MAJOR(inode->i_rdev)] = arg;
- return 0;
- case BLKRAGET:
- if (!arg) return -EINVAL;
- err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
- if (err) return (err);
- put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
- return (0);
- case BLKGETSIZE:
- if (!arg) return -EINVAL;
- err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
- if (err) return (err);
- put_user(ez[dev].nr_sects,(long *) arg);
- return (0);
- case BLKFLSBUF:
- if(!suser()) return -EACCES;
- if(!(inode->i_rdev)) return -EINVAL;
- fsync_dev(inode->i_rdev);
- invalidate_buffers(inode->i_rdev);
- return 0;
- case BLKRRPART:
- return ez_revalidate(inode->i_rdev);
- RO_IOCTLS(inode->i_rdev,arg);
- default:
- return -EINVAL;
- }
-}
-
-static int ez_release (struct inode *inode, struct file *file)
-
-{ kdev_t devp;
-
- devp = inode->i_rdev;
- if (DEVICE_NR(devp) == 0) {
- fsync_dev(devp);
- invalidate_inodes(devp);
- invalidate_buffers(devp);
- ez_access--;
- if (!ez_access) ez_doorlock(IDE_DOORUNLOCK);
- MOD_DEC_USE_COUNT;
- }
- return 0;
-}
-
-static int ez_check_media( kdev_t dev)
-
-{ int t;
-
- t = ez_changed;
- ez_changed = 0;
- return t;
-}
-
-static int ez_revalidate(kdev_t dev)
-
-{ int p;
- long flags;
- kdev_t devp;
-
- save_flags(flags);
- cli();
- if (ez_access > 1) {
- restore_flags(flags);
- return -EBUSY;
- }
- ez_valid = 0;
- restore_flags(flags);
-
- for (p=(EZ_PARTNS-1);p>=0;p--) {
- devp = MKDEV(MAJOR_NR, p);
- fsync_dev(devp);
- invalidate_inodes(devp);
- invalidate_buffers(devp);
- ez[p].start_sect = 0;
- ez[p].nr_sects = 0;
- }
-
- ez_get_capacity();
- ez[0].nr_sects = ez_capacity;
- resetup_one_dev(&ez_gendisk,0);
-
- ez_valid = 1;
- wake_up(&ez_wait_open);
-
- return 0;
-}
-
-#ifdef MODULE
-
-/* Glue for modules ... */
-
-void cleanup_module(void);
-
-int init_module(void)
-
-{ int err;
- long flags;
-
- save_flags(flags);
- cli();
-
- err = ez_init();
- if (err) {
- restore_flags(flags);
- return err;
- }
- ez_geninit(&ez_gendisk);
-
- if (!ez_gendisk.nr_real) {
- restore_flags(flags);
- return -1;
- }
-
- ez_valid = 0;
- resetup_one_dev(&ez_gendisk,0);
- ez_valid = 1;
-
- restore_flags(flags);
- return 0;
-}
-
-void cleanup_module(void)
-
-{ struct gendisk **gdp;
- long flags;
-
- save_flags(flags);
- cli();
-
- unregister_blkdev(MAJOR_NR,"ez");
-
- for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next))
- if (*gdp == &ez_gendisk) break;
- if (*gdp) *gdp = (*gdp)->next;
-
- if (ez_gendisk.nr_real) {
- release_region(ez_base,3);
- if (ez_irq) free_irq(ez_irq,NULL);
- }
-
- restore_flags(flags);
-}
-
-#else
-
-/* ez_setup: process lilo command parameters ...
-
- syntax: ez=base[,irq[,rep[,nybble]]]
-*/
-
-__initfunc(void ez_setup(char *str, int *ints))
-
-{ if (ints[0] > 0) ez_base = ints[1];
- if (ints[0] > 1) ez_irq = ints[2];
- if (ints[0] > 2) ez_rep = ints[3];
- if (ints[0] > 3) ez_nybble = ints[4];
-}
-
-#endif
-
-/* Now the actual hardware interface to the EZ135p */
-
-static void out_p( short port, char byte)
-
-{ int i;
-
- for(i=0;i<ez_rep;i++) outb(byte,ez_base+port);
-}
-
-static int in_p( short port)
-
-{ int i;
- char c;
-
- c=inb(ez_base+port);
- for(i=1;i<ez_rep;i++) c=inb(ez_base+port);
- return c & 0xff;
-}
-
-#define w0(byte) out_p(0,byte)
-#define w2(byte) out_p(2,byte)
-#define r0() (in_p(0) & 0xff)
-#define r1() (in_p(1) & 0xff)
-
-/* register access functions */
-
-static int read_regr( char regr )
-
-{ int h, l;
-
- if (ez_mode == 1) { /* nybble mode */
- w0(regr);
- w2(1); w2(3);
- l = r1() >> 4;
- w2(4);
- h = r1() & 0xf0;
- return h + l;
- } else { /* byte mode */
- w0(regr+0x20);
- w2(1); w2(0x25);
- h = r0();
- w2(4);
- return h;
- }
-}
-
-static void write_regr( char regr, char val )
-
-{ w0(regr);
- w2(1);
- w0(val);
- w2(4);
-}
-
-/* connect / disconnect code */
-
-static void prefix( char byte )
-
-{ w2(4); w0(0x22); w0(0xaa); w0(0x55); w0(0);
- w0(0xff); w0(0x87); w0(0x78); w0(byte);
- w2(5); w2(4); w0(0xff);
-}
-
-static void connect ( void )
-
-{ prefix(0x40); prefix(0x50); prefix(0xe0);
- w0(0); w2(1); w2(4);
- read_regr(0xd);
- write_regr(0x6d,0xe8);
- write_regr(0x6c,0x1c);
- write_regr(0x72,0x10);
- write_regr(0x6a,0x38);
- write_regr(0x68,0x10);
- read_regr(0x12);
- write_regr(0x72,0x10);
- read_regr(0xd);
- write_regr(0x6d,0xaa);
- write_regr(0x6d,0xaa);
-}
-
-static void disconnect ( void )
-
-{ read_regr(0xd);
- write_regr(0x6d,0xa8);
- prefix(0x30);
-}
-
-/* basic i/o */
-
-static void read_block( char * buf )
-
-/* the nybble mode read has a curious optimisation in it: there are actually
- five bits available on each read. The extra bit is used to signal that
- the next nybble is identical ... I wonder how much research went into
- designing this use of the extra bit ?
-*/
-
-{ int j, k, n0, n1, n2, n3;
-
- read_regr(0xd); write_regr(0x6d,0xe9);
-
- j = 0;
- if (ez_mode == 1) { /* nybble mode */
-
- w0(7); w2(1); w2(3); w0(0xff);
- for(k=0;k<256;k++) {
- w2(6); n0 = r1();
- if (n0 & 8) n1 = n0; else { w2(4); n1 = r1(); }
- w2(7); n2 = r1();
- if (n2 & 8) n3 = n2; else { w2(5); n3 = r1(); }
- buf[j++] = (n0 >> 4) + (n1 & 0xf0);
- buf[j++] = (n2 >> 4) + (n3 & 0xf0);
- }
-
- } else { /* byte mode */
-
- w0(0x27); w2(1); w2(0x25); w0(0);
- for(k=0;k<256;k++) {
- w2(0x24); buf[j++] = r0();
- w2(0x25); buf[j++] = r0();
- }
- w2(0x26); w2(0x27); w0(0); w2(0x25); w2(4);
-
- }
-}
-
-static void write_block( char * buf )
-
-{ int j;
-
- read_regr(0xd); write_regr(0x6d,0xe9);
-
- w0(0x67); w2(1); w2(5);
- for(j=0;j<256;j++) {
- w0(buf[2*j]); w2(4);
- w0(buf[2*j+1]); w2(5);
- }
- w2(7); w2(4);
-}
-
-/* ide command interface */
-
-void ez_print_error( char * msg, int status )
-
-{ char *e, *p;
- int i;
-
- e = ez_scratch;
- for(i=0;i<18;i++) if (status & (1<<i)) {
- p = ez_errs[i];
- while ((*e++=*p++));
- *(e-1) = ' ';
- }
- if (status) e--;
- *e = 0;
- printk("ez: %s: status = 0x%x (%s)\n",msg,status,ez_scratch);
-}
-
-static int wait_for( int w, char * msg ) /* polled wait */
-
-{ int k, r, e;
-
- k=0;
- while(k < EZ_SPIN) {
- r = read_regr(0x1f);
- k++;
- if (ez_timeout) break;
- if (((r & w) == w) && !(r & STAT_BUSY)) break;
- EZ_DELAY;
- }
- e = (read_regr(0x19)<<8) + r;
- if ((k >= EZ_SPIN) || ez_timeout) e |= (ERR_TMO|STAT_ERR);
- if ((e & STAT_ERR) & (msg != NULL)) ez_print_error(msg,e);
- return e;
-}
-
-static void send_command( int n, int s, int h, int c0, int c1, int func )
-
-{
- read_regr(0xd); write_regr(0x6d,0xa9);
-
- write_regr(0x76,0);
- write_regr(0x79,0); /* the IDE task file */
- write_regr(0x7a,n);
- write_regr(0x7b,s);
- write_regr(0x7c,c0);
- write_regr(0x7d,c1);
- write_regr(0x7e,0xa0+h);
- write_regr(0x7f,func);
-
- udelay(1);
-}
-
-static void ez_ide_command( int func, int block )
-
-{ int c1, c0, h, s;
-
- s = ( block % ez_sectors) + 1;
- h = ( block / ez_sectors) % ez_heads;
- c0 = ( block / (ez_sectors*ez_heads)) % 256;
- c1 = ( block / (ez_sectors*ez_heads*256));
-
- send_command(1,s,h,c0,c1,func);
-}
-
-static void ez_gate_intr( int flag )
-
-{ if (flag) write_regr(0x6d,0x39); /* gate interrupt line to bus */
- if (flag && ez_irq) w2(0x14); /* enable IRQ */
- if (!flag) w2(4); /* disable IRQ */
-}
-
-static int check_int( void ) /* is the interrupt bit set ? */
-
-{ return (r1() & 0x40);
-}
-
-static void ez_doorlock( int func )
-
-{ connect();
- if (wait_for(STAT_READY,"Lock") & STAT_ERR) {
- disconnect();
- return;
- }
- ez_ide_command(func,0);
- wait_for(STAT_READY,"Lock done");
- disconnect();
-}
-
-/* ez_media_check: check for and acknowledge the MC flag */
-
-__initfunc(static void ez_media_check( void ))
-
-{ int r;
-
- ez_changed = 0;
- connect();
- r = wait_for(STAT_READY,"Media check ready");
- if (!(r & STAT_ERR)) {
- ez_ide_command(IDE_READ,0); /* try to read block 0 */
- r = wait_for(STAT_DRQ,"Media check");
- if (!(r & STAT_ERR)) read_block(ez_scratch);
- } else ez_changed = 1; /* say changed if other error */
- if (r & ERR_MC) {
- ez_changed = 1;
- ez_ide_command(IDE_ACKCHANGE,0);
- wait_for(STAT_READY,"Ack. media change");
- }
- disconnect();
-}
-
-__initfunc(static int ez_identify( void ))
-
-
-{ int k, r;
-
- connect();
- wait_for(0,NULL); /* wait until not busy, quietly */
- ez_ide_command(IDE_IDENTIFY,0);
-
- if (ez_irq) { /* check that the interrupt works */
- ez_gate_intr(1);
- k = 0;
- while ((k++ < EZ_ISPIN) && !ez_int_seen) EZ_DELAY;
- ez_gate_intr(0);
- r = read_regr(0x1f);
- if ((!ez_int_seen) || !(r & STAT_DRQ)) {
- free_irq(ez_irq,NULL);
- ez_irq = 0;
- }
- }
-
- if (wait_for(STAT_DRQ,NULL) & STAT_ERR) {
- disconnect();
- return 0;
- }
- read_block(ez_scratch);
- disconnect();
- return 1;
-}
-
-#define word_val(n) (ez_scratch[2*n]+256*ez_scratch[2*n+1])
-
-__initfunc(static void ez_get_capacity( void ))
-
-{ int ez_cylinders;
-
- connect();
- wait_for(0,NULL);
- ez_ide_command(IDE_IDENTIFY,0);
- if (wait_for(STAT_DRQ,"Get capacity") & STAT_ERR) {
- disconnect();
- return;
- }
- read_block(ez_scratch);
- disconnect();
- ez_sectors = word_val(6);
- ez_heads = word_val(3);
- ez_cylinders = word_val(1);
- ez_capacity = ez_sectors*ez_heads*ez_cylinders;
- printk("ez: Capacity = %d, (%d/%d/%d)\n",ez_capacity,ez_cylinders,
- ez_heads,ez_sectors);
-}
-
-__initfunc(static void ez_standby_off( void ))
-
-{ connect();
- wait_for(0,NULL);
- send_command(0,0,0,0,0,IDE_STANDBY);
- wait_for(0,NULL);
- disconnect();
-}
-
-__initfunc(static int ez_port_check( void )) /* check for 8-bit port */
-
-{ int r;
-
- w2(0);
- w0(0x55); if (r0() != 0x55) return 0;
- w0(0xaa); if (r0() != 0xaa) return 0;
- w2(0x20); w0(0x55); r = r0(); w0(0xaa);
- if (r0() == r) return 2;
- if (r0() == 0xaa) return 1;
- return 0;
-}
-
-__initfunc(static int ez_detect( void ))
-
-{ int j, k;
- char sig[EZ_SIGLEN] = EZ_SIG;
- char id[EZ_ID_LEN+1];
- long flags;
-
- if (check_region(ez_base,3)) {
- printk("ez: Ports at 0x%x are not available\n",ez_base);
- return 0;
- }
-
- ez_mode = ez_port_check();
- if (!ez_mode) {
- printk("ez: No parallel port at 0x%x\n",ez_base);
- return 0;
- }
-
- if (ez_irq && request_irq(ez_irq,ez_interrupt,0,"ez",NULL)) ez_irq = 0;
-
- if (ez_nybble) ez_mode = 1;
-
- request_region(ez_base,3,"ez");
-
- save_flags(flags);
- sti();
-
- k = 0;
- if (ez_identify()) {
- k = 1;
- for(j=0;j<EZ_SIGLEN;j++)
- k &= (ez_scratch[j+EZ_SIGOFF] == sig[j]);
- }
- if (k) {
- for(j=0;j<EZ_ID_LEN;j++) id[j^1] = ez_scratch[j+EZ_SIGOFF];
- id[EZ_ID_LEN] = 0;
- if (!ez_irq) printk("ez %s: %s at 0x%x, %d-bit mode.\n",
- EZ_VERSION,id,ez_base,4*ez_mode);
- else printk("ez %s: %s at 0x%x, IRQ %d, %d-bit mode.\n",
- EZ_VERSION,id,ez_base,ez_irq,4*ez_mode);
- ez_standby_off();
- ez_media_check();
- ez_get_capacity();
- restore_flags(flags);
- return 1;
- }
- restore_flags(flags);
- release_region(ez_base,3);
- if (ez_irq) free_irq(ez_irq,NULL);
- printk("ez: Drive not detected\n");
- return 0;
-}
-
-/* interrupt management */
-
-static void ez_set_intr( void (*continuation)(void) )
-
-{ ez_continuation = continuation;
- ez_loops = 1; ez_timeout = 0;
- ez_gate_intr(1);
- if (ez_irq) {
- ez_timer.expires = jiffies + EZ_TMO;
- add_timer(&ez_timer);
- } else queue_task(&ez_tq,&tq_scheduler);
-}
-
-static void ez_pseudo( void *data )
-
-{ void (*con)(void);
-
- ez_timeout = (ez_loops >= EZ_TMO);
- if (check_int() || ez_timeout) {
- con = ez_continuation;
- ez_continuation = NULL;
- if (con) con();
- } else {
- ez_loops++;
- queue_task(&ez_tq,&tq_scheduler);
- }
-}
-
-static void ez_timer_int( unsigned long data)
-
-{ void (*con)(void);
-
- con = ez_continuation;
- if (!con) return;
- ez_continuation = NULL;
- ez_gate_intr(0);
- ez_timeout = 1;
- con();
-}
-
-static void ez_interrupt( int irq, void * dev_id, struct pt_regs * regs)
-
-{ void (*con)(void);
-
- ez_int_seen = 1;
- con = ez_continuation;
- if (!con) return;
- ez_gate_intr(0);
- del_timer(&ez_timer);
- ez_continuation = NULL;
- con();
-}
-
-/* The i/o request engine */
-
-#define EZ_DONE(s) { disconnect(); end_request(s); ez_busy = 0;\
- cli(); do_ez_request(); return; }
-
-static void do_ez_read( void )
-
-{ ez_busy = 1;
- if (!ez_count) {
- ez_busy = 0;
- return;
- }
- sti();
- connect();
- if (wait_for(STAT_READY,"do_ez_read") & STAT_ERR) EZ_DONE(0);
- ez_ide_command(IDE_READ,ez_block);
- ez_set_intr(do_ez_read_drq);
-}
-
-static void do_ez_read_drq( void )
-
-{ sti();
- if (wait_for(STAT_DRQ,"do_ez_read_drq") & STAT_ERR) EZ_DONE(0);
- read_block(ez_buf);
- ez_count--;
- if (ez_count) {
- ez_buf += 512;
- ez_block++;
- disconnect();
- do_ez_read();
- return;
- }
- EZ_DONE(1);
-}
-
-static void do_ez_write( void )
-
-{ ez_busy = 1;
- if (!ez_count) {
- ez_busy = 0;
- return;
- }
- sti();
- connect();
- if (wait_for(STAT_READY,"do_ez_write") & STAT_ERR)
- EZ_DONE(0);
- ez_ide_command(IDE_WRITE,ez_block);
- if (wait_for(STAT_DRQ,"do_ez_write_drq") & STAT_ERR)
- EZ_DONE(0);
- write_block(ez_buf);
- ez_set_intr(do_ez_write_done);
-}
-
-static void do_ez_write_done( void )
-
-{ sti();
- if (wait_for(STAT_READY,"do_ez_write_done") & STAT_ERR) EZ_DONE(0);
- ez_count--;
- if (ez_count) {
- ez_buf += 512;
- ez_block++;
- disconnect();
- do_ez_write();
- return;
- }
- EZ_DONE(1);
-}
-
-/* end of ez.c */
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index d479bc1be..8823e4c97 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -148,11 +148,32 @@ static int allowed_drive_mask = 0x33;
#include <asm/io.h>
#include <asm/uaccess.h>
-static int use_virtual_dma=0; /* virtual DMA for Intel */
+static int can_use_virtual_dma=2;
+/* =======
+ * can use virtual DMA:
+ * 0 = use of virtual DMA disallowed by config
+ * 1 = use of virtual DMA prescribed by config
+ * 2 = no virtual DMA preference configured. By default try hard DMA,
+ * but fall back on virtual DMA when not enough memory available
+ */
+
+static int use_virtual_dma=0;
+/* =======
+ * use virtual DMA
+ * 0 using hard DMA
+ * 1 using virtual DMA
+ * This variable is set to virtual when a DMA mem problem arises, and
+ * reset back in floppy_grab_irq_and_dma.
+ * It is not safe to reset it in other circumstances, because the floppy
+ * driver may have several buffers in use at once, and we do currently not
+ * record each buffers capabilities
+ */
+
static unsigned short virtual_dma_port=0x3f0;
void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs);
static int set_dor(int fdc, char mask, char data);
static inline int __get_order(unsigned long size);
+#define K_64 0x10000 /* 64KB */
#include <asm/floppy.h>
@@ -189,6 +210,20 @@ static inline int __get_order(unsigned long size)
#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL,__get_order(size))
#endif
+static inline void fallback_on_nodma_alloc(char **addr, size_t l)
+{
+#ifdef FLOPPY_CAN_FALLBACK_ON_NODMA
+ if(*addr)
+ return; /* we have the memory */
+ if(can_use_virtual_dma != 2)
+ return; /* no fallback allowed */
+ printk("DMA memory shortage. Temporarily falling back on virtual DMA\n");
+ *addr = (char *) nodma_mem_alloc(l);
+#else
+ return;
+#endif
+}
+
/* End dma memory related stuff */
static unsigned int fake_change = 0;
@@ -258,7 +293,6 @@ static inline int DRIVE(kdev_t x) {
*/
#define MAX_DISK_SIZE 4 /* 3984*/
-#define K_64 0x10000 /* 64KB */
/*
* globals used by 'result()'
@@ -1015,17 +1049,20 @@ static void setup_DMA(void)
FDCS->reset=1;
return;
}
- if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
- printk("DMA crossing 64-K boundary %p-%p\n",
- raw_cmd->kernel_data,
- raw_cmd->kernel_data + raw_cmd->length);
+#endif
+ INT_OFF;
+ fd_disable_dma(FLOPPY_DMA);
+#ifdef fd_dma_setup
+ if(fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length,
+ (raw_cmd->flags & FD_RAW_READ)?
+ DMA_MODE_READ : DMA_MODE_WRITE,
+ FDCS->address) < 0) {
+ INT_ON;
cont->done(0);
FDCS->reset=1;
return;
}
-#endif
- INT_OFF;
- fd_disable_dma(FLOPPY_DMA);
+#else
fd_clear_dma_ff(FLOPPY_DMA);
fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
fd_set_dma_mode(FLOPPY_DMA, (raw_cmd->flags & FD_RAW_READ)
@@ -1035,6 +1072,7 @@ static void setup_DMA(void)
fd_set_dma_count(FLOPPY_DMA, raw_cmd->length);
virtual_dma_port = FDCS->address;
fd_enable_dma(FLOPPY_DMA);
+#endif
INT_ON;
floppy_disable_hlt();
}
@@ -1673,10 +1711,11 @@ void floppy_interrupt(int irq, void *dev_id, struct pt_regs * regs)
} while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2);
}
if (handler) {
- if(softirq_trylock()) {
+ int cpu = smp_processor_id();
+ if(softirq_trylock(cpu)) {
/* got the lock, call the handler immediately */
handler();
- softirq_endlock();
+ softirq_endlock(cpu);
} else
/* we interrupted a bottom half. Defer handler */
schedule_bh( (void *)(void *) handler);
@@ -1845,19 +1884,29 @@ static void floppy_ready(void)
DPRINT("calling disk change from floppy_ready\n");
}
#endif
-
if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
disk_change(current_drive) &&
!DP->select_delay)
twaddle(); /* this clears the dcl on certain drive/controller
* combinations */
+#ifdef fd_chose_dma_mode
+ if ((raw_cmd->flags & FD_RAW_READ) ||
+ (raw_cmd->flags & FD_RAW_WRITE))
+ fd_chose_dma_mode(raw_cmd->kernel_data,
+ raw_cmd->length);
+#endif
+
if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
perpendicular_mode();
fdc_specify(); /* must be done here because of hut, hlt ... */
seek_floppy();
- } else
+ } else {
+ if ((raw_cmd->flags & FD_RAW_READ) ||
+ (raw_cmd->flags & FD_RAW_WRITE))
+ fdc_specify();
setup_rw_floppy();
+ }
}
static void floppy_start(void)
@@ -2035,7 +2084,7 @@ static void format_interrupt(void)
#define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2)
#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1))
-#define CT(x) ((x) | 0x40)
+#define CT(x) ((x) | 0xc0)
static void setup_format_params(int track)
{
struct fparm {
@@ -2404,6 +2453,7 @@ static void copy_buffer(int ssize, int max_sector, int max_sector_2)
#endif
}
+#if 0
static inline int check_dma_crossing(char *start,
unsigned long length, char *message)
{
@@ -2414,6 +2464,33 @@ static inline int check_dma_crossing(char *start,
} else
return 0;
}
+#endif
+
+/* work around a bug in pseudo DMA
+ * (on some FDCs) pseudo DMA does not stop when the CPU stops
+ * sending data. Hence we need a different way to signal the
+ * transfer length: We use SECT_PER_TRACK. Unfortunately, this
+ * does not work with MT, hence we can only transfer one head at
+ * a time
+ */
+static int virtualdmabug_workaround() {
+ int hard_sectors, end_sector;
+ if(CT(COMMAND) == FD_WRITE) {
+ COMMAND &= ~0x80; /* switch off multiple track mode */
+
+ hard_sectors = raw_cmd->length >> (7 + SIZECODE);
+ end_sector = SECTOR + hard_sectors - 1;
+#ifdef FLOPPY_SANITY_CHECK
+ if(end_sector > SECT_PER_TRACK) {
+ printk("too many sectors %d > %d\n",
+ end_sector, SECT_PER_TRACK);
+ return 0;
+ }
+#endif
+ SECT_PER_TRACK = end_sector; /* make sure SECT_PER_TRACK points
+ * to end of transfer */
+ }
+}
/*
* Formulate a read/write request.
@@ -2491,11 +2568,17 @@ static int make_raw_rw_request(void)
CODE2SIZE;
SECT_PER_TRACK = _floppy->sect << 2 >> SIZECODE;
SECTOR = ((sector_t % _floppy->sect) << 2 >> SIZECODE) + 1;
+
+ /* tracksize describes the size which can be filled up with sectors
+ * of size ssize.
+ */
tracksize = _floppy->sect - _floppy->sect % ssize;
if (tracksize < _floppy->sect){
SECT_PER_TRACK ++;
if (tracksize <= sector_t % _floppy->sect)
SECTOR--;
+
+ /* if we are beyond tracksize, fill up using smaller sectors */
while (tracksize <= sector_t % _floppy->sect){
while(tracksize + ssize > _floppy->sect){
SIZECODE--;
@@ -2505,8 +2588,12 @@ static int make_raw_rw_request(void)
tracksize += ssize;
}
max_sector = HEAD * _floppy->sect + tracksize;
- } else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing)
+ } else if (!TRACK && !HEAD && !(_floppy->rate & FD_2M) && probing) {
+ max_sector = _floppy->sect;
+ } else if (!HEAD && CT(COMMAND) == FD_WRITE) {
+ /* for virtual DMA bug workaround */
max_sector = _floppy->sect;
+ }
aligned_sector_t = sector_t - (sector_t % _floppy->sect) % ssize;
max_size = CURRENT->nr_sectors;
@@ -2572,9 +2659,11 @@ static int make_raw_rw_request(void)
indirect, direct, sector_t);
return 0;
}
- check_dma_crossing(raw_cmd->kernel_data,
+/* check_dma_crossing(raw_cmd->kernel_data,
raw_cmd->length,
- "end of make_raw_request [1]");
+ "end of make_raw_request [1]");*/
+
+ virtualdmabug_workaround();
return 2;
}
}
@@ -2620,8 +2709,8 @@ static int make_raw_rw_request(void)
raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
raw_cmd->length <<= 9;
#ifdef FLOPPY_SANITY_CHECK
- check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length,
- "end of make_raw_request");
+ /*check_dma_crossing(raw_cmd->kernel_data, raw_cmd->length,
+ "end of make_raw_request");*/
if ((raw_cmd->length < current_count_sectors << 9) ||
(raw_cmd->kernel_data != CURRENT->buffer &&
CT(COMMAND) == FD_WRITE &&
@@ -2680,6 +2769,8 @@ static int make_raw_rw_request(void)
return 0;
}
#endif
+
+ virtualdmabug_workaround();
return 2;
}
@@ -3008,6 +3099,8 @@ static inline int raw_cmd_copyin(int cmd, char *param,
if (ptr->length <= 0)
return -EINVAL;
ptr->kernel_data =(char*)fd_dma_mem_alloc(ptr->length);
+ fallback_on_nodma_alloc(&ptr->kernel_data,
+ ptr->length);
if (!ptr->kernel_data)
return -ENOMEM;
ptr->buffer_length = ptr->length;
@@ -3295,8 +3388,8 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
return -EINVAL;
/* permission checks */
- if (((cmd & 0x80) && !suser()) ||
- ((cmd & 0x40) && !IOCTL_ALLOWED))
+ if (((cmd & 0x40) && !IOCTL_ALLOWED) ||
+ ((cmd & 0x80) && !suser()))
return -EPERM;
/* copyin */
@@ -3578,18 +3671,22 @@ static int floppy_open(struct inode * inode, struct file * filp)
try = 32; /* Only 24 actually useful */
tmp=(char *)fd_dma_mem_alloc(1024 * try);
- if (!tmp) {
+ if (!tmp && !floppy_track_buffer) {
try >>= 1; /* buffer only one side */
INFBOUND(try, 16);
tmp= (char *)fd_dma_mem_alloc(1024*try);
}
- if (!tmp) {
+ if(!tmp && !floppy_track_buffer) {
+ fallback_on_nodma_alloc(&tmp, 2048 * try);
+ }
+ if (!tmp && !floppy_track_buffer) {
DPRINT("Unable to allocate DMA memory\n");
RETERR(ENXIO);
}
- if (floppy_track_buffer)
- fd_dma_mem_free((unsigned long)tmp,try*1024);
- else {
+ if (floppy_track_buffer) {
+ if(tmp)
+ fd_dma_mem_free((unsigned long)tmp,try*1024);
+ } else {
buffer_min = buffer_max = -1;
floppy_track_buffer = tmp;
max_buffer_sectors = try;
@@ -3886,9 +3983,9 @@ static struct param_table {
{ "silent_dcl_clear", floppy_set_flags, 0, 1, FD_SILENT_DCL_CLEAR },
{ "debug", floppy_set_flags, 0, 1, FD_DEBUG },
- { "nodma", 0, &use_virtual_dma, 1, 0 },
- { "omnibook", 0, &use_virtual_dma, 1, 0 },
- { "dma", 0, &use_virtual_dma, 0, 0 },
+ { "nodma", 0, &can_use_virtual_dma, 1, 0 },
+ { "omnibook", 0, &can_use_virtual_dma, 1, 0 },
+ { "yesdma", 0, &can_use_virtual_dma, 0, 0 },
{ "fifo_depth", 0, &fifo_depth, 0xa, 0 },
{ "nofifo", 0, &no_fifo, 0x20, 0 },
@@ -3937,6 +4034,7 @@ __initfunc(void floppy_setup(char *str, int *ints))
static int have_no_fdc= -EIO;
+
__initfunc(int floppy_init(void))
{
int i,unit,drive;
@@ -3972,6 +4070,7 @@ __initfunc(int floppy_init(void))
#endif
}
+ use_virtual_dma = can_use_virtual_dma & 1;
fdc_state[0].address = FDC1;
if (fdc_state[0].address == -1) {
unregister_blkdev(MAJOR_NR,"fd");
@@ -4017,6 +4116,8 @@ __initfunc(int floppy_init(void))
FDCS->address = -1;
continue;
}
+ if(can_use_virtual_dma == 2 && FDCS->version < FDC_82072A)
+ can_use_virtual_dma = 0;
have_no_fdc = 0;
/* Not all FDCs seem to be able to handle the version command
@@ -4032,7 +4133,7 @@ __initfunc(int floppy_init(void))
initialising=0;
if (have_no_fdc) {
DPRINT("no floppy controllers found\n");
- unregister_blkdev(MAJOR_NR,"fd");
+ unregister_blkdev(MAJOR_NR,"fd");
}
return have_no_fdc;
}
@@ -4063,6 +4164,7 @@ static int floppy_grab_irq_and_dma(void)
usage_count--;
return -1;
}
+
for (fdc=0; fdc< N_FDC; fdc++){
if (FDCS->address != -1){
if (check_region(FDCS->address, 6) < 0 ||
@@ -4247,7 +4349,7 @@ int init_module(void)
void cleanup_module(void)
{
- int fdc, dummy;
+ int dummy;
unregister_blkdev(MAJOR_NR, "fd");
diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c
index c6b0a02b6..f7b839848 100644
--- a/drivers/block/genhd.c
+++ b/drivers/block/genhd.c
@@ -2,7 +2,7 @@
* Code extracted from
* linux/kernel/hd.c
*
- * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1991-1998 Linus Torvalds
*
*
* Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
@@ -105,6 +105,7 @@ static void add_partition (struct gendisk *hd, int minor, int start, int size)
static inline int is_extended_partition(struct partition *p)
{
return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
+ SYS_IND(p) == WIN98_EXTENDED_PARTITION ||
SYS_IND(p) == LINUX_EXTENDED_PARTITION);
}
@@ -254,6 +255,45 @@ static void extended_partition(struct gendisk *hd, kdev_t dev)
done:
brelse(bh);
}
+#ifdef CONFIG_SOLARIS_X86_PARTITION
+static void
+solaris_x86_partition(struct gendisk *hd, kdev_t dev, long offset) {
+
+ struct buffer_head *bh;
+ struct solaris_x86_vtoc *v;
+ struct solaris_x86_slice *s;
+ int i;
+
+ if(!(bh = bread(dev, 0, get_ptable_blocksize(dev))))
+ return;
+ v = (struct solaris_x86_vtoc *)(bh->b_data + 512);
+ if(v->v_sanity != SOLARIS_X86_VTOC_SANE) {
+ brelse(bh);
+ return;
+ }
+ printk(" <solaris:");
+ if(v->v_version != 1) {
+ printk(" cannot handle version %ld vtoc>", v->v_version);
+ brelse(bh);
+ return;
+ }
+ for(i=0; i<SOLARIS_X86_NUMSLICE; i++) {
+ s = &v->v_slice[i];
+
+ if (s->s_size == 0)
+ continue;
+ printk(" [s%d]", i);
+ /* solaris partitions are relative to current MS-DOS
+ * one but add_partition starts relative to sector
+ * zero of the disk. Therefore, must add the offset
+ * of the current partition */
+ add_partition(hd, current_minor, s->s_start+offset, s->s_size);
+ current_minor++;
+ }
+ brelse(bh);
+ printk(" >");
+}
+#endif
#ifdef CONFIG_BSD_DISKLABEL
/*
@@ -420,6 +460,18 @@ check_table:
printk(" >");
}
#endif
+#ifdef CONFIG_SOLARIS_X86_PARTITION
+
+ /* james@bpgc.com: Solaris has a nasty indicator: 0x82
+ * which also means linux swap. For that reason, all
+ * of the prints are done inside the
+ * solaris_x86_partition routine */
+
+ if(SYS_IND(p) == SOLARIS_X86_PARTITION) {
+ solaris_x86_partition(hd, MKDEV(hd->major, minor),
+ first_sector+START_SECT(p));
+ }
+#endif
}
/*
* Check for old-style Disk Manager partition table
@@ -792,7 +844,9 @@ static int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec)
int blk, blocks_in_map;
int dev_bsize, dev_pos, pos;
unsigned secsize;
+#ifdef CONFIG_PMAC
int first_bootable = 1;
+#endif
struct mac_partition *part;
struct mac_driver_desc *md;
@@ -1112,6 +1166,9 @@ __initfunc(void device_setup(void))
#ifdef CONFIG_PARPORT
extern int parport_init(void);
#endif
+#ifdef CONFIG_MD_BOOT
+ extern void md_setup_drive(void) __init;
+#endif
struct gendisk *p;
int nr=0;
@@ -1142,4 +1199,7 @@ __initfunc(void device_setup(void))
#endif
rd_load();
#endif
+#ifdef CONFIG_MD_BOOT
+ md_setup_drive();
+#endif
}
diff --git a/drivers/block/ide-cd.c b/drivers/block/ide-cd.c
index 3a8e48722..24f00c950 100644
--- a/drivers/block/ide-cd.c
+++ b/drivers/block/ide-cd.c
@@ -3,13 +3,14 @@
* linux/drivers/block/ide-cd.c
* Copyright (C) 1994, 1995, 1996 scott snyder <snyder@fnald0.fnal.gov>
* Copyright (C) 1996-1998 Erik Andersen <andersee@debian.org>
+ *
* May be copied or modified under the terms of the GNU General Public
* License. See linux/COPYING for more information.
*
* ATAPI CD-ROM driver. To be used with ide.c.
* See Documentation/cdrom/ide-cd for usage information.
*
- * Suggestions are welcome. Patches that work are more welcome though.
+ * Suggestions are welcome. Patches that work are more welcome though. ;-)
* For those wishing to work on this driver, please be sure you download
* and comply with the latest ATAPI standard. This document can be
* obtained by anonymous ftp from fission.dt.wdc.com in directory:
@@ -27,10 +28,8 @@
* unless you have a patch to fix it. I am working on it...)
* -Implement ide_cdrom_select_speed using the generic cdrom interface
* -Fix ide_cdrom_reset so that it works (it does nothing right now)
- *
- * MOSTLY DONE LIST:
- * Query the drive to find what features are available
- * before trying to use them.
+ * -Query the drive to find what features are available before trying to
+ * use them (like trying to close the tray in drives that can't).
*
*
* ----------------------------------
@@ -180,12 +179,21 @@
*
* 4.06 Dec 17, 1997 -- fixed endless "tray open" messages -ml
* 4.07 Dec 17, 1997 -- fallback to set pc->stat on "tray open"
+ * 4.08 Dec 18, 1997 -- spew less noise when tray is empty
+ * -- fix speed display for ACER 24X, 18X
+ * 4.09 Jan 04, 1998 -- fix handling of the last block so we return
+ * an end of file instead of an I/O error (Gadi)
+ * 4.10 Jan 24, 1998 -- fixed a bug so now changers can change to a new
+ * slot when there is no disc in the current slot.
+ * -- Fixed a memory leak where info->changer_info was
+ * malloc'ed but never free'd when closing the device.
+ * -- Cleaned up the global namespace a bit by making more
+ * functions static that should already have been.
*
*************************************************************************/
-#define IDECD_VERSION "4.07"
+#define IDECD_VERSION "4.10"
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
@@ -243,6 +251,15 @@ void cdrom_analyze_sense_data (ide_drive_t *drive,
failed_command->c[0] == SCMD_READ_SUBCHANNEL)
return;
}
+ if (reqbuf->error_code == 0x70 && reqbuf->sense_key == 0x02
+ && reqbuf->asc == 0x3a && reqbuf->ascq == 0x00)
+ {
+ /*
+ * No disc in drive ("Medium not present"),
+ * so keep the noise level down to a dull roar.
+ */
+ return;
+ }
#if VERBOSE_IDE_CD_ERRORS
{
@@ -466,7 +483,7 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
/* Check for tray open. */
if (sense_key == NOT_READY) {
cdrom_saw_media_change (drive);
-
+#if 0 /* let the upper layers do the complaining */
/* Print an error message to the syslog.
Exception: don't print anything if this
is a read subchannel command. This is
@@ -474,12 +491,13 @@ static int cdrom_decode_status (ide_drive_t *drive, int good_stat,
with this command, and we don't want
to uselessly fill up the syslog. */
if (pc->c[0] != SCMD_READ_SUBCHANNEL)
- printk ("%s: tray open or drive not ready\n",
- drive->name);
+ printk ("%s: tray open or drive not ready\n", drive->name);
+#endif
} else if (sense_key == UNIT_ATTENTION) {
/* Check for media change. */
cdrom_saw_media_change (drive);
printk ("%s: media changed\n", drive->name);
+ return 0;
} else {
/* Otherwise, print an error. */
ide_dump_status (drive, "packet command error",
@@ -1297,7 +1315,7 @@ int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
/****************************************************************************
* cdrom driver request routine.
*/
-
+static
void ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block)
{
if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
@@ -1651,7 +1669,7 @@ cdrom_read_toc (ide_drive_t *drive,
if (stat) toc->capacity = 0x1fffff;
HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS]
- = toc->capacity * SECTORS_PER_FRAME;
+ = (toc->capacity * SECTORS_PER_FRAME) >> (BLOCK_SIZE_BITS - 9);
drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
/* Remember that we've read this stuff. */
@@ -2455,10 +2473,6 @@ int ide_cdrom_select_disc (struct cdrom_device_info *cdi, int slot)
if (drive->usage > 1)
return -EBUSY;
- stat = cdrom_check_status (drive, &my_reqbuf);
- if (stat && my_reqbuf.sense_key == NOT_READY)
- return -ENOENT;
-
if (slot == CDSL_NONE) {
(void) cdrom_load_unload (drive, -1, NULL);
cdrom_saw_media_change (drive);
@@ -2734,12 +2748,13 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive)
}
}
- if (drive->id && drive->id->model[0]) {
- CDROM_STATE_FLAGS (drive)->current_speed = (ntohs(buf.cap.curspeed) + (176/2)) / 176;
- CDROM_CONFIG_FLAGS (drive)->max_speed = (ntohs(buf.cap.maxspeed) + (176/2)) / 176;
- } else { /* no-name ACERs (AOpen) have it backwards */
+ /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+ if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
CDROM_STATE_FLAGS (drive)->current_speed = (((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
CDROM_CONFIG_FLAGS (drive)->max_speed = (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+ } else {
+ CDROM_STATE_FLAGS (drive)->current_speed = (ntohs(buf.cap.curspeed) + (176/2)) / 176;
+ CDROM_CONFIG_FLAGS (drive)->max_speed = (ntohs(buf.cap.maxspeed) + (176/2)) / 176;
}
printk ("%s: ATAPI %dX CDROM",
@@ -2752,13 +2767,23 @@ int ide_cdrom_probe_capabilities (ide_drive_t *drive)
printk (" changer w/%d slots", nslots);
else
printk (" drive");
- printk (" %s/%dkB Cache\n",
- (CDROM_CONFIG_FLAGS (drive)->is_changer)? "&" : "w",
+ printk (", %dkB Cache\n",
ntohs(buf.cap.buffer_size) );
return nslots;
}
+static void ide_cdrom_add_settings(ide_drive_t *drive)
+{
+ int major = HWIF(drive)->major;
+ int minor = drive->select.b.unit << PARTN_BITS;
+
+ ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
+ ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL);
+ ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
+ ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL);
+}
+
static
int ide_cdrom_setup (ide_drive_t *drive)
{
@@ -2871,11 +2896,12 @@ int ide_cdrom_setup (ide_drive_t *drive)
info->devinfo.handle = NULL;
return 1;
}
+ ide_cdrom_add_settings(drive);
return 0;
}
/* Forwarding functions to generic routines. */
-
+static
int ide_cdrom_ioctl (ide_drive_t *drive,
struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
@@ -2883,6 +2909,7 @@ int ide_cdrom_ioctl (ide_drive_t *drive,
return cdrom_fops.ioctl (inode, file, cmd, arg);
}
+static
int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
{
int rc;
@@ -2896,6 +2923,7 @@ int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
return rc;
}
+static
void ide_cdrom_release (struct inode *inode, struct file *file,
ide_drive_t *drive)
{
@@ -2903,6 +2931,7 @@ void ide_cdrom_release (struct inode *inode, struct file *file,
MOD_DEC_USE_COUNT;
}
+static
int ide_cdrom_check_media_change (ide_drive_t *drive)
{
return cdrom_fops.check_media_change
@@ -2911,6 +2940,7 @@ int ide_cdrom_check_media_change (ide_drive_t *drive)
}
+static
int ide_cdrom_cleanup(ide_drive_t *drive)
{
struct cdrom_info *info = drive->driver_data;
@@ -2922,6 +2952,8 @@ int ide_cdrom_cleanup(ide_drive_t *drive)
kfree (info->sector_buffer);
if (info->toc != NULL)
kfree (info->toc);
+ if (info->changer_info != NULL)
+ kfree (info->changer_info);
if (devinfo->handle == drive && unregister_cdrom (devinfo))
printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name);
kfree (info);
@@ -2929,13 +2961,6 @@ int ide_cdrom_cleanup(ide_drive_t *drive)
return 0;
}
-int ide_cdrom_init (void);
-static ide_module_t ide_cdrom_module = {
- IDE_DRIVER_MODULE,
- ide_cdrom_init,
- NULL
-};
-
static ide_driver_t ide_cdrom_driver = {
"ide-cdrom", /* name */
IDECD_VERSION, /* version */
@@ -2956,6 +2981,13 @@ static ide_driver_t ide_cdrom_driver = {
NULL /* proc */
};
+int ide_cdrom_init (void);
+static ide_module_t ide_cdrom_module = {
+ IDE_DRIVER_MODULE,
+ ide_cdrom_init,
+ &ide_cdrom_driver,
+ NULL
+};
#ifdef MODULE
int init_module (void)
@@ -2968,7 +3000,7 @@ void cleanup_module(void)
ide_drive_t *drive;
int failed = 0;
- while ((drive = ide_scan_devices (ide_cdrom, &ide_cdrom_driver, failed)) != NULL)
+ while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, &ide_cdrom_driver, failed)) != NULL)
if (ide_cdrom_cleanup (drive)) {
printk ("%s: cleanup_module() called while still busy\n", drive->name);
failed++;
@@ -2984,7 +3016,7 @@ int ide_cdrom_init (void)
int failed = 0;
MOD_INC_USE_COUNT;
- while ((drive = ide_scan_devices (ide_cdrom, NULL, failed++)) != NULL) {
+ while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) {
info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL);
if (info == NULL) {
printk ("%s: Can't allocate a cdrom structure\n", drive->name);
diff --git a/drivers/block/ide-disk.c b/drivers/block/ide-disk.c
index 5e178b6cd..4ad8bf606 100644
--- a/drivers/block/ide-disk.c
+++ b/drivers/block/ide-disk.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide-disk.c Version 1.03 Nov 30, 1997
+ * linux/drivers/block/ide-disk.c Version 1.04 Jan 7, 1998
*
* Copyright (C) 1994-1998 Linus Torvalds & authors (see below)
*/
@@ -13,11 +13,12 @@
* Version 1.00 move disk only code from ide.c to ide-disk.c
* support optional byte-swapping of all data
* Version 1.01 fix previous byte-swapping code
- * Verions 1.02 remove ", LBA" from drive identification msgs
- * Verions 1.03 fix display of id->buf_size for big-endian
+ * Version 1.02 remove ", LBA" from drive identification msgs
+ * Version 1.03 fix display of id->buf_size for big-endian
+ * Version 1.04 add /proc configurable settings and S.M.A.R.T support
*/
-#define IDEDISK_VERSION "1.03"
+#define IDEDISK_VERSION "1.04"
#undef REALLY_SLOW_IO /* most systems can safely undef this */
@@ -104,12 +105,15 @@ static void read_intr (ide_drive_t *drive)
int i;
unsigned int msect, nsect;
struct request *rq;
+ unsigned long flags;
if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
ide_error(drive, "read_intr", stat);
return;
}
msect = drive->mult_count;
+
+ spin_lock_irqsave(&io_request_lock,flags);
read_next:
rq = HWGROUP(drive)->rq;
if (msect) {
@@ -118,6 +122,12 @@ read_next:
msect -= nsect;
} else
nsect = 1;
+ /*
+ * PIO input can take longish times, so we drop the spinlock.
+ * On SMP, bad things might happen if syscall level code adds
+ * a new request while we do this PIO, so we just freeze all
+ * request queue handling while doing the PIO. FIXME
+ */
idedisk_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
#ifdef DEBUG
printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
@@ -135,6 +145,7 @@ read_next:
goto read_next;
ide_set_handler (drive, &read_intr, WAIT_CMD);
}
+ spin_unlock_irqrestore(&io_request_lock,flags);
}
/*
@@ -146,7 +157,10 @@ static void write_intr (ide_drive_t *drive)
int i;
ide_hwgroup_t *hwgroup = HWGROUP(drive);
struct request *rq = hwgroup->rq;
+ unsigned long flags;
+ int error = 0;
+ spin_lock_irqsave(&io_request_lock,flags);
if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
#ifdef DEBUG
printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
@@ -165,10 +179,16 @@ static void write_intr (ide_drive_t *drive)
idedisk_output_data (drive, rq->buffer, SECTOR_WORDS);
ide_set_handler (drive, &write_intr, WAIT_CMD);
}
- return;
+ goto out;
}
- }
- ide_error(drive, "write_intr", stat);
+ } else
+ error = 1;
+
+out:
+ spin_unlock_irqrestore(&io_request_lock,flags);
+
+ if (error)
+ ide_error(drive, "write_intr", stat);
}
/*
@@ -216,13 +236,16 @@ static void multwrite_intr (ide_drive_t *drive)
int i;
ide_hwgroup_t *hwgroup = HWGROUP(drive);
struct request *rq = &hwgroup->wrq;
+ unsigned long flags;
+ int error = 0;
+ spin_lock_irqsave(&io_request_lock,flags);
if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
if (stat & DRQ_STAT) {
if (rq->nr_sectors) {
ide_multwrite(drive, drive->mult_count);
ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
- return;
+ goto out;
}
} else {
if (!rq->nr_sectors) { /* all done? */
@@ -231,11 +254,17 @@ static void multwrite_intr (ide_drive_t *drive)
i -= rq->current_nr_sectors;
ide_end_request(1, hwgroup);
}
- return;
+ goto out;
}
}
- }
- ide_error(drive, "multwrite_intr", stat);
+ } else
+ error = 1;
+
+out:
+ spin_unlock_irqrestore(&io_request_lock,flags);
+
+ if (error)
+ ide_error(drive, "multwrite_intr", stat);
}
/*
@@ -353,7 +382,7 @@ static void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long bl
return;
}
if (!drive->unmask)
- cli();
+ __cli();
if (drive->mult_count) {
HWGROUP(drive)->wrq = *rq; /* scratchpad */
ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
@@ -372,17 +401,13 @@ static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *dr
{
MOD_INC_USE_COUNT;
if (drive->removable && drive->usage == 1) {
- byte door_lock[] = {WIN_DOORLOCK,0,0,0};
- struct request rq;
check_disk_change(inode->i_rdev);
- ide_init_drive_cmd (&rq);
- rq.buffer = door_lock;
/*
* Ignore the return code from door_lock,
* since the open() has already succeeded,
* and the door_lock is irrelevant at this point.
*/
- (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ (void) ide_wait_cmd(drive, WIN_DOORLOCK, 0, 0, 0, NULL);
}
return 0;
}
@@ -390,12 +415,8 @@ static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *dr
static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
{
if (drive->removable && !drive->usage) {
- byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
- struct request rq;
invalidate_buffers(inode->i_rdev);
- ide_init_drive_cmd (&rq);
- rq.buffer = door_unlock;
- (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ (void) ide_wait_cmd(drive, WIN_DOORUNLOCK, 0, 0, 0, NULL);
}
MOD_DEC_USE_COUNT;
}
@@ -467,17 +488,120 @@ static void idedisk_pre_reset (ide_drive_t *drive)
drive->special.b.set_multmode = 1;
}
+static int proc_idedisk_read_cache
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ char *out = page;
+ int len;
+
+ if (drive->id)
+ len = sprintf(out,"%i\n", drive->id->buf_size / 2);
+ else
+ len = sprintf(out,"(none)\n");
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int smart_enable(ide_drive_t *drive)
+{
+ return ide_wait_cmd(drive, WIN_SMART, 0, SMART_ENABLE, 0, NULL);
+}
+
+static int get_smart_values(ide_drive_t *drive, byte *buf)
+{
+ (void) smart_enable(drive);
+ return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_VALUES, 1, buf);
+}
+
+static int get_smart_thresholds(ide_drive_t *drive, byte *buf)
+{
+ (void) smart_enable(drive);
+ return ide_wait_cmd(drive, WIN_SMART, 0, SMART_READ_THRESHOLDS, 1, buf);
+}
+
+static int proc_idedisk_read_smart_thresholds
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *)data;
+ int len = 0, i = 0;
+
+ if (!get_smart_thresholds(drive, page)) {
+ unsigned short *val = ((unsigned short *)page) + 2;
+ char *out = ((char *)val) + (SECTOR_WORDS * 4);
+ page = out;
+ do {
+ out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+ val += 1;
+ } while (i < (SECTOR_WORDS * 2));
+ len = out - page;
+ }
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static int proc_idedisk_read_smart_values
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *)data;
+ int len = 0, i = 0;
+
+ if (!get_smart_values(drive, page)) {
+ unsigned short *val = ((unsigned short *)page) + 2;
+ char *out = ((char *)val) + (SECTOR_WORDS * 4);
+ page = out;
+ do {
+ out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
+ val += 1;
+ } while (i < (SECTOR_WORDS * 2));
+ len = out - page;
+ }
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
static ide_proc_entry_t idedisk_proc[] = {
+ { "cache", proc_idedisk_read_cache, NULL },
{ "geometry", proc_ide_read_geometry, NULL },
+ { "smart_values", proc_idedisk_read_smart_values, NULL },
+ { "smart_thresholds", proc_idedisk_read_smart_thresholds, NULL },
{ NULL, NULL, NULL }
};
-int idedisk_init (void);
-static ide_module_t idedisk_module = {
- IDE_DRIVER_MODULE,
- idedisk_init,
- NULL
-};
+static int set_multcount(ide_drive_t *drive, int arg)
+{
+ struct request rq;
+
+ if (drive->special.b.set_multmode)
+ return -EBUSY;
+ ide_init_drive_cmd (&rq);
+ drive->mult_req = arg;
+ drive->special.b.set_multmode = 1;
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
+ return (drive->mult_count == arg) ? 0 : -EIO;
+}
+
+static int set_nowerr(ide_drive_t *drive, int arg)
+{
+ drive->nowerr = arg;
+ drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
+ return 0;
+}
+
+static void idedisk_add_settings(ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ int major = HWIF(drive)->major;
+ int minor = drive->select.b.unit << PARTN_BITS;
+
+ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 1023, 1, 1, &drive->bios_cyl, NULL);
+ ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL);
+ ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL);
+ ide_add_setting(drive, "bswap", SETTING_READ, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL);
+ ide_add_setting(drive, "multcount", id ? SETTING_RW : SETTING_READ, HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, TYPE_BYTE, 0, id ? id->max_multsect : 0, 1, 2, &drive->mult_count, set_multcount);
+ ide_add_setting(drive, "nowerr", SETTING_RW, HDIO_GET_NOWERR, HDIO_SET_NOWERR, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr);
+ ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
+ ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL);
+ ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
+
+}
/*
* IDE subdriver functions, registered with ide.c
@@ -502,6 +626,14 @@ static ide_driver_t idedisk_driver = {
idedisk_proc /* proc */
};
+int idedisk_init (void);
+static ide_module_t idedisk_module = {
+ IDE_DRIVER_MODULE,
+ idedisk_init,
+ &idedisk_driver,
+ NULL
+};
+
static int idedisk_cleanup (ide_drive_t *drive)
{
return ide_unregister_subdriver(drive);
@@ -512,6 +644,8 @@ static void idedisk_setup (ide_drive_t *drive)
struct hd_driveid *id = drive->id;
unsigned long capacity, check;
+ idedisk_add_settings(drive);
+
if (id == NULL)
return;
@@ -600,7 +734,7 @@ int idedisk_init (void)
int failed = 0;
MOD_INC_USE_COUNT;
- while ((drive = ide_scan_devices (ide_disk, NULL, failed++)) != NULL) {
+ while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) {
/* SunDisk drives: ignore "second" drive; can mess up non-Sun systems! FIXME */
struct hd_driveid *id = drive->id;
@@ -635,7 +769,7 @@ void cleanup_module (void)
ide_drive_t *drive;
int failed = 0;
- while ((drive = ide_scan_devices (ide_disk, &idedisk_driver, failed)) != NULL)
+ while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, &idedisk_driver, failed)) != NULL)
if (idedisk_cleanup (drive)) {
printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name);
failed++;
diff --git a/drivers/block/ide-dma.c b/drivers/block/ide-dma.c
index bba735580..faf6ceaf7 100644
--- a/drivers/block/ide-dma.c
+++ b/drivers/block/ide-dma.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide-dma.c Version 4.07 December 5, 1997
+ * linux/drivers/block/ide-dma.c Version 4.08 December 31, 1997
*
* Copyright (c) 1995-1998 Mark Lord
* May be copied or modified under the terms of the GNU General Public License
@@ -171,8 +171,8 @@ int ide_build_dmatable (ide_drive_t *drive)
}
/*
* Fill in the dma table, without crossing any 64kB boundaries.
- * The hardware requires 16-bit alignment of all blocks
- * (trm290 requires 32-bit alignment).
+ * Most hardware requires 16-bit alignment of all blocks,
+ * but the trm290 requires 32-bit alignment.
*/
if ((addr & 3)) {
printk("%s: misaligned DMA buffer\n", drive->name);
@@ -247,7 +247,7 @@ static int config_drive_for_dma (ide_drive_t *drive)
int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
- unsigned int dma_base = hwif->dma_base;
+ unsigned long dma_base = hwif->dma_base;
unsigned int count, reading = 0;
switch (func) {
@@ -288,12 +288,12 @@ int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
}
}
-void ide_setup_dma (ide_hwif_t *hwif, unsigned int dma_base, unsigned int num_ports) /* __init */
+void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports) /* __init */
{
static unsigned long dmatable = 0;
static unsigned leftover = 0;
- printk(" %s: BM-DMA at 0x%04x-0x%04x", hwif->name, dma_base, dma_base + num_ports - 1);
+ printk(" %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1);
if (check_region(dma_base, num_ports)) {
printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
return;
@@ -306,7 +306,7 @@ void ide_setup_dma (ide_hwif_t *hwif, unsigned int dma_base, unsigned int num_po
* safely use __get_free_page() here instead
* of __get_dma_pages() -- no ISA limitations.
*/
- dmatable = __get_free_pages(GFP_KERNEL,1,0);
+ dmatable = __get_free_pages(GFP_KERNEL,1);
leftover = dmatable ? PAGE_SIZE : 0;
}
if (!dmatable) {
@@ -356,25 +356,26 @@ __initfunc(static long read_pcicfg_dword (byte fn, unsigned short reg))
/*
* Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space:
*/
-unsigned int ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) /* __init */
+unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) /* __init */
{
- unsigned int new, dma_base = 0;
+ unsigned long new, dma_base = 0;
byte bus = hwif->pci_bus, fn = hwif->pci_fn;
if (hwif->mate && hwif->mate->dma_base) {
dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
- } else if (pcibios_read_config_dword(bus, fn, 0x20, &dma_base)) {
+ } else if (pcibios_read_config_dword(bus, fn, 0x20, (unsigned int *)&dma_base)) {
printk("%s: failed to read dma_base\n", name);
dma_base = 0;
} else if ((dma_base &= ~0xf) == 0 || dma_base == ~0xf) {
- printk("%s: dma_base is invalid (0x%04x, BIOS problem)\n", name, dma_base);
+ printk("%s: dma_base is invalid (0x%04lx, BIOS problem)\n", name, dma_base);
new = ide_find_free_region(16 + extra);
hwif->no_autodma = 1; /* default DMA off if we had to configure it here */
if (new) {
- printk("%s: setting dma_base to 0x%04x\n", name, new);
+ printk("%s: setting dma_base to 0x%04lx\n", name, new);
new |= 1;
(void) pcibios_write_config_dword(bus, fn, 0x20, new);
- (void) pcibios_read_config_dword(bus, fn, 0x20, &dma_base);
+ dma_base = 0;
+ (void) pcibios_read_config_dword(bus, fn, 0x20, (unsigned int *)&dma_base);
if (dma_base != new) {
if (bus == 0) {
printk("%s: operation failed, bypassing BIOS to try again\n", name);
diff --git a/drivers/block/ide-floppy.c b/drivers/block/ide-floppy.c
index 5572a07ba..d79f79ac4 100644
--- a/drivers/block/ide-floppy.c
+++ b/drivers/block/ide-floppy.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide-floppy.c Version 0.8 Feb 21, 1997
+ * linux/drivers/block/ide-floppy.c Version 0.8 Dec 7, 1997
*
* Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
*/
@@ -1327,6 +1327,20 @@ static int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
return 0;
}
+static void idefloppy_add_settings(ide_drive_t *drive)
+{
+ int major = HWIF(drive)->major;
+ int minor = drive->select.b.unit << PARTN_BITS;
+
+ ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_SHORT, 0, 1023, 1, 1, &drive->bios_cyl, NULL);
+ ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL);
+ ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL);
+ ide_add_setting(drive, "breada_readahead", SETTING_RW, BLKRAGET, BLKRASET, TYPE_INT, 0, 255, 1, 2, &read_ahead[major], NULL);
+ ide_add_setting(drive, "file_readahead", SETTING_RW, BLKFRAGET, BLKFRASET, TYPE_INTA, 0, INT_MAX, 1, 1024, &max_readahead[major][minor], NULL);
+ ide_add_setting(drive, "max_kb_per_request", SETTING_RW, BLKSECTGET, BLKSECTSET, TYPE_INTA, 1, 255, 1, 2, &max_sectors[major][minor], NULL);
+
+}
+
/*
* Driver initialization.
*/
@@ -1351,6 +1365,7 @@ static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy)
}
(void) idefloppy_get_capacity (drive);
+ idefloppy_add_settings(drive);
}
static int idefloppy_cleanup (ide_drive_t *drive)
@@ -1364,11 +1379,9 @@ static int idefloppy_cleanup (ide_drive_t *drive)
return 0;
}
-int idefloppy_init (void);
-static ide_module_t idefloppy_module = {
- IDE_DRIVER_MODULE,
- idefloppy_init,
- NULL
+static ide_proc_entry_t idefloppy_proc[] = {
+ { "geometry", proc_ide_read_geometry, NULL },
+ { NULL, NULL, NULL }
};
/*
@@ -1391,7 +1404,15 @@ static ide_driver_t idefloppy_driver = {
NULL, /* pre_reset */
idefloppy_capacity, /* capacity */
NULL, /* special */
- NULL /* proc */
+ idefloppy_proc /* proc */
+};
+
+int idefloppy_init (void);
+static ide_module_t idefloppy_module = {
+ IDE_DRIVER_MODULE,
+ idefloppy_init,
+ &idefloppy_driver,
+ NULL
};
/*
@@ -1404,7 +1425,7 @@ int idefloppy_init (void)
int failed = 0;
MOD_INC_USE_COUNT;
- while ((drive = ide_scan_devices (ide_floppy, NULL, failed++)) != NULL) {
+ while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) {
if (!idefloppy_identify_device (drive, drive->id)) {
printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name);
continue;
@@ -1437,7 +1458,7 @@ void cleanup_module (void)
ide_drive_t *drive;
int failed = 0;
- while ((drive = ide_scan_devices (ide_floppy, &idefloppy_driver, failed)) != NULL)
+ while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, &idefloppy_driver, failed)) != NULL)
if (idefloppy_cleanup (drive)) {
printk ("%s: cleanup_module() called while still busy\n", drive->name);
failed++;
diff --git a/drivers/block/ide-pci.c b/drivers/block/ide-pci.c
index adcf99525..d8fa6edf5 100644
--- a/drivers/block/ide-pci.c
+++ b/drivers/block/ide-pci.c
@@ -1,13 +1,13 @@
/*
- * linux/drivers/block/ide-pci.c Version 1.00 December 8, 1997
+ * linux/drivers/block/ide-pci.c Version 1.02 December 29, 1997
*
* Copyright (c) 1995-1998 Mark Lord
* May be copied or modified under the terms of the GNU General Public License
*/
/*
- * This modules provides support for automatic detection and
- * configuration of all PCI IDE interfaces present in a system.
+ * This module provides support for automatic detection and
+ * configuration of all PCI IDE interfaces present in a system.
*/
#include <linux/config.h>
@@ -41,6 +41,7 @@
#define DEVID_NS87410 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410})
#define DEVID_NS87415 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415})
#define DEVID_HT6565 ((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565})
+#define DEVID_AEC6210 ((ide_pci_devid_t){0x1191, 0x0005})
#define IDE_IGNORE ((void *)-1)
@@ -102,6 +103,7 @@ static ide_pci_device_t ide_pci_chipsets[] __initdata = {
{DEVID_OPTI621X,"OPTI621X", INIT_OPTI621, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
{DEVID_TRM290, "TRM290", INIT_TRM290, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
{DEVID_NS87415, "NS87415", INIT_NS87415, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_AEC6210, "AEC6210", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
{IDE_PCI_DEVID_NULL, "PCI_IDE", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }};
/*
@@ -111,7 +113,7 @@ static ide_pci_device_t ide_pci_chipsets[] __initdata = {
* by the BIOS, to avoid conflicts later in the init cycle,
* but we don't. FIXME
*/
-unsigned int ide_find_free_region (unsigned short size) /* __init */
+unsigned long ide_find_free_region (unsigned short size) /* __init */
{
static unsigned short base = 0x5800; /* it works for me */
unsigned short i;
@@ -228,10 +230,11 @@ __initfunc(static int ide_setup_pci_baseregs (byte bus, byte fn, const char *nam
__initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int ccode, ide_pci_device_t *d))
{
unsigned int port, at_least_one_hwif_enabled = 0, no_autodma = 0;
- unsigned short pcicmd = 0;
+ unsigned short pcicmd = 0, tried_config = 0;
byte tmp = 0, progif = 0, pciirq = 0;
ide_hwif_t *hwif, *mate = NULL;
+check_if_enabled:
if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd)
|| pcibios_read_config_byte(bus, fn, 0x09, &progif)
|| pcibios_read_config_byte(bus, fn, 0x3c, &pciirq))
@@ -247,21 +250,32 @@ __initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int cco
* Maybe the user deliberately *disabled* the device,
* but we'll eventually ignore it again if no drives respond.
*/
- if (ide_setup_pci_baseregs(bus, fn, d->name)
- || pcibios_write_config_word(bus, fn, 0x04, pcicmd|1)
- || pcibios_read_config_word(bus, fn, 0x04, &pcicmd)
- || !(pcicmd & 1))
+ if (tried_config++
+ || ide_setup_pci_baseregs(bus, fn, d->name)
+ || pcibios_write_config_word(bus, fn, 0x04, pcicmd|1))
{
printk("%s: device disabled (BIOS)\n", d->name);
return;
}
no_autodma = 1; /* default DMA off if we had to configure it here */
- printk("%s: device enabled (Linux)\n", d->name);
+ goto check_if_enabled;
}
- if (!pciirq || pciirq >= NR_IRQS) { /* is pciirq invalid? */
- if (pciirq || (progif & 0x5)) /* don't complain if using "legacy" mode */
- printk("%s: BIOS returned %d for IRQ (ignored)\n", d->name, pciirq);
- pciirq = 0; /* probe for it instead */
+ if (tried_config)
+ printk("%s: device enabled (Linux)\n", d->name);
+ /*
+ * Can we trust the reported IRQ?
+ */
+ if ((ccode >> 16) != PCI_CLASS_STORAGE_IDE || (progif & 5) != 5) {
+ printk("%s: not 100%% native mode: will probe irqs later\n", d->name);
+ pciirq = 0;
+ } else if (tried_config) {
+ printk("%s: will probe irqs later\n", d->name);
+ pciirq = 0;
+ } else if (!pciirq || pciirq >= NR_IRQS) {
+ printk("%s: bad irq from BIOS (%d): will probe later\n", d->name, pciirq);
+ pciirq = 0;
+ } else {
+ printk("%s: 100%% native mode on irq %d\n", d->name, pciirq);
}
/*
* Set up the IDE ports
@@ -286,7 +300,8 @@ __initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int cco
hwif->pci_fn = fn;
hwif->pci_devid = d->devid;
hwif->channel = port;
- hwif->irq = pciirq;
+ if (!hwif->irq)
+ hwif->irq = pciirq;
if (mate) {
hwif->mate = mate;
mate->mate = hwif;
@@ -296,7 +311,7 @@ __initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int cco
#ifdef CONFIG_BLK_DEV_IDEDMA
if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) || ((ccode >> 16) == PCI_CLASS_STORAGE_IDE && (ccode & 0x8000))) {
unsigned int extra = (!mate && IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246)) ? 16 : 0;
- unsigned int dma_base = ide_get_or_set_dma_base(hwif, extra, d->name);
+ unsigned long dma_base = ide_get_or_set_dma_base(hwif, extra, d->name);
if (dma_base && !(pcicmd & 4)) {
/*
* Set up BM-DMA capability (PnP BIOS should have done this)
@@ -311,7 +326,7 @@ __initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int cco
if (dma_base)
ide_setup_dma(hwif, dma_base, 8);
else
- printk("%s: %s Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, ccode=0x%04x, dma_base=0x%04x\n",
+ printk("%s: %s Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, ccode=0x%04x, dma_base=0x%04lx\n",
hwif->name, d->name, pcicmd, ccode, dma_base);
}
#endif /* CONFIG_BLK_DEV_IDEDMA */
@@ -352,10 +367,10 @@ static inline void ide_scan_pci_device (unsigned int bus, unsigned int fn)
continue; /* OPTI Viper-M uses same devid for functions 0 and 1 */
else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || (ccode >> 16) == PCI_CLASS_STORAGE_IDE) {
if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL))
- printk("%s: unknown IDE device on PCI bus %d function %d, VID=%04x, DID=%04x\n",
+ printk("%s: unknown IDE controller on PCI bus %d function %d, VID=%04x, DID=%04x\n",
d->name, bus, fn, devid.vid, devid.did);
else
- printk("%s: PCI bus %d function %d\n", d->name, bus, fn);
+ printk("%s: IDE controller on PCI bus %d function %d\n", d->name, bus, fn);
ide_setup_pci_device(bus, fn, ccode, d);
}
} while (hedt == 0x80 && (++fn & 7));
@@ -379,4 +394,3 @@ void ide_scan_pcibus (void) /* __init */
}
}
}
-
diff --git a/drivers/block/ide-probe.c b/drivers/block/ide-probe.c
index 8e9336037..57b4a1f06 100644
--- a/drivers/block/ide-probe.c
+++ b/drivers/block/ide-probe.c
@@ -582,7 +582,7 @@ static void init_gendisk (ide_hwif_t *hwif)
{
struct gendisk *gd, **gdp;
unsigned int unit, units, minors;
- int *bs, *max_sect;
+ int *bs, *max_sect, *max_ra;
/* figure out maximum drive number on the interface */
for (units = MAX_DRIVES; units > 0; --units) {
@@ -595,15 +595,18 @@ static void init_gendisk (ide_hwif_t *hwif)
gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
bs = kmalloc (minors*sizeof(int), GFP_KERNEL);
max_sect = kmalloc (minors*sizeof(int), GFP_KERNEL);
+ max_ra = kmalloc (minors*sizeof(int), GFP_KERNEL);
memset(gd->part, 0, minors * sizeof(struct hd_struct));
/* cdroms and msdos f/s are examples of non-1024 blocksizes */
blksize_size[hwif->major] = bs;
max_sectors[hwif->major] = max_sect;
+ max_readahead[hwif->major] = max_ra;
for (unit = 0; unit < minors; ++unit) {
*bs++ = BLOCK_SIZE;
- *max_sect++ = 244;
+ *max_sect++ = MAX_SECTORS;
+ *max_ra++ = MAX_READAHEAD;
}
for (unit = 0; unit < units; ++unit)
@@ -621,6 +624,11 @@ static void init_gendisk (ide_hwif_t *hwif)
for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
hwif->gd = *gdp = gd; /* link onto tail of list */
+
+ for (unit = 0; unit < units; ++unit) {
+ if (hwif->drives[unit].present)
+ ide_add_generic_settings(hwif->drives + unit);
+ }
}
static int hwif_init (ide_hwif_t *hwif)
@@ -674,6 +682,10 @@ static int hwif_init (ide_hwif_t *hwif)
return hwif->present;
}
+
+int ideprobe_init(void);
+
+
static ide_module_t ideprobe_module = {
IDE_PROBE_MODULE,
ideprobe_init,
diff --git a/drivers/block/ide-proc.c b/drivers/block/ide-proc.c
index 55ad22521..e9dad1c26 100644
--- a/drivers/block/ide-proc.c
+++ b/drivers/block/ide-proc.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/proc_ide.c Version 1.01 December 12, 1997
+ * linux/drivers/block/ide-proc.c Version 1.03 January 2, 1998
*
* Copyright (C) 1997-1998 Mark Lord
*/
@@ -13,18 +13,40 @@
* This should provide better utilities, and less kernel bloat.
*
* The entire pci config space for a PCI interface chipset can be
- * retrieved by just reading it. e.g. "cat /proc/ide3/pci"
+ * retrieved by just reading it. e.g. "cat /proc/ide3/config"
*
- * To modify registers, do something like:
- * echo "40:88" >/proc/ide/ide3/pci
+ * To modify registers *safely*, do something like:
+ * echo "P40:88" >/proc/ide/ide3/config
* That expression writes 0x88 to pci config register 0x40
* on the chip which controls ide3. Multiple tuples can be issued,
* and the writes will be completed as an atomic set:
- * echo "40:88 41:35 42:00 43:00" >/proc/ide/ide3/pci
- * All numbers must be pairs of ascii hex digits.
+ * echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config
*
- * Also useful, "cat /proc/ide0/hda/identify" will issue an IDENTIFY
- * (or PACKET_IDENTIFY) command to /dev/hda, and then dump out the
+ * All numbers must be specified using pairs of ascii hex digits.
+ * It is important to note that these writes will be performed
+ * after waiting for the IDE controller (both interfaces)
+ * to be completely idle, to ensure no corruption of I/O in progress.
+ *
+ * Non-PCI registers can also be written, using "R" in place of "P"
+ * in the above examples. The size of the port transfer is determined
+ * by the number of pairs of hex digits given for the data. If a two
+ * digit value is given, the write will be a byte operation; if four
+ * digits are used, the write will be performed as a 16-bit operation;
+ * and if eight digits are specified, a 32-bit "dword" write will be
+ * performed. Odd numbers of digits are not permitted.
+ *
+ * If there is an error *anywhere* in the string of registers/data
+ * then *none* of the writes will be performed.
+ *
+ * Drive/Driver settings can be retrieved by reading the drive's
+ * "settings" files. e.g. "cat /proc/ide0/hda/settings"
+ * To write a new value "val" into a specific setting "name", use:
+ * echo "name:val" >/proc/ide/ide0/hda/settings
+ *
+ * Also useful, "cat /proc/ide0/hda/[identify, smart_values,
+ * smart_thresholds, capabilities]" will issue an IDENTIFY /
+ * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
+ * SENSE CAPABILITIES command to /dev/hda, and then dump out the
* returned data as 256 16-bit words. The "hdparm" utility will
* be updated someday soon to use this mechanism.
*
@@ -44,29 +66,13 @@
#include <linux/pci.h>
#include <linux/bios32.h>
#include <linux/ctype.h>
+#include <asm/io.h>
#include "ide.h"
#ifndef MIN
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
-/*
- * Standard exit stuff:
- */
-#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \
-{ \
- len -= off; \
- if (len < count) { \
- *eof = 1; \
- if (len <= 0) \
- return 0; \
- } else \
- len = count; \
- *start = page + off; \
- return len; \
-}
-
-
#ifdef CONFIG_PCI
static int ide_getxdigit(char c)
@@ -81,13 +87,14 @@ static int ide_getxdigit(char c)
return digit;
}
-
-static int xx_xx_parse_error (const char *start, unsigned long maxlen)
+static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
{
- char errbuf[7];
- int i, len = MIN(6, maxlen);
+ char errbuf[16];
+ int i;
+ if (len >= sizeof(errbuf))
+ len = sizeof(errbuf) - 1;
for (i = 0; i < len; ++i) {
- char c = start[i];
+ char c = data[i];
if (!c || c == '\n')
c = '\0';
else if (iscntrl(c))
@@ -95,17 +102,17 @@ static int xx_xx_parse_error (const char *start, unsigned long maxlen)
errbuf[i] = c;
}
errbuf[i] = '\0';
- printk("proc_ide: error: expected 'xx:xx', but got '%s'\n", errbuf);
+ printk("proc_ide: error: %s: '%s'\n", msg, errbuf);
return -EINVAL;
}
-static int proc_ide_write_pci
+static int proc_ide_write_config
(struct file *file, const char *buffer, unsigned long count, void *data)
{
ide_hwif_t *hwif = (ide_hwif_t *)data;
int for_real = 0;
- unsigned long n, flags;
- const char *start;
+ unsigned long startn = 0, n, flags;
+ const char *start = NULL, *msg = NULL;
if (!suser())
return -EACCES;
@@ -122,55 +129,109 @@ static int proc_ide_write_pci
*/
save_flags(flags);
do {
- const char *p = buffer;
- n = count;
+ const char *p;
if (for_real) {
unsigned long timeout = jiffies + (3 * HZ);
+ ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
+ ide_hwgroup_t *mategroup = NULL;
+ if (hwif->mate && hwif->mate->hwgroup)
+ mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
cli(); /* ensure all PCI writes are done together */
- while (((ide_hwgroup_t *)(hwif->hwgroup))->active || (hwif->mate && ((ide_hwgroup_t *)(hwif->mate->hwgroup))->active)) {
- sti();
- if (0 < (signed long)(timeout - jiffies)) {
+ while (mygroup->active || (mategroup && mategroup->active)) {
+ restore_flags(flags);
+ if (0 < (signed long)(jiffies - timeout)) {
printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name);
return -EBUSY;
}
cli();
}
}
- while (n) {
- int d1, d2, rc;
- byte reg, val;
+ p = buffer;
+ n = count;
+ while (n > 0) {
+ int d, digits;
+ unsigned int reg = 0, val = 0, is_pci;
start = p;
-#if 0
- printk("loop(%d): n=%ld, input=%.5s\n", for_real, n, p);
-#endif
- if (n < 5)
- goto parse_error;
- if (0 > (d1 = ide_getxdigit(*p++)) || 0 > (d2 = ide_getxdigit(*p++)))
+ startn = n--;
+ switch (*p++) {
+ case 'R': is_pci = 0;
+ break;
+ case 'P': is_pci = 1;
+ break;
+ default: msg = "expected 'R' or 'P'";
+ goto parse_error;
+ }
+ digits = 0;
+ while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+ reg = (reg << 4) | d;
+ --n;
+ ++p;
+ ++digits;
+ }
+ if (!digits || (digits > 4) || (is_pci && reg > 0xff)) {
+ msg = "bad/missing register number";
goto parse_error;
- reg = (d1 << 4) | d2;
- if (*p++ != ':')
+ }
+ if (--n < 0 || *p++ != ':') {
+ msg = "missing ':'";
goto parse_error;
- if (0 > (d1 = ide_getxdigit(*p++)) || 0 > (d2 = ide_getxdigit(*p++)))
+ }
+ digits = 0;
+ while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+ val = (val << 4) | d;
+ --n;
+ ++p;
+ ++digits;
+ }
+ if (digits != 2 && digits != 4 && digits != 8) {
+ msg = "bad data, 2/4/8 digits required";
goto parse_error;
- val = (d1 << 4) | d2;
- if (n > 5 && !isspace(*p))
+ }
+ if (n > 0 && !isspace(*p)) {
+ msg = "expected whitespace after data";
goto parse_error;
- n -= 5;
- while (n && isspace(*p)) {
+ }
+ while (n > 0 && isspace(*p)) {
--n;
++p;
}
+ if (is_pci && (reg & ((digits >> 1) - 1))) {
+ msg = "misaligned access";
+ goto parse_error;
+ }
if (for_real) {
#if 0
- printk("proc_ide_write_pci: reg=0x%02x, val=0x%02x\n", reg, val);
+ printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? 'PCI' : 'non-PCI', reg, val, digits);
#endif
- rc = pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, reg, val);
- if (rc) {
- restore_flags(flags);
- printk("proc_ide_write_pci: error writing bus %d fn %d reg 0x%02x value 0x%02x\n",
- hwif->pci_bus, hwif->pci_fn, reg, val);
- printk("proc_ide_write_pci: %s\n", pcibios_strerror(rc));
- return -EIO;
+ if (is_pci) {
+ int rc = 0;
+ switch (digits) {
+ case 2: msg = "byte";
+ rc = pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, reg, val);
+ break;
+ case 4: msg = "word";
+ rc = pcibios_write_config_word(hwif->pci_bus, hwif->pci_fn, reg, val);
+ break;
+ case 8: msg = "dword";
+ rc = pcibios_write_config_dword(hwif->pci_bus, hwif->pci_fn, reg, val);
+ break;
+ }
+ if (rc) {
+ restore_flags(flags);
+ printk("proc_ide_write_config: error writing %s at bus %d fn %d reg 0x%x value 0x%x\n",
+ msg, hwif->pci_bus, hwif->pci_fn, reg, val);
+ printk("proc_ide_write_config: %s\n", pcibios_strerror(rc));
+ return -EIO;
+ }
+ } else { /* not pci */
+ switch (digits) {
+ case 2: outb(val, reg);
+ break;
+ case 4: outw(val, reg);
+ break;
+ case 8: outl(val, reg);
+ break;
+ }
}
}
}
@@ -179,25 +240,26 @@ static int proc_ide_write_pci
return count;
parse_error:
restore_flags(flags);
- return xx_xx_parse_error(start, n);
+ printk("parse error\n");
+ return xx_xx_parse_error(start, startn, msg);
}
-static int proc_ide_read_pci
+static int proc_ide_read_config
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
ide_hwif_t *hwif = (ide_hwif_t *)data;
char *out = page;
int len, reg = 0;
- out += sprintf(out, "Bus %d Function %d Vendor %04x Device %04x Channel %d\n",
+ out += sprintf(out, "pci bus %d function %d vendor %04x device %04x channel %d\n",
hwif->pci_bus, hwif->pci_fn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel);
do {
byte val;
int rc = pcibios_read_config_byte(hwif->pci_bus, hwif->pci_fn, reg, &val);
if (rc) {
- printk("proc_ide_read_pci: error reading bus %d fn %d reg 0x%02x\n",
+ printk("proc_ide_read_config: error reading bus %d fn %d reg 0x%02x\n",
hwif->pci_bus, hwif->pci_fn, reg);
- printk("proc_ide_read_pci: %s\n", pcibios_strerror(rc));
+ printk("proc_ide_read_config: %s\n", pcibios_strerror(rc));
return -EIO;
out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n');
} else
@@ -212,15 +274,40 @@ static int proc_ide_read_imodel
{
ide_hwif_t *hwif = (ide_hwif_t *) data;
int len;
- const char *vids, *dids;
- vids = pci_strvendor(hwif->pci_devid.vid);
- dids = pci_strdev(hwif->pci_devid.vid, hwif->pci_devid.did);
- len = sprintf(page,"%s: %s\n", vids ? vids : "(none)", dids ? dids : "(none)");
+ len = sprintf(page,"%04x: %04x\n", hwif->pci_devid.vid, hwif->pci_devid.did);
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
}
#endif /* CONFIG_PCI */
+static int ide_getdigit(char c)
+{
+ int digit;
+ if (isdigit(c))
+ digit = c - '0';
+ else
+ digit = -1;
+ return digit;
+}
+
+static int proc_ide_read_drivers
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ char *out = page;
+ int len;
+ ide_module_t *p = ide_modules;
+ ide_driver_t *driver;
+
+ while (p) {
+ driver = (ide_driver_t *) p->info;
+ if (p->type == IDE_DRIVER_MODULE && driver)
+ out += sprintf(out, "%s version %s\n", driver->name, driver->version);
+ p = p->next;
+ }
+ len = out - page;
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
static int proc_ide_read_type
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
@@ -270,22 +357,9 @@ static int proc_ide_read_channel
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
}
-static int proc_ide_get_identify (ide_drive_t *drive, byte *buf)
+static int proc_ide_get_identify(ide_drive_t *drive, byte *buf)
{
- struct request rq;
- byte *end;
-
- ide_init_drive_cmd(&rq);
- rq.buffer = buf;
- *buf++ = (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY;
- *buf++ = 0;
- *buf++ = 0;
- *buf++ = 1;
- end = buf + (SECTOR_WORDS * 4);
- while (buf != end)
- *buf++ = 0; /* pre-zero it, in case identify fails */
- (void) ide_do_drive_cmd(drive, &rq, ide_wait);
- return 0;
+ return ide_wait_cmd(drive, (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY, 0, 0, 1, buf);
}
static int proc_ide_read_identify
@@ -311,27 +385,141 @@ static int proc_ide_read_settings
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
ide_drive_t *drive = (ide_drive_t *) data;
+ ide_settings_t *setting = (ide_settings_t *) drive->settings;
char *out = page;
- int len;
-
- out += sprintf(out,"multcount %i\n", drive->mult_count);
- out += sprintf(out,"io_32bit %i\n", drive->io_32bit);
- out += sprintf(out,"unmaskirq %i\n", drive->unmask);
- out += sprintf(out,"using_dma %i\n", drive->using_dma);
- out += sprintf(out,"nowerr %i\n", drive->bad_wstat == BAD_R_STAT);
- out += sprintf(out,"keepsettings %i\n", drive->keep_settings);
- out += sprintf(out,"nice %i/%i/%i\n", drive->nice0, drive->nice1, drive->nice2);
+ int len, rc, mul_factor, div_factor;
+
+ out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
+ out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
+ while(setting) {
+ mul_factor = setting->mul_factor;
+ div_factor = setting->div_factor;
+ out += sprintf(out, "%-24s", setting->name);
+ if ((rc = ide_read_setting(drive, setting)) >= 0)
+ out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
+ else
+ out += sprintf(out, "%-16s", "write-only");
+ out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
+ if (setting->rw & SETTING_READ)
+ out += sprintf(out, "r");
+ if (setting->rw & SETTING_WRITE)
+ out += sprintf(out, "w");
+ out += sprintf(out, "\n");
+ setting = setting->next;
+ }
len = out - page;
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
}
+#define MAX_LEN 30
+
+static int proc_ide_write_settings
+ (struct file *file, const char *buffer, unsigned long count, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ ide_hwif_t *hwif = HWIF(drive);
+ char name[MAX_LEN + 1];
+ int for_real = 0, len;
+ unsigned long n, flags;
+ const char *start = NULL;
+ ide_settings_t *setting;
+
+ if (!suser())
+ return -EACCES;
+
+ /*
+ * Skip over leading whitespace
+ */
+ while (count && isspace(*buffer)) {
+ --count;
+ ++buffer;
+ }
+ /*
+ * Do one full pass to verify all parameters,
+ * then do another to actually write the pci regs.
+ */
+ save_flags(flags);
+ do {
+ const char *p;
+ if (for_real) {
+ unsigned long timeout = jiffies + (3 * HZ);
+ ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
+ ide_hwgroup_t *mategroup = NULL;
+ if (hwif->mate && hwif->mate->hwgroup)
+ mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
+ cli(); /* ensure all PCI writes are done together */
+ while (mygroup->active || (mategroup && mategroup->active)) {
+ restore_flags(flags);
+ if (0 < (signed long)(jiffies - timeout)) {
+ printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name);
+ return -EBUSY;
+ }
+ cli();
+ }
+ }
+ p = buffer;
+ n = count;
+ while (n > 0) {
+ int d, digits;
+ unsigned int val = 0;
+ start = p;
+
+ while (n > 0 && *p != ':') {
+ --n;
+ p++;
+ }
+ if (*p != ':')
+ goto parse_error;
+ len = IDE_MIN(p - start, MAX_LEN);
+ strncpy(name, start, IDE_MIN(len, MAX_LEN));
+ name[len] = 0;
+
+ if (n > 0) {
+ --n;
+ p++;
+ } else
+ goto parse_error;
+
+ digits = 0;
+ while (n > 0 && (d = ide_getdigit(*p)) >= 0) {
+ val = (val * 10) + d;
+ --n;
+ ++p;
+ ++digits;
+ }
+ if (n > 0 && !isspace(*p))
+ goto parse_error;
+ while (n > 0 && isspace(*p)) {
+ --n;
+ ++p;
+ }
+ setting = ide_find_setting_by_name(drive, name);
+ if (!setting)
+ goto parse_error;
+
+ if (for_real)
+ ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
+ }
+ } while (!for_real++);
+ restore_flags(flags);
+ return count;
+parse_error:
+ restore_flags(flags);
+ printk("proc_ide_write_settings(): parse error\n");
+ return -EINVAL;
+}
+
int proc_ide_read_capacity
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
ide_drive_t *drive = (ide_drive_t *) data;
+ ide_driver_t *driver = (ide_driver_t *) drive->driver;
int len;
- len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive));
+ if (!driver)
+ len = sprintf(page, "(none)\n");
+ else
+ len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive));
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
}
@@ -373,6 +561,18 @@ static int proc_ide_read_driver
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
}
+static int proc_ide_write_driver
+ (struct file *file, const char *buffer, unsigned long count, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+
+ if (!suser())
+ return -EACCES;
+ if (ide_replace_subdriver(drive, buffer))
+ return -EINVAL;
+ return count;
+}
+
static int proc_ide_read_media
(char *page, char **start, off_t off, int count, int *eof, void *data)
{
@@ -397,14 +597,12 @@ static int proc_ide_read_media
PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
}
-
static ide_proc_entry_t generic_drive_entries[] = {
- { "capacity", proc_ide_read_capacity, NULL },
- { "driver", proc_ide_read_driver, NULL },
+ { "driver", proc_ide_read_driver, proc_ide_write_driver },
{ "identify", proc_ide_read_identify, NULL },
{ "media", proc_ide_read_media, NULL },
{ "model", proc_ide_read_dmodel, NULL },
- { "settings", proc_ide_read_settings, NULL },
+ { "settings", proc_ide_read_settings, proc_ide_write_settings },
{ NULL, NULL, NULL }
};
@@ -415,8 +613,12 @@ void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p)
if (!drive->proc || !p)
return;
while (p->name != NULL) {
- ent = create_proc_entry(p->name, 0, drive->proc);
+ mode_t mode = S_IFREG|S_IRUSR;
+ if (!strcmp(p->name,"settings"))
+ mode |= S_IWUSR;
+ ent = create_proc_entry(p->name, mode, drive->proc);
if (!ent) return;
+ ent->nlink = 1;
ent->data = drive;
ent->read_proc = p->read_proc;
ent->write_proc = p->write_proc;
@@ -434,9 +636,16 @@ void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p)
}
}
-static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *parent)
+static int proc_ide_readlink(struct proc_dir_entry *de, char *page)
+{
+ int n = (de->name[2] - 'a') / 2;
+ return sprintf(page, "ide%d/%s", n, de->name);
+}
+
+static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *parent, struct proc_dir_entry *root)
{
int d;
+ struct proc_dir_entry *ent;
for (d = 0; d < MAX_DRIVES; d++) {
ide_drive_t *drive = &hwif->drives[d];
@@ -446,6 +655,12 @@ static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *par
drive->proc = create_proc_entry(drive->name, S_IFDIR, parent);
if (drive->proc)
ide_add_proc_entries(drive, generic_drive_entries);
+
+ ent = create_proc_entry(drive->name, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, root);
+ if (!ent) return;
+ ent->data = drive;
+ ent->readlink_proc = proc_ide_readlink;
+ ent->nlink = 1;
}
}
@@ -463,11 +678,12 @@ static void create_proc_ide_interfaces (struct proc_dir_entry *parent)
if (!hwif_ent) return;
#ifdef CONFIG_PCI
if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) {
- ent = create_proc_entry("pci", 0, hwif_ent);
+ ent = create_proc_entry("config", S_IFREG|S_IRUSR|S_IWUSR, hwif_ent);
if (!ent) return;
+ ent->nlink = 1;
ent->data = hwif;
- ent->read_proc = proc_ide_read_pci;
- ent->write_proc = proc_ide_write_pci;;
+ ent->read_proc = proc_ide_read_config;
+ ent->write_proc = proc_ide_write_config;;
ent = create_proc_entry("model", 0, hwif_ent);
if (!ent) return;
@@ -492,14 +708,18 @@ static void create_proc_ide_interfaces (struct proc_dir_entry *parent)
ent->data = hwif;
ent->read_proc = proc_ide_read_type;
- create_proc_ide_drives(hwif, hwif_ent);
+ create_proc_ide_drives(hwif, hwif_ent, parent);
}
}
void proc_ide_init(void)
{
- struct proc_dir_entry *ent;
- ent = create_proc_entry("ide", S_IFDIR, 0);
+ struct proc_dir_entry *root, *ent;
+ root = create_proc_entry("ide", S_IFDIR, 0);
+ if (!root) return;
+ create_proc_ide_interfaces(root);
+
+ ent = create_proc_entry("drivers", 0, root);
if (!ent) return;
- create_proc_ide_interfaces(ent);
+ ent->read_proc = proc_ide_read_drivers;
}
diff --git a/drivers/block/ide-tape.c b/drivers/block/ide-tape.c
index a30fc9a58..f3f4ef8f1 100644
--- a/drivers/block/ide-tape.c
+++ b/drivers/block/ide-tape.c
@@ -1,7 +1,7 @@
/*
- * linux/drivers/block/ide-tape.c Version 1.12 Dec 7, 1997
+ * linux/drivers/block/ide-tape.c Version 1.13 Jan 2, 1998
*
- * Copyright (C) 1995, 1996 Gadi Oxman <gadio@netvision.net.il>
+ * Copyright (C) 1995 - 1998 Gadi Oxman <gadio@netvision.net.il>
*
* This driver was constructed as a student project in the software laboratory
* of the faculty of electrical engineering in the Technion - Israel's
@@ -211,6 +211,7 @@
* of bytes written to the tape was not an integral
* number of tape blocks.
* Add support for INTERRUPT DRQ devices.
+ * Ver 1.13 Jan 2 98 Add "speed == 0" work-around for HP COLORADO 5GB
*
* Here are some words from the first releases of hd.c, which are quoted
* in ide.c and apply here as well:
@@ -320,7 +321,7 @@
* sharing a (fast) ATA-2 disk with any (slow) new ATAPI device.
*/
-#define IDETAPE_VERSION "1.12"
+#define IDETAPE_VERSION "1.13"
#include <linux/config.h>
#include <linux/module.h>
@@ -675,7 +676,7 @@ typedef struct {
*/
int nr_stages; /* Number of currently used stages */
int nr_pending_stages; /* Number of pending stages */
- int max_stages; /* We will not allocate more than this number of stages */
+ int max_stages, min_pipeline, max_pipeline; /* We will not allocate more than this number of stages */
idetape_stage_t *first_stage; /* The first stage which will be removed from the pipeline */
idetape_stage_t *active_stage; /* The currently active stage */
idetape_stage_t *next_stage; /* Will be serviced after the currently active request */
@@ -1409,12 +1410,15 @@ static void idetape_switch_buffers (idetape_tape_t *tape, idetape_stage_t *stage
static void idetape_increase_max_pipeline_stages (ide_drive_t *drive)
{
idetape_tape_t *tape = drive->driver_data;
+ int increase = (tape->max_pipeline - tape->min_pipeline) / 10;
#if IDETAPE_DEBUG_LOG
printk (KERN_INFO "Reached idetape_increase_max_pipeline_stages\n");
#endif /* IDETAPE_DEBUG_LOG */
- tape->max_stages = IDE_MIN (tape->max_stages + IDETAPE_INCREASE_STAGES_RATE, IDETAPE_MAX_PIPELINE_STAGES);
+ tape->max_stages += increase;
+ tape->max_stages = IDE_MAX(tape->max_stages, tape->min_pipeline);
+ tape->max_stages = IDE_MIN(tape->max_stages, tape->max_pipeline);
}
/*
@@ -2520,7 +2524,7 @@ static void idetape_discard_read_pipeline (ide_drive_t *drive)
while (tape->first_stage != NULL)
idetape_remove_stage_head (drive);
tape->nr_pending_stages = 0;
- tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+ tape->max_stages = tape->min_pipeline;
}
/*
@@ -2610,7 +2614,7 @@ static void idetape_empty_write_pipeline (ide_drive_t *drive)
* as some systems are constantly on, and the system load
* can be totally different on the next backup).
*/
- tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+ tape->max_stages = tape->min_pipeline;
#if IDETAPE_DEBUG_BUGS
if (tape->first_stage != NULL || tape->next_stage != NULL || tape->last_stage != NULL || tape->nr_stages != 0) {
printk (KERN_ERR "ide-tape: ide-tape pipeline bug\n");
@@ -3321,6 +3325,9 @@ static int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
unsigned short mask,i;
#endif /* IDETAPE_DEBUG_LOG */
+ if (!id)
+ return 0;
+
*((unsigned short *) &gcw) = id->config;
#if IDETAPE_DEBUG_LOG
@@ -3421,10 +3428,7 @@ static int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id)
printk (KERN_ERR "ide-tape: Device type is not set to tape\n");
else if (!gcw.removable)
printk (KERN_ERR "ide-tape: The removable flag is not set\n");
- else if (gcw.drq_type != 2) {
- printk (KERN_ERR "ide-tape: Sorry, DRQ types other than Accelerated DRQ\n");
- printk (KERN_ERR "ide-tape: are still not supported by the driver\n");
- } else if (gcw.packet_size != 0) {
+ else if (gcw.packet_size != 0) {
printk (KERN_ERR "ide-tape: Packet size is not 12 bytes long\n");
if (gcw.packet_size == 1)
printk (KERN_ERR "ide-tape: Sorry, padding to 16 bytes is still not supported\n");
@@ -3460,6 +3464,15 @@ static void idetape_get_mode_sense_results (ide_drive_t *drive)
capabilities->speed = ntohs (capabilities->speed);
capabilities->buffer_size = ntohs (capabilities->buffer_size);
+ if (!capabilities->speed) {
+ printk("ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name);
+ capabilities->speed = 650;
+ }
+ if (!capabilities->max_speed) {
+ printk("ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name);
+ capabilities->max_speed = 650;
+ }
+
tape->capabilities = *capabilities; /* Save us a copy */
tape->tape_block_size = capabilities->blk512 ? 512:1024;
#if IDETAPE_DEBUG_LOG
@@ -3493,6 +3506,24 @@ static void idetape_get_mode_sense_results (ide_drive_t *drive)
#endif /* IDETAPE_DEBUG_LOG */
}
+static void idetape_add_settings(ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+/*
+ * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function
+ */
+ ide_add_setting(drive, "buffer", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 2, &tape->capabilities.buffer_size, NULL);
+ ide_add_setting(drive, "pipeline_min", SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->min_pipeline, NULL);
+ ide_add_setting(drive, "pipeline", SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->max_stages, NULL);
+ ide_add_setting(drive, "pipeline_max", SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->max_pipeline, NULL);
+ ide_add_setting(drive, "pipeline_used",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->nr_stages, NULL);
+ ide_add_setting(drive, "speed", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->capabilities.speed, NULL);
+ ide_add_setting(drive, "stage", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1024, &tape->stage_size, NULL);
+ ide_add_setting(drive, "tdsc", SETTING_RW, -1, -1, TYPE_INT, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX, 1000, HZ, &tape->best_dsc_rw_frequency, NULL);
+ ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL);
+}
+
/*
* ide_setup is called to:
*
@@ -3521,7 +3552,9 @@ static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor)
tape->name[0] = 'h'; tape->name[1] = 't'; tape->name[2] = '0' + minor;
tape->chrdev_direction = idetape_direction_none;
tape->pc = tape->pc_stack;
- tape->max_stages = IDETAPE_MIN_PIPELINE_STAGES;
+ tape->min_pipeline = IDETAPE_MIN_PIPELINE_STAGES;
+ tape->max_pipeline = IDETAPE_MAX_PIPELINE_STAGES;
+ tape->max_stages = tape->min_pipeline;
*((unsigned short *) &gcw) = drive->id->config;
if (gcw.drq_type == 1)
set_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags);
@@ -3577,6 +3610,8 @@ static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor)
drive->name, tape->name, tape->capabilities.speed, (tape->capabilities.buffer_size * 512) / tape->stage_size,
tape->stage_size / 1024, tape->max_stages * tape->stage_size / 1024,
tape->best_dsc_rw_frequency * 1000 / HZ, drive->using_dma ? ", DMA":"");
+
+ idetape_add_settings(drive);
}
static int idetape_cleanup (ide_drive_t *drive)
@@ -3605,12 +3640,21 @@ static int idetape_cleanup (ide_drive_t *drive)
return 0;
}
-int idetape_init (void);
+static int proc_idetape_read_name
+ (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ ide_drive_t *drive = (ide_drive_t *) data;
+ idetape_tape_t *tape = drive->driver_data;
+ char *out = page;
+ int len;
-static ide_module_t idetape_module = {
- IDE_DRIVER_MODULE,
- idetape_init,
- NULL
+ len = sprintf(out,"%s\n", tape->name);
+ PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
+static ide_proc_entry_t idetape_proc[] = {
+ { "name", proc_idetape_read_name, NULL },
+ { NULL, NULL, NULL }
};
/*
@@ -3633,7 +3677,15 @@ static ide_driver_t idetape_driver = {
idetape_pre_reset, /* pre_reset */
NULL, /* capacity */
NULL, /* special */
- NULL /* proc */
+ idetape_proc /* proc */
+};
+
+int idetape_init (void);
+static ide_module_t idetape_module = {
+ IDE_DRIVER_MODULE,
+ idetape_init,
+ &idetape_driver,
+ NULL
};
/*
@@ -3669,7 +3721,7 @@ int idetape_init (void)
for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ )
idetape_chrdevs[minor].drive = NULL;
- if ((drive = ide_scan_devices (ide_tape, NULL, failed++)) == NULL) {
+ if ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) == NULL) {
ide_register_module (&idetape_module);
MOD_DEC_USE_COUNT;
return 0;
@@ -3698,7 +3750,7 @@ int idetape_init (void)
idetape_setup (drive, tape, minor);
idetape_chrdevs[minor].drive = drive;
supported++; failed--;
- } while ((drive = ide_scan_devices (ide_tape, NULL, failed++)) != NULL);
+ } while ((drive = ide_scan_devices (ide_tape, idetape_driver.name, NULL, failed++)) != NULL);
if (!idetape_chrdev_present && !supported) {
unregister_chrdev (IDETAPE_MAJOR, "ht");
} else
diff --git a/drivers/block/ide.c b/drivers/block/ide.c
index 3b9a17283..7ec27d45d 100644
--- a/drivers/block/ide.c
+++ b/drivers/block/ide.c
@@ -1,5 +1,5 @@
/*
- * linux/drivers/block/ide.c Version 6.11 December 5, 1997
+ * linux/drivers/block/ide.c Version 6.12 January 2, 1998
*
* Copyright (C) 1994-1998 Linus Torvalds & authors (see below)
*/
@@ -98,6 +98,8 @@
* Version 6.11 fix probe error in ide_scan_devices()
* fix ancient "jiffies" polling bugs
* mask all hwgroup interrupts on each irq entry
+ * Version 6.12 integrate ioctl and proc interfaces
+ * fix parsing of "idex=" command line parameter
*
* Some additional driver compile-time options are in ide.h
*
@@ -151,7 +153,7 @@ static int ide_lock = 0;
/*
* ide_modules keeps track of the available IDE chipset/probe/driver modules.
*/
-static ide_module_t *ide_modules = NULL;
+ide_module_t *ide_modules = NULL;
/*
* This is declared extern in ide.h, for access by other IDE modules:
@@ -168,13 +170,13 @@ static unsigned long read_timer(void)
unsigned long t, flags;
int i;
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
t = jiffies * 11932;
outb_p(0, 0x43);
i = inb_p(0x40);
i |= inb(0x40) << 8;
- restore_flags(flags);
+ __restore_flags(flags);
return (t - i);
}
#endif /* DISK_RECOVERY_TIME */
@@ -312,11 +314,11 @@ void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
#if SUPPORT_VLB_SYNC
if (io_32bit & 2) {
unsigned long flags;
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
do_vlb_sync(IDE_NSECTOR_REG);
insl(IDE_DATA_REG, buffer, wcount);
- restore_flags(flags);
+ __restore_flags(flags);
} else
#endif /* SUPPORT_VLB_SYNC */
insl(IDE_DATA_REG, buffer, wcount);
@@ -345,11 +347,11 @@ void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
#if SUPPORT_VLB_SYNC
if (io_32bit & 2) {
unsigned long flags;
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
do_vlb_sync(IDE_NSECTOR_REG);
outsl(IDE_DATA_REG, buffer, wcount);
- restore_flags(flags);
+ __restore_flags(flags);
} else
#endif /* SUPPORT_VLB_SYNC */
outsl(IDE_DATA_REG, buffer, wcount);
@@ -571,8 +573,8 @@ static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
ide_hwif_t *hwif = HWIF(drive);
ide_hwgroup_t *hwgroup = HWGROUP(drive);
- save_flags(flags);
- cli(); /* Why ? */
+ __save_flags(flags);
+ __cli(); /* Why ? */
/* For an ATAPI device, first try an ATAPI SRST. */
if (drive->media != ide_disk && !do_not_try_atapi) {
@@ -582,7 +584,7 @@ static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
- restore_flags (flags);
+ __restore_flags (flags);
return;
}
@@ -610,7 +612,7 @@ static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
ide_set_handler (drive, &reset_pollfunc, HZ/20);
#endif /* OK_TO_RESET_CONTROLLER */
- restore_flags (flags);
+ __restore_flags (flags);
}
/*
@@ -638,15 +640,15 @@ void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
args[2] = IN_BYTE(IDE_NSECTOR_REG);
}
}
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
drive->queue = rq->next;
blk_dev[MAJOR(rq->rq_dev)].current_request = NULL;
HWGROUP(drive)->rq = NULL;
rq->rq_status = RQ_INACTIVE;
if (rq->sem != NULL)
up(rq->sem);
- restore_flags(flags);
+ __restore_flags(flags);
}
/*
@@ -657,8 +659,8 @@ byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
unsigned long flags;
byte err = 0;
- save_flags (flags);
- ide_sti();
+ __save_flags (flags);
+ /* ide_sti(); HACK */
printk("%s: %s: status=0x%02x", drive->name, msg, stat);
#if FANCY_STATUS_DUMPS
printk(" { ");
@@ -711,7 +713,7 @@ byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
#endif /* FANCY_STATUS_DUMPS */
printk("\n");
}
- restore_flags (flags);
+ __restore_flags (flags);
return err;
}
@@ -809,20 +811,20 @@ static void drive_cmd_intr (ide_drive_t *drive)
{
struct request *rq = HWGROUP(drive)->rq;
byte *args = (byte *) rq->buffer;
- byte test, stat = GET_STAT();
+ byte stat = GET_STAT();
+ int retries = 10;
- ide_sti();
+ /* ide_sti(); HACK */
if ((stat & DRQ_STAT) && args && args[3]) {
byte io_32bit = drive->io_32bit;
drive->io_32bit = 0;
ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
drive->io_32bit = io_32bit;
- stat = GET_STAT();
+ while (((stat = GET_STAT()) & BUSY_STAT) && retries--)
+ udelay(100);
}
- test = stat;
- if (drive->media == ide_cdrom)
- test = stat &~BUSY_STAT;
- if (OK_STAT(test,READY_STAT,BAD_STAT))
+
+ if (OK_STAT(stat, READY_STAT, BAD_STAT))
ide_end_drive_cmd (drive, stat, GET_ERR());
else
ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
@@ -870,17 +872,17 @@ int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeou
udelay(1); /* spec allows drive 400ns to assert "BUSY" */
if ((stat = GET_STAT()) & BUSY_STAT) {
- save_flags(flags);
- ide_sti();
+ __save_flags(flags);
+ /* ide_sti(); HACK */
timeout += jiffies;
while ((stat = GET_STAT()) & BUSY_STAT) {
if (0 < (signed long)(jiffies - timeout)) {
- restore_flags(flags);
+ __restore_flags(flags);
ide_error(drive, "status timeout", stat);
return 1;
}
}
- restore_flags(flags);
+ __restore_flags(flags);
}
udelay(1); /* allow status to settle, then read it again */
if (OK_STAT((stat = GET_STAT()), good, bad))
@@ -901,6 +903,10 @@ static void execute_drive_cmd (ide_drive_t *drive, struct request *rq)
printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
drive->name, args[0], args[1], args[2], args[3]);
#endif
+ if (args[0] == WIN_SMART) {
+ OUT_BYTE(0x4f, IDE_LCYL_REG);
+ OUT_BYTE(0xc2, IDE_HCYL_REG);
+ }
OUT_BYTE(args[2],IDE_FEATURE_REG);
ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
return;
@@ -926,7 +932,7 @@ static inline void do_request (ide_hwgroup_t *hwgroup, ide_hwif_t *hwif, ide_dri
struct request *rq = drive->queue;
unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
- ide_sti();
+ /* ide_sti(); HACK */
#ifdef DEBUG
printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
#endif
@@ -1087,7 +1093,7 @@ static inline void ide_leave_hwgroup (ide_hwgroup_t *hwgroup)
*/
void ide_do_request (ide_hwgroup_t *hwgroup)
{
- cli(); /* paranoia */
+ __cli(); /* paranoia */
if (hwgroup->handler != NULL) {
printk("%s: EEeekk!! handler not NULL in ide_do_request()\n", hwgroup->hwif->name);
return;
@@ -1102,7 +1108,7 @@ void ide_do_request (ide_hwgroup_t *hwgroup)
blk_dev[hwif->major].current_request = hwgroup->rq = drive->queue;
drive->service_start = jiffies;
do_request(hwgroup, hwif, drive);
- cli();
+ __cli();
} else {
ide_leave_hwgroup(hwgroup); /* no work left for this hwgroup */
return;
@@ -1151,27 +1157,27 @@ static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
}
}
-void do_ide0_request (void) /* invoked with cli() */
+void do_ide0_request (void) /* invoked with __cli() */
{
do_hwgroup_request (ide_hwifs[0].hwgroup);
}
#if MAX_HWIFS > 1
-void do_ide1_request (void) /* invoked with cli() */
+void do_ide1_request (void) /* invoked with __cli() */
{
do_hwgroup_request (ide_hwifs[1].hwgroup);
}
#endif /* MAX_HWIFS > 1 */
#if MAX_HWIFS > 2
-void do_ide2_request (void) /* invoked with cli() */
+void do_ide2_request (void) /* invoked with __cli() */
{
do_hwgroup_request (ide_hwifs[2].hwgroup);
}
#endif /* MAX_HWIFS > 2 */
#if MAX_HWIFS > 3
-void do_ide3_request (void) /* invoked with cli() */
+void do_ide3_request (void) /* invoked with __cli() */
{
do_hwgroup_request (ide_hwifs[3].hwgroup);
}
@@ -1184,8 +1190,8 @@ void ide_timer_expiry (unsigned long data)
ide_handler_t *handler;
unsigned long flags;
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
if ((handler = hwgroup->handler) != NULL) {
hwgroup->handler = NULL;
@@ -1196,7 +1202,7 @@ void ide_timer_expiry (unsigned long data)
(void) hwgroup->hwif->dmaproc (ide_dma_end, drive);
ide_error(drive, "irq timeout", GET_STAT());
}
- cli();
+ __cli();
if (hwgroup->handler == NULL) {
set_recovery_timer(HWIF(drive));
drive->service_time = jiffies - drive->service_start;
@@ -1204,7 +1210,7 @@ void ide_timer_expiry (unsigned long data)
}
} else
do_hwgroup_request (hwgroup);
- restore_flags(flags);
+ __restore_flags(flags);
}
/*
@@ -1217,7 +1223,7 @@ void ide_timer_expiry (unsigned long data)
* drive enters "idle", "standby", or "sleep" mode, so if the status looks
* "good", we just ignore the interrupt completely.
*
- * This routine assumes cli() is in effect when called.
+ * This routine assumes __cli() is in effect when called.
*
* If an unexpected interrupt happens on irq15 while we are handling irq14
* and if the two interfaces are "serialized" (CMD640), then it looks like
@@ -1255,10 +1261,11 @@ static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
}
/*
- * entry point for all interrupts, caller does cli() for us
+ * entry point for all interrupts, caller does __cli() for us
*/
void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
{
+ unsigned long flags;
ide_hwgroup_t *hwgroup = dev_id;
ide_hwif_t *hwif = hwgroup->hwif;
ide_handler_t *handler;
@@ -1284,19 +1291,21 @@ void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
#endif /* temporary */
hwgroup->handler = NULL;
del_timer(&(hwgroup->timer));
- if (drive->unmask)
- ide_sti();
+ /* if (drive->unmask)
+ ide_sti(); HACK */
handler(drive);
- cli(); /* this is necessary, as next rq may be different irq */
+ /* this is necessary, as next rq may be different irq */
+ spin_lock_irqsave(&io_request_lock,flags);
if (hwgroup->handler == NULL) {
set_recovery_timer(HWIF(drive));
drive->service_time = jiffies - drive->service_start;
ide_do_request(hwgroup);
}
+ spin_unlock_irqrestore(&io_request_lock,flags);
} else {
unexpected_intr(irq, hwgroup);
}
- cli();
+ __cli();
hwif = hwgroup->hwif;
do {
if (hwif->irq != irq) enable_irq(hwif->irq);
@@ -1384,8 +1393,8 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
if (action == ide_wait)
rq->sem = &sem;
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
cur_rq = drive->queue;
if (cur_rq == NULL || action == ide_preempt) {
@@ -1403,11 +1412,11 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
}
if (!hwgroup->active) {
do_hwgroup_request(hwgroup);
- cli();
+ __cli();
}
if (action == ide_wait && rq->rq_status != RQ_INACTIVE)
down(&sem); /* wait for it to be serviced */
- restore_flags(flags);
+ __restore_flags(flags);
return rq->errors ? -EIO : 0; /* return -EIO if errors */
}
@@ -1429,15 +1438,15 @@ int ide_revalidate_disk(kdev_t i_rdev)
return -ENODEV;
major = MAJOR(i_rdev);
minor = drive->select.b.unit << PARTN_BITS;
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
if (drive->busy || (drive->usage > 1)) {
- restore_flags(flags);
+ __restore_flags(flags);
return -EBUSY;
};
drive->busy = 1;
MOD_INC_USE_COUNT;
- restore_flags(flags);
+ __restore_flags(flags);
for (p = 0; p < (1<<PARTN_BITS); ++p) {
if (drive->part[p].nr_sects > 0) {
@@ -1556,6 +1565,22 @@ static int ide_release(struct inode * inode, struct file * file)
return 0;
}
+int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
+{
+ if (!drive->present || drive->busy || drive->usage)
+ goto abort;
+ if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
+ goto abort;
+ strncpy(drive->driver_req, driver, 9);
+ ide_init_module(IDE_DRIVER_MODULE);
+ drive->driver_req[0] = 0;
+ ide_init_module(IDE_DRIVER_MODULE);
+ if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver))
+ return 0;
+abort:
+ return 1;
+}
+
void ide_unregister (unsigned int index)
{
struct gendisk *gd, **gdp;
@@ -1567,8 +1592,8 @@ void ide_unregister (unsigned int index)
if (index >= MAX_HWIFS)
return;
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
hwif = &ide_hwifs[index];
if (!hwif->present)
goto abort;
@@ -1640,6 +1665,7 @@ void ide_unregister (unsigned int index)
unregister_blkdev(hwif->major, hwif->name);
kfree(blksize_size[hwif->major]);
kfree(max_sectors[hwif->major]);
+ kfree(max_readahead[hwif->major]);
blk_dev[hwif->major].request_fn = NULL;
blk_dev[hwif->major].data = NULL;
blk_dev[hwif->major].queue = NULL;
@@ -1657,7 +1683,7 @@ void ide_unregister (unsigned int index)
}
init_hwif_data (index); /* restore hwif data to pristine status */
abort:
- restore_flags(flags);
+ __restore_flags(flags);
}
int ide_register (int arg1, int arg2, int irq)
@@ -1696,20 +1722,229 @@ found:
return hwif->present ? index : -1;
}
+void ide_add_setting(ide_drive_t *drive, char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
+{
+ ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
+
+ while ((*p) && strcmp((*p)->name, name) < 0)
+ p = &((*p)->next);
+ if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
+ goto abort;
+ memset(setting, 0, sizeof(*setting));
+ if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL)
+ goto abort;
+ strcpy(setting->name, name); setting->rw = rw;
+ setting->read_ioctl = read_ioctl; setting->write_ioctl = write_ioctl;
+ setting->data_type = data_type; setting->min = min;
+ setting->max = max; setting->mul_factor = mul_factor;
+ setting->div_factor = div_factor; setting->data = data;
+ setting->set = set; setting->next = *p;
+ if (drive->driver)
+ setting->auto_remove = 1;
+ *p = setting;
+ return;
+abort:
+ if (setting)
+ kfree(setting);
+}
+
+void ide_remove_setting(ide_drive_t *drive, char *name)
+{
+ ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting;
+
+ while ((*p) && strcmp((*p)->name, name))
+ p = &((*p)->next);
+ if ((setting = (*p)) == NULL)
+ return;
+ (*p) = setting->next;
+ kfree(setting->name);
+ kfree(setting);
+}
+
+static ide_settings_t *ide_find_setting_by_ioctl(ide_drive_t *drive, int cmd)
+{
+ ide_settings_t *setting = drive->settings;
+
+ while (setting) {
+ if (setting->read_ioctl == cmd || setting->write_ioctl == cmd)
+ break;
+ setting = setting->next;
+ }
+ return setting;
+}
+
+ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name)
+{
+ ide_settings_t *setting = drive->settings;
+
+ while (setting) {
+ if (strcmp(setting->name, name) == 0)
+ break;
+ setting = setting->next;
+ }
+ return setting;
+}
+
+static void auto_remove_settings(ide_drive_t *drive)
+{
+ ide_settings_t *setting;
+repeat:
+ setting = drive->settings;
+ while (setting) {
+ if (setting->auto_remove) {
+ ide_remove_setting(drive, setting->name);
+ goto repeat;
+ }
+ setting = setting->next;
+ }
+}
+
+int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting)
+{
+ if (!(setting->rw & SETTING_READ))
+ return -EINVAL;
+ switch(setting->data_type) {
+ case TYPE_BYTE:
+ return *((u8 *) setting->data);
+ case TYPE_SHORT:
+ return *((u16 *) setting->data);
+ case TYPE_INT:
+ case TYPE_INTA:
+ return *((u32 *) setting->data);
+ default:
+ return -EINVAL;
+ }
+}
+
+int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val)
+{
+ unsigned long flags;
+ int i, rc = 0;
+ u32 *p;
+
+ if (!suser())
+ return -EACCES;
+ if (!(setting->rw & SETTING_WRITE))
+ return -EPERM;
+ if (val < setting->min || val > setting->max)
+ return -EINVAL;
+ __save_flags(flags);
+ __cli();
+ if (setting->set)
+ rc = setting->set(drive, val);
+ else switch (setting->data_type) {
+ case TYPE_BYTE:
+ *((u8 *) setting->data) = val;
+ break;
+ case TYPE_SHORT:
+ *((u16 *) setting->data) = val;
+ break;
+ case TYPE_INT:
+ *((u32 *) setting->data) = val;
+ break;
+ case TYPE_INTA:
+ p = (u32 *) setting->data;
+ for (i = 0; i < 1 << PARTN_BITS; i++, p++)
+ *p = val;
+ break;
+ }
+ __restore_flags(flags);
+ return rc;
+}
+
+static int set_io_32bit(ide_drive_t *drive, int arg)
+{
+ drive->io_32bit = arg;
+#ifdef CONFIG_BLK_DEV_DTC2278
+ if (HWIF(drive)->chipset == ide_dtc2278)
+ HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+ return 0;
+}
+
+static int set_using_dma(ide_drive_t *drive, int arg)
+{
+ if (!drive->driver || !DRIVER(drive)->supports_dma)
+ return -EPERM;
+ if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
+ return -EPERM;
+ if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive))
+ return -EIO;
+ return 0;
+}
+
+static int set_pio_mode(ide_drive_t *drive, int arg)
+{
+ struct request rq;
+
+ if (!HWIF(drive)->tuneproc)
+ return -ENOSYS;
+ if (drive->special.b.set_tune)
+ return -EBUSY;
+ ide_init_drive_cmd(&rq);
+ drive->tune_req = (byte) arg;
+ drive->special.b.set_tune = 1;
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
+ return 0;
+}
+
+void ide_add_generic_settings(ide_drive_t *drive)
+{
+/*
+ * drive setting name read/write access read ioctl write ioctl data type min max mul_factor div_factor data pointer set function
+ */
+ ide_add_setting(drive, "io_32bit", drive->no_io_32bit ? SETTING_READ : SETTING_RW, HDIO_GET_32BIT, HDIO_SET_32BIT, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit);
+ ide_add_setting(drive, "keepsettings", SETTING_RW, HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL);
+ ide_add_setting(drive, "nice1", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL);
+ ide_add_setting(drive, "pio_mode", SETTING_WRITE, -1, HDIO_SET_PIO_MODE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode);
+ ide_add_setting(drive, "slow", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->slow, NULL);
+ ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL);
+ ide_add_setting(drive, "using_dma", SETTING_RW, HDIO_GET_DMA, HDIO_SET_DMA, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma);
+}
+
+int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf)
+{
+ struct request rq;
+ byte buffer[4];
+
+ if (!buf)
+ buf = buffer;
+ memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors);
+ ide_init_drive_cmd(&rq);
+ rq.buffer = buf;
+ *buf++ = cmd;
+ *buf++ = nsect;
+ *buf++ = feature;
+ *buf++ = sectors;
+ return ide_do_drive_cmd(drive, &rq, ide_wait);
+}
+
static int ide_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err, major, minor;
ide_drive_t *drive;
- unsigned long flags;
struct request rq;
kdev_t dev;
+ ide_settings_t *setting;
if (!inode || !(dev = inode->i_rdev))
return -EINVAL;
major = MAJOR(dev); minor = MINOR(dev);
if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
return -ENODEV;
+
+ if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) {
+ if (cmd == setting->read_ioctl) {
+ err = ide_read_setting(drive, setting);
+ return err >= 0 ? put_user(err, (long *) arg) : err;
+ } else {
+ if ((MINOR(inode->i_rdev) & PARTN_MASK))
+ return -EINVAL;
+ return ide_write_setting(drive, setting, arg);
+ }
+ }
+
ide_init_drive_cmd (&rq);
switch (cmd) {
case HDIO_GETGEO:
@@ -1729,54 +1964,13 @@ static int ide_ioctl (struct inode *inode, struct file *file,
invalidate_buffers(inode->i_rdev);
return 0;
- case BLKRASET:
- if (!suser()) return -EACCES;
- if(arg > 0xff) return -EINVAL;
- read_ahead[MAJOR(inode->i_rdev)] = arg;
- return 0;
-
- case BLKRAGET:
- return put_user(read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
-
case BLKGETSIZE: /* Return device size */
return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
- case BLKFRASET:
- if (!suser()) return -EACCES;
- max_readahead[major][minor] = arg;
- return 0;
-
- case BLKFRAGET:
- return put_user(max_readahead[major][minor], (long *) arg);
-
- case BLKSECTSET:
- if (!suser()) return -EACCES;
- if (!arg || arg > 0xff) return -EINVAL;
- max_sectors[major][minor] = arg;
- return 0;
-
- case BLKSECTGET:
- return put_user(max_sectors[major][minor], (long *) arg);
-
case BLKRRPART: /* Re-read partition tables */
if (!suser()) return -EACCES;
return ide_revalidate_disk(inode->i_rdev);
- case HDIO_GET_KEEPSETTINGS:
- return put_user(drive->keep_settings, (long *) arg);
-
- case HDIO_GET_UNMASKINTR:
- return put_user(drive->unmask, (long *) arg);
-
- case HDIO_GET_DMA:
- return put_user(drive->using_dma, (long *) arg);
-
- case HDIO_GET_32BIT:
- return put_user(drive->io_32bit, (long *) arg);
-
- case HDIO_GET_MULTCOUNT:
- return put_user(drive->mult_count, (long *) arg);
-
case HDIO_GET_IDENTITY:
if (MINOR(inode->i_rdev) & PARTN_MASK)
return -EINVAL;
@@ -1791,99 +1985,13 @@ static int ide_ioctl (struct inode *inode, struct file *file,
#endif
return 0;
- case HDIO_GET_NOWERR:
- return put_user(drive->bad_wstat == BAD_R_STAT, (long *) arg);
-
case HDIO_GET_NICE:
- {
- long nice = 0;
-
- nice |= drive->dsc_overlap << IDE_NICE_DSC_OVERLAP;
- nice |= drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP;
- nice |= drive->nice0 << IDE_NICE_0;
- nice |= drive->nice1 << IDE_NICE_1;
- nice |= drive->nice2 << IDE_NICE_2;
- return put_user(nice, (long *) arg);
- }
-
- case HDIO_SET_DMA:
- if (!suser()) return -EACCES;
- if (drive->driver != NULL && !DRIVER(drive)->supports_dma)
- return -EPERM;
- if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
- return -EPERM;
- case HDIO_SET_KEEPSETTINGS:
- case HDIO_SET_UNMASKINTR:
- case HDIO_SET_NOWERR:
- if (arg > 1)
- return -EINVAL;
- case HDIO_SET_32BIT:
- if (!suser()) return -EACCES;
- if ((MINOR(inode->i_rdev) & PARTN_MASK))
- return -EINVAL;
- save_flags(flags);
- cli();
- switch (cmd) {
- case HDIO_SET_DMA:
- if (!(HWIF(drive)->dmaproc)) {
- restore_flags(flags);
- return -EPERM;
- }
- if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive)) {
- restore_flags(flags);
- return -EIO;
- }
- break;
- case HDIO_SET_KEEPSETTINGS:
- drive->keep_settings = arg;
- break;
- case HDIO_SET_UNMASKINTR:
- if (arg && drive->no_unmask) {
- restore_flags(flags);
- return -EPERM;
- }
- drive->unmask = arg;
- break;
- case HDIO_SET_NOWERR:
- drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
- break;
- case HDIO_SET_32BIT:
- if (arg > (1 + (SUPPORT_VLB_SYNC<<1))) {
- restore_flags(flags);
- return -EINVAL;
- }
- if (arg && drive->no_io_32bit) {
- restore_flags(flags);
- return -EPERM;
- }
- drive->io_32bit = arg;
-#ifdef CONFIG_BLK_DEV_DTC2278
- if (HWIF(drive)->chipset == ide_dtc2278)
- HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
-#endif /* CONFIG_BLK_DEV_DTC2278 */
- break;
- }
- restore_flags(flags);
- return 0;
-
- case HDIO_SET_MULTCOUNT:
- if (!suser()) return -EACCES;
- if (MINOR(inode->i_rdev) & PARTN_MASK)
- return -EINVAL;
- if (drive->id && arg > drive->id->max_multsect)
- return -EINVAL;
- save_flags(flags);
- cli();
- if (drive->special.b.set_multmode) {
- restore_flags(flags);
- return -EBUSY;
- }
- drive->mult_req = arg;
- drive->special.b.set_multmode = 1;
- restore_flags(flags);
- (void) ide_do_drive_cmd (drive, &rq, ide_wait);
- return (drive->mult_count == arg) ? 0 : -EIO;
-
+ return put_user(drive->dsc_overlap << IDE_NICE_DSC_OVERLAP |
+ drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP |
+ drive->nice0 << IDE_NICE_0 |
+ drive->nice1 << IDE_NICE_1 |
+ drive->nice2 << IDE_NICE_2,
+ (long *) arg);
case HDIO_DRIVE_CMD:
{
byte args[4], *argbuf = args;
@@ -1900,31 +2008,13 @@ static int ide_ioctl (struct inode *inode, struct file *file,
return -ENOMEM;
memcpy(argbuf, args, 4);
}
- rq.buffer = argbuf;
- err = ide_do_drive_cmd(drive, &rq, ide_wait);
+ err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf);
if (copy_to_user((void *)arg, argbuf, argsize))
err = -EFAULT;
if (argsize > 4)
kfree(argbuf);
return err;
}
- case HDIO_SET_PIO_MODE:
- if (!suser()) return -EACCES;
- if (MINOR(inode->i_rdev) & PARTN_MASK)
- return -EINVAL;
- if (!HWIF(drive)->tuneproc)
- return -ENOSYS;
- save_flags(flags);
- cli();
- if (drive->special.b.set_tune) {
- restore_flags(flags);
- return -EBUSY;
- }
- drive->tune_req = (byte) arg;
- drive->special.b.set_tune = 1;
- restore_flags(flags);
- (void) ide_do_drive_cmd (drive, &rq, ide_wait);
- return 0;
case HDIO_SCAN_HWIF:
{
@@ -2222,7 +2312,7 @@ __initfunc(void ide_setup (char *s))
if (i > 0 || i <= -7) { /* is parameter a chipset name? */
if (hwif->chipset != ide_unknown)
goto bad_option; /* chipset already specified */
- if (i != -7 && hw != 0)
+ if (i <= -7 && hw != 0)
goto bad_hwif; /* chipset drivers are for "ide0=" only */
if (ide_hwifs[hw^1].chipset != ide_unknown)
goto bad_option; /* chipset for 2nd port already specified */
@@ -2577,7 +2667,7 @@ static void setup_driver_defaults (ide_drive_t *drive)
if (d->special == NULL) d->special = default_special;
}
-ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n)
+ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n)
{
unsigned int unit, index, i;
@@ -2587,31 +2677,39 @@ ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n)
search:
for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
ide_hwif_t *hwif = &ide_hwifs[index];
- if (hwif->present) {
- for (unit = 0; unit < MAX_DRIVES; ++unit) {
- ide_drive_t *drive = &hwif->drives[unit];
- if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
- return drive;
- }
+ if (!hwif->present)
+ continue;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ char *req = drive->driver_req;
+ if (*req && !strstr(name, req))
+ continue;
+ if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
+ return drive;
}
}
return NULL;
}
+static ide_proc_entry_t generic_subdriver_entries[] = {
+ { "capacity", proc_ide_read_capacity, NULL },
+ { NULL, NULL, NULL }
+};
+
int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version)
{
unsigned long flags;
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL ||
drive->busy || drive->usage) {
- restore_flags(flags);
+ __restore_flags(flags);
return 1;
}
drive->driver = driver;
setup_driver_defaults(drive);
- restore_flags(flags);
+ __restore_flags(flags);
if (drive->autotune != 2) {
if (driver->supports_dma && HWIF(drive)->dmaproc != NULL)
(void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
@@ -2619,6 +2717,7 @@ int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int versio
drive->nice1 = 1;
}
drive->revalidate = 1;
+ ide_add_proc_entries(drive, generic_subdriver_entries);
ide_add_proc_entries(drive, driver->proc);
return 0;
}
@@ -2627,15 +2726,17 @@ int ide_unregister_subdriver (ide_drive_t *drive)
{
unsigned long flags;
- save_flags(flags);
- cli();
+ __save_flags(flags);
+ __cli();
if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) {
- restore_flags(flags);
+ __restore_flags(flags);
return 1;
}
ide_remove_proc_entries(drive, DRIVER(drive)->proc);
+ ide_remove_proc_entries(drive, generic_subdriver_entries);
+ auto_remove_settings(drive);
drive->driver = NULL;
- restore_flags(flags);
+ __restore_flags(flags);
return 0;
}
@@ -2692,6 +2793,7 @@ EXPORT_SYMBOL(ide_geninit);
EXPORT_SYMBOL(ide_fops);
EXPORT_SYMBOL(ide_get_queue);
EXPORT_SYMBOL(do_ide0_request);
+EXPORT_SYMBOL(ide_add_generic_settings);
#if MAX_HWIFS > 1
EXPORT_SYMBOL(do_ide1_request);
#endif /* MAX_HWIFS > 1 */
@@ -2724,9 +2826,12 @@ EXPORT_SYMBOL(ide_end_drive_cmd);
EXPORT_SYMBOL(ide_end_request);
EXPORT_SYMBOL(ide_revalidate_disk);
EXPORT_SYMBOL(ide_cmd);
+EXPORT_SYMBOL(ide_wait_cmd);
EXPORT_SYMBOL(ide_stall_queue);
EXPORT_SYMBOL(ide_add_proc_entries);
EXPORT_SYMBOL(ide_remove_proc_entries);
+EXPORT_SYMBOL(ide_add_setting);
+EXPORT_SYMBOL(ide_remove_setting);
EXPORT_SYMBOL(proc_ide_read_geometry);
EXPORT_SYMBOL(ide_register);
diff --git a/drivers/block/ide.h b/drivers/block/ide.h
index dfcb80fa5..5d2168dd8 100644
--- a/drivers/block/ide.h
+++ b/drivers/block/ide.h
@@ -206,23 +206,23 @@ typedef struct ide_drive_s {
special_t special; /* special action flags */
unsigned present : 1; /* drive is physically present */
unsigned noprobe : 1; /* from: hdx=noprobe */
- unsigned keep_settings : 1; /* restore settings after drive reset */
+ byte keep_settings; /* restore settings after drive reset */
unsigned busy : 1; /* currently doing revalidate_disk() */
unsigned removable : 1; /* 1 if need to do check_media_change */
- unsigned using_dma : 1; /* disk is using dma for read/write */
+ byte using_dma; /* disk is using dma for read/write */
unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */
- unsigned unmask : 1; /* flag: okay to unmask other irqs */
+ byte unmask; /* flag: okay to unmask other irqs */
unsigned no_unmask : 1; /* disallow setting unmask bit */
unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */
unsigned nobios : 1; /* flag: do not probe bios for drive */
- unsigned slow : 1; /* flag: slow data port */
+ byte slow; /* flag: slow data port */
unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
unsigned revalidate : 1; /* request revalidation */
- unsigned bswap : 1; /* flag: byte swap data */
- unsigned dsc_overlap : 1; /* flag: DSC overlap */
+ byte bswap; /* flag: byte swap data */
+ byte dsc_overlap; /* flag: DSC overlap */
unsigned atapi_overlap : 1; /* flag: ATAPI overlap (not supported) */
unsigned nice0 : 1; /* flag: give obvious excess bandwidth */
- unsigned nice1 : 1; /* flag: give potential excess bandwidth */
+ byte nice1; /* flag: give potential excess bandwidth */
unsigned nice2 : 1; /* flag: give a share in our own bandwidth */
#if FAKE_FDISK_FOR_EZDRIVE
unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */
@@ -236,6 +236,7 @@ typedef struct ide_drive_s {
byte tune_req; /* requested drive tuning setting */
byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
byte bad_wstat; /* used for ignoring WRERR_STAT */
+ byte nowerr; /* used for ignoring WRERR_STAT */
byte sect0; /* offset of first sector for DM6:DDO */
byte usage; /* current "open()" count for drive */
byte head; /* "real" number of heads */
@@ -257,6 +258,8 @@ typedef struct ide_drive_s {
void *driver; /* (ide_driver_t *) */
void *driver_data; /* extra driver data */
struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
+ void *settings; /* /proc/ide/ drive settings */
+ char driver_req[10]; /* requests specific driver */
} ide_drive_t;
/*
@@ -325,9 +328,9 @@ typedef struct hwif_s {
ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */
unsigned long *dmatable; /* dma physical region descriptor table */
struct hwif_s *mate; /* other hwif from same PCI chip */
- unsigned int dma_base; /* base addr for dma ports */
- unsigned int config_data; /* for use by chipset-specific code */
- unsigned int select_data; /* for use by chipset-specific code */
+ unsigned long dma_base; /* base addr for dma ports */
+ unsigned long config_data; /* for use by chipset-specific code */
+ unsigned long select_data; /* for use by chipset-specific code */
struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
int irq; /* our irq number */
byte major; /* our major number */
@@ -366,10 +369,47 @@ typedef struct hwgroup_s {
} ide_hwgroup_t;
/*
+ * configurable drive settings
+ */
+
+#define TYPE_INT 0
+#define TYPE_INTA 1
+#define TYPE_BYTE 2
+#define TYPE_SHORT 3
+
+#define SETTING_READ (1 << 0)
+#define SETTING_WRITE (1 << 1)
+#define SETTING_RW (SETTING_READ | SETTING_WRITE)
+
+typedef int (ide_procset_t)(ide_drive_t *, int);
+typedef struct ide_settings_s {
+ char *name;
+ int rw;
+ int read_ioctl;
+ int write_ioctl;
+ int data_type;
+ int min;
+ int max;
+ int mul_factor;
+ int div_factor;
+ void *data;
+ ide_procset_t *set;
+ int auto_remove;
+ struct ide_settings_s *next;
+} ide_settings_t;
+
+void ide_add_setting(ide_drive_t *drive, char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set);
+void ide_remove_setting(ide_drive_t *drive, char *name);
+ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name);
+int ide_read_setting(ide_drive_t *t, ide_settings_t *setting);
+int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val);
+void ide_add_generic_settings(ide_drive_t *drive);
+
+/*
* /proc/ide interface
*/
typedef struct {
- char *name;
+ const char *name;
read_proc_t *read_proc;
write_proc_t *write_proc;
} ide_proc_entry_t;
@@ -377,9 +417,26 @@ typedef struct {
void proc_ide_init(void);
void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p);
void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p);
+read_proc_t proc_ide_read_capacity;
read_proc_t proc_ide_read_geometry;
/*
+ * Standard exit stuff:
+ */
+#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \
+{ \
+ len -= off; \
+ if (len < count) { \
+ *eof = 1; \
+ if (len <= 0) \
+ return 0; \
+ } else \
+ len = count; \
+ *start = page + off; \
+ return len; \
+}
+
+/*
* Subdrivers support.
*/
#define IDE_SUBDRIVER_VERSION 1
@@ -394,6 +451,7 @@ typedef int (ide_check_media_change_proc)(ide_drive_t *);
typedef void (ide_pre_reset_proc)(ide_drive_t *);
typedef unsigned long (ide_capacity_proc)(ide_drive_t *);
typedef void (ide_special_proc)(ide_drive_t *);
+typedef void (ide_setting_proc)(ide_drive_t *);
typedef struct ide_driver_s {
const char *name;
@@ -429,6 +487,7 @@ typedef int (ide_module_init_proc)(void);
typedef struct ide_module_s {
int type;
ide_module_init_proc *init;
+ void *info;
struct ide_module_s *next;
} ide_module_t;
@@ -442,6 +501,7 @@ typedef struct ide_module_s {
*/
#ifndef _IDE_C
extern ide_hwif_t ide_hwifs[]; /* master data repository */
+extern ide_module_t *ide_modules;
#endif
/*
@@ -575,6 +635,11 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
/*
+ * Issue ATA command and wait for completion.
+ */
+int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf);
+
+/*
* ide_system_bus_speed() returns what we think is the system VESA/PCI
* bus speed (in MHz). This is used for calculating interface PIO timings.
* The default is 40 for known PCI systems, 50 otherwise.
@@ -638,20 +703,21 @@ int idescsi_init (void);
int ide_register_module (ide_module_t *module);
void ide_unregister_module (ide_module_t *module);
-ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n);
+ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n);
int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version);
int ide_unregister_subdriver (ide_drive_t *drive);
+int ide_replace_subdriver(ide_drive_t *drive, const char *driver);
#ifdef CONFIG_BLK_DEV_IDEPCI
-unsigned int ide_find_free_region (unsigned short size) __init;
+unsigned long ide_find_free_region (unsigned short size) __init;
void ide_scan_pcibus (void) __init;
#endif
#ifdef CONFIG_BLK_DEV_IDEDMA
int ide_build_dmatable (ide_drive_t *drive);
void ide_dma_intr (ide_drive_t *drive);
int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
-void ide_setup_dma (ide_hwif_t *hwif, unsigned int dmabase, unsigned int num_ports) __init;
-unsigned int ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init;
+void ide_setup_dma (ide_hwif_t *hwif, unsigned long dmabase, unsigned int num_ports) __init;
+unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init;
#endif
#ifdef CONFIG_BLK_DEV_IDE
diff --git a/drivers/block/ide_modes.h b/drivers/block/ide_modes.h
index 589fbfaa7..21679622c 100644
--- a/drivers/block/ide_modes.h
+++ b/drivers/block/ide_modes.h
@@ -15,7 +15,7 @@
* breaking the fragile cmd640.c support.
*/
-#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS)
+#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621)
/*
* Standard (generic) timings for PIO modes, from ATA2 specification.
@@ -222,5 +222,5 @@ byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode,
}
#endif /* _IDE_C */
-#endif /* defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) */
+#endif /* defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) || defined(CONFIG_BLK_DEV_OPTI621) */
#endif /* _IDE_MODES_H */
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index d4a83f9a1..01cf3923a 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -22,6 +22,11 @@
#include <asm/io.h>
#include <linux/blk.h>
+#include <linux/module.h>
+
+#define ATOMIC_ON() do { } while (0)
+#define ATOMIC_OFF() do { } while (0)
+
/*
* The request-struct contains all necessary data
* to load a nr of sectors into memory
@@ -35,6 +40,11 @@ static struct request all_requests[NR_REQUEST];
DECLARE_TASK_QUEUE(tq_disk);
/*
+ * Protect the request list against multiple users..
+ */
+spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED;
+
+/*
* used to wait on when there are no free requests
*/
struct wait_queue * wait_for_request = NULL;
@@ -95,7 +105,7 @@ int * max_sectors[MAX_BLKDEV] = { NULL, NULL, };
static inline int get_max_sectors(kdev_t dev)
{
if (!max_sectors[MAJOR(dev)])
- return 244; /* 254? */
+ return MAX_SECTORS;
return max_sectors[MAJOR(dev)][MINOR(dev)];
}
@@ -335,6 +345,7 @@ void make_request(int major,int rw, struct buffer_head * bh)
unsigned int sector, count;
struct request * req;
int rw_ahead, max_req, max_sectors;
+ unsigned long flags;
count = bh->b_size >> 9;
sector = bh->b_rsector;
@@ -402,13 +413,20 @@ void make_request(int major,int rw, struct buffer_head * bh)
* Try to coalesce the new request with old requests
*/
max_sectors = get_max_sectors(bh->b_rdev);
- cli();
+
+ /*
+ * Now we acquire the request spinlock, we have to be mega careful
+ * not to schedule or do something nonatomic
+ */
+ spin_lock_irqsave(&io_request_lock,flags);
+ ATOMIC_ON();
+
req = *get_queue(bh->b_rdev);
if (!req) {
/* MD and loop can't handle plugging without deadlocking */
if (major != MD_MAJOR && major != LOOP_MAJOR &&
major != DDV_MAJOR)
- plug_device(blk_dev + major);
+ plug_device(blk_dev + major); /* is atomic */
} else switch (major) {
case IDE0_MAJOR: /* same as HD_MAJOR */
case IDE1_MAJOR:
@@ -462,14 +480,18 @@ void make_request(int major,int rw, struct buffer_head * bh)
continue;
mark_buffer_clean(bh);
- sti();
+ ATOMIC_OFF();
+ spin_unlock_irqrestore(&io_request_lock,flags);
return;
+
} while ((req = req->next) != NULL);
}
/* find an unused request. */
req = get_request(max_req, bh->b_rdev);
- sti();
+
+ ATOMIC_OFF();
+ spin_unlock_irqrestore(&io_request_lock,flags);
/* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
if (!req) {
@@ -641,9 +663,12 @@ void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buf)
if (j == 0) {
req[j] = get_request_wait(max_req, rdev);
} else {
- cli();
+ unsigned long flags;
+ spin_lock_irqsave(&io_request_lock,flags);
+ ATOMIC_ON();
req[j] = get_request(max_req, rdev);
- sti();
+ ATOMIC_OFF();
+ spin_unlock_irqrestore(&io_request_lock,flags);
if (req[j] == NULL)
break;
}
@@ -665,12 +690,6 @@ void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buf)
}
}
}
-#ifdef CONFIG_BLK_DEV_EZ
-extern void ez_init( void );
-#endif
-#ifdef CONFIG_BPCD
-extern void bpcd_init( void );
-#endif
__initfunc(int blk_dev_init(void))
{
@@ -684,6 +703,7 @@ __initfunc(int blk_dev_init(void))
dev->plug.rq_status = RQ_INACTIVE;
dev->plug.cmd = -1;
dev->plug.next = NULL;
+ dev->plug_tq.sync = 0;
dev->plug_tq.routine = &unplug_device;
dev->plug_tq.data = dev;
}
@@ -723,8 +743,8 @@ __initfunc(int blk_dev_init(void))
#ifdef CONFIG_BLK_DEV_XD
xd_init();
#endif
-#ifdef CONFIG_BLK_DEV_EZ
- ez_init();
+#ifdef CONFIG_PARIDE
+ { extern void paride_init(void); paride_init(); };
#endif
#ifdef CONFIG_MAC_FLOPPY
swim3_init();
@@ -761,9 +781,6 @@ __initfunc(int blk_dev_init(void))
#ifdef CONFIG_GSCD
gscd_init();
#endif CONFIG_GSCD
-#ifdef CONFIG_BPCD
- bpcd_init();
-#endif CONFIG_BPCD
#ifdef CONFIG_CM206
cm206_init();
#endif
@@ -783,4 +800,6 @@ __initfunc(int blk_dev_init(void))
ddv_init();
#endif
return 0;
-}
+};
+
+EXPORT_SYMBOL(io_request_lock);
diff --git a/drivers/block/md.c b/drivers/block/md.c
index 039e7919c..4feeb0ce9 100644
--- a/drivers/block/md.c
+++ b/drivers/block/md.c
@@ -8,6 +8,7 @@
A lot of inspiration came from hd.c ...
kerneld support by Boris Tobotras <boris@xtalk.msk.su>
+ boot support for linear and striped mode by Harald Hoyer <HarryH@Royal.Net>
RAID-1/RAID-5 extensions by:
Ingo Molnar, Miguel de Icaza, Gadi Oxman
@@ -59,6 +60,10 @@
#include <asm/bitops.h>
#include <asm/atomic.h>
+#ifdef CONFIG_MD_BOOT
+extern kdev_t name_to_kdev_t(char *line) __init;
+#endif
+
static struct hd_struct md_hd_struct[MAX_MD_DEV];
static int md_blocksizes[MAX_MD_DEV];
int md_maxreadahead[MAX_MD_DEV];
@@ -846,6 +851,7 @@ EXPORT_SYMBOL(md_do_sync);
static struct proc_dir_entry proc_md = {
PROC_MD, 6, "mdstat",
S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations,
};
static void md_geninit (struct gendisk *gdisk)
@@ -987,10 +993,13 @@ int md_thread(void * arg)
cli();
if (!test_bit(THREAD_WAKEUP, &thread->flags)) {
do {
- spin_lock_irq(&current->sigmask_lock);
+ spin_lock(&current->sigmask_lock);
flush_signals(current);
- spin_unlock_irq(&current->sigmask_lock);
+ spin_unlock(&current->sigmask_lock);
interruptible_sleep_on(&thread->wqueue);
+ cli();
+ if (test_bit(THREAD_WAKEUP, &thread->flags))
+ break;
} while (signal_pending(current));
}
}
@@ -1054,7 +1063,7 @@ int md_do_sync(struct md_dev *mddev)
*/
curr_bsize = blksize_size[major][minor];
if (curr_bsize != blocksize) {
-diff_blocksize:
+ diff_blocksize:
if (curr_bsize > blocksize)
/*
* this is safe, rounds downwards.
@@ -1162,6 +1171,102 @@ void mdsyncd (void *data)
}
+#ifdef CONFIG_MD_BOOT
+struct {
+ int set;
+ int ints[100];
+ char str[100];
+} md_setup_args __initdata = {
+ 0,{0},{0}
+};
+
+/* called from init/main.c */
+__initfunc(void md_setup(char *str,int *ints))
+{
+ int i;
+ for(i=0;i<=ints[0];i++) {
+ md_setup_args.ints[i] = ints[i];
+ strcpy(md_setup_args.str, str);
+/* printk ("md: ints[%d]=%d.\n", i, ints[i]);*/
+ }
+ md_setup_args.set=1;
+ return;
+}
+
+__initfunc(void do_md_setup(char *str,int *ints))
+{
+ int minor, pers, factor, fault;
+ kdev_t dev;
+ int i=1;
+
+ if(ints[0] < 4) {
+ printk ("md: Too few Arguments (%d).\n", ints[0]);
+ return;
+ }
+
+ minor=ints[i++];
+
+ if (minor >= MAX_MD_DEV) {
+ printk ("md: Minor device number too high.\n");
+ return;
+ }
+
+ pers = 0;
+
+ switch(ints[i++]) { /* Raidlevel */
+ case -1:
+#ifdef CONFIG_MD_LINEAR
+ pers = LINEAR;
+ printk ("md: Setting up md%d as linear device.\n",minor);
+#else
+ printk ("md: Linear mode not configured."
+ "Recompile the kernel with linear mode enabled!\n");
+#endif
+ break;
+ case 0:
+ pers = STRIPED;
+#ifdef CONFIG_MD_STRIPED
+ printk ("md: Setting up md%d as a striped device.\n",minor);
+#else
+ printk ("md: Striped mode not configured."
+ "Recompile the kernel with striped mode enabled!\n");
+#endif
+ break;
+/* not supported yet
+ case 1:
+ pers = RAID1;
+ printk ("md: Setting up md%d as a raid1 device.\n",minor);
+ break;
+ case 5:
+ pers = RAID5;
+ printk ("md: Setting up md%d as a raid5 device.\n",minor);
+ break;
+*/
+ default:
+ printk ("md: Unknown or not supported raid level %d.\n", ints[--i]);
+ return;
+ }
+
+ if(pers) {
+
+ factor=ints[i++]; /* Chunksize */
+ fault =ints[i++]; /* Faultlevel */
+
+ pers=pers | factor | (fault << FAULT_SHIFT);
+
+ while( str && (dev = name_to_kdev_t(str))) {
+ do_md_add (minor, dev);
+ if((str = strchr (str, ',')) != NULL)
+ str++;
+ }
+
+ do_md_run (minor, pers);
+ printk ("md: Loading md%d.\n",minor);
+ }
+
+}
+#endif
+
void linear_init (void);
void raid0_init (void);
void raid1_init (void);
@@ -1215,6 +1320,13 @@ __initfunc(int md_init (void))
#ifdef CONFIG_MD_RAID5
raid5_init ();
#endif
-
return (0);
}
+
+#ifdef CONFIG_MD_BOOT
+__initfunc(void md_setup_drive(void))
+{
+ if(md_setup_args.set)
+ do_md_setup(md_setup_args.str, md_setup_args.ints);
+}
+#endif
diff --git a/drivers/block/paride/Config.in b/drivers/block/paride/Config.in
new file mode 100644
index 000000000..cc9479184
--- /dev/null
+++ b/drivers/block/paride/Config.in
@@ -0,0 +1,20 @@
+#
+# PARIDE configuration
+#
+comment 'Parallel IDE high-level drivers'
+dep_tristate ' Parallel port IDE disks' CONFIG_PARIDE_PD $CONFIG_PARIDE
+dep_tristate ' Parallel port ATAPI CD-ROMs' CONFIG_PARIDE_PCD $CONFIG_PARIDE
+dep_tristate ' Parallel port ATAPI disks' CONFIG_PARIDE_PF $CONFIG_PARIDE
+dep_tristate ' Parallel port ATAPI tapes' CONFIG_PARIDE_PT $CONFIG_PARIDE
+comment 'Parallel IDE protocol modules'
+dep_tristate ' ATEN EH-100 protocol' CONFIG_PARIDE_ATEN $CONFIG_PARIDE
+dep_tristate ' MicroSolutions backpack protocol' CONFIG_PARIDE_BPCK $CONFIG_PARIDE
+dep_tristate ' DataStor Commuter protocol' CONFIG_PARIDE_COMM $CONFIG_PARIDE
+dep_tristate ' DataStor EP-2000 protocol' CONFIG_PARIDE_DSTR $CONFIG_PARIDE
+dep_tristate ' Shuttle EPAT/EPEZ protocol' CONFIG_PARIDE_EPAT $CONFIG_PARIDE
+dep_tristate ' Shuttle EPIA protocol' CONFIG_PARIDE_EPIA $CONFIG_PARIDE
+dep_tristate ' FreeCom power protocol' CONFIG_PARIDE_FRPW $CONFIG_PARIDE
+dep_tristate ' KingByte KBIC-951A/971A protocols' CONFIG_PARIDE_KBIC $CONFIG_PARIDE
+dep_tristate ' OnSpec 90c20 protocol' CONFIG_PARIDE_ON20 $CONFIG_PARIDE
+dep_tristate ' OnSpec 90c26 protocol' CONFIG_PARIDE_ON26 $CONFIG_PARIDE
+#
diff --git a/drivers/block/paride/Makefile b/drivers/block/paride/Makefile
new file mode 100644
index 000000000..61e710801
--- /dev/null
+++ b/drivers/block/paride/Makefile
@@ -0,0 +1,141 @@
+#
+# Makefile for PARIDE
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := paride.a
+MX_OBJS :=
+LX_OBJS :=
+MI_OBJS :=
+MIX_OBJS :=
+
+ifeq ($(CONFIG_PARIDE),y)
+ LX_OBJS += paride.o
+else
+ ifeq ($(CONFIG_PARIDE),m)
+ MX_OBJS += paride.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PD),y)
+ LX_OBJS += pd.o
+else
+ ifeq ($(CONFIG_PARIDE_PD),m)
+ MX_OBJS += pd.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PCD),y)
+ LX_OBJS += pcd.o
+else
+ ifeq ($(CONFIG_PARIDE_PCD),m)
+ MX_OBJS += pcd.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PF),y)
+ LX_OBJS += pf.o
+else
+ ifeq ($(CONFIG_PARIDE_PF),m)
+ MX_OBJS += pf.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_PT),y)
+ LX_OBJS += pt.o
+else
+ ifeq ($(CONFIG_PARIDE_PT),m)
+ MX_OBJS += pt.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ATEN),y)
+ LX_OBJS += aten.o
+else
+ ifeq ($(CONFIG_PARIDE_ATEN),m)
+ MX_OBJS += aten.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_BPCK),y)
+ LX_OBJS += bpck.o
+else
+ ifeq ($(CONFIG_PARIDE_BPCK),m)
+ MX_OBJS += bpck.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_COMM),y)
+ LX_OBJS += comm.o
+else
+ ifeq ($(CONFIG_PARIDE_COMM),m)
+ MX_OBJS += comm.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_DSTR),y)
+ LX_OBJS += dstr.o
+else
+ ifeq ($(CONFIG_PARIDE_DSTR),m)
+ MX_OBJS += dstr.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_KBIC),y)
+ LX_OBJS += kbic.o
+else
+ ifeq ($(CONFIG_PARIDE_KBIC),m)
+ MX_OBJS += kbic.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPAT),y)
+ LX_OBJS += epat.o
+else
+ ifeq ($(CONFIG_PARIDE_EPAT),m)
+ MX_OBJS += epat.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_EPIA),y)
+ LX_OBJS += epia.o
+else
+ ifeq ($(CONFIG_PARIDE_EPIA),m)
+ MX_OBJS += epia.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_FRPW),y)
+ LX_OBJS += frpw.o
+else
+ ifeq ($(CONFIG_PARIDE_FRPW),m)
+ MX_OBJS += frpw.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON20),y)
+ LX_OBJS += on20.o
+else
+ ifeq ($(CONFIG_PARIDE_ON20),m)
+ MX_OBJS += on20.o
+ endif
+endif
+
+ifeq ($(CONFIG_PARIDE_ON26),y)
+ LX_OBJS += on26.o
+else
+ ifeq ($(CONFIG_PARIDE_ON26),m)
+ MX_OBJS += on26.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/block/paride/aten.c b/drivers/block/paride/aten.c
new file mode 100644
index 000000000..dd11b03f1
--- /dev/null
+++ b/drivers/block/paride/aten.c
@@ -0,0 +1,166 @@
+/*
+ aten.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU 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.
+
+*/
+
+#define ATEN_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.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 void aten_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void aten_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol aten = {"aten",0,2,2,1,1,
+ aten_write_regr,
+ aten_read_regr,
+ aten_write_block,
+ aten_read_block,
+ aten_connect,
+ aten_disconnect,
+ 0,
+ 0,
+ 0,
+ aten_log_adapter,
+ aten_inc_use,
+ aten_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &aten ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &aten );
+}
+
+#endif
+
+/* end of aten.c */
diff --git a/drivers/block/paride/bpck.c b/drivers/block/paride/bpck.c
new file mode 100644
index 000000000..21caad680
--- /dev/null
+++ b/drivers/block/paride/bpck.c
@@ -0,0 +1,476 @@
+/*
+ bpck.c (c) 1996,1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ bpck.c is a low-level protocol driver for the MicroSolutions
+ "backpack" parallel port IDE adapter.
+
+*/
+
+#define BPCK_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.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;
+
+ bpck_force_spp(pi);
+
+ om = pi->mode;
+ pi->mode = 0;
+
+ 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;
+}
+
+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 void bpck_inc_use( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void bpck_dec_use( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol bpck = { "bpck",0,5,2,4,256,
+ bpck_write_regr,
+ bpck_read_regr,
+ bpck_write_block,
+ bpck_read_block,
+ bpck_connect,
+ bpck_disconnect,
+ bpck_test_port,
+ bpck_probe_unit,
+ bpck_test_proto,
+ bpck_log_adapter,
+ bpck_inc_use,
+ bpck_dec_use
+ };
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register(&bpck) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister(&bpck);
+}
+
+#endif
+
+/* end of bpck.c */
diff --git a/drivers/block/paride/comm.c b/drivers/block/paride/comm.c
new file mode 100644
index 000000000..794bbb9b4
--- /dev/null
+++ b/drivers/block/paride/comm.c
@@ -0,0 +1,222 @@
+/*
+ comm.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU 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.
+*/
+
+#define COMM_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.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); 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); 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); r1(); w2(0x24);
+ for (i=0;i<count;i++) buf[i] = r4();
+ w2(4);
+ break;
+
+ case 3: w3(0x68); r1(); w2(0x24);
+ for (i=0;i<count/2;i++) ((u16 *)buf)[i] = r4w();
+ w2(4);
+ break;
+
+ case 4: w3(0x68); 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); r1();
+ for (k=0;k<count;k++) w4(buf[k^1]);
+ break;
+
+ case 3: w3(0x48); r1();
+ for (k=0;k<count/2;k++) w4w(pi_swab16(buf,k));
+ break;
+
+ case 4: w3(0x48); 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 void comm_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void comm_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol comm = {"comm",0,5,2,1,1,
+ comm_write_regr,
+ comm_read_regr,
+ comm_write_block,
+ comm_read_block,
+ comm_connect,
+ comm_disconnect,
+ 0,
+ 0,
+ 0,
+ comm_log_adapter,
+ comm_inc_use,
+ comm_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &comm ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &comm );
+}
+
+#endif
+
+/* end of comm.c */
diff --git a/drivers/block/paride/dstr.c b/drivers/block/paride/dstr.c
new file mode 100644
index 000000000..53cedcc2c
--- /dev/null
+++ b/drivers/block/paride/dstr.c
@@ -0,0 +1,237 @@
+/*
+ dstr.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ dstr.c is a low-level protocol driver for the
+ DataStor EP2000 parallel to IDE adapter chip.
+
+*/
+
+#define DSTR_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.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 void dstr_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void dstr_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol dstr = {"dstr",0,5,2,1,1,
+ dstr_write_regr,
+ dstr_read_regr,
+ dstr_write_block,
+ dstr_read_block,
+ dstr_connect,
+ dstr_disconnect,
+ 0,
+ 0,
+ 0,
+ dstr_log_adapter,
+ dstr_inc_use,
+ dstr_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &dstr ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &dstr );
+}
+
+#endif
+
+/* end of dstr.c */
diff --git a/drivers/block/paride/epat.c b/drivers/block/paride/epat.c
new file mode 100644
index 000000000..f6c19c994
--- /dev/null
+++ b/drivers/block/paride/epat.c
@@ -0,0 +1,315 @@
+/*
+ epat.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU 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.
+
+*/
+
+#define EPAT_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.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))
+
+/* 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 CCP stuff should be fixed to handle multiple EPATs on a chain */
+
+#define CCP(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();
+ CCP(0); CCP(0xe0);
+ w0(0); w2(1); w2(4);
+ if (pi->mode >= 3) {
+ w0(0); w2(1); w2(4); w2(0xc);
+ w0(0x40); w2(6); w2(7); w2(4); w2(0xc); w2(4);
+ }
+ WR(8,0x10); WR(0xc,0x14); WR(0xa,0x38); WR(0x12,0x10);
+}
+
+static void epat_disconnect ( PIA *pi )
+
+{ CCP(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 void epat_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void epat_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epat = {"epat",0,6,3,1,1,
+ epat_write_regr,
+ epat_read_regr,
+ epat_write_block,
+ epat_read_block,
+ epat_connect,
+ epat_disconnect,
+ 0,
+ 0,
+ epat_test_proto,
+ epat_log_adapter,
+ epat_inc_use,
+ epat_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &epat) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &epat);
+}
+
+#endif
+
+/* end of epat.c */
diff --git a/drivers/block/paride/epia.c b/drivers/block/paride/epia.c
new file mode 100644
index 000000000..73d83fe8d
--- /dev/null
+++ b/drivers/block/paride/epia.c
@@ -0,0 +1,318 @@
+/*
+ epia.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU 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.
+
+*/
+
+#define EPIA_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.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]++;
+ }
+ }
+ 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 void epia_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void epia_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol epia = {"epia",0,6,3,1,1,
+ epia_write_regr,
+ epia_read_regr,
+ epia_write_block,
+ epia_read_block,
+ epia_connect,
+ epia_disconnect,
+ 0,
+ 0,
+ epia_test_proto,
+ epia_log_adapter,
+ epia_inc_use,
+ epia_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &epia ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &epia );
+}
+
+#endif
+
+/* end of epia.c */
+
diff --git a/drivers/block/paride/frpw.c b/drivers/block/paride/frpw.c
new file mode 100644
index 000000000..fe7b1452f
--- /dev/null
+++ b/drivers/block/paride/frpw.c
@@ -0,0 +1,256 @@
+/*
+ frpw.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license
+
+ frpw.c is a low-level protocol driver for the Freecom "Power"
+ parallel port IDE adapter.
+
+*/
+
+#define FRPW_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.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;
+
+ }
+}
+
+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;
+ }
+}
+
+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 )
+
+{ int olddelay, a, b;
+
+ 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 pi->private to record the chip type:
+ 0 = untested, 2 = Xilinx, 3 = ASIC
+*/
+
+static int frpw_test_proto( PIA *pi, char * scratch, int verbose )
+
+{ int k, r;
+
+ if (!pi->private) pi->private = frpw_test_pnp(pi) + 2;
+
+ if ((pi->private == 2) && (pi->mode > 2)) {
+ if (verbose)
+ printk("%s: frpw: Xilinx does not support mode %d\n",
+ pi->device, pi->mode);
+ return 1;
+ }
+
+ if ((pi->private == 3) && (pi->mode == 2)) {
+ if (verbose)
+ printk("%s: frpw: ASIC does not support mode 2\n",
+ pi->device);
+ return 1;
+ }
+
+ 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, mode %d, test=%d\n",
+ pi->device,pi->port,pi->mode,r);
+ }
+
+ return r;
+}
+
+
+static void frpw_log_adapter( PIA *pi, char * scratch, int verbose )
+
+{ char *mode_string[4] = {"4-bit","8-bit","EPP-X","EPP-A"};
+
+ printk("%s: frpw %s, Freecom (%s) adapter at 0x%x, ", pi->device,
+ FRPW_VERSION,(pi->private == 2)?"Xilinx":"ASIC",pi->port);
+ printk("mode %d (%s), delay %d\n",pi->mode,
+ mode_string[pi->mode],pi->delay);
+
+}
+
+static void frpw_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void frpw_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol frpw = {"frpw",0,4,2,2,1,
+ frpw_write_regr,
+ frpw_read_regr,
+ frpw_write_block,
+ frpw_read_block,
+ frpw_connect,
+ frpw_disconnect,
+ 0,
+ 0,
+ frpw_test_proto,
+ frpw_log_adapter,
+ frpw_inc_use,
+ frpw_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &frpw ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &frpw );
+}
+
+#endif
+
+/* end of frpw.c */
diff --git a/drivers/block/paride/kbic.c b/drivers/block/paride/kbic.c
new file mode 100644
index 000000000..47f5cd29f
--- /dev/null
+++ b/drivers/block/paride/kbic.c
@@ -0,0 +1,305 @@
+/*
+ kbic.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU 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.
+
+*/
+
+#define KBIC_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.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 void kbic_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void kbic_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol k951 = {"k951",0,6,3,1,1,
+ kbic_write_regr,
+ kbic_read_regr,
+ kbic_write_block,
+ kbic_read_block,
+ k951_connect,
+ k951_disconnect,
+ 0,
+ 0,
+ 0,
+ k951_log_adapter,
+ kbic_inc_use,
+ kbic_dec_use
+ };
+
+
+struct pi_protocol k971 = {"k971",0,6,3,1,1,
+ kbic_write_regr,
+ kbic_read_regr,
+ kbic_write_block,
+ kbic_read_block,
+ k971_connect,
+ k971_disconnect,
+ 0,
+ 0,
+ 0,
+ k971_log_adapter,
+ kbic_inc_use,
+ kbic_dec_use
+ };
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ int s5,s7;
+
+ s5 = pi_register(&k951);
+ s7 = pi_register(&k971);
+
+ return (s5 || s7) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &k951 );
+ pi_unregister( &k971 );
+}
+
+#endif
+
+/* end of kbic.c */
diff --git a/drivers/block/paride/on20.c b/drivers/block/paride/on20.c
new file mode 100644
index 000000000..c9b3fa35b
--- /dev/null
+++ b/drivers/block/paride/on20.c
@@ -0,0 +1,157 @@
+/*
+ on20.c (c) 1996 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ on20.c is a low-level protocol driver for the
+ Onspec 90c20 parallel to IDE adapter.
+*/
+
+#define ON20_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.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 void on20_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void on20_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on20 = {"on20",0,2,2,1,1,
+ on20_write_regr,
+ on20_read_regr,
+ on20_write_block,
+ on20_read_block,
+ on20_connect,
+ on20_disconnect,
+ 0,
+ 0,
+ 0,
+ on20_log_adapter,
+ on20_inc_use,
+ on20_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &on20 ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &on20 );
+}
+
+#endif
+
+/* end of on20.c */
diff --git a/drivers/block/paride/on26.c b/drivers/block/paride/on26.c
new file mode 100644
index 000000000..21e6e017e
--- /dev/null
+++ b/drivers/block/paride/on26.c
@@ -0,0 +1,261 @@
+/*
+ on26.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ on26.c is a low-level protocol driver for the
+ OnSpec 90c26 parallel to IDE adapter chip.
+
+*/
+
+#define ON26_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/types.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); 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(0xff);w0(0xaa);w0(0x55);w0(0);w0(0xff);\
+ w0(0x87);w0(0x78);w0(x);w2(4);
+
+static void on26_connect ( PIA *pi )
+
+{ int x;
+
+ pi->saved_r0 = r0();
+ pi->saved_r2 = r2();
+
+ CCP(0x20);
+ w2(0xcd); w2(0xcc); w0(0xff);
+ 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);
+ w2(0xcd); w2(0xcc); w0(0xff);
+ w0(pi->saved_r0);
+ w2(pi->saved_r2);
+}
+
+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 void on26_inc_use ( void )
+
+{ MOD_INC_USE_COUNT;
+}
+
+static void on26_dec_use ( void )
+
+{ MOD_DEC_USE_COUNT;
+}
+
+struct pi_protocol on26 = {"on26",0,5,2,1,1,
+ on26_write_regr,
+ on26_read_regr,
+ on26_write_block,
+ on26_read_block,
+ on26_connect,
+ on26_disconnect,
+ 0,
+ 0,
+ 0,
+ on26_log_adapter,
+ on26_inc_use,
+ on26_dec_use
+ };
+
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ return pi_register( &on26 ) - 1;
+}
+
+void cleanup_module(void)
+
+{ pi_unregister( &on26 );
+}
+
+#endif
+
+/* end of on26.c */
+
diff --git a/drivers/block/paride/paride.c b/drivers/block/paride/paride.c
new file mode 100644
index 000000000..0d7cf2f01
--- /dev/null
+++ b/drivers/block/paride/paride.c
@@ -0,0 +1,485 @@
+/*
+ paride.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is the base module for the family of device drivers
+ that support parallel port IDE devices.
+
+*/
+
+#define PI_VERSION "1.0"
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_PARPORT_MODULE
+#define CONFIG_PARPORT
+#endif
+
+#ifdef CONFIG_PARPORT
+#include <linux/parport.h>
+#endif
+
+#include "paride.h"
+
+#define MAX_PROTOS 32
+
+static struct pi_protocol *protocols[MAX_PROTOS];
+
+
+void pi_write_regr( PIA *pi, int cont, int regr, int val)
+
+{ pi->proto->write_regr(pi,cont,regr,val);
+}
+
+int pi_read_regr( PIA *pi, int cont, int regr)
+
+{ return pi->proto->read_regr(pi,cont,regr);
+}
+
+void pi_write_block( PIA *pi, char * buf, int count)
+
+{ pi->proto->write_block(pi,buf,count);
+}
+
+void pi_read_block( PIA *pi, char * buf, int count)
+
+{ pi->proto->read_block(pi,buf,count);
+}
+
+#ifdef CONFIG_PARPORT
+
+static void pi_wake_up( void *p)
+
+{ PIA *pi = (PIA *) p;
+ long flags;
+ void (*cont)(void) = NULL;
+
+ save_flags(flags);
+ cli();
+
+ if (pi->claim_cont && !parport_claim(pi->pardev)) {
+ cont = pi->claim_cont;
+ pi->claim_cont = NULL;
+ pi->claimed = 1;
+ }
+
+ restore_flags(flags);
+ wake_up(&(pi->parq));
+ if (cont) cont();
+}
+
+#endif
+
+void pi_do_claimed( PIA *pi, void(*cont)(void))
+
+#ifdef CONFIG_PARPORT
+
+{ long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (!pi->pardev || !parport_claim(pi->pardev)) {
+ pi->claimed = 1;
+ restore_flags(flags);
+ cont();
+ } else {
+ pi->claim_cont = cont;
+ restore_flags(flags);
+ }
+}
+
+#else
+
+{ cont();
+}
+
+#endif
+
+static void pi_claim( PIA *pi)
+
+{ if (pi->claimed) return;
+ pi->claimed = 1;
+#ifdef CONFIG_PARPORT
+ if (pi->pardev)
+ while (parport_claim((struct pardevice *)(pi->pardev)))
+ sleep_on(&(pi->parq));
+#endif
+}
+
+static void pi_unclaim( PIA *pi)
+
+{ pi->claimed = 0;
+#ifdef CONFIG_PARPORT
+ if (pi->pardev) parport_release((struct pardevice *)(pi->pardev));
+#endif
+}
+
+void pi_connect( PIA *pi)
+
+{ pi_claim(pi);
+ pi->proto->connect(pi);
+}
+
+void pi_disconnect( PIA *pi)
+
+{ pi->proto->disconnect(pi);
+ pi_unclaim(pi);
+}
+
+static void pi_unregister_parport( PIA *pi)
+
+{
+#ifdef CONFIG_PARPORT
+ if (pi->pardev) {
+ parport_unregister_device((struct pardevice *)(pi->pardev));
+ pi->pardev = NULL;
+ }
+#endif
+}
+
+void pi_release( PIA *pi)
+
+{ pi_unregister_parport(pi);
+ if ((!pi->pardev)&&(pi->reserved))
+ release_region(pi->port,pi->reserved);
+ pi->proto->dec_use();
+}
+
+#define WR(r,v) pi_write_regr(pi,0,r,v)
+#define RR(r) (pi_read_regr(pi,0,r))
+
+static int pi_test_proto( PIA *pi, char * scratch, int verbose )
+
+{ int j, k;
+ int e[2] = {0,0};
+
+ if (pi->proto->test_proto) {
+ pi_claim(pi);
+ j = pi->proto->test_proto(pi,scratch,verbose);
+ pi_unclaim(pi);
+ return j;
+ }
+
+ pi_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]++;
+ }
+ }
+
+ pi_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 */
+}
+
+int pi_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 0;
+ }
+ k = 0;
+ while((k<MAX_PROTOS) && (protocols[k])) k++;
+ if (k == MAX_PROTOS) {
+ printk("paride: protocol table full\n");
+ return 0;
+ }
+ MOD_INC_USE_COUNT;
+ protocols[k] = pr;
+ pr->index = k;
+ printk("paride: %s registered as protocol %d\n",pr->name,k);
+ return 1;
+}
+
+void pi_unregister( PIP *pr)
+
+{ if (!pr) return;
+ if (protocols[pr->index] != pr) {
+ printk("paride: %s not registered\n",pr->name);
+ return;
+ }
+ protocols[pr->index] = 0;
+ MOD_DEC_USE_COUNT;
+}
+
+static void pi_register_parport( PIA *pi, int verbose)
+
+{
+#ifdef CONFIG_PARPORT
+
+ struct parport *pp;
+
+ pp = parport_enumerate();
+
+ while((pp)&&(pp->base != pi->port)) pp = pp->next;
+
+ if (!pp) return;
+
+ pi->pardev = (void *) parport_register_device(
+ pp,pi->device,NULL,pi_wake_up,NULL,0,(void *)pi);
+
+ pi->parq = NULL;
+
+ if (verbose) printk("%s: 0x%x is %s\n",pi->device,pi->port,pp->name);
+
+ pi->parname = pp->name;
+
+#endif
+}
+
+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;
+ if ((!pi->pardev) && check_region(pi->port,range)) 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;
+ if ((!pi->pardev) && check_region(pi->port,range)) 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;
+ }
+
+ pi_register_parport(pi,verbose);
+
+ if ((!pi->pardev) && check_region(pi->port,3)) 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 (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++) {
+ if (protocols[p]) {
+ pi->proto = protocols[p];
+ pi->proto->inc_use();
+ if (delay == -1) pi->delay = pi->proto->default_delay;
+ else pi->delay = delay;
+ pi->devtype = devtype;
+ pi->device = device;
+ pi->private = 0;
+
+ pi->parname = NULL;
+ pi->pardev = NULL;
+ pi->parq = NULL;
+ 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;
+ }
+ pi->proto->dec_use();
+ }
+ }
+
+ if (!pi->port) {
+ if (autoprobe) printk("%s: Autoprobe failed\n",device);
+ else printk("%s: Adapter not found\n",device);
+ return 0;
+ }
+
+ if (!pi->pardev)
+ request_region(pi->port,pi->reserved,pi->device);
+
+ 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;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+
+{ int k;
+
+ for (k=0;k<MAX_PROTOS;k++) protocols[k] = 0;
+ printk("paride: version %s installed\n",PI_VERSION);
+ return 0;
+}
+
+void cleanup_module(void)
+
+{
+}
+
+#else
+
+void paride_init( void )
+
+{
+
+#ifdef CONFIG_PARIDE_ATEN
+ { extern struct pi_protocol aten;
+ pi_register(&aten);
+ };
+#endif
+#ifdef CONFIG_PARIDE_BPCK
+ { extern struct pi_protocol bpck;
+ pi_register(&bpck);
+ };
+#endif
+#ifdef CONFIG_PARIDE_COMM
+ { extern struct pi_protocol comm;
+ pi_register(&comm);
+ };
+#endif
+#ifdef CONFIG_PARIDE_DSTR
+ { extern struct pi_protocol dstr;
+ pi_register(&dstr);
+ };
+#endif
+#ifdef CONFIG_PARIDE_EPAT
+ { extern struct pi_protocol epat;
+ pi_register(&epat);
+ };
+#endif
+#ifdef CONFIG_PARIDE_EPIA
+ { extern struct pi_protocol epia;
+ pi_register(&epia);
+ };
+#endif
+#ifdef CONFIG_PARIDE_FRPW
+ { extern struct pi_protocol frpw;
+ pi_register(&frpw);
+ };
+#endif
+#ifdef CONFIG_PARIDE_KBIC
+ { extern struct pi_protocol k951;
+ extern struct pi_protocol k971;
+ pi_register(&k951);
+ pi_register(&k971);
+ };
+#endif
+#ifdef CONFIG_PARIDE_ON20
+ { extern struct pi_protocol on20;
+ pi_register(&on20);
+ };
+#endif
+#ifdef CONFIG_PARIDE_ON26
+ { extern struct pi_protocol on26;
+ pi_register(&on26);
+ };
+#endif
+
+#ifdef CONFIG_PARIDE_PD
+ { extern int pd_init(void);
+ pd_init();
+ };
+#endif
+#ifdef CONFIG_PARIDE_PCD
+ { extern int pcd_init(void);
+ pcd_init();
+ };
+#endif
+#ifdef CONFIG_PARIDE_PF
+ { extern int pf_init(void);
+ pf_init();
+ };
+#endif
+#ifdef CONFIG_PARIDE_PT
+ { extern int pt_init(void);
+ pt_init();
+ };
+#endif
+}
+
+#endif
+
+/* end of paride.c */
diff --git a/drivers/block/paride/paride.h b/drivers/block/paride/paride.h
new file mode 100644
index 000000000..04de51441
--- /dev/null
+++ b/drivers/block/paride/paride.h
@@ -0,0 +1,157 @@
+/* paride.h (c) 1997 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.
+
+*/
+
+#define PARIDE_H_VERSION "1.0"
+
+/* 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 */
+
+/* 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 */
+ int private; /* for protocol module */
+
+ struct wait_queue *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));
+
+/* macros and functions exported to the protocol modules */
+
+#define delay_p (pi->delay?udelay(pi->delay):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);
+
+ void (*inc_use)(void);
+ void (*dec_use)(void);
+};
+
+typedef struct pi_protocol PIP;
+
+extern int pi_register( PIP * );
+extern void pi_unregister ( PIP * );
+
+/* end of paride.h */
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
new file mode 100644
index 000000000..ca520d32b
--- /dev/null
+++ b/drivers/block/paride/pcd.c
@@ -0,0 +1,816 @@
+/*
+ pcd.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is high-level driver for parallel port ATAPI CDrom
+ drives based on chips supported by the paride module.
+
+ By default, the driver will autoprobe for a single parallel
+ port ATAPI CDrom 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 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.
+
+ <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 is done while the driver probes for
+ devices. Set it to 0 for a quiet load, or 1 to
+ see all the progress messages. (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 1997.01.24 Added test unit ready support
+
+*/
+
+#define PCD_VERSION "1.01"
+#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 on
+ 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;
+
+#define D_PRT 0
+#define D_PRO 1
+#define D_UNI 2
+#define D_MOD 3
+#define D_SLV 4
+#define D_DLY 5
+
+#define DU (*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/cdrom.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pcd_stt[6] = {{"drive0",6,drive0},
+ {"drive1",6,drive1},
+ {"drive2",6,drive2},
+ {"drive3",6,drive3},
+ {"disable",1,&disable},
+ {"nice",1,&nice}};
+
+void pcd_setup( char *str, int *ints)
+
+{ generic_setup(pcd_stt,6,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(nice,"i");
+MODULE_PARM(drive0,"1-6i");
+MODULE_PARM(drive1,"1-6i");
+MODULE_PARM(drive2,"1-6i");
+MODULE_PARM(drive3,"1-6i");
+
+#include "paride.h"
+
+/* set up defines for blk.h, why don't all drivers do it this way ? */
+
+#define MAJOR_NR major
+#define DEVICE_NAME "PCD"
+#define DEVICE_REQUEST do_pcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.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
+
+#define PCD_SPIN (10000/PCD_DELAY)*PCD_TMO
+
+#define IDE_ERR 0x01
+#define IDE_DRQ 0x08
+#define IDE_READY 0x40
+#define IDE_BUSY 0x80
+
+int pcd_init(void);
+void cleanup_module( void );
+
+static int pcd_open(struct inode *inode, struct file *file);
+static void do_pcd_request(void);
+static void do_pcd_read(int unit);
+static int pcd_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static int pcd_release (struct inode *inode, struct file *file);
+
+static int pcd_detect(void);
+static void pcd_lock(int unit);
+static void pcd_unlock(int unit);
+static void pcd_eject(int unit);
+static int pcd_check_media(int unit);
+static void do_pcd_read_drq(void);
+
+static int pcd_blocksizes[PCD_UNITS];
+
+#define PCD_NAMELEN 8
+
+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 access; /* count of active opens */
+ int present; /* does this unit exist ? */
+ char name[PCD_NAMELEN]; /* pcd0, pcd1, etc */
+ };
+
+struct pcd_unit pcd[PCD_UNITS];
+
+/* 'unit' must be defined in all functions - either as a local or a param */
+
+#define PCD pcd[unit]
+#define PI PCD.pi
+
+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 int pcd_unit = -1; /* unit of current request & bufblk */
+static int pcd_retries; /* retries on current request */
+static int pcd_busy = 0; /* 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 */
+
+/* kernel glue structures */
+
+static struct file_operations pcd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ pcd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ pcd_open, /* open */
+ pcd_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* media change ? */
+ NULL /* revalidate new media */
+};
+
+static void pcd_init_units( void )
+
+{ int unit, j;
+
+ pcd_drive_count = 0;
+ for (unit=0;unit<PCD_UNITS;unit++) {
+ PCD.pi = & PCD.pia;
+ PCD.access = 0;
+ PCD.present = 0;
+ PCD.last_sense = 0;
+ j = 0;
+ while ((j < PCD_NAMELEN-2) && (PCD.name[j]=name[j])) j++;
+ PCD.name[j++] = '0' + unit;
+ PCD.name[j] = 0;
+ PCD.drive = DU[D_SLV];
+ if (DU[D_PRT]) pcd_drive_count++;
+ }
+}
+
+int pcd_init (void) /* preliminary initialisation */
+
+{ int i;
+
+ if (disable) return -1;
+
+ pcd_init_units();
+
+ if (pcd_detect()) return -1;
+
+ if (register_blkdev(MAJOR_NR,name,&pcd_fops)) {
+ printk("pcd: unable to get major number %d\n",MAJOR_NR);
+ return -1;
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
+
+ for (i=0;i<PCD_UNITS;i++) pcd_blocksizes[i] = 1024;
+ blksize_size[MAJOR_NR] = pcd_blocksizes;
+
+ return 0;
+}
+
+static int pcd_open (struct inode *inode, struct file *file)
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+ if (file->f_mode & 2) return -EROFS; /* wants to write ? */
+
+ MOD_INC_USE_COUNT;
+
+ if (pcd_check_media(unit)) {
+ MOD_DEC_USE_COUNT;
+ return -ENXIO;
+ }
+
+ pcd_lock(unit);
+
+ PCD.access++;
+ return 0;
+}
+
+static void do_pcd_request (void)
+
+{ int unit;
+
+ if (pcd_busy) return;
+ while (1) {
+ if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+ INIT_REQUEST;
+ if (CURRENT->cmd == READ) {
+ unit = MINOR(CURRENT->rq_dev);
+ if (unit != pcd_unit) {
+ pcd_bufblk = -1;
+ pcd_unit = unit;
+ }
+ pcd_sector = CURRENT->sector;
+ pcd_count = CURRENT->nr_sectors;
+ pcd_buf = CURRENT->buffer;
+ do_pcd_read(unit);
+ if (pcd_busy) return;
+ }
+ else end_request(0);
+ }
+}
+
+static int pcd_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg)
+
+/* we currently support only the EJECT ioctl. */
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+ if ((unit >= PCD_UNITS) || (!PCD.present)) return -ENODEV;
+
+ switch (cmd) {
+ case CDROMEJECT: if (PCD.access == 1) {
+ pcd_eject(unit);
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pcd_release (struct inode *inode, struct file *file)
+
+{ kdev_t devp;
+ int unit;
+
+ struct super_block *sb;
+
+ devp = inode->i_rdev;
+ unit = DEVICE_NR(devp);
+
+ if ((unit >= PCD_UNITS) || (PCD.access <= 0))
+ return -EINVAL;
+
+ PCD.access--;
+
+ if (!PCD.access) {
+ fsync_dev(devp);
+
+ sb = get_super(devp);
+ if (sb) invalidate_inodes(sb);
+
+ invalidate_buffers(devp);
+ pcd_unlock(unit);
+
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+int init_module(void)
+
+{ int err;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ err = pcd_init();
+
+ restore_flags(flags);
+ return err;
+}
+
+void cleanup_module(void)
+
+{ long flags;
+ int unit;
+
+ save_flags(flags);
+ cli();
+ unregister_blkdev(MAJOR_NR,name);
+
+ for (unit=0;unit<PCD_UNITS;unit++)
+ if (PCD.present) pi_release(PI);
+
+ restore_flags(flags);
+}
+
+#endif
+
+#define WR(c,r,v) pi_write_regr(PI,c,r,v)
+#define RR(c,r) (pi_read_regr(PI,c,r))
+
+static int pcd_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{ int j, r, e, s, p;
+
+ j = 0;
+ while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PCD_SPIN))
+ udelay(PCD_DELAY);
+
+ if ((r&(IDE_ERR&stop))||(j>=PCD_SPIN)) {
+ s = RR(0,7);
+ e = RR(0,1);
+ p = RR(0,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",
+ PCD.name,fun,msg,r,s,e,j,p);
+ return (s<<8)+r;
+ }
+ return 0;
+}
+
+static int pcd_command( int unit, char * cmd, int dlen, char * fun )
+
+{ pi_connect(PI);
+
+ WR(0,6,0xa0 + 0x10*PCD.drive);
+
+ if (pcd_wait(unit,IDE_BUSY|IDE_DRQ,0,fun,"before command")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ WR(0,4,dlen % 256);
+ WR(0,5,dlen / 256);
+ WR(0,7,0xa0); /* ATAPI packet command */
+
+ if (pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_ERR,fun,"command DRQ")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ if (RR(0,2) != 1) {
+ printk("%s: %s: command phase error\n",PCD.name,fun);
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ pi_write_block(PI,cmd,12);
+
+ return 0;
+}
+
+static int pcd_completion( int unit, char * buf, char * fun )
+
+{ int r, s, n;
+
+ r = pcd_wait(unit,IDE_BUSY,IDE_DRQ|IDE_READY|IDE_ERR,fun,"completion");
+
+ if ((RR(0,2)&2) && (RR(0,7)&IDE_DRQ)) {
+ n = (RR(0,4)+256*RR(0,5));
+ pi_read_block(PI,buf,n);
+ }
+
+ s = pcd_wait(unit,IDE_BUSY,IDE_READY|IDE_ERR,fun,"data done");
+
+ pi_disconnect(PI);
+
+ return (r?r:s);
+}
+
+static void pcd_req_sense( int unit, int quiet )
+
+{ char rs_cmd[12] = { 0x03,0,0,0,16,0,0,0,0,0,0,0 };
+ char buf[16];
+ int r;
+
+ r = pcd_command(unit,rs_cmd,16,"Request sense");
+ udelay(1000);
+ if (!r) pcd_completion(unit,buf,"Request sense");
+
+ PCD.last_sense = -1;
+ if (!r) {
+ if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+ PCD.name,buf[2]&0xf,buf[12],buf[13]);
+ PCD.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8)
+ | ((buf[13]&0xff)<<16) ;
+ }
+}
+
+static int pcd_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{ int r;
+
+ r = pcd_command(unit,cmd,dlen,fun);
+ udelay(1000);
+ if (!r) r = pcd_completion(unit,buf,fun);
+ if (r) pcd_req_sense(unit,!fun);
+
+ return r;
+}
+
+#define DBMSG(msg) NULL
+
+static void pcd_lock(int unit)
+
+{ char lo_cmd[12] = { 0x1e,0,0,0,1,0,0,0,0,0,0,0 };
+ char cl_cmd[12] = { 0x1b,0,0,0,3,0,0,0,0,0,0,0 };
+
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd1"));
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd2"));
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd3"));
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,DBMSG("cd4"));
+ pcd_atapi(unit,cl_cmd,0,pcd_scratch,"close door");
+
+ pcd_atapi(unit,lo_cmd,0,pcd_scratch,DBMSG("ld"));
+ pcd_atapi(unit,lo_cmd,0,pcd_scratch,"lock door");
+}
+
+static void pcd_unlock( int unit )
+
+{ char un_cmd[12] = { 0x1e,0,0,0,0,0,0,0,0,0,0,0 };
+
+ pcd_atapi(unit,un_cmd,0,pcd_scratch,"unlock door");
+}
+
+static void pcd_eject( int unit)
+
+{ char ej_cmd[12] = { 0x1b,0,0,0,2,0,0,0,0,0,0,0 };
+
+ pcd_unlock(unit);
+ pcd_atapi(unit,ej_cmd,0,pcd_scratch,"eject");
+}
+
+#define PCD_RESET_TMO 30 /* in tenths of a second */
+
+static void pcd_sleep( int cs )
+
+{ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + cs;
+ schedule();
+}
+
+static int pcd_reset( int unit )
+
+/* 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.
+*/
+
+{ int i, k, flg;
+ int expect[5] = {1,1,1,0x14,0xeb};
+ long flags;
+
+ pi_connect(PI);
+ WR(0,6,0xa0 + 0x10*PCD.drive);
+ WR(0,7,8);
+
+ save_flags(flags);
+ sti();
+
+ pcd_sleep(2); /* delay a bit*/
+
+ k = 0;
+ while ((k++ < PCD_RESET_TMO) && (RR(1,6)&IDE_BUSY))
+ pcd_sleep(10);
+
+ restore_flags(flags);
+
+ flg = 1;
+ for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+ if (verbose) {
+ printk("%s: Reset (%d) signature = ",PCD.name,k);
+ for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+ if (!flg) printk(" (incorrect)");
+ printk("\n");
+ }
+
+ pi_disconnect(PI);
+ return flg-1;
+}
+
+static int pcd_ready_wait( int unit, 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) {
+ PCD.last_sense = 0;
+ pcd_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready"));
+ p = PCD.last_sense;
+ if (!p) return 0;
+ if (!((p == 0x010402)||((p & 0xff) == 6))) return p;
+ k++;
+ pcd_sleep(100);
+ }
+ return 0x000020; /* timeout */
+}
+
+static int pcd_check_media( int unit )
+
+{ char rc_cmd[12] = { 0x25,0,0,0,0,0,0,0,0,0,0,0};
+
+ pcd_ready_wait(unit,PCD_READY_TMO);
+ return (pcd_atapi(unit,rc_cmd,8,pcd_scratch,DBMSG("check media")));
+}
+
+static int pcd_identify( int unit, 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(unit,id_cmd,36,pcd_buffer,"identify");
+
+ if (s) return -1;
+ if ((pcd_buffer[0] & 0x1f) != 5) {
+ if (verbose) printk("%s: %s is not a CDrom\n",
+ PCD.name,PCD.drive?"Slave":"Master");
+ return -1;
+ }
+ for (k=0;k<16;k++) id[k] = pcd_buffer[16+k]; id[16] = 0;
+ k = 16; while ((k >= 0) && (id[k] <= 0x20)) { id[k] = 0; k--; }
+
+ printk("%s: %s: %s\n",PCD.name,PCD.drive?"Slave":"Master",id);
+
+ return 0;
+}
+
+static int pcd_probe( int unit, int ms, char * id )
+
+/* returns 0, with id set if drive is detected
+ -1, if drive detection failed
+*/
+
+{ if (ms == -1) {
+ for (PCD.drive=0;PCD.drive<=1;PCD.drive++)
+ if (!pcd_reset(unit) && !pcd_identify(unit,id))
+ return 0;
+ } else {
+ PCD.drive = ms;
+ if (!pcd_reset(unit) && !pcd_identify(unit,id))
+ return 0;
+ }
+ return -1;
+}
+
+static int pcd_detect( void )
+
+{ char id[18];
+ int k, unit;
+
+ 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 */
+ unit = 0;
+ if (pi_init(PI,1,-1,-1,-1,-1,-1,pcd_buffer,
+ PI_PCD,verbose,PCD.name)) {
+ if (!pcd_probe(unit,-1,id)) {
+ PCD.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ } else for (unit=0;unit<PCD_UNITS;unit++) if (DU[D_PRT])
+ if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+ DU[D_PRO],DU[D_DLY],pcd_buffer,PI_PCD,verbose,
+ PCD.name)) {
+ if (!pcd_probe(unit,DU[D_SLV],id)) {
+ PCD.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ if (k) return 0;
+
+ printk("%s: No CDrom drive found\n",name);
+ return -1;
+}
+
+/* I/O request processing */
+
+static int pcd_ready( void )
+
+{ int unit = pcd_unit;
+
+ return (((RR(1,6)&(IDE_BUSY|IDE_DRQ))==IDE_DRQ)) ;
+}
+
+static void pcd_transfer( void )
+
+{ int k, o;
+
+ while (pcd_count && (pcd_sector/4 == pcd_bufblk)) {
+ o = (pcd_sector % 4) * 512;
+ for(k=0;k<512;k++) pcd_buf[k] = pcd_buffer[o+k];
+ pcd_count--;
+ pcd_buf += 512;
+ pcd_sector++;
+ }
+}
+
+static void pcd_start( void )
+
+{ int unit = pcd_unit;
+ 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(unit,rd_cmd,2048,"read block")) {
+ pcd_bufblk = -1;
+ pcd_busy = 0;
+ cli();
+ end_request(0);
+ do_pcd_request();
+ return;
+ }
+
+ udelay(1000);
+
+ ps_set_intr(do_pcd_read_drq,pcd_ready,PCD_TMO,nice);
+
+}
+
+static void do_pcd_read( int unit )
+
+{ pcd_busy = 1;
+ pcd_retries = 0;
+ pcd_transfer();
+ if (!pcd_count) {
+ end_request(1);
+ pcd_busy = 0;
+ return;
+ }
+ sti();
+
+ pi_do_claimed(PI,pcd_start);
+}
+
+static void do_pcd_read_drq( void )
+
+{ int unit = pcd_unit;
+
+ sti();
+
+ if (pcd_completion(unit,pcd_buffer,"read block")) {
+ if (pcd_retries < PCD_RETRIES) {
+ udelay(1000);
+ pcd_retries++;
+ pi_do_claimed(PI,pcd_start);
+ return;
+ }
+ cli();
+ pcd_busy = 0;
+ pcd_bufblk = -1;
+ end_request(0);
+ do_pcd_request();
+ return;
+ }
+
+ do_pcd_read(unit);
+ do_pcd_request();
+}
+
+/* end of pcd.c */
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
new file mode 100644
index 000000000..aa431ae33
--- /dev/null
+++ b/drivers/block/paride/pd.c
@@ -0,0 +1,1088 @@
+/*
+ pd.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU 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-7 integers as follows:
+ drive2
+ drive3 <prt>,<pro>,<uni>,<mod>,<geo>,<sby>,<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)
+
+ <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)
+
+
+ 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 is done while the driver probes for
+ devices. Set it to 0 for a quiet load, or to 1
+ see all the progress messages. (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
+
+*/
+
+#define PD_VERSION "1.01"
+#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 on
+ 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[7] = {0,0,0,-1,0,1,-1};
+static int drive1[7] = {0,0,0,-1,0,1,-1};
+static int drive2[7] = {0,0,0,-1,0,1,-1};
+static int drive3[7] = {0,0,0,-1,0,1,-1};
+
+static int (*drives[4])[7] = {&drive0,&drive1,&drive2,&drive3};
+static int pd_drive_count;
+
+#define D_PRT 0
+#define D_PRO 1
+#define D_UNI 2
+#define D_MOD 3
+#define D_GEO 4
+#define D_SBY 5
+#define D_DLY 6
+
+#define DU (*drives[unit])
+
+/* end of parameters */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h> /* for the eject ioctl */
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pd_stt[7] = {{"drive0",7,drive0},
+ {"drive1",7,drive1},
+ {"drive2",7,drive2},
+ {"drive3",7,drive3},
+ {"disable",1,&disable},
+ {"cluster",1,&cluster},
+ {"nice",1,&nice}};
+
+void pd_setup( char *str, int *ints)
+
+{ generic_setup(pd_stt,7,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(cluster,"i");
+MODULE_PARM(nice,"i");
+MODULE_PARM(drive0,"1-7i");
+MODULE_PARM(drive1,"1-7i");
+MODULE_PARM(drive2,"1-7i");
+MODULE_PARM(drive3,"1-7i");
+
+#include "paride.h"
+
+#define PD_BITS 4
+
+/* set up defines for blk.h, why don't all drivers do it this way ? */
+
+#define MAJOR_NR major
+#define DEVICE_NAME "PD"
+#define DEVICE_REQUEST do_pd_request
+#define DEVICE_NR(device) (MINOR(device)>>PD_BITS)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "pseudo.h"
+
+#define PD_PARTNS (1<<PD_BITS)
+#define PD_DEVS PD_PARTNS*PD_UNITS
+
+/* 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 (10000/PD_SPIN_DEL)*PD_TMO
+
+#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
+
+int pd_init(void);
+void pd_setup(char * str, int * ints);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static void pd_geninit(struct gendisk *ignored);
+static int pd_open(struct inode *inode, struct file *file);
+static void do_pd_request(void);
+static int pd_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int pd_release (struct inode *inode, struct file *file);
+static int pd_revalidate(kdev_t dev);
+static int pd_detect(void);
+static void do_pd_read(void);
+static void do_pd_write(void);
+static void do_pd_read_drq( void );
+static void do_pd_write_done( void );
+
+static int pd_identify (int unit);
+static void pd_media_check(int unit);
+static void pd_doorlock(int unit, int func);
+static int pd_check_media(kdev_t dev);
+static void pd_eject( int unit);
+
+static struct hd_struct pd_hd[PD_DEVS];
+static int pd_sizes[PD_DEVS];
+static int pd_blocksizes[PD_DEVS];
+
+#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 changed; /* Have we seen a disk change ? */
+ int removable; /* removable media device ? */
+ int standby;
+ int alt_geom;
+ int present;
+ char name[PD_NAMELEN]; /* pda, pdb, etc ... */
+ };
+
+struct pd_unit pd[PD_UNITS];
+
+/* 'unit' must be defined in all functions - either as a local or a param */
+
+#define PD pd[unit]
+#define PI PD.pi
+
+static int pd_valid = 1; /* serialise partition checks */
+static char pd_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 pd_retries = 0; /* i/o error retry count */
+static int pd_busy = 0; /* request being processed ? */
+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 int pd_unit; /* unit of current request */
+static int pd_dev; /* minor of current request */
+static int pd_poffs; /* partition offset of current minor */
+static char * pd_buf; /* buffer for request in progress */
+
+static struct wait_queue *pd_wait_open = NULL;
+
+static char *pd_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR",
+ "READY","BUSY","AMNF","TK0NF","ABRT","MCR",
+ "IDNF","MC","UNC","???","TMO"};
+
+/* kernel glue structures */
+
+static struct gendisk pd_gendisk = {
+ PD_MAJOR, /* Major number */
+ PD_NAME, /* Major name */
+ PD_BITS, /* Bits to shift to get real from partition */
+ PD_PARTNS, /* Number of partitions per real */
+ PD_UNITS, /* maximum number of real */
+ pd_geninit, /* init function */
+ pd_hd, /* hd struct */
+ pd_sizes, /* block sizes */
+ 0, /* number */
+ NULL, /* internal */
+ NULL /* next */
+};
+
+static struct file_operations pd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ pd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ pd_open, /* open */
+ pd_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ pd_check_media, /* media change ? */
+ pd_revalidate /* revalidate new media */
+};
+
+void pd_init_units( void )
+
+{ int unit, j;
+
+ pd_drive_count = 0;
+ for (unit=0;unit<PD_UNITS;unit++) {
+ PD.pi = & PD.pia;
+ PD.access = 0;
+ PD.changed = 1;
+ PD.capacity = 0;
+ PD.present = 0;
+ j = 0;
+ while ((j < PD_NAMELEN-2) && (PD.name[j]=name[j])) j++;
+ PD.name[j++] = 'a' + unit;
+ PD.name[j] = 0;
+ PD.alt_geom = DU[D_GEO];
+ PD.standby = DU[D_SBY];
+ if (DU[D_PRT]) pd_drive_count++;
+ }
+}
+
+int pd_init (void)
+
+{ int i;
+
+ if (disable) return -1;
+
+ if (register_blkdev(MAJOR_NR,name,&pd_fops)) {
+ printk("%s: unable to get major number %d\n",
+ name,major);
+ return -1;
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
+
+ pd_gendisk.major = major;
+ pd_gendisk.major_name = name;
+ pd_gendisk.next = gendisk_head;
+ gendisk_head = &pd_gendisk;
+
+ for(i=0;i<PD_DEVS;i++) pd_blocksizes[i] = 1024;
+ blksize_size[MAJOR_NR] = pd_blocksizes;
+
+ printk("%s: %s version %s, major %d, cluster %d, nice %d\n",
+ name,name,PD_VERSION,major,cluster,nice);
+
+ return 0;
+}
+
+static void pd_geninit (struct gendisk *ignored)
+
+{ pd_init_units();
+ pd_gendisk.nr_real = pd_detect();
+
+#ifdef MODULE
+ if (!pd_gendisk.nr_real) cleanup_module();
+#endif
+
+}
+
+static int pd_open (struct inode *inode, struct file *file)
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+ MOD_INC_USE_COUNT;
+
+ while (!pd_valid) sleep_on(&pd_wait_open);
+
+ PD.access++;
+
+ if (PD.removable) {
+ pd_media_check(unit);
+ pd_doorlock(unit,IDE_DOORLOCK);
+ }
+ return 0;
+}
+
+static int pd_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg)
+
+{ struct hd_geometry *geo = (struct hd_geometry *) arg;
+ int dev, err, unit;
+
+ if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+ dev = MINOR(inode->i_rdev);
+ unit = DEVICE_NR(inode->i_rdev);
+ if (dev >= PD_DEVS) return -EINVAL;
+ if (!PD.present) return -ENODEV;
+
+ switch (cmd) {
+ case CDROMEJECT:
+ if (PD.access == 1) pd_eject(unit);
+ return 0;
+ case HDIO_GETGEO:
+ if (!geo) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+ if (err) return err;
+
+ if (PD.alt_geom) {
+ put_user(PD.capacity/(PD_LOG_HEADS*PD_LOG_SECTS),
+ (short *) &geo->cylinders);
+ put_user(PD_LOG_HEADS, (char *) &geo->heads);
+ put_user(PD_LOG_SECTS, (char *) &geo->sectors);
+ } else {
+ put_user(PD.cylinders, (short *) &geo->cylinders);
+ put_user(PD.heads, (char *) &geo->heads);
+ put_user(PD.sectors, (char *) &geo->sectors);
+ }
+ put_user(pd_hd[dev].start_sect,(long *)&geo->start);
+ return 0;
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+ case BLKRAGET:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+ if (err) return (err);
+ put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+ return (0);
+ case BLKGETSIZE:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+ if (err) return (err);
+ put_user(pd_hd[dev].nr_sects,(long *) arg);
+ return (0);
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+ case BLKRRPART:
+ return pd_revalidate(inode->i_rdev);
+ RO_IOCTLS(inode->i_rdev,arg);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pd_release (struct inode *inode, struct file *file)
+
+{ kdev_t devp;
+ int unit;
+
+ struct super_block *sb;
+
+ devp = inode->i_rdev;
+ unit = DEVICE_NR(devp);
+
+ if ((unit >= PD_UNITS) || (PD.access <= 0))
+ return -EINVAL;
+
+ PD.access--;
+
+ if (!PD.access) {
+ fsync_dev(devp);
+
+ sb = get_super(devp);
+ if (sb) invalidate_inodes(sb);
+
+ invalidate_buffers(devp);
+ if (PD.removable) pd_doorlock(unit,IDE_DOORUNLOCK);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static int pd_check_media( kdev_t dev)
+
+{ int r, unit;
+
+ unit = DEVICE_NR(dev);
+ if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+ if (!PD.removable) return 0;
+ pd_media_check(unit);
+ r = PD.changed;
+ PD.changed = 0;
+ return r;
+}
+
+static int pd_revalidate(kdev_t dev)
+
+{ int p, unit, minor;
+ long flags;
+ kdev_t devp;
+
+ struct super_block *sb;
+
+ unit = DEVICE_NR(dev);
+ if ((unit >= PD_UNITS) || (!PD.present)) return -ENODEV;
+
+ save_flags(flags);
+ cli();
+ if (PD.access > 1) {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ pd_valid = 0;
+ restore_flags(flags);
+
+ for (p=(PD_PARTNS-1);p>=0;p--) {
+ minor = p + unit*PD_PARTNS;
+ devp = MKDEV(MAJOR_NR, minor);
+ fsync_dev(devp);
+
+ sb = get_super(devp);
+ if (sb) invalidate_inodes(sb);
+
+ invalidate_buffers(devp);
+ pd_hd[minor].start_sect = 0;
+ pd_hd[minor].nr_sects = 0;
+ }
+
+ pd_identify(unit);
+ resetup_one_dev(&pd_gendisk,unit);
+
+ pd_valid = 1;
+ wake_up(&pd_wait_open);
+
+ return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void cleanup_module(void);
+
+int init_module(void)
+
+{ int err, unit;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ err = pd_init();
+ if (err) {
+ restore_flags(flags);
+ return err;
+ }
+
+ pd_geninit(&pd_gendisk);
+
+ if (!pd_gendisk.nr_real) {
+ restore_flags(flags);
+ return -1;
+ }
+
+ pd_valid = 0;
+ for (unit=0;unit<PD_UNITS;unit++)
+ if (PD.present) resetup_one_dev(&pd_gendisk,unit);
+ pd_valid = 1;
+
+ restore_flags(flags);
+ return 0;
+}
+
+void cleanup_module(void)
+
+{ struct gendisk **gdp;
+ long flags;
+ int unit;
+
+ save_flags(flags);
+ cli();
+
+ unregister_blkdev(MAJOR_NR,name);
+
+ for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next))
+ if (*gdp == &pd_gendisk) break;
+ if (*gdp) *gdp = (*gdp)->next;
+
+ for (unit=0;unit<PD_UNITS;unit++)
+ if (PD.present) pi_release(PI);
+
+ restore_flags(flags);
+}
+
+#endif
+
+#define WR(c,r,v) pi_write_regr(PI,c,r,v)
+#define RR(c,r) (pi_read_regr(PI,c,r))
+
+/* ide command interface */
+
+static void pd_print_error( int unit, char * msg, int status )
+
+{ int i;
+
+ printk("%s: %s: status = 0x%x =",PD.name,msg,status);
+ for(i=0;i<18;i++) if (status & (1<<i)) printk(" %s",pd_errs[i]);
+ printk("\n");
+}
+
+static void pd_reset( int unit )
+
+{ pi_connect(PI);
+ WR(1,6,4);
+ udelay(50);
+ WR(1,6,0);
+ pi_disconnect(PI);
+ udelay(250);
+}
+
+#define DBMSG(msg) NULL
+
+static int pd_wait_for( int unit, int w, char * msg ) /* polled wait */
+
+{ int k, r, e;
+
+ k=0;
+ while(k < PD_SPIN) {
+ r = RR(1,6);
+ k++;
+ if (((r & w) == w) && !(r & STAT_BUSY)) break;
+ udelay(PD_SPIN_DEL);
+ }
+ e = (RR(0,1)<<8) + RR(0,7);
+ if (k >= PD_SPIN) e |= ERR_TMO;
+ if ((e & (STAT_ERR|ERR_TMO)) && (msg != NULL))
+ pd_print_error(unit,msg,e);
+ return e;
+}
+
+static void pd_send_command( int unit, int n, int s, int h,
+ int c0, int c1, int func )
+
+{
+ WR(0,6,0xa0+h);
+ WR(0,1,0); /* the IDE task file */
+ WR(0,2,n);
+ WR(0,3,s);
+ WR(0,4,c0);
+ WR(0,5,c1);
+ WR(0,7,func);
+
+ udelay(1);
+}
+
+static void pd_ide_command( int unit, int func, int block, int count )
+
+/* Don't use this call if the capacity is zero. */
+
+{ int c1, c0, h, s;
+
+ s = ( block % PD.sectors) + 1;
+ h = ( block / PD.sectors) % PD.heads;
+ c0 = ( block / (PD.sectors*PD.heads)) % 256;
+ c1 = ( block / (PD.sectors*PD.heads*256));
+
+ pd_send_command(unit,count,s,h,c0,c1,func);
+}
+
+/* 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( int unit )
+
+{ pi_connect(PI);
+ pd_wait_for(unit,0,DBMSG("before init_dev_parms"));
+ pd_send_command(unit,PD.sectors,0,PD.heads-1,0,0,IDE_INIT_DEV_PARMS);
+ udelay(300);
+ pd_wait_for(unit,0,"Initialise device parameters");
+ pi_disconnect(PI);
+}
+
+static void pd_doorlock( int unit, int func )
+
+{ pi_connect(PI);
+ if (pd_wait_for(unit,STAT_READY,"Lock") & STAT_ERR) {
+ pi_disconnect(PI);
+ return;
+ }
+ pd_send_command(unit,1,0,0,0,0,func);
+ pd_wait_for(unit,STAT_READY,"Lock done");
+ pi_disconnect(PI);
+}
+
+static void pd_eject( int unit )
+
+{ pi_connect(PI);
+ pd_wait_for(unit,0,DBMSG("before unlock on eject"));
+ pd_send_command(unit,1,0,0,0,0,IDE_DOORUNLOCK);
+ pd_wait_for(unit,0,DBMSG("after unlock on eject"));
+ pd_wait_for(unit,0,DBMSG("before eject"));
+ pd_send_command(unit,0,0,0,0,0,IDE_EJECT);
+ pd_wait_for(unit,0,DBMSG("after eject"));
+ pi_disconnect(PI);
+}
+
+static void pd_media_check( int unit )
+
+{ int r;
+
+ pi_connect(PI);
+ r = pd_wait_for(unit,STAT_READY,DBMSG("before media_check"));
+ if (!(r & STAT_ERR)) {
+ pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);
+ r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after READ_VRFY"));
+ } else PD.changed = 1; /* say changed if other error */
+ if (r & ERR_MC) {
+ PD.changed = 1;
+ pd_send_command(unit,1,0,0,0,0,IDE_ACKCHANGE);
+ pd_wait_for(unit,STAT_READY,DBMSG("RDY after ACKCHANGE"));
+ pd_send_command(unit,1,1,0,0,0,IDE_READ_VRFY);
+ r = pd_wait_for(unit,STAT_READY,DBMSG("RDY after VRFY"));
+ }
+ pi_disconnect(PI);
+
+}
+
+static void pd_standby_off( int unit )
+
+{ pi_connect(PI);
+ pd_wait_for(unit,0,DBMSG("before STANDBY"));
+ pd_send_command(unit,0,0,0,0,0,IDE_STANDBY);
+ pd_wait_for(unit,0,DBMSG("after STANDBY"));
+ pi_disconnect(PI);
+}
+
+#define word_val(n) ((pd_scratch[2*n]&0xff)+256*(pd_scratch[2*n+1]&0xff))
+
+static int pd_identify( int unit )
+
+{ int j;
+ char id[PD_ID_LEN+1];
+
+ pd_reset(unit);
+
+ pi_connect(PI);
+ WR(0,6,0xa0);
+ pd_wait_for(unit,0,DBMSG("before IDENT"));
+ pd_send_command(unit,1,0,0,0,0,IDE_IDENTIFY);
+
+ if (pd_wait_for(unit,STAT_DRQ,DBMSG("IDENT DRQ")) & STAT_ERR) {
+ pi_disconnect(PI);
+ return 0;
+ }
+ pi_read_block(PI,pd_scratch,512);
+ pi_disconnect(PI);
+ PD.sectors = word_val(6);
+ PD.heads = word_val(3);
+ PD.cylinders = word_val(1);
+ PD.capacity = PD.sectors*PD.heads*PD.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;
+
+ PD.removable = (word_val(0) & 0x80);
+
+ printk("%s: %s, %d blocks [%dM], (%d/%d/%d), %s media\n",
+ PD.name,id,PD.capacity,PD.capacity/2048,
+ PD.cylinders,PD.heads,PD.sectors,
+ PD.removable?"removable":"fixed");
+
+ if (PD.capacity) pd_init_dev_parms(unit);
+ if (!PD.standby) pd_standby_off(unit);
+
+ pd_hd[unit<<PD_BITS].nr_sects = PD.capacity;
+ pd_hd[unit<<PD_BITS].start_sect = 0;
+
+ return 1;
+}
+
+static int pd_detect( void )
+
+{ long flags;
+ int k, unit;
+
+ k = 0;
+ if (pd_drive_count == 0) { /* nothing spec'd - so autoprobe for 1 */
+ unit = 0;
+ if (pi_init(PI,1,-1,-1,-1,-1,-1,pd_scratch,
+ PI_PD,verbose,PD.name)) {
+ save_flags(flags);
+ sti();
+ if (pd_identify(unit)) {
+ PD.present = 1;
+ k = 1;
+ } else pi_release(PI);
+ restore_flags(flags);
+ }
+
+ } else for (unit=0;unit<PD_UNITS;unit++) if (DU[D_PRT])
+ if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+ DU[D_PRO],DU[D_DLY],pd_scratch,
+ PI_PD,verbose,PD.name)) {
+ save_flags(flags);
+ sti();
+ if (pd_identify(unit)) {
+ PD.present = 1;
+ k = unit+1;
+ } else pi_release(PI);
+ restore_flags(flags);
+ }
+
+/* We lie about the number of drives found, as the generic partition
+ scanner assumes that the drives are numbered sequentially from 0.
+ This can result in some bogus error messages if non-sequential
+ drive numbers are used.
+*/
+
+ if (k) return k;
+
+ printk("%s: no valid drive found\n",name);
+ return 0;
+}
+
+/* The i/o request engine */
+
+static int pd_ready( void )
+
+{ int unit = pd_unit;
+
+ return (!(RR(1,6) & STAT_BUSY)) ;
+}
+
+static void do_pd_request (void)
+
+{ struct buffer_head * bh;
+ struct request * req;
+ int unit;
+
+ if (pd_busy) return;
+repeat:
+ if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+ INIT_REQUEST;
+
+ pd_dev = MINOR(CURRENT->rq_dev);
+ pd_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+ pd_block = CURRENT->sector;
+ pd_count = CURRENT->nr_sectors;
+
+ bh = CURRENT->bh;
+ req = CURRENT;
+ if (bh->b_reqnext)
+ printk("%s: OUCH: b_reqnext != NULL\n",PD.name);
+
+ if ((pd_dev >= PD_DEVS) ||
+ ((pd_block+pd_count) > pd_hd[pd_dev].nr_sects)) {
+ end_request(0);
+ goto repeat;
+ }
+
+ pd_cmd = CURRENT->cmd;
+ pd_run = pd_count;
+ while ((pd_run <= cluster) &&
+ (req = req->next) &&
+ (pd_block+pd_run == req->sector) &&
+ (pd_cmd == req->cmd) &&
+ (pd_dev == MINOR(req->rq_dev)))
+ pd_run += req->nr_sectors;
+
+ pd_poffs = pd_hd[pd_dev].start_sect;
+ pd_block += pd_poffs;
+ pd_buf = CURRENT->buffer;
+ pd_retries = 0;
+
+ if (pd_cmd == READ) pi_do_claimed(PI,do_pd_read);
+ else if (pd_cmd == WRITE) pi_do_claimed(PI,do_pd_write);
+ else { end_request(0);
+ goto repeat;
+ }
+}
+
+static void pd_next_buf( int unit )
+
+{ cli();
+ end_request(1);
+ if (!pd_run) { sti(); return; }
+
+/* paranoia */
+
+ if ((!CURRENT) ||
+ (CURRENT->cmd != pd_cmd) ||
+ (MINOR(CURRENT->rq_dev) != pd_dev) ||
+ (CURRENT->rq_status == RQ_INACTIVE) ||
+ (CURRENT->sector+pd_poffs != pd_block))
+ printk("%s: OUCH: request list changed unexpectedly\n",
+ PD.name);
+
+ pd_count = CURRENT->nr_sectors;
+ pd_buf = CURRENT->buffer;
+ sti();
+}
+
+static void do_pd_read( void )
+
+{ int unit = pd_unit;
+
+ pd_busy = 1;
+
+ sti();
+
+ pi_connect(PI);
+ if (pd_wait_for(unit,STAT_READY,"do_pd_read") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_read);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pd_ide_command(unit,IDE_READ,pd_block,pd_run);
+ ps_set_intr(do_pd_read_drq,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_read_drq( void )
+
+{ int unit = pd_unit;
+
+ sti();
+
+ while (1) {
+ if (pd_wait_for(unit,STAT_DRQ,"do_pd_read_drq") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_read);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pi_read_block(PI,pd_buf,512);
+ pd_count--; pd_run--;
+ pd_buf += 512;
+ pd_block++;
+ if (!pd_run) break;
+ if (!pd_count) pd_next_buf(unit);
+ }
+ pi_disconnect(PI);
+ end_request(1);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+}
+
+static void do_pd_write( void )
+
+{ int unit = pd_unit;
+
+ pd_busy = 1;
+
+ sti();
+
+ pi_connect(PI);
+ if (pd_wait_for(unit,STAT_READY,"do_pd_write") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_write);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pd_ide_command(unit,IDE_WRITE,pd_block,pd_run);
+ while (1) {
+ if (pd_wait_for(unit,STAT_DRQ,"do_pd_write_drq") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_write);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pi_write_block(PI,pd_buf,512);
+ pd_count--; pd_run--;
+ pd_buf += 512;
+ pd_block++;
+ if (!pd_run) break;
+ if (!pd_count) pd_next_buf(unit);
+ }
+ ps_set_intr(do_pd_write_done,pd_ready,PD_TMO,nice);
+}
+
+static void do_pd_write_done( void )
+
+{ int unit = pd_unit;
+
+ sti();
+ if (pd_wait_for(unit,STAT_READY,"do_pd_write_done") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pd_retries < PD_MAX_RETRIES) {
+ pd_retries++;
+ pi_do_claimed(PI,do_pd_write);
+ return;
+ }
+ end_request(0);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+ return;
+ }
+ pi_disconnect(PI);
+ end_request(1);
+ pd_busy = 0;
+ cli();
+ do_pd_request();
+}
+
+/* end of pd.c */
+
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
new file mode 100644
index 000000000..00d629ddd
--- /dev/null
+++ b/drivers/block/paride/pf.c
@@ -0,0 +1,1072 @@
+/*
+ pf.c (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU 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 is done while the driver probes for
+ devices. Set it to 0 for a quiet load, or 1 to
+ see all the progress messages. (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:
+
+ 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.
+
+*/
+
+#define PF_VERSION "1.0"
+#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 on
+ 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;
+
+#define D_PRT 0
+#define D_PRO 1
+#define D_UNI 2
+#define D_MOD 3
+#define D_SLV 4
+#define D_LUN 5
+#define D_DLY 6
+
+#define DU (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pf_stt[7] = {{"drive0",7,drive0},
+ {"drive1",7,drive1},
+ {"drive2",7,drive2},
+ {"drive3",7,drive3},
+ {"disable",1,&disable},
+ {"cluster",1,&cluster},
+ {"nice",1,&nice}};
+
+void pf_setup( char *str, int *ints)
+
+{ generic_setup(pf_stt,7,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(cluster,"i");
+MODULE_PARM(nice,"i");
+MODULE_PARM(drive0,"1-7i");
+MODULE_PARM(drive1,"1-7i");
+MODULE_PARM(drive2,"1-7i");
+MODULE_PARM(drive3,"1-7i");
+
+#include "paride.h"
+
+/* set up defines for blk.h, why don't all drivers do it this way ? */
+
+#define MAJOR_NR major
+#define DEVICE_NAME "PF"
+#define DEVICE_REQUEST do_pf_request
+#define DEVICE_NR(device) MINOR(device)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.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 (10000/PF_SPIN_DEL)*PF_TMO
+
+#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
+
+int pf_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static int pf_open(struct inode *inode, struct file *file);
+static void do_pf_request(void);
+static int pf_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+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_write(void);
+static void do_pf_read_drq( void );
+static void do_pf_write_done( void );
+
+static int pf_identify (int unit);
+static void pf_lock(int unit, int func);
+static void pf_eject(int unit);
+static int pf_check_media(kdev_t dev);
+
+static int pf_blocksizes[PF_UNITS];
+
+#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 capacity; /* Size of this volume in sectors */
+ int present; /* device present ? */
+ char name[PF_NAMELEN]; /* pf0, pf1, ... */
+ };
+
+struct pf_unit pf[PF_UNITS];
+
+/* 'unit' must be defined in all functions - either as a local or a param */
+
+#define PF pf[unit]
+#define PI PF.pi
+
+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 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 int pf_unit; /* 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 file_operations pf_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ pf_ioctl, /* ioctl */
+ NULL, /* mmap */
+ pf_open, /* open */
+ pf_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ pf_check_media, /* media change ? */
+ NULL /* revalidate new media */
+};
+
+void pf_init_units( void )
+
+{ int unit, j;
+
+ pf_drive_count = 0;
+ for (unit=0;unit<PF_UNITS;unit++) {
+ PF.pi = & PF.pia;
+ PF.access = 0;
+ PF.media_status = PF_NM;
+ PF.capacity = 0;
+ PF.present = 0;
+ PF.drive = DU[D_SLV];
+ PF.lun = DU[D_LUN];
+ j = 0;
+ while ((j < PF_NAMELEN-2) && (PF.name[j]=name[j])) j++;
+ PF.name[j++] = '0' + unit;
+ PF.name[j] = 0;
+ if (DU[D_PRT]) pf_drive_count++;
+ }
+}
+
+int pf_init (void) /* preliminary initialisation */
+
+{ int i;
+
+ if (disable) return -1;
+
+ pf_init_units();
+
+ if (pf_detect()) return -1;
+ pf_busy = 0;
+
+ if (register_blkdev(MAJOR_NR,name,&pf_fops)) {
+ printk("pf_init: unable to get major number %d\n",
+ major);
+ return -1;
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */
+
+ for (i=0;i<PF_UNITS;i++) pf_blocksizes[i] = 1024;
+ blksize_size[MAJOR_NR] = pf_blocksizes;
+
+ return 0;
+}
+
+static int pf_open (struct inode *inode, struct file *file)
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PF_UNITS) || (!PF.present)) return -ENODEV;
+
+ MOD_INC_USE_COUNT;
+
+ pf_identify(unit);
+
+ if (PF.media_status == PF_NM) {
+ MOD_DEC_USE_COUNT;
+ return -ENODEV;
+ }
+
+ if ((PF.media_status == PF_RO) && (file ->f_mode & 2)) {
+ MOD_DEC_USE_COUNT;
+ return -EROFS;
+ }
+
+ PF.access++;
+ if (PF.removable) pf_lock(unit,1);
+
+ return 0;
+}
+
+static int pf_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg)
+
+{ int err, unit;
+ struct hd_geometry *geo = (struct hd_geometry *) arg;
+
+ if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+ unit = DEVICE_NR(inode->i_rdev);
+ if (unit >= PF_UNITS) return -EINVAL;
+ if (!PF.present) return -ENODEV;
+
+ switch (cmd) {
+ case CDROMEJECT:
+ if (PF.access == 1) {
+ pf_eject(unit);
+ return 0;
+ }
+ case HDIO_GETGEO:
+ if (!geo) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+ if (err) return err;
+ if (PF.capacity < PF_FD_MAX) {
+ put_user(PF.capacity/(PF_FD_HDS*PF_FD_SPT),
+ (short *) &geo->cylinders);
+ put_user(PF_FD_HDS, (char *) &geo->heads);
+ put_user(PF_FD_SPT, (char *) &geo->sectors);
+ } else {
+ put_user(PF.capacity/(PF_HD_HDS*PF_HD_SPT),
+ (short *) &geo->cylinders);
+ put_user(PF_HD_HDS, (char *) &geo->heads);
+ put_user(PF_HD_SPT, (char *) &geo->sectors);
+ }
+ put_user(0,(long *)&geo->start);
+ return 0;
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+ case BLKRAGET:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+ if (err) return (err);
+ put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+ return (0);
+ case BLKGETSIZE:
+ if (!arg) return -EINVAL;
+ err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+ if (err) return (err);
+ put_user(PF.capacity,(long *) arg);
+ return (0);
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+ RO_IOCTLS(inode->i_rdev,arg);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int pf_release (struct inode *inode, struct file *file)
+
+{ kdev_t devp;
+ int unit;
+
+ struct super_block *sb;
+
+ devp = inode->i_rdev;
+ unit = DEVICE_NR(devp);
+
+ if ((unit >= PF_UNITS) || (PF.access <= 0))
+ return -EINVAL;
+
+ PF.access--;
+
+ if (!PF.access) {
+ fsync_dev(devp);
+
+ sb = get_super(devp);
+ if (sb) invalidate_inodes(sb);
+
+ invalidate_buffers(devp);
+ if (PF.removable) pf_lock(unit,0);
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+}
+
+static int pf_check_media( kdev_t dev)
+
+{ return 1;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void cleanup_module(void);
+
+int init_module(void)
+
+{ int err;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ err = pf_init();
+
+ restore_flags(flags);
+ return err;
+}
+
+void cleanup_module(void)
+
+{ long flags;
+ int unit;
+
+ save_flags(flags);
+ cli();
+
+ unregister_blkdev(MAJOR_NR,name);
+
+ for (unit=0;unit<PF_UNITS;unit++)
+ if (PF.present) pi_release(PI);
+
+ restore_flags(flags);
+}
+
+#endif
+
+#define WR(c,r,v) pi_write_regr(PI,c,r,v)
+#define RR(c,r) (pi_read_regr(PI,c,r))
+
+#define LUN (0x20*PF.lun)
+#define DRIVE (0xa0+0x10*PF.drive)
+
+static int pf_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{ int j, r, e, s, p;
+
+ j = 0;
+ while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PF_SPIN))
+ udelay(PF_SPIN_DEL);
+
+ if ((r&(STAT_ERR&stop))||(j>=PF_SPIN)) {
+ s = RR(0,7);
+ e = RR(0,1);
+ p = RR(0,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( int unit, char * cmd, int dlen, char * fun )
+
+{ pi_connect(PI);
+
+ WR(0,6,DRIVE);
+
+ if (pf_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ WR(0,4,dlen % 256);
+ WR(0,5,dlen / 256);
+ WR(0,7,0xa0); /* ATAPI packet command */
+
+ if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,fun,"command DRQ")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ if (RR(0,2) != 1) {
+ printk("%s: %s: command phase error\n",PF.name,fun);
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ pi_write_block(PI,cmd,12);
+
+ return 0;
+}
+
+static int pf_completion( int unit, char * buf, char * fun )
+
+{ int r, s, n;
+
+ r = pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+ fun,"completion");
+
+ if ((RR(0,2)&2) && (RR(0,7)&STAT_DRQ)) {
+ n = (RR(0,4)+256*RR(0,5));
+ pi_read_block(PI,buf,n);
+ }
+
+ s = pf_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done");
+
+ pi_disconnect(PI);
+
+ return (r?r:s);
+}
+
+static void pf_req_sense( int unit, int quiet )
+
+{ char rs_cmd[12] = { ATAPI_REQ_SENSE,LUN,0,0,16,0,0,0,0,0,0,0 };
+ char buf[16];
+ int r;
+
+ r = pf_command(unit,rs_cmd,16,"Request sense");
+ udelay(1000);
+ if (!r) pf_completion(unit,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( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{ int r;
+
+ r = pf_command(unit,cmd,dlen,fun);
+ udelay(1000);
+ if (!r) r = pf_completion(unit,buf,fun);
+ if (r) pf_req_sense(unit,!fun);
+
+ return r;
+}
+
+#define DBMSG(msg) NULL
+
+static void pf_lock(int unit, int func)
+
+{ char lo_cmd[12] = { ATAPI_LOCK,LUN,0,0,func,0,0,0,0,0,0,0 };
+
+ pf_atapi(unit,lo_cmd,0,pf_scratch,func?"unlock":"lock");
+}
+
+
+static void pf_eject( int unit )
+
+{ char ej_cmd[12] = { ATAPI_DOOR,LUN,0,0,2,0,0,0,0,0,0,0 };
+
+ pf_lock(unit,0);
+ pf_atapi(unit,ej_cmd,0,pf_scratch,"eject");
+}
+
+#define PF_RESET_TMO 30 /* in tenths of a second */
+
+static void pf_sleep( int cs )
+
+{ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + cs;
+ schedule();
+}
+
+
+static int pf_reset( int unit )
+
+/* 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.
+*/
+
+{ int i, k, flg;
+ int expect[5] = {1,1,1,0x14,0xeb};
+ long flags;
+
+ pi_connect(PI);
+ WR(0,6,DRIVE);
+ WR(0,7,8);
+
+ save_flags(flags);
+ sti();
+
+ pf_sleep(2);
+
+ k = 0;
+ while ((k++ < PF_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+ pf_sleep(10);
+
+ restore_flags(flags);
+
+ flg = 1;
+ for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+ if (verbose) {
+ printk("%s: Reset (%d) signature = ",PF.name,k);
+ for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+ if (!flg) printk(" (incorrect)");
+ printk("\n");
+ }
+
+ pi_disconnect(PI);
+ return flg-1;
+}
+
+static void pf_mode_sense( int unit )
+
+{ char ms_cmd[12] = { ATAPI_MODE_SENSE,LUN,0,0,0,0,0,0,8,0,0,0};
+ char buf[8];
+
+ pf_atapi(unit,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( int unit )
+
+{ char rc_cmd[12] = { ATAPI_CAPACITY,LUN,0,0,0,0,0,0,0,0,0,0};
+ char buf[8];
+ int bs;
+
+ if (pf_atapi(unit,rc_cmd,8,buf,DBMSG("get capacity"))) {
+ PF.media_status = PF_NM;
+ return;
+ }
+ PF.capacity = xl(buf,0) + 1;
+ bs = xl(buf,4);
+ if (bs != 512) {
+ PF.capacity = 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( int unit )
+
+{ int dt, s;
+ char *ms[2] = {"master","slave"};
+ char mf[10], id[18];
+ char id_cmd[12] = { ATAPI_IDENTIFY,LUN,0,0,36,0,0,0,0,0,0,0};
+ char buf[36];
+
+ s = pf_atapi(unit,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(unit);
+ pf_mode_sense(unit);
+ pf_mode_sense(unit);
+
+ pf_get_capacity(unit);
+
+ 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(", %d blocks\n",PF.capacity);
+ }
+
+ return 0;
+}
+
+static int pf_probe( int unit )
+
+/* returns 0, with id set if drive is detected
+ -1, if drive detection failed
+*/
+
+{ if (PF.drive == -1) {
+ for (PF.drive=0;PF.drive<=1;PF.drive++)
+ if (!pf_reset(unit)) {
+ if (PF.lun != -1) return pf_identify(unit);
+ else for (PF.lun=0;PF.lun<8;PF.lun++)
+ if (!pf_identify(unit)) return 0;
+ }
+ } else {
+ if (pf_reset(unit)) return -1;
+ if (PF.lun != -1) return pf_identify(unit);
+ for (PF.lun=0;PF.lun<8;PF.lun++)
+ if (!pf_identify(unit)) return 0;
+ }
+ return -1;
+}
+
+static int pf_detect( void )
+
+{ 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) {
+ unit = 0;
+ if (pi_init(PI,1,-1,-1,-1,-1,-1,pf_scratch,
+ PI_PF,verbose,PF.name)) {
+ if (!pf_probe(unit)) {
+ PF.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ } else for (unit=0;unit<PF_UNITS;unit++) if (DU[D_PRT])
+ if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+ DU[D_PRO],DU[D_DLY],pf_scratch,PI_PF,verbose,
+ PF.name)) {
+ if (!pf_probe(unit)) {
+ PF.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ if (k) return 0;
+
+ printk("%s: No ATAPI disk detected\n",name);
+ return -1;
+}
+
+/* The i/o request engine */
+
+static int pf_start( int unit, int cmd, int b, int c )
+
+{ int i;
+ char io_cmd[12] = {cmd,LUN,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(unit,io_cmd,c*512,"start i/o");
+
+ udelay(1000);
+
+ return i;
+}
+
+static int pf_ready( void )
+
+{ int unit = pf_unit;
+
+ return (((RR(1,6)&(STAT_BUSY|pf_mask)) == pf_mask));
+}
+
+static void do_pf_request (void)
+
+{ struct buffer_head * bh;
+ struct request * req;
+ int unit;
+
+ if (pf_busy) return;
+repeat:
+ if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+ INIT_REQUEST;
+
+ pf_unit = unit = DEVICE_NR(CURRENT->rq_dev);
+ pf_block = CURRENT->sector;
+ pf_count = CURRENT->nr_sectors;
+
+ bh = CURRENT->bh;
+ req = CURRENT;
+ if (bh->b_reqnext)
+ printk("%s: OUCH: b_reqnext != NULL\n",PF.name);
+
+ if ((pf_unit >= PF_UNITS) || (pf_block+pf_count > PF.capacity)) {
+ end_request(0);
+ goto repeat;
+ }
+
+ pf_cmd = CURRENT->cmd;
+ pf_run = pf_count;
+ while ((pf_run <= cluster) &&
+ (req = req->next) &&
+ (pf_block+pf_run == req->sector) &&
+ (pf_cmd == req->cmd) &&
+ (pf_unit == DEVICE_NR(req->rq_dev)))
+ pf_run += req->nr_sectors;
+
+ pf_buf = CURRENT->buffer;
+ pf_retries = 0;
+
+
+ if (pf_cmd == READ) pi_do_claimed(PI,do_pf_read);
+ else if (pf_cmd == WRITE) pi_do_claimed(PI,do_pf_write);
+ else { end_request(0);
+ goto repeat;
+ }
+}
+
+static void pf_next_buf( int unit )
+
+{ cli();
+ end_request(1);
+ if (!pf_run) { sti(); return; }
+
+/* paranoia */
+
+ if ((!CURRENT) ||
+ (CURRENT->cmd != pf_cmd) ||
+ (DEVICE_NR(CURRENT->rq_dev) != pf_unit) ||
+ (CURRENT->rq_status == RQ_INACTIVE) ||
+ (CURRENT->sector != pf_block))
+ printk("%s: OUCH: request list changed unexpectedly\n",
+ PF.name);
+
+ pf_count = CURRENT->nr_sectors;
+ pf_buf = CURRENT->buffer;
+ sti();
+}
+
+static void do_pf_read( void )
+
+{ int unit = pf_unit;
+
+ pf_busy = 1;
+
+ sti();
+
+ if (pf_start(unit,ATAPI_READ_10,pf_block,pf_run)) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_read);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+ pf_mask=STAT_DRQ;
+ ps_set_intr(do_pf_read_drq,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_read_drq( void )
+
+{ int unit = pf_unit;
+
+ sti();
+ while (1) {
+ if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+ "read block","completion") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_req_sense(unit,0);
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_read);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+ pi_read_block(PI,pf_buf,512);
+ pf_count--; pf_run--;
+ pf_buf += 512;
+ pf_block++;
+ if (!pf_run) break;
+ if (!pf_count) pf_next_buf(unit);
+ }
+ pi_disconnect(PI);
+ end_request(1);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+}
+
+static void do_pf_write( void )
+
+{ int unit = pf_unit;
+
+ pf_busy = 1;
+
+ sti();
+
+ if (pf_start(unit,ATAPI_WRITE_10,pf_block,pf_run)) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_write);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+
+ while (1) {
+ if (pf_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,
+ "write block","data wait") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_write);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+ pi_write_block(PI,pf_buf,512);
+ pf_count--; pf_run--;
+ pf_buf += 512;
+ pf_block++;
+ if (!pf_run) break;
+ if (!pf_count) pf_next_buf(unit);
+ }
+ pf_mask = 0;
+ ps_set_intr(do_pf_write_done,pf_ready,PF_TMO,nice);
+}
+
+static void do_pf_write_done( void )
+
+{ int unit = pf_unit;
+
+ sti();
+ if (pf_wait(unit,STAT_BUSY,0,"write block","done") & STAT_ERR) {
+ pi_disconnect(PI);
+ if (pf_retries < PF_MAX_RETRIES) {
+ pf_retries++;
+ pi_do_claimed(PI,do_pf_write);
+ return;
+ }
+ end_request(0);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+ return;
+ }
+ pi_disconnect(PI);
+ end_request(1);
+ pf_busy = 0;
+ cli();
+ do_pf_request();
+}
+
+/* end of pf.c */
+
diff --git a/drivers/block/paride/pseudo.h b/drivers/block/paride/pseudo.h
new file mode 100644
index 000000000..44c74c7c9
--- /dev/null
+++ b/drivers/block/paride/pseudo.h
@@ -0,0 +1,138 @@
+/*
+ pseudo.h (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU 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 true, 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).
+
+*/
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+
+static void ps_timer_int( unsigned long data);
+static void ps_tq_int( void *data);
+
+static int ps_use_tq = 1;
+static void (* ps_continuation)(void);
+static int (* ps_ready)(void);
+static int ps_then;
+static int ps_timeout;
+static int ps_timer_active = 0;
+static int ps_tq_active = 0;
+
+static struct timer_list ps_timer = {0,0,0,0,ps_timer_int};
+static struct tq_struct ps_tq = {0,0,ps_tq_int,NULL};
+
+static void ps_set_intr( void (*continuation)(void),
+ int (*ready)(void),
+ int timeout, int nice )
+
+{ long flags;
+
+ save_flags(flags);
+ cli();
+
+ ps_continuation = continuation;
+ ps_ready = ready;
+ ps_then = jiffies;
+ ps_timeout = jiffies + timeout;
+ ps_use_tq = !nice;
+
+ if (ps_use_tq && !ps_tq_active) {
+#ifdef HAVE_DISABLE_HLT
+ disable_hlt();
+#endif
+ ps_tq_active = 1;
+ queue_task(&ps_tq,&tq_scheduler);
+ }
+
+ if (!ps_timer_active) {
+ ps_timer_active = 1;
+ ps_timer.expires = jiffies;
+ add_timer(&ps_timer);
+ }
+
+ restore_flags(flags);
+}
+
+static void ps_tq_int( void *data )
+
+{ void (*con)(void);
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ con = ps_continuation;
+
+#ifdef HAVE_DISABLE_HLT
+ enable_hlt();
+#endif
+
+ ps_tq_active = 0;
+
+ if (!con) {
+ restore_flags(flags);
+ return;
+ }
+ if (ps_ready() || (jiffies >= ps_timeout)) {
+ ps_continuation = NULL;
+ restore_flags(flags);
+ con();
+ return;
+ }
+
+#ifdef HAVE_DISABLE_HLT
+ disable_hlt();
+#endif
+
+ ps_tq_active = 1;
+ queue_task(&ps_tq,&tq_scheduler);
+ restore_flags(flags);
+}
+
+static void ps_timer_int( unsigned long data)
+
+{ void (*con)(void);
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ con = ps_continuation;
+ ps_timer_active = 0;
+ if (!con) {
+ restore_flags(flags);
+ return;
+ }
+ if (ps_ready() || (jiffies >= ps_timeout)) {
+ ps_continuation = NULL;
+ restore_flags(flags);
+ con();
+ return;
+ }
+ ps_timer_active = 1;
+ ps_timer.expires = jiffies;
+ add_timer(&ps_timer);
+ restore_flags(flags);
+}
+
+/* end of pseudo.h */
+
diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c
new file mode 100644
index 000000000..922e98271
--- /dev/null
+++ b/drivers/block/paride/pt.c
@@ -0,0 +1,959 @@
+/*
+ pt.c (c) 1998 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is the high-level driver for parallel port ATAPI tape
+ drives based on chips supported by the paride module.
+
+ The driver implements both rewinding and non-rewinding
+ devices, filemarks, and the rewind ioctl. It allocates
+ a small internal "bounce buffer" for each open device, but
+ otherwise expects buffering and blocking to be done at the
+ user level. As with most block-structured tapes, short
+ writes are padded to full tape blocks, so reading back a file
+ may return more data than was actually written.
+
+ By default, the driver will autoprobe for a single parallel
+ port ATAPI tape drive, but if their individual parameters are
+ specified, the driver can handle up to 4 drives.
+
+ The rewinding devices are named /dev/pt0, /dev/pt1, ...
+ while the non-rewinding devices are /dev/npt0, /dev/npt1, etc.
+
+ The behaviour of the pt 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 (96) 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 "pt").
+
+ verbose This parameter controls the amount of logging
+ that is done while the driver probes for
+ devices. Set it to 0 for a quiet load, or 1 to
+ see all the progress messages. (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:
+
+ pt.drive0
+ pt.drive1
+ pt.drive2
+ pt.drive3
+
+ In addition, you can use the parameter pt.disable to disable
+ the driver entirely.
+
+*/
+
+#define PT_VERSION "1.0"
+#define PT_MAJOR 96
+#define PT_NAME "pt"
+#define PT_UNITS 4
+
+/* Here are things one can override from the insmod command.
+ Most are autoprobed by paride unless set here. Verbose is on
+ by default.
+
+*/
+
+static int verbose = 0;
+static int major = PT_MAJOR;
+static char *name = PT_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 pt_drive_count;
+
+#define D_PRT 0
+#define D_PRO 1
+#define D_UNI 2
+#define D_MOD 3
+#define D_SLV 4
+#define D_DLY 5
+
+#define DU (*drives[unit])
+
+/* end of parameters */
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/mtio.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MODULE
+
+#include "setup.h"
+
+static STT pt_stt[5] = {{"drive0",6,drive0},
+ {"drive1",6,drive1},
+ {"drive2",6,drive2},
+ {"drive3",6,drive3},
+ {"disable",1,&disable}};
+
+void pt_setup( char *str, int *ints)
+
+{ generic_setup(pt_stt,5,str);
+}
+
+#endif
+
+MODULE_PARM(verbose,"i");
+MODULE_PARM(major,"i");
+MODULE_PARM(name,"s");
+MODULE_PARM(drive0,"1-6i");
+MODULE_PARM(drive1,"1-6i");
+MODULE_PARM(drive2,"1-6i");
+MODULE_PARM(drive3,"1-6i");
+
+#include "paride.h"
+
+#define PT_MAX_RETRIES 5
+#define PT_TMO 800 /* interrupt timeout in jiffies */
+#define PT_SPIN_DEL 50 /* spin delay in micro-seconds */
+#define PT_RESET_TMO 30 /* 3 seconds */
+#define PT_READY_TMO 60 /* 60 seconds */
+#define PT_REWIND_TMO 1200 /* 20 minutes */
+
+#define PT_SPIN (10000/PT_SPIN_DEL)*PT_TMO
+
+#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 STAT_SENSE 0x1f000
+
+#define ATAPI_TEST_READY 0x00
+#define ATAPI_REWIND 0x01
+#define ATAPI_REQ_SENSE 0x03
+#define ATAPI_READ_6 0x08
+#define ATAPI_WRITE_6 0x0a
+#define ATAPI_WFM 0x10
+#define ATAPI_IDENTIFY 0x12
+#define ATAPI_MODE_SENSE 0x1a
+#define ATAPI_LOG_SENSE 0x4d
+
+int pt_init(void);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+
+static int pt_open(struct inode *inode, struct file *file);
+static int pt_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int pt_release (struct inode *inode, struct file *file);
+static ssize_t pt_read(struct file * filp, char * buf,
+ size_t count, loff_t *ppos);
+static ssize_t pt_write(struct file * filp, const char * buf,
+ size_t count, loff_t *ppos);
+static int pt_detect(void);
+
+static int pt_identify (int unit);
+
+/* bits in PT.flags */
+
+#define PT_MEDIA 1
+#define PT_WRITE_OK 2
+#define PT_REWIND 4
+#define PT_WRITING 8
+#define PT_READING 16
+#define PT_EOF 32
+
+#define PT_NAMELEN 8
+#define PT_BUFSIZE 16384
+
+struct pt_unit {
+ struct pi_adapter pia; /* interface to paride layer */
+ struct pi_adapter *pi;
+ int flags; /* various state flags */
+ int last_sense; /* result of last request sense */
+ int drive; /* drive */
+ int access; /* count of active opens ... */
+ int bs; /* block size */
+ int capacity; /* Size of tape in KB */
+ int present; /* device present ? */
+ char *bufptr;
+ char name[PT_NAMELEN]; /* pf0, pf1, ... */
+ };
+
+struct pt_unit pt[PT_UNITS];
+
+/* 'unit' must be defined in all functions - either as a local or a param */
+
+#define PT pt[unit]
+#define PI PT.pi
+
+static char pt_scratch[512]; /* scratch block buffer */
+
+/* kernel glue structures */
+
+static struct file_operations pt_fops = {
+ NULL, /* lseek - default */
+ pt_read, /* read */
+ pt_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ pt_ioctl, /* ioctl */
+ NULL, /* mmap */
+ pt_open, /* open */
+ pt_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* media change ? */
+ NULL /* revalidate new media */
+};
+
+void pt_init_units( void )
+
+{ int unit, j;
+
+ pt_drive_count = 0;
+ for (unit=0;unit<PT_UNITS;unit++) {
+ PT.pi = & PT.pia;
+ PT.access = 0;
+ PT.flags = 0;
+ PT.last_sense = 0;
+ PT.present = 0;
+ PT.bufptr = NULL;
+ PT.drive = DU[D_SLV];
+ j = 0;
+ while ((j < PT_NAMELEN-2) && (PT.name[j]=name[j])) j++;
+ PT.name[j++] = '0' + unit;
+ PT.name[j] = 0;
+ if (DU[D_PRT]) pt_drive_count++;
+ }
+}
+
+int pt_init (void) /* preliminary initialisation */
+
+{ int unit;
+
+ if (disable) return -1;
+
+ pt_init_units();
+
+ if (pt_detect()) return -1;
+
+ if (register_chrdev(major,name,&pt_fops)) {
+ printk("pt_init: unable to get major number %d\n",
+ major);
+ for (unit=0;unit<PT_UNITS;unit++)
+ if (PT.present) pi_release(PI);
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void cleanup_module(void);
+
+int init_module(void)
+
+{ int err;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ err = pt_init();
+
+ restore_flags(flags);
+ return err;
+}
+
+void cleanup_module(void)
+
+{ long flags;
+ int unit;
+
+ save_flags(flags);
+ cli();
+
+ unregister_chrdev(major,name);
+
+ for (unit=0;unit<PT_UNITS;unit++)
+ if (PT.present) pi_release(PI);
+
+ restore_flags(flags);
+}
+
+#endif
+
+#define WR(c,r,v) pi_write_regr(PI,c,r,v)
+#define RR(c,r) (pi_read_regr(PI,c,r))
+
+#define DRIVE (0xa0+0x10*PT.drive)
+
+static int pt_wait( int unit, int go, int stop, char * fun, char * msg )
+
+{ int j, r, e, s, p;
+
+ j = 0;
+ while ((((r=RR(1,6))&go)||(stop&&(!(r&stop))))&&(j++<PT_SPIN))
+ udelay(PT_SPIN_DEL);
+
+ if ((r&(STAT_ERR&stop))||(j>=PT_SPIN)) {
+ s = RR(0,7);
+ e = RR(0,1);
+ p = RR(0,2);
+ if (j >= PT_SPIN) e |= 0x100;
+ if (fun) printk("%s: %s %s: alt=0x%x stat=0x%x err=0x%x"
+ " loop=%d phase=%d\n",
+ PT.name,fun,msg,r,s,e,j,p);
+ return (e<<8)+s;
+ }
+ return 0;
+}
+
+static int pt_command( int unit, char * cmd, int dlen, char * fun )
+
+{ pi_connect(PI);
+
+ WR(0,6,DRIVE);
+
+ if (pt_wait(unit,STAT_BUSY|STAT_DRQ,0,fun,"before command")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ WR(0,4,dlen % 256);
+ WR(0,5,dlen / 256);
+ WR(0,7,0xa0); /* ATAPI packet command */
+
+ if (pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR,fun,"command DRQ")) {
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ if (RR(0,2) != 1) {
+ printk("%s: %s: command phase error\n",PT.name,fun);
+ pi_disconnect(PI);
+ return -1;
+ }
+
+ pi_write_block(PI,cmd,12);
+
+ return 0;
+}
+
+static int pt_completion( int unit, char * buf, char * fun )
+
+{ int r, s, n, p;
+
+ r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_READY|STAT_ERR,
+ fun,"completion");
+
+ if (RR(0,7)&STAT_DRQ) {
+ n = (RR(0,4)+256*RR(0,5));
+ p = RR(0,2)&3;
+ if (p == 0) pi_write_block(PI,buf,n);
+ if (p == 2) pi_read_block(PI,buf,n);
+ }
+
+ s = pt_wait(unit,STAT_BUSY,STAT_READY|STAT_ERR,fun,"data done");
+
+ pi_disconnect(PI);
+
+ return (r?r:s);
+}
+
+static void pt_req_sense( int unit, int quiet )
+
+{ char rs_cmd[12] = { ATAPI_REQ_SENSE,0,0,0,16,0,0,0,0,0,0,0 };
+ char buf[16];
+ int r;
+
+ r = pt_command(unit,rs_cmd,16,"Request sense");
+ udelay(1000);
+ if (!r) pt_completion(unit,buf,"Request sense");
+
+ PT.last_sense = -1;
+ if (!r) {
+ if (!quiet) printk("%s: Sense key: %x, ASC: %x, ASQ: %x\n",
+ PT.name,buf[2]&0xf,buf[12],buf[13]);
+ PT.last_sense = (buf[2]&0xf) | ((buf[12]&0xff)<<8)
+ | ((buf[13]&0xff)<<16) ;
+ }
+}
+
+static int pt_atapi( int unit, char * cmd, int dlen, char * buf, char * fun )
+
+{ int r;
+
+ r = pt_command(unit,cmd,dlen,fun);
+ udelay(1000);
+ if (!r) r = pt_completion(unit,buf,fun);
+ if (r) pt_req_sense(unit,!fun);
+
+ return r;
+}
+
+static void pt_sleep( int cs )
+
+{ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + cs;
+ schedule();
+}
+
+static int pt_poll_dsc( int unit, int pause, int tmo, char *msg )
+
+{ int k, e, s;
+
+ k = 0;
+ while (k < tmo) {
+ pt_sleep(pause);
+ k++;
+ pi_connect(PI);
+ WR(0,6,DRIVE);
+ s = RR(0,7);
+ e = RR(0,1);
+ pi_disconnect(PI);
+ if (s & (STAT_ERR|STAT_SEEK)) break;
+ }
+ if ((k >= tmo) || (s & STAT_ERR)) {
+ if (k >= tmo) printk("%s: %s DSC timeout\n",PT.name,msg);
+ else printk("%s: %s stat=0x%x err=0x%x\n",PT.name,msg,s,e);
+ pt_req_sense(unit,0);
+ return 0;
+ }
+ return 1;
+}
+
+static void pt_media_access_cmd( int unit, int tmo, char *cmd, char *fun)
+
+{ if (pt_command(unit,cmd,0,fun)) {
+ pt_req_sense(unit,0);
+ return;
+ }
+ pi_disconnect(PI);
+ pt_poll_dsc(unit,100,tmo,fun);
+}
+
+static void pt_rewind( int unit )
+
+{ char rw_cmd[12] = {ATAPI_REWIND,0,0,0,0,0,0,0,0,0,0,0};
+
+ pt_media_access_cmd(unit,PT_REWIND_TMO,rw_cmd,"rewind");
+}
+
+static void pt_write_fm( int unit )
+
+{ char wm_cmd[12] = {ATAPI_WFM,0,0,0,1,0,0,0,0,0,0,0};
+
+ pt_media_access_cmd(unit,PT_TMO,wm_cmd,"write filemark");
+}
+
+#define DBMSG(msg) NULL
+
+static int pt_reset( int unit )
+
+{ int i, k, flg;
+ int expect[5] = {1,1,1,0x14,0xeb};
+ long flags;
+
+ pi_connect(PI);
+ WR(0,6,DRIVE);
+ WR(0,7,8);
+
+ save_flags(flags);
+ sti();
+
+ pt_sleep(2);
+
+ k = 0;
+ while ((k++ < PT_RESET_TMO) && (RR(1,6)&STAT_BUSY))
+ pt_sleep(10);
+
+ restore_flags(flags);
+
+ flg = 1;
+ for(i=0;i<5;i++) flg &= (RR(0,i+1) == expect[i]);
+
+ if (verbose) {
+ printk("%s: Reset (%d) signature = ",PT.name,k);
+ for (i=0;i<5;i++) printk("%3x",RR(0,i+1));
+ if (!flg) printk(" (incorrect)");
+ printk("\n");
+ }
+
+ pi_disconnect(PI);
+ return flg-1;
+}
+
+static int pt_ready_wait( int unit, int tmo )
+
+{ char tr_cmd[12] = {ATAPI_TEST_READY,0,0,0,0,0,0,0,0,0,0,0};
+ int k, p;
+
+ k = 0;
+ while (k < tmo) {
+ PT.last_sense = 0;
+ pt_atapi(unit,tr_cmd,0,NULL,DBMSG("test unit ready"));
+ p = PT.last_sense;
+ if (!p) return 0;
+ if (!((p == 0x010402)||((p & 0xff) == 6))) return p;
+ k++;
+ pt_sleep(100);
+ }
+ return 0x000020; /* timeout */
+}
+
+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 xn( char *buf, int offs, int size )
+
+{ int v,k;
+
+ v=0;
+ for(k=0;k<size;k++) v=v*256+(buf[k+offs]&0xff);
+ return v;
+}
+
+static int pt_identify( int unit )
+
+{ int dt, 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 ms_cmd[12] = { ATAPI_MODE_SENSE,0,0x2a,0,128,0,0,0,0,0,0,0};
+ char ls_cmd[12] = { ATAPI_LOG_SENSE,0,0x71,0,0,0,0,0,128,0,0,0};
+ char buf[36];
+
+ s = pt_atapi(unit,id_cmd,36,buf,"identify");
+ if (s) return -1;
+
+ dt = buf[0] & 0x1f;
+ if (dt != 1) {
+ if (verbose)
+ printk("%s: Drive %d, unsupported type %d\n",
+ PT.name,PT.drive,dt);
+ return -1;
+ }
+
+ xs(buf,mf,8,8);
+ xs(buf,id,16,16);
+
+ PT.flags = 0;
+ PT.capacity = 0;
+ PT.bs = 0;
+
+ if (!pt_ready_wait(unit,PT_READY_TMO)) PT.flags |= PT_MEDIA;
+
+ if (!pt_atapi(unit,ms_cmd,36,buf,"mode sense")) {
+ if (!(buf[2] & 0x80)) PT.flags |= PT_WRITE_OK;
+ PT.bs = xn(buf,10,2);
+ }
+
+ if (!pt_atapi(unit,ls_cmd,36,buf,"log sense"))
+ PT.capacity = xn(buf,24,4);
+
+ printk("%s: %s %s, %s",
+ PT.name,mf,id,ms[PT.drive]);
+ if (!(PT.flags & PT_MEDIA))
+ printk(", no media\n");
+ else { if (!(PT.flags & PT_WRITE_OK)) printk(", RO");
+ printk(", blocksize %d, %d MB\n",
+ PT.bs,PT.capacity/1024);
+ }
+
+ return 0;
+}
+
+static int pt_probe( int unit )
+
+/* returns 0, with id set if drive is detected
+ -1, if drive detection failed
+*/
+
+{ if (PT.drive == -1) {
+ for (PT.drive=0;PT.drive<=1;PT.drive++)
+ if (!pt_reset(unit)) return pt_identify(unit);
+ } else {
+ if (!pt_reset(unit)) return pt_identify(unit);
+ }
+ return -1;
+}
+
+static int pt_detect( void )
+
+{ int k, unit;
+
+ printk("%s: %s version %s, major %d\n",
+ name,name,PT_VERSION,major);
+
+ k = 0;
+ if (pt_drive_count == 0) {
+ unit = 0;
+ if (pi_init(PI,1,-1,-1,-1,-1,-1,pt_scratch,
+ PI_PT,verbose,PT.name)) {
+ if (!pt_probe(unit)) {
+ PT.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ } else for (unit=0;unit<PT_UNITS;unit++) if (DU[D_PRT])
+ if (pi_init(PI,0,DU[D_PRT],DU[D_MOD],DU[D_UNI],
+ DU[D_PRO],DU[D_DLY],pt_scratch,PI_PT,verbose,
+ PT.name)) {
+ if (!pt_probe(unit)) {
+ PT.present = 1;
+ k++;
+ } else pi_release(PI);
+ }
+
+ if (k) return 0;
+
+ printk("%s: No ATAPI tape drive detected\n",name);
+ return -1;
+}
+
+#define DEVICE_NR(dev) (MINOR(dev) % 128)
+
+static int pt_open (struct inode *inode, struct file *file)
+
+{ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PT_UNITS) || (!PT.present)) return -ENODEV;
+
+ PT.access++;
+
+ if (PT.access > 1) {
+ PT.access--;
+ return -EBUSY;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ pt_identify(unit);
+
+ if (!PT.flags & PT_MEDIA) {
+ PT.access--;
+ MOD_DEC_USE_COUNT;
+ return -ENODEV;
+ }
+
+ if ((!PT.flags & PT_WRITE_OK) && (file ->f_mode & 2)) {
+ PT.access--;
+ MOD_DEC_USE_COUNT;
+ return -EROFS;
+ }
+
+ if (!(MINOR(inode->i_rdev) & 128))
+ PT.flags |= PT_REWIND;
+
+ PT.bufptr = kmalloc(PT_BUFSIZE,GFP_KERNEL);
+ if (PT.bufptr == NULL) {
+ PT.access--;
+ MOD_DEC_USE_COUNT;
+ printk("%s: buffer allocation failed\n",PT.name);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int pt_ioctl(struct inode *inode,struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int unit;
+ struct mtop mtop;
+
+ if (!inode || !inode->i_rdev)
+ return -EINVAL;
+ unit = DEVICE_NR(inode->i_rdev);
+ if (unit >= PT_UNITS)
+ return -EINVAL;
+ if (!PT.present)
+ return -ENODEV;
+
+ switch (cmd) {
+ case MTIOCTOP:
+ if (copy_from_user((char *)&mtop, (char *)arg,
+ sizeof(struct mtop))) return -EFAULT;
+
+ switch (mtop.mt_op) {
+
+ case MTREW:
+ pt_rewind(unit);
+ return 0;
+
+ default:
+ printk("%s: Unimplemented mt_op %d\n",PT.name,
+ mtop.mt_op);
+ return -EINVAL;
+ }
+
+ default:
+ printk("%s: Unimplemented ioctl 0x%x\n",PT.name,cmd);
+ return -EINVAL;
+
+ }
+}
+
+
+static int pt_release (struct inode *inode, struct file *file)
+{
+ int unit = DEVICE_NR(inode->i_rdev);
+
+ if ((unit >= PT_UNITS) || (PT.access <= 0))
+ return -EINVAL;
+
+ if (PT.flags & PT_WRITING) pt_write_fm(unit);
+
+ if (PT.flags & PT_REWIND) pt_rewind(unit);
+
+ PT.access--;
+
+ kfree(PT.bufptr);
+ PT.bufptr = NULL;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+}
+
+static ssize_t pt_read(struct file * filp, char * buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *ino = filp->f_dentry->d_inode;
+ int unit = DEVICE_NR(ino->i_rdev);
+ char rd_cmd[12] = {ATAPI_READ_6,1,0,0,0,0,0,0,0,0,0,0};
+ int k, n, r, p, s, t, b;
+
+ if (!(PT.flags & (PT_READING|PT_WRITING))) {
+ PT.flags |= PT_READING;
+ if (pt_atapi(unit,rd_cmd,0,NULL,"start read-ahead"))
+ return -EIO;
+ } else if (PT.flags & PT_WRITING) return -EIO;
+
+ if (PT.flags & PT_EOF) return 0;
+
+ t = 0;
+
+ while (count > 0) {
+
+ if (!pt_poll_dsc(unit,1,PT_TMO,"read")) return -EIO;
+
+ n = count;
+ if (n > 32768) n = 32768; /* max per command */
+ b = (n-1+PT.bs)/PT.bs;
+ n = b*PT.bs; /* rounded up to even block */
+
+ rd_cmd[4] = b;
+
+ r = pt_command(unit,rd_cmd,n,"read");
+
+ udelay(1000);
+
+ if (r) {
+ pt_req_sense(unit,0);
+ return -EIO;
+ }
+
+ while (1) {
+
+ r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY,
+ DBMSG("read DRQ"),"");
+
+ if (r & STAT_SENSE) {
+ pi_disconnect(PI);
+ pt_req_sense(unit,0);
+ return -EIO;
+ }
+
+ if (r) PT.flags |= PT_EOF;
+
+ s = RR(0,7);
+
+ if (!(s & STAT_DRQ)) break;
+
+ n = (RR(0,4)+256*RR(0,5));
+ p = (RR(0,2)&3);
+ if (p != 2) {
+ pi_disconnect(PI);
+ printk("%s: Phase error on read: %d\n",PT.name,p);
+ return -EIO;
+ }
+
+ while (n > 0) {
+ k = n;
+ if (k > PT_BUFSIZE) k = PT_BUFSIZE;
+ pi_read_block(PI,PT.bufptr,k);
+ n -= k;
+ b = k;
+ if (b > count) b = count;
+ copy_to_user(buf+t,PT.bufptr,b);
+ t += b;
+ count -= b;
+ }
+
+ }
+ pi_disconnect(PI);
+ if (PT.flags & PT_EOF) break;
+ }
+
+ return t;
+
+}
+
+static ssize_t pt_write(struct file * filp, const char * buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *ino = filp->f_dentry->d_inode;
+ int unit = DEVICE_NR(ino->i_rdev);
+ char wr_cmd[12] = {ATAPI_WRITE_6,1,0,0,0,0,0,0,0,0,0,0};
+ int k, n, r, p, s, t, b;
+
+ if (!(PT.flags & PT_WRITE_OK)) return -EROFS;
+
+ if (!(PT.flags & (PT_READING|PT_WRITING))) {
+ PT.flags |= PT_WRITING;
+ if (pt_atapi(unit,wr_cmd,0,NULL,"start buffer-available mode"))
+ return -EIO;
+ } else if (PT.flags&PT_READING) return -EIO;
+
+ if (PT.flags & PT_EOF) return -ENOSPC;
+
+ t = 0;
+
+ while (count > 0) {
+
+ if (!pt_poll_dsc(unit,1,PT_TMO,"write")) return -EIO;
+
+ n = count;
+ if (n > 32768) n = 32768; /* max per command */
+ b = (n-1+PT.bs)/PT.bs;
+ n = b*PT.bs; /* rounded up to even block */
+
+ wr_cmd[4] = b;
+
+ r = pt_command(unit,wr_cmd,n,"write");
+
+ udelay(1000);
+
+ if (r) { /* error delivering command only */
+ pt_req_sense(unit,0);
+ return -EIO;
+ }
+
+ while (1) {
+
+ r = pt_wait(unit,STAT_BUSY,STAT_DRQ|STAT_ERR|STAT_READY,
+ DBMSG("write DRQ"),NULL);
+
+ if (r & STAT_SENSE) {
+ pi_disconnect(PI);
+ pt_req_sense(unit,0);
+ return -EIO;
+ }
+
+ if (r) PT.flags |= PT_EOF;
+
+ s = RR(0,7);
+
+ if (!(s & STAT_DRQ)) break;
+
+ n = (RR(0,4)+256*RR(0,5));
+ p = (RR(0,2)&3);
+ if (p != 0) {
+ pi_disconnect(PI);
+ printk("%s: Phase error on write: %d \n",PT.name,p);
+ return -EIO;
+ }
+
+ while (n > 0) {
+ k = n;
+ if (k > PT_BUFSIZE) k = PT_BUFSIZE;
+ b = k;
+ if (b > count) b = count;
+ copy_from_user(PT.bufptr,buf+t,b);
+ pi_write_block(PI,PT.bufptr,k);
+ t += b;
+ count -= b;
+ n -= k;
+ }
+
+ }
+ pi_disconnect(PI);
+ if (PT.flags & PT_EOF) break;
+ }
+
+ return t;
+}
+
+/* end of pt.c */
+
diff --git a/drivers/block/paride/setup.h b/drivers/block/paride/setup.h
new file mode 100644
index 000000000..6d941cc09
--- /dev/null
+++ b/drivers/block/paride/setup.h
@@ -0,0 +1,56 @@
+/*
+ setup.h (c) 1997 Grant R. Guenther <grant@torque.net>
+ Under the terms of the GNU public license.
+
+ This is a table driven setup function for kernel modules
+ using the module.variable=val,... command line notation.
+
+*/
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+struct setup_tab_t {
+
+ char *tag; /* variable name */
+ int size; /* number of elements in array */
+ int *iv; /* pointer to variable */
+};
+
+typedef struct setup_tab_t STT;
+
+/* t is a table that describes the variables that can be set
+ by gen_setup
+ n is the number of entries in the table
+ ss is a string of the form:
+
+ <tag>=[<val>,...]<val>
+*/
+
+static void generic_setup( STT t[], int n, char *ss )
+
+{ int j,k;
+
+ k = 0;
+ for (j=0;j<n;j++) {
+ k = strlen(t[j].tag);
+ if (strncmp(ss,t[j].tag,k) == 0) break;
+ }
+ if (j == n) return;
+
+ if (ss[k] == 0) {
+ t[j].iv[0] = 1;
+ return;
+ }
+
+ if (ss[k] != '=') return;
+ ss += (k+1);
+
+ k = 0;
+ while (ss && isdigit(*ss) && (k < t[j].size)) {
+ t[j].iv[k++] = simple_strtoul(ss,NULL,0);
+ if ((ss = strchr(ss,',')) != NULL) ss++;
+ }
+}
+
+/* end of setup.h */
diff --git a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c
index b4d5146de..cf13eccaf 100644
--- a/drivers/block/ps2esdi.c
+++ b/drivers/block/ps2esdi.c
@@ -95,7 +95,7 @@ static int ps2esdi_release(struct inode *inode, struct file *file);
static int ps2esdi_ioctl(struct inode *inode, struct file *file,
u_int cmd, u_long arg);
-static int ps2esdi_reread_partitions(int dev);
+static int ps2esdi_reread_partitions(kdev_t dev);
static int ps2esdi_read_status_words(int num_words, int max_words, u_short * buffer);
@@ -1038,14 +1038,9 @@ static void dump_cmd_complete_status(u_int int_ret_code)
}
-
static int ps2esdi_open(struct inode *inode, struct file *file)
{
- int dev = DEVICE_NR(MINOR(inode->i_rdev));
-
-#if 0
- printk("%s: dev= %d\n", DEVICE_NAME, dev);
-#endif
+ int dev = DEVICE_NR(inode->i_rdev);
if (dev < ps2esdi_drives) {
while (!ps2esdi_valid[dev])
@@ -1062,10 +1057,10 @@ static int ps2esdi_open(struct inode *inode, struct file *file)
static int ps2esdi_release(struct inode *inode, struct file *file)
{
- int dev = DEVICE_NR(MINOR(inode->i_rdev));
+ int dev = DEVICE_NR(inode->i_rdev);
if (dev < ps2esdi_drives) {
- sync_dev(dev);
+ sync_dev(inode->i_rdev);
access_count[dev]--;
}
return 0;
@@ -1078,7 +1073,7 @@ static int ps2esdi_ioctl(struct inode *inode,
{
struct ps2esdi_geometry *geometry = (struct ps2esdi_geometry *) arg;
- int dev = DEVICE_NR(MINOR(inode->i_rdev)), err;
+ int dev = DEVICE_NR(inode->i_rdev), err;
if (inode && (dev < ps2esdi_drives))
switch (cmd) {
@@ -1131,9 +1126,9 @@ static int ps2esdi_ioctl(struct inode *inode,
-static int ps2esdi_reread_partitions(int dev)
+static int ps2esdi_reread_partitions(kdev_t dev)
{
- int target = DEVICE_NR(MINOR(dev));
+ int target = DEVICE_NR(dev);
int start = target << ps2esdi_gendisk.minor_shift;
int partition;
@@ -1145,15 +1140,20 @@ static int ps2esdi_reread_partitions(int dev)
for (partition = ps2esdi_gendisk.max_p - 1;
partition >= 0; partition--) {
- sync_dev(MAJOR_NR << 8 | start | partition);
- invalidate_inodes(MAJOR_NR << 8 | start | partition);
- invalidate_buffers(MAJOR_NR << 8 | start | partition);
+ int minor = (start | partition);
+ kdev_t devp = MKDEV(MAJOR_NR, minor);
+ struct super_block * sb = get_super(devp);
+
+ sync_dev(devp);
+ if (sb)
+ invalidate_inodes(sb);
+ invalidate_buffers(devp);
ps2esdi_gendisk.part[start + partition].start_sect = 0;
ps2esdi_gendisk.part[start + partition].nr_sects = 0;
- };
+ }
ps2esdi_gendisk.part[start].nr_sects = ps2esdi_info[target].head *
- ps2esdi_info[target].cyl * ps2esdi_info[target].sect;
+ ps2esdi_info[target].cyl * ps2esdi_info[target].sect;
resetup_one_dev(&ps2esdi_gendisk, target);
ps2esdi_valid[target] = 1;
diff --git a/drivers/block/raid5.c b/drivers/block/raid5.c
index 918dd2ee2..6fefac06d 100644
--- a/drivers/block/raid5.c
+++ b/drivers/block/raid5.c
@@ -1019,7 +1019,7 @@ static void handle_stripe(struct stripe_head *sh)
if (sh->bh_new[i])
continue;
block = (int) compute_blocknr(sh, i);
- bh = efind_buffer(MKDEV(MD_MAJOR, minor), block, sh->size);
+ bh = find_buffer(MKDEV(MD_MAJOR, minor), block, sh->size);
if (bh && bh->b_count == 0 && buffer_dirty(bh) && !buffer_locked(bh)) {
PRINTK(("Whee.. sector %lu, index %d (%d) found in the buffer cache!\n", sh->sector, i, block));
add_stripe_bh(sh, bh, i, WRITE);
@@ -1372,7 +1372,7 @@ static int raid5_run (int minor, struct md_dev *mddev)
memset (raid_conf, 0, sizeof (*raid_conf));
raid_conf->mddev = mddev;
- if ((raid_conf->stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER, 0)) == NULL)
+ if ((raid_conf->stripe_hashtbl = (struct stripe_head **) __get_free_pages(GFP_ATOMIC, HASH_PAGES_ORDER)) == NULL)
goto abort;
memset(raid_conf->stripe_hashtbl, 0, HASH_PAGES * PAGE_SIZE);
diff --git a/drivers/block/rz1000.c b/drivers/block/rz1000.c
index 670fe3e58..4093427f0 100644
--- a/drivers/block/rz1000.c
+++ b/drivers/block/rz1000.c
@@ -15,6 +15,7 @@
#undef REALLY_SLOW_IO /* most systems can safely undef this */
+#include <linux/config.h> /* for CONFIG_BLK_DEV_IDEPCI */
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index 6ce5c037d..58ced412f 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -741,8 +741,8 @@ static int floppy_ioctl(struct inode *inode, struct file *filp,
struct floppy_state *fs;
int err;
- if (((cmd & 0x80) && !suser())
- || ((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT))))
+ if (((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT))) ||
+ ((cmd & 0x80) && !suser()))
return -EPERM;
fs = &floppy_states[0];
diff --git a/drivers/block/trm290.c b/drivers/block/trm290.c
index f88f597d8..5d3d3f37c 100644
--- a/drivers/block/trm290.c
+++ b/drivers/block/trm290.c
@@ -220,10 +220,10 @@ __initfunc(void ide_init_trm290 (ide_hwif_t *hwif))
&& !pcibios_read_config_dword(hwif->pci_bus, hwif->pci_fn, 0x20, &cfgbase) && cfgbase)
{
hwif->config_data = cfgbase & ~1;
- printk("TRM290: chip config base at 0x%04x\n", hwif->config_data);
+ printk("TRM290: chip config base at 0x%04lx\n", hwif->config_data);
} else {
- hwif->config_data = 0x3df4;
- printk("TRM290: using default config base at 0x%04x\n", hwif->config_data);
+ hwif->config_data = 0x3df0;
+ printk("TRM290: using default config base at 0x%04lx\n", hwif->config_data);
}
save_flags(flags);
@@ -241,7 +241,7 @@ __initfunc(void ide_init_trm290 (ide_hwif_t *hwif))
hwif->irq = hwif->channel ? 15 : 14; /* legacy mode */
else if (!hwif->irq && hwif->mate && hwif->mate->irq)
hwif->irq = hwif->mate->irq; /* sharing IRQ with mate */
- ide_setup_dma(hwif, (hwif->channel ? hwif->config_data ^ 0x0080 : hwif->config_data) + 4, 2);
+ ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 2);
hwif->dmaproc = &trm290_dmaproc;
hwif->selectproc = &trm290_selectproc;
hwif->no_autodma = 1; /* play it safe for now */
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index f4ba2f26e..c71d1f8e8 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -20,13 +20,14 @@
*
* Modularized: 04/10/96 by Todd Fries, tfries@umr.edu
*
- * Revised: 13/09/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl
+ * Revised: 13/12/97 by Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl
* Fixed some problems with disk initialization and module initiation.
* Added support for manual geometry setting (except Seagate controllers)
* in form:
* xd_geo=<cyl_xda>,<head_xda>,<sec_xda>[,<cyl_xdb>,<head_xdb>,<sec_xdb>]
- * Recovered DMA access. Abridged messages. Added support for DTC5051CX &
- * WD1002-27X controllers. Added alternate jumper geometry setting.
+ * Recovered DMA access. Abridged messages. Added support for DTC5051CX,
+ * WD1002-27X & XEBEC controllers. Driver uses now some jumper settings.
+ * Extended ioctl() support.
*/
#include <linux/module.h>
@@ -38,6 +39,7 @@
#include <linux/timer.h>
#include <linux/genhd.h>
#include <linux/hdreg.h>
+#include <linux/ioport.h>
#include <linux/init.h>
#include <asm/system.h>
@@ -114,12 +116,15 @@ static XD_SIGNATURE xd_sigs[] __initdata = {
{ 0x0010,"ST11R BIOS",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11M/R" }, /* Risto Kankkunen, risto.kankkunen@cs.helsinki.fi */
{ 0x0010,"ST11 BIOS v1.7",xd_seagate_init_controller,xd_seagate_init_drive," Seagate ST11R" }, /* Alan Hourihane, alanh@fairlite.demon.co.uk */
{ 0x1000,"(c)Copyright 1987 SMS",xd_omti_init_controller,xd_omti_init_drive,"n OMTI 5520" }, /* Dirk Melchers, dirk@merlin.nbg.sub.org */
+ { 0x0006,"COPYRIGHT XEBEC (C) 1984",xd_xebec_init_controller,xd_xebec_init_drive," XEBEC" }, /* Andrzej Krzysztofowicz, ankry@mif.pg.gda.pl */
};
static unsigned int xd_bases[] __initdata =
{
0xC8000, 0xCA000, 0xCC000,
- 0xCE000, 0xD0000, 0xD8000,
+ 0xCE000, 0xD0000, 0xD2000,
+ 0xD4000, 0xD6000, 0xD8000,
+ 0xDA000, 0xDC000, 0xDE000,
0xE0000
};
@@ -219,6 +224,11 @@ __initfunc(static void xd_geninit (struct gendisk *ignored))
if (xd_detect(&controller,&address)) {
printk("Detected a%s controller (type %d) at address %06x\n",xd_sigs[controller].name,controller,address);
+ if (check_region(xd_iobase,4)) {
+ printk("xd: Ports at 0x%x are not available\n",xd_iobase);
+ return;
+ }
+ request_region(xd_iobase,4,"xd");
if (controller)
xd_sigs[controller].init_controller(address);
xd_drives = xd_initdrives(xd_sigs[controller].init_drive);
@@ -331,6 +341,8 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
if(arg > 0xff) return -EINVAL;
read_ahead[MAJOR(inode->i_rdev)] = arg;
return 0;
+ case BLKRAGET:
+ return put_user(read_ahead[MAJOR(inode->i_rdev)], (long*) arg);
case BLKGETSIZE:
if (!arg) return -EINVAL;
return put_user(xd_struct[MINOR(inode->i_rdev)].nr_sects,(long *) arg);
@@ -339,6 +351,19 @@ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg)
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
+ case HDIO_SET_DMA:
+ if (!suser()) return -EACCES;
+ if (xdc_busy) return -EBUSY;
+ nodma = !arg;
+ if (nodma && xd_dma_buffer) {
+ xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200);
+ xd_dma_buffer = 0;
+ }
+ return 0;
+ case HDIO_GET_DMA:
+ return put_user(!nodma, (long *) arg);
+ case HDIO_GET_MULTCOUNT:
+ return put_user(xd_maxsectors, (long *) arg);
case BLKRRPART:
return xd_reread_partitions(inode->i_rdev);
RO_IOCTLS(inode->i_rdev,arg);
@@ -929,6 +954,81 @@ __initfunc(static void xd_omti_init_drive (u_char drive))
xd_info[drive].control = 2;
}
+/* Xebec support (AK) */
+__initfunc(static void xd_xebec_init_controller (unsigned int address))
+{
+/* iobase may be set manually in range 0x300 - 0x33C
+ irq may be set manually to 2(9),3,4,5,6,7
+ dma may be set manually to 1,2,3
+ (How to detect them ???)
+BIOS address may be set manually in range 0x0 - 0xF8000
+If you need non-standard settings use the xd=... command */
+
+ switch (address) {
+ case 0x00000:
+ case 0xC8000: /* initially: xd_iobase==0x320 */
+ case 0xD0000:
+ case 0xD2000:
+ case 0xD4000:
+ case 0xD6000:
+ case 0xD8000:
+ case 0xDA000:
+ case 0xDC000:
+ case 0xDE000:
+ case 0xE0000: break;
+ default: printk("xd_xebec_init_controller: unsupported BIOS address %06x\n",address);
+ break;
+ }
+
+ xd_maxsectors = 0x01;
+ outb(0,XD_RESET); /* reset the controller */
+
+ xd_timer.expires = jiffies + XD_INIT_DISK_DELAY;
+ add_timer(&xd_timer);
+ sleep_on(&xdc_wait);
+}
+
+__initfunc(static void xd_xebec_init_drive (u_char drive))
+{
+ /* values from controller's BIOS - BIOS chip may be removed */
+ static u_short geometry_table[][5] = {
+ {0x132,4,0x080,0x080,0x7},
+ {0x132,4,0x080,0x080,0x17},
+ {0x264,2,0x100,0x100,0x7},
+ {0x264,2,0x100,0x100,0x17},
+ {0x132,8,0x080,0x080,0x7},
+ {0x132,8,0x080,0x080,0x17},
+ {0x264,4,0x100,0x100,0x6},
+ {0x264,4,0x100,0x100,0x17},
+ {0x2BC,5,0x2BC,0x12C,0x6},
+ {0x3A5,4,0x3A5,0x3A5,0x7},
+ {0x26C,6,0x26C,0x26C,0x7},
+ {0x200,8,0x200,0x100,0x17},
+ {0x400,5,0x400,0x400,0x7},
+ {0x400,6,0x400,0x400,0x7},
+ {0x264,8,0x264,0x200,0x17},
+ {0x33E,7,0x33E,0x200,0x7}};
+ u_char n;
+
+ n = inb(XD_JUMPER) & 0x0F; /* BIOS's drive number: same geometry
+ is assumed for BOTH drives */
+ if (xd_geo[3*drive])
+ xd_manual_geo_set(drive);
+ else {
+ xd_info[drive].heads = (u_char)(geometry_table[n][1]); /* heads */
+ xd_info[drive].cylinders = geometry_table[n][0]; /* cylinders */
+ xd_info[drive].sectors = 17; /* sectors */
+#if 0
+ xd_info[drive].rwrite = geometry_table[n][2]; /* reduced write */
+ xd_info[drive].precomp = geometry_table[n][3] /* write precomp */
+ xd_info[drive].ecc = 0x0B; /* ecc length */
+#endif /* 0 */
+ }
+ xd_info[drive].control = geometry_table[n][4]; /* control byte */
+ xd_setparam(CMD_XBSETPARAM,drive,xd_info[drive].heads,xd_info[drive].cylinders,geometry_table[n][2],geometry_table[n][3],0x0B);
+ xd_recalibrate(drive);
+}
+
/* xd_override_init_drive: this finds disk geometry in a "binary search" style, narrowing in on the "correct" number of heads
etc. by trying values until it gets the highest successful value. Idea courtesy Salvador Abreu (spa@fct.unl.pt). */
__initfunc(static void xd_override_init_drive (u_char drive))
@@ -1008,7 +1108,9 @@ __initfunc(static void xd_setparam (u_char command,u_char drive,u_char heads,u_s
cmdblk[12] = (u_char) (wprecomp & 0xFF);
cmdblk[13] = ecc;
- if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
+ /* Some controllers require geometry info as data, not command */
+
+ if (xd_command(cmdblk,PIO_MODE,0,&cmdblk[6],0,XD_TIMEOUT * 2))
printk("xd: error setting characteristics for xd%c\n", 'a'+drive);
}
@@ -1020,6 +1122,23 @@ MODULE_PARM(xd, "1-4i");
MODULE_PARM(xd_geo, "3-6i");
MODULE_PARM(nodma, "i");
+static void xd_done (void)
+{
+ struct gendisk ** gdp;
+
+ blksize_size[MAJOR_NR] = NULL;
+ blk_dev[MAJOR_NR].request_fn = NULL;
+ blk_size[MAJOR_NR] = NULL;
+ hardsect_size[MAJOR_NR] = NULL;
+ read_ahead[MAJOR_NR] = 0;
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+ if (*gdp == &xd_gendisk)
+ break;
+ if (*gdp)
+ *gdp = (*gdp)->next;
+ release_region(xd_iobase,4);
+}
+
int init_module(void)
{
int i,count = 0;
@@ -1036,6 +1155,7 @@ int init_module(void)
if (!xd_drives) {
/* no drives detected - unload module */
unregister_blkdev(MAJOR_NR, "xd");
+ xd_done();
return (-1);
}
for (i = 0; i < xd_drives; i++)
@@ -1047,7 +1167,20 @@ int init_module(void)
void cleanup_module(void)
{
+ int partition,dev,start;
+
unregister_blkdev(MAJOR_NR, "xd");
+ for (dev = 0; dev < xd_drives; dev++) {
+ start = dev << xd_gendisk.minor_shift;
+ for (partition = xd_gendisk.max_p - 1; partition >= 0; partition--) {
+ int minor = (start | partition);
+ kdev_t devp = MKDEV(MAJOR_NR, minor);
+ start = dev << xd_gendisk.minor_shift;
+ sync_dev(devp);
+ invalidate_buffers(devp);
+ }
+ }
+ xd_done();
if (xd_drives) {
free_irq(xd_irq, NULL);
free_dma(xd_dma);
diff --git a/drivers/block/xd.h b/drivers/block/xd.h
index 6fff452b7..b45fae4e7 100644
--- a/drivers/block/xd.h
+++ b/drivers/block/xd.h
@@ -46,6 +46,7 @@
#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 */
@@ -136,6 +137,8 @@ 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);