!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/drivers/block/   drwxr-xr-x
Free 318.37 GB of 458.09 GB (69.5%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     acsi.c (46.65 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
 * acsi.c -- Device driver for Atari ACSI hard disks
 *
 * Copyright 1994 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
 *
 * Some parts are based on hd.c by Linus Torvalds
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive for
 * more details.
 *
 */

/*
 * Still to in this file:
 *  - If a command ends with an error status (!= 0), the following
 *    REQUEST SENSE commands (4 to fill the ST-DMA FIFO) are done by
 *    polling the _IRQ signal (not interrupt-driven). This should be
 *    avoided in future because it takes up a non-neglectible time in
 *    the interrupt service routine while interrupts are disabled.
 *    Maybe a timer interrupt will get lost :-(
 */

/*
 * General notes:
 *
 *  - All ACSI devices (disks, CD-ROMs, ...) use major number 28.
 *    Minors are organized like it is with SCSI: The upper 4 bits
 *    identify the device, the lower 4 bits the partition.
 *    The device numbers (the upper 4 bits) are given in the same
 *    order as the devices are found on the bus.
 *  - Up to 8 LUNs are supported for each target (if CONFIG_ACSI_MULTI_LUN
 *    is defined), but only a total of 16 devices (due to minor
 *    numbers...). Note that Atari allows only a maximum of 4 targets
 *    (i.e. controllers, not devices) on the ACSI bus!
 *  - A optimizing scheme similar to SCSI scatter-gather is implemented.
 *  - Removable media are supported. After a medium change to device
 *    is reinitialized (partition check etc.). Also, if the device
 *    knows the PREVENT/ALLOW MEDIUM REMOVAL command, the door should
 *    be locked and unlocked when mounting the first or unmounting the
 *    last filesystem on the device. The code is untested, because I
 *    don't have a removable hard disk.
 *
 */

#define MAJOR_NR ACSI_MAJOR

#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/genhd.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/blk.h>
#include <linux/slab.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 <linux/blkpg.h>

#include <asm/setup.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/atarihw.h>
#include <asm/atariints.h>
#include <asm/atari_acsi.h>
#include <asm/atari_stdma.h>
#include <asm/atari_stram.h>


#define DEBUG
#undef DEBUG_DETECT
#undef NO_WRITE

#define MAX_ERRORS             8    /* Max read/write errors/sector */
#define MAX_LUN                8    /* Max LUNs per target */
#define MAX_DEV                   16

#define ACSI_BUFFER_SIZE            (16*1024) /* "normal" ACSI buffer size */
#define ACSI_BUFFER_MINSIZE            (2048)       /* min. buf size if ext. DMA */
#define ACSI_BUFFER_SIZE_ORDER         2          /* order size for above */
#define ACSI_BUFFER_MINSIZE_ORDER    0             /* order size for above */
#define ACSI_BUFFER_SECTORS    (ACSI_BUFFER_SIZE/512)

#define ACSI_BUFFER_ORDER \
    (ATARIHW_PRESENT(EXTD_DMA) ? \
     ACSI_BUFFER_MINSIZE_ORDER : \
     ACSI_BUFFER_SIZE_ORDER)

#define ACSI_TIMEOUT        (4*HZ)

/* minimum delay between two commands */

#define COMMAND_DELAY 500

typedef enum {
    NONE, HARDDISK, CDROM
} ACSI_TYPE;

struct acsi_info_struct {
    ACSI_TYPE        type;            /* type of device */
    unsigned        target;            /* target number */
    unsigned        lun;            /* LUN in target controller */
    unsigned        removable : 1;    /* Flag for removable media */
    unsigned        read_only : 1;    /* Flag for read only devices */
    unsigned        old_atari_disk : 1; /* Is an old Atari disk       */
    unsigned        changed : 1;    /* Medium has been changed */
    unsigned long     size;            /* #blocks */
} acsi_info[MAX_DEV];

/*
 *    SENSE KEYS
 */

#define NO_SENSE        0x00
#define RECOVERED_ERROR     0x01
#define NOT_READY        0x02
#define MEDIUM_ERROR        0x03
#define HARDWARE_ERROR        0x04
#define ILLEGAL_REQUEST     0x05
#define UNIT_ATTENTION        0x06
#define DATA_PROTECT        0x07
#define BLANK_CHECK        0x08
#define COPY_ABORTED        0x0a
#define ABORTED_COMMAND     0x0b
#define VOLUME_OVERFLOW     0x0d
#define MISCOMPARE        0x0e


/*
 *    DEVICE TYPES
 */

#define TYPE_DISK    0x00
#define TYPE_TAPE    0x01
#define TYPE_WORM    0x04
#define TYPE_ROM    0x05
#define TYPE_MOD    0x07
#define TYPE_NO_LUN    0x7f

/* The data returned by MODE SENSE differ between the old Atari
 * hard disks and SCSI disks connected to ACSI. In the following, both
 * formats are defined and some macros to operate on them potably.
 */

typedef struct {
    unsigned long    dummy[2];
    unsigned long    sector_size;
    unsigned char    format_code;
#define ATARI_SENSE_FORMAT_FIX    1    
#define ATARI_SENSE_FORMAT_CHNG    2
    unsigned char    cylinders_h;
    unsigned char    cylinders_l;
    unsigned char    heads;
    unsigned char    reduced_h;
    unsigned char    reduced_l;
    unsigned char    precomp_h;
    unsigned char    precomp_l;
    unsigned char    landing_zone;
    unsigned char    steprate;
    unsigned char    type;
#define ATARI_SENSE_TYPE_FIXCHNG_MASK        4
#define ATARI_SENSE_TYPE_SOFTHARD_MASK        8
#define ATARI_SENSE_TYPE_FIX                4
#define ATARI_SENSE_TYPE_CHNG                0
#define ATARI_SENSE_TYPE_SOFT                0
#define ATARI_SENSE_TYPE_HARD                8
    unsigned char    sectors;
} ATARI_SENSE_DATA;

#define ATARI_CAPACITY(sd) \
    (((int)((sd).cylinders_h<<8)|(sd).cylinders_l) * \
     (sd).heads * (sd).sectors)


typedef struct {
    unsigned char   dummy1;
    unsigned char   medium_type;
    unsigned char   dummy2;
    unsigned char   descriptor_size;
    unsigned long   block_count;
    unsigned long   sector_size;
    /* Page 0 data */
    unsigned char    page_code;
    unsigned char    page_size;
    unsigned char    page_flags;
    unsigned char    qualifier;
} SCSI_SENSE_DATA;

#define SCSI_CAPACITY(sd)     ((sd).block_count & 0xffffff)


typedef union {
    ATARI_SENSE_DATA    atari;
    SCSI_SENSE_DATA        scsi;
} SENSE_DATA;

#define SENSE_TYPE_UNKNOWN    0
#define SENSE_TYPE_ATARI    1
#define SENSE_TYPE_SCSI        2

#define SENSE_TYPE(sd)                                        \
    (((sd).atari.dummy[0] == 8 &&                            \
      ((sd).atari.format_code == 1 ||                        \
       (sd).atari.format_code == 2)) ? SENSE_TYPE_ATARI :    \
     ((sd).scsi.dummy1 >= 11) ? SENSE_TYPE_SCSI :            \
     SENSE_TYPE_UNKNOWN)
     
#define CAPACITY(sd)                            \
    (SENSE_TYPE(sd) == SENSE_TYPE_ATARI ?        \
     ATARI_CAPACITY((sd).atari) :                \
     SCSI_CAPACITY((sd).scsi))

#define SECTOR_SIZE(sd)                            \
    (SENSE_TYPE(sd) == SENSE_TYPE_ATARI ?        \
     (sd).atari.sector_size :                    \
     (sd).scsi.sector_size & 0xffffff)

/* Default size if capacity cannot be determined (1 GByte) */
#define    DEFAULT_SIZE    0x1fffff

#define CARTRCH_STAT(dev,buf)                    \
    (acsi_info[(dev)].old_atari_disk ?            \
     (((buf)[0] & 0x7f) == 0x28) :                    \
     ((((buf)[0] & 0x70) == 0x70) ?                    \
      (((buf)[2] & 0x0f) == 0x06) :                    \
      (((buf)[0] & 0x0f) == 0x06)))                    \

/* These two are also exported to other drivers that work on the ACSI bus and
 * need an ST-RAM buffer. */
char             *acsi_buffer;
unsigned long     phys_acsi_buffer;

static int                NDevices = 0;
static int                acsi_sizes[MAX_DEV<<4] = { 0, };
static int                acsi_blocksizes[MAX_DEV<<4] = { 0, };
static struct hd_struct    acsi_part[MAX_DEV<<4] = { {0,0}, };
static int                 access_count[MAX_DEV] = { 0, };
static char             busy[MAX_DEV] = { 0, };
static DECLARE_WAIT_QUEUE_HEAD(busy_wait);

static int                CurrentNReq;
static int                CurrentNSect;
static char                *CurrentBuffer;


#define SET_TIMER()    mod_timer(&acsi_timer, jiffies + ACSI_TIMEOUT)
#define CLEAR_TIMER()    del_timer(&acsi_timer)

static unsigned long    STramMask;
#define STRAM_ADDR(a)    (((a) & STramMask) == 0)



/* ACSI commands */

static char tur_cmd[6]        = { 0x00, 0, 0, 0, 0, 0 };
static char modesense_cmd[6]  = { 0x1a, 0, 0, 0, 24, 0 };
static char modeselect_cmd[6] = { 0x15, 0, 0, 0, 12, 0 };
static char inquiry_cmd[6]    = { 0x12, 0, 0, 0,255, 0 };
static char reqsense_cmd[6]   = { 0x03, 0, 0, 0, 4, 0 };
static char read_cmd[6]       = { 0x08, 0, 0, 0, 0, 0 };
static char write_cmd[6]      = { 0x0a, 0, 0, 0, 0, 0 };
static char pa_med_rem_cmd[6] = { 0x1e, 0, 0, 0, 0, 0 };

#define CMDSET_TARG_LUN(cmd,targ,lun)            \
    do {                        \
        cmd[0] = (cmd[0] & ~0xe0) | (targ)<<5;    \
        cmd[1] = (cmd[1] & ~0xe0) | (lun)<<5;    \
    } while(0)

#define CMDSET_BLOCK(cmd,blk)                        \
    do {                                            \
        unsigned long __blk = (blk);                \
        cmd[3] = __blk; __blk >>= 8;                \
        cmd[2] = __blk; __blk >>= 8;                \
        cmd[1] = (cmd[1] & 0xe0) | (__blk & 0x1f);    \
    } while(0)

#define CMDSET_LEN(cmd,len)                        \
    do {                                        \
        cmd[4] = (len);                            \
    } while(0)

/* ACSI errors (from REQUEST SENSE); There are two tables, one for the
 * old Atari disks and one for SCSI on ACSI disks.
 */

struct acsi_error {
    unsigned char    code;
    const char        *text;
} atari_acsi_errors[] = {
    { 0x00, "No error (??)" },
    { 0x01, "No index pulses" },
    { 0x02, "Seek not complete" },
    { 0x03, "Write fault" },
    { 0x04, "Drive not ready" },
    { 0x06, "No Track 00 signal" },
    { 0x10, "ECC error in ID field" },
    { 0x11, "Uncorrectable data error" },
    { 0x12, "ID field address mark not found" },
    { 0x13, "Data field address mark not found" },
    { 0x14, "Record not found" },
    { 0x15, "Seek error" },
    { 0x18, "Data check in no retry mode" },
    { 0x19, "ECC error during verify" },
    { 0x1a, "Access to bad block" },
    { 0x1c, "Unformatted or bad format" },
    { 0x20, "Invalid command" },
    { 0x21, "Invalid block address" },
    { 0x23, "Volume overflow" },
    { 0x24, "Invalid argument" },
    { 0x25, "Invalid drive number" },
    { 0x26, "Byte zero parity check" },
    { 0x28, "Cartride changed" },
    { 0x2c, "Error count overflow" },
    { 0x30, "Controller selftest failed" }
},

    scsi_acsi_errors[] = {
    { 0x00, "No error (??)" },
    { 0x01, "Recovered error" },
    { 0x02, "Drive not ready" },
    { 0x03, "Uncorrectable medium error" },
    { 0x04, "Hardware error" },
    { 0x05, "Illegal request" },
    { 0x06, "Unit attention (Reset or cartridge changed)" },
    { 0x07, "Data protection" },
    { 0x08, "Blank check" },
    { 0x0b, "Aborted Command" },
    { 0x0d, "Volume overflow" }
};



/***************************** Prototypes *****************************/

static int acsicmd_dma( const char *cmd, char *buffer, int blocks, int
                        rwflag, int enable);
static int acsi_reqsense( char *buffer, int targ, int lun);
static void acsi_print_error( const unsigned char *errblk, int dev );
static void acsi_interrupt (int irq, void *data, struct pt_regs *fp);
static void unexpected_acsi_interrupt( void );
static void bad_rw_intr( void );
static void read_intr( void );
static void write_intr( void);
static void acsi_times_out( unsigned long dummy );
static void copy_to_acsibuffer( void );
static void copy_from_acsibuffer( void );
static void do_end_requests( void );
static void do_acsi_request( request_queue_t * );
static void redo_acsi_request( void );
static int acsi_ioctl( struct inode *inode, struct file *file, unsigned int
                       cmd, unsigned long arg );
static int acsi_open( struct inode * inode, struct file * filp );
static int acsi_release( struct inode * inode, struct file * file );
static void acsi_prevent_removal( int target, int flag );
static int acsi_change_blk_size( int target, int lun);
static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd );
static void acsi_geninit(void);
static int revalidate_acsidisk( int dev, int maxusage );
static int acsi_revalidate (dev_t);

/************************* End of Prototypes **************************/


struct timer_list acsi_timer = { NULL, NULL, 0, 0, acsi_times_out };


#ifdef CONFIG_ATARI_SLM

extern int attach_slm( int target, int lun );
extern int slm_init( void );

#endif



/***********************************************************************
 *
 *   ACSI primitives
 *
 **********************************************************************/


/*
 * The following two functions wait for _IRQ to become Low or High,
 * resp., with a timeout. The 'timeout' parameter is in jiffies
 * (10ms).
 * If the functions are called with timer interrupts on (int level <
 * 6), the timeout is based on the 'jiffies' variable to provide exact
 * timeouts for device probing etc.
 * If interrupts are disabled, the number of tries is based on the
 * 'loops_per_jiffy' variable. A rough estimation is sufficient here...
 */

#define INT_LEVEL                                                    \
    ({    unsigned __sr;                                                \
        __asm__ __volatile__ ( "movew    %/sr,%0" : "=dm" (__sr) );    \
        (__sr >> 8) & 7;                                            \
    })

int acsi_wait_for_IRQ( unsigned timeout )

{
    if (INT_LEVEL < 6) {
        unsigned long maxjif = jiffies + timeout;
        while (time_before(jiffies, maxjif))
            if (!(mfp.par_dt_reg & 0x20)) return( 1 );
    }
    else {
        long tries = loops_per_jiffy / 8 * timeout;
        while( --tries >= 0 )
            if (!(mfp.par_dt_reg & 0x20)) return( 1 );
    }        
    return( 0 ); /* timeout! */
}


int acsi_wait_for_noIRQ( unsigned timeout )

{
    if (INT_LEVEL < 6) {
        unsigned long maxjif = jiffies + timeout;
        while (time_before(jiffies, maxjif))
            if (mfp.par_dt_reg & 0x20) return( 1 );
    }
    else {
        long tries = loops_per_jiffy * timeout / 8;
        while( tries-- >= 0 )
            if (mfp.par_dt_reg & 0x20) return( 1 );
    }        
    return( 0 ); /* timeout! */
}

static struct timeval start_time;

void
acsi_delay_start(void)
{
    do_gettimeofday(&start_time);
}

/* wait from acsi_delay_start to now usec (<1E6) usec */

void
acsi_delay_end(long usec)
{
    struct timeval end_time;
    long deltau,deltas;
    do_gettimeofday(&end_time);
    deltau=end_time.tv_usec - start_time.tv_usec;
    deltas=end_time.tv_sec - start_time.tv_sec;
    if (deltas > 1 || deltas < 0)
        return;
    if (deltas > 0)
        deltau += 1000*1000;
    if (deltau >= usec)
        return;
    udelay(usec-deltau);
}

/* acsicmd_dma() sends an ACSI command and sets up the DMA to transfer
 * 'blocks' blocks of 512 bytes from/to 'buffer'.
 * Because the _IRQ signal is used for handshaking the command bytes,
 * the ACSI interrupt has to be disabled in this function. If the end
 * of the operation should be signalled by a real interrupt, it has to be
 * reenabled afterwards.
 */

static int acsicmd_dma( const char *cmd, char *buffer, int blocks, int rwflag, int enable)

{    unsigned long    flags, paddr;
    int                i;

#ifdef NO_WRITE
    if (rwflag || *cmd == 0x0a) {
        printk( "ACSI: Write commands disabled!\n" );
        return( 0 );
    }
#endif
    
    rwflag = rwflag ? 0x100 : 0;
    paddr = virt_to_phys( buffer );

    acsi_delay_end(COMMAND_DELAY);
    DISABLE_IRQ();

    save_flags(flags);  
    cli();
    /* Low on A1 */
    dma_wd.dma_mode_status = 0x88 | rwflag;
    MFPDELAY();

    /* set DMA address */
    dma_wd.dma_lo = (unsigned char)paddr;
    paddr >>= 8;
    MFPDELAY();
    dma_wd.dma_md = (unsigned char)paddr;
    paddr >>= 8;
    MFPDELAY();
    if (ATARIHW_PRESENT(EXTD_DMA))
        st_dma_ext_dmahi = (unsigned short)paddr;
    else
        dma_wd.dma_hi = (unsigned char)paddr;
    MFPDELAY();
    restore_flags(flags);

    /* send the command bytes except the last */
    for( i = 0; i < 5; ++i ) {
        DMA_LONG_WRITE( *cmd++, 0x8a | rwflag );
        udelay(20);
        if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */
    }

    /* Clear FIFO and switch DMA to correct direction */  
    dma_wd.dma_mode_status = 0x92 | (rwflag ^ 0x100);  
    MFPDELAY();
    dma_wd.dma_mode_status = 0x92 | rwflag;
    MFPDELAY();

    /* How many sectors for DMA */
    dma_wd.fdc_acces_seccount = blocks;
    MFPDELAY();
    
    /* send last command byte */
    dma_wd.dma_mode_status = 0x8a | rwflag;
    MFPDELAY();
    DMA_LONG_WRITE( *cmd++, 0x0a | rwflag );
    if (enable)
        ENABLE_IRQ();
    udelay(80);

    return( 1 );
}


/*
 * acsicmd_nodma() sends an ACSI command that requires no DMA.
 */

int acsicmd_nodma( const char *cmd, int enable)

{    int    i;

    acsi_delay_end(COMMAND_DELAY);
    DISABLE_IRQ();

    /* send first command byte */
    dma_wd.dma_mode_status = 0x88;
    MFPDELAY();
    DMA_LONG_WRITE( *cmd++, 0x8a );
    udelay(20);
    if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */

    /* send the intermediate command bytes */
    for( i = 0; i < 4; ++i ) {
        DMA_LONG_WRITE( *cmd++, 0x8a );
        udelay(20);
        if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */
    }

    /* send last command byte */
    DMA_LONG_WRITE( *cmd++, 0x0a );
    if (enable)
        ENABLE_IRQ();
    udelay(80);
    
    return( 1 );
    /* Note that the ACSI interrupt is still disabled after this
     * function. If you want to get the IRQ delivered, enable it manually!
     */
}


static int acsi_reqsense( char *buffer, int targ, int lun)

{
    CMDSET_TARG_LUN( reqsense_cmd, targ, lun);
    if (!acsicmd_dma( reqsense_cmd, buffer, 1, 0, 0 )) return( 0 );
    if (!acsi_wait_for_IRQ( 10 )) return( 0 );
    acsi_getstatus();
    if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 );
    if (!acsi_wait_for_IRQ( 10 )) return( 0 );
    acsi_getstatus();
    if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 );
    if (!acsi_wait_for_IRQ( 10 )) return( 0 );
    acsi_getstatus();
    if (!acsicmd_nodma( reqsense_cmd, 0 )) return( 0 );
    if (!acsi_wait_for_IRQ( 10 )) return( 0 );
    acsi_getstatus();
    dma_cache_maintenance( virt_to_phys(buffer), 16, 0 );
    
    return( 1 );
}    


/*
 * ACSI status phase: get the status byte from the bus
 *
 * I've seen several times that a 0xff status is read, propably due to
 * a timing error. In this case, the procedure is repeated after the
 * next _IRQ edge.
 */

int acsi_getstatus( void )

{    int    status;

    DISABLE_IRQ();
    for(;;) {
        if (!acsi_wait_for_IRQ( 100 )) {
            acsi_delay_start();
            return( -1 );
        }
        dma_wd.dma_mode_status = 0x8a;
        MFPDELAY();
        status = dma_wd.fdc_acces_seccount;
        if (status != 0xff) break;
#ifdef DEBUG
        printk("ACSI: skipping 0xff status byte\n" );
#endif
        udelay(40);
        acsi_wait_for_noIRQ( 20 );
    }
    dma_wd.dma_mode_status = 0x80;
    udelay(40);
    acsi_wait_for_noIRQ( 20 );

    acsi_delay_start();
    return( status & 0x1f ); /* mask of the device# */
}


#if (defined(CONFIG_ATARI_SLM) || defined(CONFIG_ATARI_SLM_MODULE))

/* Receive data in an extended status phase. Needed by SLM printer. */

int acsi_extstatus( char *buffer, int cnt )

{    int    status;

    DISABLE_IRQ();
    udelay(80);
    while( cnt-- > 0 ) {
        if (!acsi_wait_for_IRQ( 40 )) return( 0 );
        dma_wd.dma_mode_status = 0x8a;
        MFPDELAY();
        status = dma_wd.fdc_acces_seccount;
        MFPDELAY();
        *buffer++ = status & 0xff;
        udelay(40);
    }
    return( 1 );
}


/* Finish an extended status phase */

void acsi_end_extstatus( void )

{
    dma_wd.dma_mode_status = 0x80;
    udelay(40);
    acsi_wait_for_noIRQ( 20 );
    acsi_delay_start();
}


/* Send data in an extended command phase */

int acsi_extcmd( unsigned char *buffer, int cnt )

{
    while( cnt-- > 0 ) {
        DMA_LONG_WRITE( *buffer++, 0x8a );
        udelay(20);
        if (!acsi_wait_for_IRQ( HZ/2 )) return( 0 ); /* timeout */
    }
    return( 1 );
}

#endif


static void acsi_print_error( const unsigned char *errblk, int dev  )

{    int atari_err, i, errcode;
    struct acsi_error *arr;

    atari_err = acsi_info[dev].old_atari_disk;
    if (atari_err)
        errcode = errblk[0] & 0x7f;
    else
        if ((errblk[0] & 0x70) == 0x70)
            errcode = errblk[2] & 0x0f;
        else
            errcode = errblk[0] & 0x0f;
    
    printk( KERN_ERR "ACSI error 0x%02x", errcode );

    if (errblk[0] & 0x80)
        printk( " for sector %d",
                ((errblk[1] & 0x1f) << 16) |
                (errblk[2] << 8) | errblk[0] );

    arr = atari_err ? atari_acsi_errors : scsi_acsi_errors;
    i = atari_err ? sizeof(atari_acsi_errors)/sizeof(*atari_acsi_errors) :
                    sizeof(scsi_acsi_errors)/sizeof(*scsi_acsi_errors);
    
    for( --i; i >= 0; --i )
        if (arr[i].code == errcode) break;
    if (i >= 0)
        printk( ": %s\n", arr[i].text );
}

/*******************************************************************
 *
 * ACSI interrupt routine
 *   Test, if this is a ACSI interrupt and call the irq handler
 *   Otherwise ignore this interrupt.
 *
 *******************************************************************/

static void acsi_interrupt(int irq, void *data, struct pt_regs *fp )

{    void (*acsi_irq_handler)(void) = DEVICE_INTR;

    DEVICE_INTR = NULL;
    CLEAR_TIMER();

    if (!acsi_irq_handler)
        acsi_irq_handler = unexpected_acsi_interrupt;
    acsi_irq_handler();
}


/******************************************************************
 *
 * The Interrupt handlers
 *
 *******************************************************************/


static void unexpected_acsi_interrupt( void )

{
    printk( KERN_WARNING "Unexpected ACSI interrupt\n" );
}


/* This function is called in case of errors. Because we cannot reset
 * the ACSI bus or a single device, there is no other choice than
 * retrying several times :-(
 */

static void bad_rw_intr( void )

{
    if (QUEUE_EMPTY)
        return;

    if (++CURRENT->errors >= MAX_ERRORS)
        end_request(0);
    /* Otherwise just retry */
}


static void read_intr( void )

{    int        status;
    
    status = acsi_getstatus();
    if (status != 0) {
        int dev = DEVICE_NR(MINOR(CURRENT->rq_dev));
        printk( KERN_ERR "ad%c: ", dev+'a' );
        if (!acsi_reqsense( acsi_buffer, acsi_info[dev].target, 
                    acsi_info[dev].lun))
            printk( "ACSI error and REQUEST SENSE failed (status=0x%02x)\n", status );
        else {
            acsi_print_error( acsi_buffer, dev );
            if (CARTRCH_STAT( dev, acsi_buffer ))
                acsi_info[dev].changed = 1;
        }
        ENABLE_IRQ();
        bad_rw_intr();
        redo_acsi_request();
        return;
    }

    dma_cache_maintenance( virt_to_phys(CurrentBuffer), CurrentNSect*512, 0 );
    if (CurrentBuffer == acsi_buffer)
        copy_from_acsibuffer();

    do_end_requests();
    redo_acsi_request();
}


static void write_intr(void)

{    int    status;

    status = acsi_getstatus();
    if (status != 0) {
        int    dev = DEVICE_NR(MINOR(CURRENT->rq_dev));
        printk( KERN_ERR "ad%c: ", dev+'a' );
        if (!acsi_reqsense( acsi_buffer, acsi_info[dev].target,
                    acsi_info[dev].lun))
            printk( "ACSI error and REQUEST SENSE failed (status=0x%02x)\n", status );
        else {
            acsi_print_error( acsi_buffer, dev );
            if (CARTRCH_STAT( dev, acsi_buffer ))
                acsi_info[dev].changed = 1;
        }
        bad_rw_intr();
        redo_acsi_request();
        return;
    }

    do_end_requests();
    redo_acsi_request();
}


static void acsi_times_out( unsigned long dummy )

{
    DISABLE_IRQ();
    if (!DEVICE_INTR) return;

    DEVICE_INTR = NULL;
    printk( KERN_ERR "ACSI timeout\n" );
    if (QUEUE_EMPTY) return;
    if (++CURRENT->errors >= MAX_ERRORS) {
#ifdef DEBUG
        printk( KERN_ERR "ACSI: too many errors.\n" );
#endif
        end_request(0);
    }

    redo_acsi_request();
}



/***********************************************************************
 *
 *  Scatter-gather utility functions
 *
 ***********************************************************************/


static void copy_to_acsibuffer( void )

{    int                    i;
    char                *src, *dst;
    struct buffer_head    *bh;
    
    src = CURRENT->buffer;
    dst = acsi_buffer;
    bh = CURRENT->bh;

    if (!bh)
        memcpy( dst, src, CurrentNSect*512 );
    else
        for( i = 0; i < CurrentNReq; ++i ) {
            memcpy( dst, src, bh->b_size );
            dst += bh->b_size;
            if ((bh = bh->b_reqnext))
                src = bh->b_data;
        }
}


static void copy_from_acsibuffer( void )

{    int                    i;
    char                *src, *dst;
    struct buffer_head    *bh;
    
    dst = CURRENT->buffer;
    src = acsi_buffer;
    bh = CURRENT->bh;

    if (!bh)
        memcpy( dst, src, CurrentNSect*512 );
    else
        for( i = 0; i < CurrentNReq; ++i ) {
            memcpy( dst, src, bh->b_size );
            src += bh->b_size;
            if ((bh = bh->b_reqnext))
                dst = bh->b_data;
        }
}


static void do_end_requests( void )

{    int        i, n;

    if (!CURRENT->bh) {
        CURRENT->nr_sectors -= CurrentNSect;
        CURRENT->current_nr_sectors -= CurrentNSect;
        CURRENT->sector += CurrentNSect;
        if (CURRENT->nr_sectors == 0)
            end_request(1);
    }
    else {
        for( i = 0; i < CurrentNReq; ++i ) {
            n = CURRENT->bh->b_size >> 9;
            CURRENT->nr_sectors -= n;
            CURRENT->current_nr_sectors -= n;
            CURRENT->sector += n;
            end_request(1);
        }
    }
}




/***********************************************************************
 *
 *  do_acsi_request and friends
 *
 ***********************************************************************/

static void do_acsi_request( request_queue_t * q )

{
    stdma_lock( acsi_interrupt, NULL );
    redo_acsi_request();
}


static void redo_acsi_request( void )

{    unsigned            block, dev, target, lun, nsect;
    char                 *buffer;
    unsigned long        pbuffer;
    struct buffer_head    *bh;
    
    if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE) {
        if (!DEVICE_INTR) {
            ENABLE_IRQ();
            stdma_release();
        }
        return;
    }

    if (DEVICE_INTR)
        return;

  repeat:
    CLEAR_TIMER();
    /* Another check here: An interrupt or timer event could have
     * happened since the last check!
     */
    if (!QUEUE_EMPTY && CURRENT->rq_status == RQ_INACTIVE) {
        if (!DEVICE_INTR) {
            ENABLE_IRQ();
            stdma_release();
        }
        return;
    }
    if (DEVICE_INTR)
        return;

    if (QUEUE_EMPTY) {
        CLEAR_INTR;
        ENABLE_IRQ();
        stdma_release();
        return;
    }
    
    if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
        panic(DEVICE_NAME ": request list destroyed");
    if (CURRENT->bh) {
        if (!CURRENT->bh && !buffer_locked(CURRENT->bh))
            panic(DEVICE_NAME ": block not locked");
    }

    dev = MINOR(CURRENT->rq_dev);
    block = CURRENT->sector;
    if (DEVICE_NR(dev) >= NDevices ||
        block+CURRENT->nr_sectors >= acsi_part[dev].nr_sects) {
#ifdef DEBUG
        printk( "ad%c: attempted access for blocks %d...%ld past end of device at block %ld.\n",
               DEVICE_NR(dev)+'a',
               block, block + CURRENT->nr_sectors - 1,
               acsi_part[dev].nr_sects);
#endif
        end_request(0);
        goto repeat;
    }
    if (acsi_info[DEVICE_NR(dev)].changed) {
        printk( KERN_NOTICE "ad%c: request denied because cartridge has "
                "been changed.\n", DEVICE_NR(dev)+'a' );
        end_request(0);
        goto repeat;
    }
    
    block += acsi_part[dev].start_sect;
    target = acsi_info[DEVICE_NR(dev)].target;
    lun    = acsi_info[DEVICE_NR(dev)].lun;

    /* Find out how many sectors should be transferred from/to
     * consecutive buffers and thus can be done with a single command.
     */
    buffer      = CURRENT->buffer;
    pbuffer     = virt_to_phys(buffer);
    nsect       = CURRENT->current_nr_sectors;
    CurrentNReq = 1;

    if ((bh = CURRENT->bh) && bh != CURRENT->bhtail) {
        if (!STRAM_ADDR(pbuffer)) {
            /* If transfer is done via the ACSI buffer anyway, we can
             * assemble as much bh's as fit in the buffer.
             */
            while( (bh = bh->b_reqnext) ) {
                if (nsect + (bh->b_size>>9) > ACSI_BUFFER_SECTORS) break;
                nsect += bh->b_size >> 9;
                ++CurrentNReq;
                if (bh == CURRENT->bhtail) break;
            }
            buffer = acsi_buffer;
            pbuffer = phys_acsi_buffer;
        }
        else {
            unsigned long pendadr, pnewadr;
            pendadr = pbuffer + nsect*512;
            while( (bh = bh->b_reqnext) ) {
                pnewadr = virt_to_phys(bh->b_data);
                if (!STRAM_ADDR(pnewadr) || pendadr != pnewadr) break;
                nsect += bh->b_size >> 9;
                pendadr = pnewadr + bh->b_size;
                ++CurrentNReq;
                if (bh == CURRENT->bhtail) break;
            }
        }
    }
    else {
        if (!STRAM_ADDR(pbuffer)) {
            buffer = acsi_buffer;
            pbuffer = phys_acsi_buffer;
            if (nsect > ACSI_BUFFER_SECTORS)
                nsect = ACSI_BUFFER_SECTORS;
        }
    }
    CurrentBuffer = buffer;
    CurrentNSect  = nsect;
    
    if (CURRENT->cmd == WRITE) {
        CMDSET_TARG_LUN( write_cmd, target, lun );
        CMDSET_BLOCK( write_cmd, block );
        CMDSET_LEN( write_cmd, nsect );
        if (buffer == acsi_buffer)
            copy_to_acsibuffer();
        dma_cache_maintenance( pbuffer, nsect*512, 1 );
        SET_INTR(write_intr);
        if (!acsicmd_dma( write_cmd, buffer, nsect, 1, 1)) {
            CLEAR_INTR;
            printk( KERN_ERR "ACSI (write): Timeout in command block\n" );
            bad_rw_intr();
            goto repeat;
        }
        SET_TIMER();
        return;
    }
    if (CURRENT->cmd == READ) {
        CMDSET_TARG_LUN( read_cmd, target, lun );
        CMDSET_BLOCK( read_cmd, block );
        CMDSET_LEN( read_cmd, nsect );
        SET_INTR(read_intr);
        if (!acsicmd_dma( read_cmd, buffer, nsect, 0, 1)) {
            CLEAR_INTR;
            printk( KERN_ERR "ACSI (read): Timeout in command block\n" );
            bad_rw_intr();
            goto repeat;
        }
        SET_TIMER();
        return;
    }
    panic("unknown ACSI command");
}



/***********************************************************************
 *
 *  Misc functions: ioctl, open, release, check_change, ...
 *
 ***********************************************************************/


static int acsi_ioctl( struct inode *inode, struct file *file,
                       unsigned int cmd, unsigned long arg )
{    int dev;

    if (!inode)
        return -EINVAL;
    dev = DEVICE_NR(MINOR(inode->i_rdev));
    if (dev >= NDevices)
        return -EINVAL;
    switch (cmd) {
      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:
      case BLKGETSIZE64:
      case BLKROSET:
      case BLKROGET:
      case BLKFLSBUF:
      case BLKPG:
        return blk_ioctl(inode->i_rdev, cmd, arg);

      case BLKRRPART: /* Re-read partition tables */
            if (!capable(CAP_SYS_ADMIN)) 
            return -EACCES;
        return revalidate_acsidisk(inode->i_rdev, 1);

      default:
        return -EINVAL;
    }
}


/*
 * Open a device, check for read-only and lock the medium if it is
 * removable.
 *
 * Changes by Martin Rogge, 9th Aug 1995:
 * Check whether check_disk_change (and therefore revalidate_acsidisk)
 * was successful. They fail when there is no medium in the drive.
 *
 * The problem of media being changed during an operation can be 
 * ignored because of the prevent_removal code.
 *
 * Added check for the validity of the device number.
 *
 */

static int acsi_open( struct inode * inode, struct file * filp )
{
    int  device;
    struct acsi_info_struct *aip;

    device = DEVICE_NR(MINOR(inode->i_rdev));
    if (device >= NDevices)
        return -ENXIO;
    aip = &acsi_info[device];
    while (busy[device])
        sleep_on(&busy_wait);

    if (access_count[device] == 0 && aip->removable) {
#if 0
        aip->changed = 1;    /* safety first */
#endif
        check_disk_change( inode->i_rdev );
        if (aip->changed)    /* revalidate was not successful (no medium) */
            return -ENXIO;
        acsi_prevent_removal(device, 1);
    }
    access_count[device]++;

    if (filp && filp->f_mode) {
        check_disk_change( inode->i_rdev );
        if (filp->f_mode & 2) {
            if (aip->read_only) {
                acsi_release( inode, filp );
                return -EROFS;
            }
        }
    }

    return 0;
}

/*
 * Releasing a block device means we sync() it, so that it can safely
 * be forgotten about...
 */

static int acsi_release( struct inode * inode, struct file * file )
{
    int device = DEVICE_NR(MINOR(inode->i_rdev));
    if (--access_count[device] == 0 && acsi_info[device].removable)
        acsi_prevent_removal(device, 0);
    return( 0 );
}

/*
 * Prevent or allow a media change for removable devices.
 */

static void acsi_prevent_removal(int device, int flag)
{
    stdma_lock( NULL, NULL );
    
    CMDSET_TARG_LUN(pa_med_rem_cmd, acsi_info[device].target,
            acsi_info[device].lun);
    CMDSET_LEN( pa_med_rem_cmd, flag );
    
    if (acsicmd_nodma(pa_med_rem_cmd, 0) && acsi_wait_for_IRQ(3*HZ))
        acsi_getstatus();
    /* Do not report errors -- some devices may not know this command. */

    ENABLE_IRQ();
    stdma_release();
}

static int acsi_media_change (dev_t dev)
{
    int device = DEVICE_NR(MINOR(dev));
    struct acsi_info_struct *aip;

    aip = &acsi_info[device];
    if (!aip->removable) 
        return 0;

    if (aip->changed)
        /* We can be sure that the medium has been changed -- REQUEST
         * SENSE has reported this earlier.
         */
        return 1;

    /* If the flag isn't set, make a test by reading block 0.
     * If errors happen, it seems to be better to say "changed"...
     */
    stdma_lock( NULL, NULL );
    CMDSET_TARG_LUN(read_cmd, aip->target, aip->lun);
    CMDSET_BLOCK( read_cmd, 0 );
    CMDSET_LEN( read_cmd, 1 );
    if (acsicmd_dma(read_cmd, acsi_buffer, 1, 0, 0) &&
        acsi_wait_for_IRQ(3*HZ)) {
        if (acsi_getstatus()) {
            if (acsi_reqsense(acsi_buffer, aip->target, aip->lun)) {
                if (CARTRCH_STAT(device, acsi_buffer))
                    aip->changed = 1;
            }
            else {
                printk( KERN_ERR "ad%c: REQUEST SENSE failed in test for "
                       "medium change; assuming a change\n", device + 'a' );
                aip->changed = 1;
            }
        }
    }
    else {
        printk( KERN_ERR "ad%c: Test for medium changed timed out; "
                "assuming a change\n", device + 'a');
        aip->changed = 1;
    }
    ENABLE_IRQ();
    stdma_release();

    /* Now, after reading a block, the changed status is surely valid. */
    return aip->changed;
}


static int acsi_change_blk_size( int target, int lun)

{    int i;

    for (i=0; i<12; i++)
        acsi_buffer[i] = 0;

    acsi_buffer[3] = 8;
    acsi_buffer[10] = 2;
    CMDSET_TARG_LUN( modeselect_cmd, target, lun);

    if (!acsicmd_dma( modeselect_cmd, acsi_buffer, 1,1,0) ||
        !acsi_wait_for_IRQ( 3*HZ ) ||
        acsi_getstatus() != 0 ) {
        return(0);
    }
    return(1);
}


static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd )

{
    int page;

    CMDSET_TARG_LUN( modesense_cmd, target, lun );
    for (page=0; page<4; page++) {
        modesense_cmd[2] = page;
        if (!acsicmd_dma( modesense_cmd, acsi_buffer, 1, 0, 0 ) ||
            !acsi_wait_for_IRQ( 3*HZ ) ||
            acsi_getstatus())
            continue;

        /* read twice to jump over the second 16-byte border! */
        udelay(300);
        if (acsi_wait_for_noIRQ( 20 ) &&
            acsicmd_nodma( modesense_cmd, 0 ) &&
            acsi_wait_for_IRQ( 3*HZ ) &&
            acsi_getstatus() == 0)
            break;
    }
    if (page == 4) {
        return(0);
    }

    dma_cache_maintenance( phys_acsi_buffer, sizeof(SENSE_DATA), 0 );
    *sd = *(SENSE_DATA *)acsi_buffer;

    /* Validity check, depending on type of data */
    
    switch( SENSE_TYPE(*sd) ) {

      case SENSE_TYPE_ATARI:
        if (CAPACITY(*sd) == 0)
            goto invalid_sense;
        break;

      case SENSE_TYPE_SCSI:
        if (sd->scsi.descriptor_size != 8)
            goto invalid_sense;
        break;

      case SENSE_TYPE_UNKNOWN:

        printk( KERN_ERR "ACSI target %d, lun %d: Cannot interpret "
                "sense data\n", target, lun ); 
        
      invalid_sense:

#ifdef DEBUG
        {    int i;
        printk( "Mode sense data for ACSI target %d, lun %d seem not valid:",
                target, lun );
        for( i = 0; i < sizeof(SENSE_DATA); ++i )
            printk( "%02x ", (unsigned char)acsi_buffer[i] );
        printk( "\n" );
        }
#endif
        return( 0 );
    }
        
    return( 1 );
}



/*******************************************************************
 *
 *  Initialization
 *
 ********************************************************************/


extern struct block_device_operations acsi_fops;

static struct gendisk acsi_gendisk = {
    major:        MAJOR_NR,
    major_name:    "ad",
    minor_shift:    4,
    max_p:        1 << 4,
    part:        acsi_part,
    sizes:        acsi_sizes,
    real_devices:    (void *)acsi_info,
    fops:        &acsi_fops,
};
    
#define MAX_SCSI_DEVICE_CODE 10

static const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
{
 "Direct-Access    ",
 "Sequential-Access",
 "Printer          ",
 "Processor        ",
 "WORM             ",
 "CD-ROM           ",
 "Scanner          ",
 "Optical Device   ",
 "Medium Changer   ",
 "Communications   "
};

static void print_inquiry(unsigned char *data)
{
    int i;

    printk(KERN_INFO "  Vendor: ");
    for (i = 8; i < 16; i++)
        {
            if (data[i] >= 0x20 && i < data[4] + 5)
            printk("%c", data[i]);
        else
            printk(" ");
        }

    printk("  Model: ");
    for (i = 16; i < 32; i++)
        {
            if (data[i] >= 0x20 && i < data[4] + 5)
            printk("%c", data[i]);
        else
            printk(" ");
        }

    printk("  Rev: ");
    for (i = 32; i < 36; i++)
        {
            if (data[i] >= 0x20 && i < data[4] + 5)
            printk("%c", data[i]);
        else
            printk(" ");
        }

    printk("\n");

    i = data[0] & 0x1f;

    printk(KERN_INFO "  Type:   %s ", (i < MAX_SCSI_DEVICE_CODE
                                       ? scsi_device_types[i]
                                       : "Unknown          "));
    printk("                 ANSI SCSI revision: %02x", data[2] & 0x07);
    if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1)
      printk(" CCS\n");
    else
      printk("\n");
}


/* 
 * Changes by Martin Rogge, 9th Aug 1995: 
 * acsi_devinit has been taken out of acsi_geninit, because it needs 
 * to be called from revalidate_acsidisk. The result of request sense 
 * is now checked for DRIVE NOT READY.
 *
 * The structure *aip is only valid when acsi_devinit returns 
 * DEV_SUPPORTED. 
 *
 */
    
#define DEV_NONE    0
#define DEV_UNKNOWN    1
#define DEV_SUPPORTED    2
#define DEV_SLM        3

static int acsi_devinit(struct acsi_info_struct *aip)
{
    int status, got_inquiry;
    SENSE_DATA sense;
    unsigned char reqsense, extsense;

    /*****************************************************************/
    /* Do a TEST UNIT READY command to test the presence of a device */
    /*****************************************************************/

    CMDSET_TARG_LUN(tur_cmd, aip->target, aip->lun);
    if (!acsicmd_nodma(tur_cmd, 0)) {
        /* timed out -> no device here */
#ifdef DEBUG_DETECT
        printk("target %d lun %d: timeout\n", aip->target, aip->lun);
#endif
        return DEV_NONE;
    }
        
    /*************************/
    /* Read the ACSI status. */
    /*************************/

    status = acsi_getstatus();
    if (status) {
        if (status == 0x12) {
            /* The SLM printer should be the only device that
             * responds with the error code in the status byte. In
             * correct status bytes, bit 4 is never set.
             */
            printk( KERN_INFO "Detected SLM printer at id %d lun %d\n",
                   aip->target, aip->lun);
            return DEV_SLM;
        }
        /* ignore CHECK CONDITION, since some devices send a
           UNIT ATTENTION */
        if ((status & 0x1e) != 0x2) {
#ifdef DEBUG_DETECT
            printk("target %d lun %d: status %d\n",
                   aip->target, aip->lun, status);
#endif
            return DEV_UNKNOWN;
        }
    }

    /*******************************/
    /* Do a REQUEST SENSE command. */
    /*******************************/

    if (!acsi_reqsense(acsi_buffer, aip->target, aip->lun)) {
        printk( KERN_WARNING "acsi_reqsense failed\n");
        acsi_buffer[0] = 0;
        acsi_buffer[2] = UNIT_ATTENTION;
    }
    reqsense = acsi_buffer[0];
    extsense = acsi_buffer[2] & 0xf;
    if (status) {
        if ((reqsense & 0x70) == 0x70) {    /* extended sense */
            if (extsense != UNIT_ATTENTION &&
                extsense != NOT_READY) {
#ifdef DEBUG_DETECT
                printk("target %d lun %d: extended sense %d\n",
                       aip->target, aip->lun, extsense);
#endif
                return DEV_UNKNOWN;
            }
        }
        else {
            if (reqsense & 0x7f) {
#ifdef DEBUG_DETECT
                printk("target %d lun %d: sense %d\n",
                       aip->target, aip->lun, reqsense);
#endif
                return DEV_UNKNOWN;
            }
        }
    }
    else 
        if (reqsense == 0x4) {    /* SH204 Bug workaround */
#ifdef DEBUG_DETECT
            printk("target %d lun %d status=0 sense=4\n",
                   aip->target, aip->lun);
#endif
            return DEV_UNKNOWN;
        }

    /***********************************************************/
    /* Do an INQUIRY command to get more infos on this device. */
    /***********************************************************/

    /* Assume default values */
    aip->removable = 1;
    aip->read_only = 0;
    aip->old_atari_disk = 0;
    aip->changed = (extsense == NOT_READY);    /* medium inserted? */
    aip->size = DEFAULT_SIZE;
    got_inquiry = 0;
    /* Fake inquiry result for old atari disks */
    memcpy(acsi_buffer, "\000\000\001\000    Adaptec 40xx"
           "                    ", 40);
    CMDSET_TARG_LUN(inquiry_cmd, aip->target, aip->lun);
    if (acsicmd_dma(inquiry_cmd, acsi_buffer, 1, 0, 0) &&
        acsi_getstatus() == 0) {
        acsicmd_nodma(inquiry_cmd, 0);
        acsi_getstatus();
        dma_cache_maintenance( phys_acsi_buffer, 256, 0 );
        got_inquiry = 1;
        aip->removable = !!(acsi_buffer[1] & 0x80);
    }
    if (aip->type == NONE)    /* only at boot time */
        print_inquiry(acsi_buffer);
    switch(acsi_buffer[0]) {
      case TYPE_DISK:
        aip->type = HARDDISK;
        break;
      case TYPE_ROM:
        aip->type = CDROM;
        aip->read_only = 1;
        break;
      default:
        return DEV_UNKNOWN;
    }
    /****************************/
    /* Do a MODE SENSE command. */
    /****************************/

    if (!acsi_mode_sense(aip->target, aip->lun, &sense)) {
        printk( KERN_WARNING "No mode sense data.\n" );
        return DEV_UNKNOWN;
    }
    if ((SECTOR_SIZE(sense) != 512) &&
        ((aip->type != CDROM) ||
         !acsi_change_blk_size(aip->target, aip->lun) ||
         !acsi_mode_sense(aip->target, aip->lun, &sense) ||
         (SECTOR_SIZE(sense) != 512))) {
        printk( KERN_WARNING "Sector size != 512 not supported.\n" );
        return DEV_UNKNOWN;
    }
    /* There are disks out there that claim to have 0 sectors... */
    if (CAPACITY(sense))
        aip->size = CAPACITY(sense);    /* else keep DEFAULT_SIZE */
    if (!got_inquiry && SENSE_TYPE(sense) == SENSE_TYPE_ATARI) {
        /* If INQUIRY failed and the sense data suggest an old
         * Atari disk (SH20x, Megafile), the disk is not removable
         */
        aip->removable = 0;
        aip->old_atari_disk = 1;
    }
    
    /******************/
    /* We've done it. */
    /******************/
    
    return DEV_SUPPORTED;
}

EXPORT_SYMBOL(acsi_delay_start);
EXPORT_SYMBOL(acsi_delay_end);
EXPORT_SYMBOL(acsi_wait_for_IRQ);
EXPORT_SYMBOL(acsi_wait_for_noIRQ);
EXPORT_SYMBOL(acsicmd_nodma);
EXPORT_SYMBOL(acsi_getstatus);
EXPORT_SYMBOL(acsi_buffer);
EXPORT_SYMBOL(phys_acsi_buffer);

#ifdef CONFIG_ATARI_SLM_MODULE
void acsi_attach_SLMs( int (*attach_func)( int, int ) );

EXPORT_SYMBOL(acsi_extstatus);
EXPORT_SYMBOL(acsi_end_extstatus);
EXPORT_SYMBOL(acsi_extcmd);
EXPORT_SYMBOL(acsi_attach_SLMs);

/* to remember IDs of SLM devices, SLM module is loaded later
 * (index is target#, contents is lun#, -1 means "no SLM") */
int SLM_devices[8];
#endif

static struct block_device_operations acsi_fops = {
    owner:            THIS_MODULE,
    open:            acsi_open,
    release:        acsi_release,
    ioctl:            acsi_ioctl,
    check_media_change:    acsi_media_change,
    revalidate:        acsi_revalidate,
};

static void acsi_geninit(void)
{
    int i, target, lun;
    struct acsi_info_struct *aip;
#ifdef CONFIG_ATARI_SLM
    int n_slm = 0;
#endif

    printk( KERN_INFO "Probing ACSI devices:\n" );
    NDevices = 0;
#ifdef CONFIG_ATARI_SLM_MODULE
    for( i = 0; i < 8; ++i )
        SLM_devices[i] = -1;
#endif
    stdma_lock(NULL, NULL);

    for (target = 0; target < 8 && NDevices < MAX_DEV; ++target) {
        lun = 0;
        do {
            aip = &acsi_info[NDevices];
            aip->type = NONE;
            aip->target = target;
            aip->lun = lun;
            i = acsi_devinit(aip);
            switch (i) {
              case DEV_SUPPORTED:
                printk( KERN_INFO "Detected ");
                switch (aip->type) {
                  case HARDDISK:
                    printk("disk");
                    break;
                  case CDROM:
                    printk("cdrom");
                    break;
                  default:
                }
                printk(" ad%c at id %d lun %d ",
                       'a' + NDevices, target, lun);
                if (aip->removable) 
                    printk("(removable) ");
                if (aip->read_only) 
                    printk("(read-only) ");
                if (aip->size == DEFAULT_SIZE)
                    printk(" unkown size, using default ");
                printk("%ld MByte\n",
                       (aip->size*512+1024*1024/2)/(1024*1024));
                NDevices++;
                break;
              case DEV_SLM:
#ifdef CONFIG_ATARI_SLM
                n_slm += attach_slm( target, lun );
                break;
#endif
#ifdef CONFIG_ATARI_SLM_MODULE
                SLM_devices[target] = lun;
                break;
#endif
                /* neither of the above: fall through to unknown device */
              case DEV_UNKNOWN:
                printk( KERN_INFO "Detected unsupported device at "
                        "id %d lun %d\n", target, lun);
                break;
            }
        }
#ifdef CONFIG_ACSI_MULTI_LUN
        while (i != DEV_NONE && ++lun < MAX_LUN);
#else
        while (0);
#endif
    }

    /* reenable interrupt */
    ENABLE_IRQ();
    stdma_release();

#ifndef CONFIG_ATARI_SLM
    printk( KERN_INFO "Found %d ACSI device(s) total.\n", NDevices );
#else
    printk( KERN_INFO "Found %d ACSI device(s) and %d SLM printer(s) total.\n",
            NDevices, n_slm );
#endif
                     
    for( i = 0; i < (MAX_DEV << 4); i++ )
        acsi_blocksizes[i] = 1024;
    blksize_size[MAJOR_NR] = acsi_blocksizes;
    for( i = 0; i < NDevices; ++i )
        register_disk(&acsi_gendisk, MKDEV(MAJOR_NR,i<<4),
                (acsi_info[i].type==HARDDISK)?1<<4:1,
                &acsi_fops,
                acsi_info[i].size);
    acsi_gendisk.nr_real = NDevices;
}

#ifdef CONFIG_ATARI_SLM_MODULE
/* call attach_slm() for each device that is a printer; needed for init of SLM
 * driver as a module, since it's not yet present if acsi.c is inited and thus
 * the bus gets scanned. */
void acsi_attach_SLMs( int (*attach_func)( int, int ) )
{
    int i, n = 0;

    for( i = 0; i < 8; ++i )
        if (SLM_devices[i] >= 0)
            n += (*attach_func)( i, SLM_devices[i] );
    printk( KERN_INFO "Found %d SLM printer(s) total.\n", n );
}
#endif /* CONFIG_ATARI_SLM_MODULE */


int acsi_init( void )

{
    int err = 0;
    if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ACSI))
        return 0;
    if (devfs_register_blkdev( MAJOR_NR, "ad", &acsi_fops )) {
        printk( KERN_ERR "Unable to get major %d for ACSI\n", MAJOR_NR );
        return -EBUSY;
    }
    if (!(acsi_buffer =
          (char *)atari_stram_alloc(ACSI_BUFFER_SIZE, "acsi"))) {
        printk( KERN_ERR "Unable to get ACSI ST-Ram buffer.\n" );
        devfs_unregister_blkdev( MAJOR_NR, "ad" );
        return -ENOMEM;
    }
    phys_acsi_buffer = virt_to_phys( acsi_buffer );
    STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000;
    
    blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
    read_ahead[MAJOR_NR] = 8;        /* 8 sector (4kB) read-ahead */
    add_gendisk(&acsi_gendisk);

#ifdef CONFIG_ATARI_SLM
    err = slm_init();
#endif
    if (!err)
        acsi_geninit();
    return err;
}


#ifdef MODULE

MODULE_LICENSE("GPL");

int init_module(void)
{
    int err;

    if ((err = acsi_init()))
        return( err );
    printk( KERN_INFO "ACSI driver loaded as module.\n");
    return( 0 );
}

void cleanup_module(void)
{
    del_timer( &acsi_timer );
    blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
    atari_stram_free( acsi_buffer );

    if (devfs_unregister_blkdev( MAJOR_NR, "ad" ) != 0)
        printk( KERN_ERR "acsi: cleanup_module failed\n");

    del_gendisk(&acsi_gendisk);
}
#endif

#define DEVICE_BUSY busy[device]
#define USAGE access_count[device]
#define GENDISK_STRUCT acsi_gendisk

/*
 * This routine is called to flush all partitions and partition tables
 * for a changed scsi disk, and then re-read the new partition table.
 * If we are revalidating a disk because of a media change, then we
 * enter with usage == 0.  If we are using an ioctl, we automatically have
 * usage == 1 (we need an open channel to use an ioctl :-), so this
 * is our limit.
 *
 * Changes by Martin Rogge, 9th Aug 1995: 
 * got cd-roms to work by calling acsi_devinit. There are only two problems:
 * First, if there is no medium inserted, the status will remain "changed".
 * That is no problem at all, but our design of three-valued logic (medium
 * changed, medium not changed, no medium inserted).
 * Secondly the check could fail completely and the drive could deliver
 * nonsensical data, which could mess up the acsi_info[] structure. In
 * that case we try to make the entry safe.
 *
 */

static int revalidate_acsidisk( int dev, int maxusage )
{
    int device;
    struct gendisk * gdev;
    int max_p, start, i;
    struct acsi_info_struct *aip;
    
    device = DEVICE_NR(MINOR(dev));
    aip = &acsi_info[device];
    gdev = &GENDISK_STRUCT;

    cli();
    if (DEVICE_BUSY || USAGE > maxusage) {
        sti();
        return -EBUSY;
    };
    DEVICE_BUSY = 1;
    sti();

    max_p = gdev->max_p;
    start = device << gdev->minor_shift;

    for( i = max_p - 1; i >= 0 ; i-- ) {
        if (gdev->part[start + i].nr_sects != 0) {
            invalidate_device(MKDEV(MAJOR_NR, start + i), 1);
            gdev->part[start + i].nr_sects = 0;
        }
        gdev->part[start+i].start_sect = 0;
    };

    stdma_lock( NULL, NULL );

    if (acsi_devinit(aip) != DEV_SUPPORTED) {
        printk( KERN_ERR "ACSI: revalidate failed for target %d lun %d\n",
               aip->target, aip->lun);
        aip->size = 0;
        aip->read_only = 1;
        aip->removable = 1;
        aip->changed = 1; /* next acsi_open will try again... */
    }

    ENABLE_IRQ();
    stdma_release();
    
    grok_partitions(gdev, device, (aip->type==HARDDISK)?1<<4:1, aip->size);

    DEVICE_BUSY = 0;
    wake_up(&busy_wait);
    return 0;
}


static int acsi_revalidate (dev_t dev)
{
  return revalidate_acsidisk (dev, 0);
}

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0198 ]--