diff options
Diffstat (limited to 'drivers/block')
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(¤t->sigmask_lock); + spin_lock(¤t->sigmask_lock); flush_signals(current); - spin_unlock_irq(¤t->sigmask_lock); + spin_unlock(¤t->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); |