|
|
GNU Mach 1.0
/*
* scsi.h Copyright (C) 1992 Drew Eckhardt
* Copyright (C) 1993, 1994, 1995 Eric Youngdale
* generic SCSI package header file by
* Initial versions: Drew Eckhardt
* Subsequent revisions: Eric Youngdale
*
* <[email protected]>
*
* Modified by Eric Youngdale [email protected] to
* add scatter-gather, multiple outstanding request, and other
* enhancements.
*/
#ifndef _SCSI_H
#define _SCSI_H
/*
* Some of the public constants are being moved to this file.
* We include it here so that what came from where is transparent.
*/
#include <linux/scsi.h>
/*
* Some defs, in case these are not defined elsewhere.
*/
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
#ifdef MACH
#ifndef LINUX_SCSI_DEBUG
#undef DEBUG
#endif
#endif
extern void scsi_make_blocked_list(void);
extern volatile int in_scan_scsis;
extern const unsigned char scsi_command_size[8];
#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
#define IDENTIFY_BASE 0x80
#define IDENTIFY(can_disconnect, lun) (IDENTIFY_BASE |\
((can_disconnect) ? 0x40 : 0) |\
((lun) & 0x07))
/*
* the return of the status word will be in the following format :
* The low byte is the status returned by the SCSI command,
* with vendor specific bits masked.
*
* The next byte is the message which followed the SCSI status.
* This allows a stos to be used, since the Intel is a little
* endian machine.
*
* The final byte is a host return code, which is one of the following.
*
* IE
* lsb msb
* status msg host code
*
* Our errors returned by OUR driver, NOT SCSI message. Or'd with
* SCSI message passed back to driver <IF any>.
*/
#define DID_OK 0x00 /* NO error */
#define DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */
#define DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */
#define DID_TIME_OUT 0x03 /* TIMED OUT for other reason */
#define DID_BAD_TARGET 0x04 /* BAD target. */
#define DID_ABORT 0x05 /* Told to abort for some other reason */
#define DID_PARITY 0x06 /* Parity error */
#define DID_ERROR 0x07 /* Internal error */
#define DID_RESET 0x08 /* Reset by somebody. */
#define DID_BAD_INTR 0x09 /* Got an interrupt we weren't expecting. */
#define DRIVER_OK 0x00 /* Driver status */
/*
* These indicate the error that occurred, and what is available.
*/
#define DRIVER_BUSY 0x01
#define DRIVER_SOFT 0x02
#define DRIVER_MEDIA 0x03
#define DRIVER_ERROR 0x04
#define DRIVER_INVALID 0x05
#define DRIVER_TIMEOUT 0x06
#define DRIVER_HARD 0x07
#define DRIVER_SENSE 0x08
#define SUGGEST_RETRY 0x10
#define SUGGEST_ABORT 0x20
#define SUGGEST_REMAP 0x30
#define SUGGEST_DIE 0x40
#define SUGGEST_SENSE 0x80
#define SUGGEST_IS_OK 0xff
#define DRIVER_MASK 0x0f
#define SUGGEST_MASK 0xf0
#define MAX_COMMAND_SIZE 12
/*
* SCSI command sets
*/
#define SCSI_UNKNOWN 0
#define SCSI_1 1
#define SCSI_1_CCS 2
#define SCSI_2 3
/*
* Every SCSI command starts with a one byte OP-code.
* The next byte's high three bits are the LUN of the
* device. Any multi-byte quantities are stored high byte
* first, and may have a 5 bit MSB in the same byte
* as the LUN.
*/
/*
* Manufacturers list
*/
#define SCSI_MAN_UNKNOWN 0
#define SCSI_MAN_NEC 1
#define SCSI_MAN_TOSHIBA 2
#define SCSI_MAN_NEC_OLDCDR 3
#define SCSI_MAN_SONY 4
#define SCSI_MAN_PIONEER 5
/*
* As the scsi do command functions are intelligent, and may need to
* redo a command, we need to keep track of the last command
* executed on each one.
*/
#define WAS_RESET 0x01
#define WAS_TIMEDOUT 0x02
#define WAS_SENSE 0x04
#define IS_RESETTING 0x08
#define IS_ABORTING 0x10
#define ASKED_FOR_SENSE 0x20
/*
* The scsi_device struct contains what we know about each given scsi
* device.
*/
typedef struct scsi_device {
struct scsi_device * next; /* Used for linked list */
unsigned char id, lun, channel;
unsigned int manufacturer; /* Manufacturer of device, for using
* vendor-specific cmd's */
int attached; /* # of high level drivers attached to
* this */
int access_count; /* Count of open channels/mounts */
struct wait_queue * device_wait;/* Used to wait if device is busy */
struct Scsi_Host * host;
void (*scsi_request_fn)(void); /* Used to jumpstart things after an
* ioctl */
void *hostdata; /* available to low-level driver */
char type;
char scsi_level;
char vendor[8], model[16], rev[4];
unsigned char current_tag; /* current tag */
unsigned char sync_min_period; /* Not less than this period */
unsigned char sync_max_offset; /* Not greater than this offset */
unsigned writeable:1;
unsigned removable:1;
unsigned random:1;
unsigned has_cmdblocks:1;
unsigned changed:1; /* Data invalid due to media change */
unsigned busy:1; /* Used to prevent races */
unsigned lockable:1; /* Able to prevent media removal */
unsigned borken:1; /* Tell the Seagate driver to be
* painfully slow on this device */
unsigned tagged_supported:1; /* Supports SCSI-II tagged queuing */
unsigned tagged_queue:1; /* SCSI-II tagged queuing enabled */
unsigned disconnect:1; /* can disconnect */
unsigned soft_reset:1; /* Uses soft reset option */
unsigned sync:1; /* Negotiate for sync transfers */
unsigned single_lun:1; /* Indicates we should only allow I/O to
one of the luns for the device at a time. */
unsigned was_reset:1; /* There was a bus reset on the bus for this
device */
unsigned expecting_cc_ua:1; /* Expecting a CHECK_CONDITION/UNIT_ATTN
because we did a bus reset. */
} Scsi_Device;
/*
* Use these to separate status msg and our bytes
*/
#define status_byte(result) (((result) >> 1) & 0xf)
#define msg_byte(result) (((result) >> 8) & 0xff)
#define host_byte(result) (((result) >> 16) & 0xff)
#define driver_byte(result) (((result) >> 24) & 0xff)
#define suggestion(result) (driver_byte(result) & SUGGEST_MASK)
#define sense_class(sense) (((sense) >> 4) & 0x7)
#define sense_error(sense) ((sense) & 0xf)
#define sense_valid(sense) ((sense) & 0x80);
/*
* These are the SCSI devices available on the system.
*/
extern Scsi_Device * scsi_devices;
/*
* Initializes all SCSI devices. This scans all scsi busses.
*/
extern int scsi_dev_init (void);
struct scatterlist {
char * address; /* Location data is to be transferred to */
char * alt_address; /* Location of actual if address is a
* dma indirect buffer. NULL otherwise */
unsigned int length;
};
#ifdef __alpha__
# define ISA_DMA_THRESHOLD (~0UL)
#else
# define ISA_DMA_THRESHOLD (0x00ffffff)
#endif
#define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data)
/*
* These are the return codes for the abort and reset functions. The mid-level
* code uses these to decide what to do next. Each of the low level abort
* and reset functions must correctly indicate what it has done.
* The descriptions are written from the point of view of the mid-level code,
* so that the return code is telling the mid-level drivers exactly what
* the low level driver has already done, and what remains to be done.
*/
/* We did not do anything.
* Wait some more for this command to complete, and if this does not work,
* try something more serious. */
#define SCSI_ABORT_SNOOZE 0
/* This means that we were able to abort the command. We have already
* called the mid-level done function, and do not expect an interrupt that
* will lead to another call to the mid-level done function for this command */
#define SCSI_ABORT_SUCCESS 1
/* We called for an abort of this command, and we should get an interrupt
* when this succeeds. Thus we should not restore the timer for this
* command in the mid-level abort function. */
#define SCSI_ABORT_PENDING 2
/* Unable to abort - command is currently on the bus. Grin and bear it. */
#define SCSI_ABORT_BUSY 3
/* The command is not active in the low level code. Command probably
* finished. */
#define SCSI_ABORT_NOT_RUNNING 4
/* Something went wrong. The low level driver will indicate the correct
* error condition when it calls scsi_done, so the mid-level abort function
* can simply wait until this comes through */
#define SCSI_ABORT_ERROR 5
/* We do not know how to reset the bus, or we do not want to. Bummer.
* Anyway, just wait a little more for the command in question, and hope that
* it eventually finishes. If it never finishes, the SCSI device could
* hang, so use this with caution. */
#define SCSI_RESET_SNOOZE 0
/* We do not know how to reset the bus, or we do not want to. Bummer.
* We have given up on this ever completing. The mid-level code will
* request sense information to decide how to proceed from here. */
#define SCSI_RESET_PUNT 1
/* This means that we were able to reset the bus. We have restarted all of
* the commands that should be restarted, and we should be able to continue
* on normally from here. We do not expect any interrupts that will return
* DID_RESET to any of the other commands in the host_queue, and the mid-level
* code does not need to do anything special to keep the commands alive.
* If a hard reset was performed then all outstanding commands on the
* bus have been restarted. */
#define SCSI_RESET_SUCCESS 2
/* We called for a reset of this bus, and we should get an interrupt
* when this succeeds. Each command should get its own status
* passed up to scsi_done, but this has not happened yet.
* If a hard reset was performed, then we expect an interrupt
* for *each* of the outstanding commands that will have the
* effect of restarting the commands.
*/
#define SCSI_RESET_PENDING 3
/* We did a reset, but do not expect an interrupt to signal DID_RESET.
* This tells the upper level code to request the sense info, and this
* should keep the command alive. */
#define SCSI_RESET_WAKEUP 4
/* Something went wrong, and we do not know how to fix it. */
#define SCSI_RESET_ERROR 5
/*
* This is a bitmask that is ored with one of the above codes.
* It tells the mid-level code that we did a hard reset.
*/
#define SCSI_RESET_BUS_RESET 0x100
/*
* Used to mask off bits and to obtain the basic action that was
* performed.
*/
#define SCSI_RESET_ACTION 0xff
void * scsi_malloc(unsigned int);
int scsi_free(void *, unsigned int);
extern unsigned int dma_free_sectors; /* How much room do we have left */
extern unsigned int need_isa_buffer; /* True if some devices need indirection
* buffers */
/*
* The Scsi_Cmnd structure is used by scsi.c internally, and for communication
* with low level drivers that support multiple outstanding commands.
*/
typedef struct scsi_pointer {
char * ptr; /* data pointer */
int this_residual; /* left in this buffer */
struct scatterlist *buffer; /* which buffer */
int buffers_residual; /* how many buffers left */
volatile int Status;
volatile int Message;
volatile int have_data_in;
volatile int sent_command;
volatile int phase;
} Scsi_Pointer;
typedef struct scsi_cmnd {
struct Scsi_Host * host;
Scsi_Device * device;
unsigned char target, lun, channel;
unsigned char cmd_len;
unsigned char old_cmd_len;
struct scsi_cmnd *next, *prev;
/* These elements define the operation we are about to perform */
unsigned char cmnd[12];
unsigned request_bufflen; /* Actual request size */
void * request_buffer; /* Actual requested buffer */
/* These elements define the operation we ultimately want to perform */
unsigned char data_cmnd[12];
unsigned short old_use_sg; /* We save use_sg here when requesting
* sense info */
unsigned short use_sg; /* Number of pieces of scatter-gather */
unsigned short sglist_len; /* size of malloc'd scatter-gather list */
unsigned short abort_reason;/* If the mid-level code requests an
* abort, this is the reason. */
unsigned bufflen; /* Size of data buffer */
void *buffer; /* Data buffer */
unsigned underflow; /* Return error if less than this amount is
* transfered */
unsigned transfersize; /* How much we are guaranteed to transfer with
* each SCSI transfer (ie, between disconnect /
* reconnects. Probably == sector size */
struct request request; /* A copy of the command we are working on */
unsigned char sense_buffer[16]; /* Sense for this command, if needed */
int retries;
int allowed;
int timeout_per_command, timeout_total, timeout;
/*
* We handle the timeout differently if it happens when a reset,
* abort, etc are in process.
*/
unsigned volatile char internal_timeout;
unsigned flags;
/* These variables are for the cdrom only. Once we have variable size
* buffers in the buffer cache, they will go away. */
int this_count;
/* End of special cdrom variables */
/* Low-level done function - can be used by low-level driver to point
* to completion function. Not used by mid/upper level code. */
void (*scsi_done)(struct scsi_cmnd *);
void (*done)(struct scsi_cmnd *); /* Mid-level done function */
/*
* The following fields can be written to by the host specific code.
* Everything else should be left alone.
*/
Scsi_Pointer SCp; /* Scratchpad used by some host adapters */
unsigned char * host_scribble; /* The host adapter is allowed to
* call scsi_malloc and get some memory
* and hang it here. The host adapter
* is also expected to call scsi_free
* to release this memory. (The memory
* obtained by scsi_malloc is guaranteed
* to be at an address < 16Mb). */
int result; /* Status code from lower level driver */
unsigned char tag; /* SCSI-II queued command tag */
unsigned long pid; /* Process ID, starts at 0 */
} Scsi_Cmnd;
/*
* scsi_abort aborts the current command that is executing on host host.
* The error code, if non zero is returned in the host byte, otherwise
* DID_ABORT is returned in the hostbyte.
*/
extern int scsi_abort (Scsi_Cmnd *, int code, int pid);
extern void scsi_do_cmd (Scsi_Cmnd *, const void *cmnd ,
void *buffer, unsigned bufflen,
void (*done)(struct scsi_cmnd *),
int timeout, int retries);
extern Scsi_Cmnd * allocate_device(struct request **, Scsi_Device *, int);
extern Scsi_Cmnd * request_queueable(struct request *, Scsi_Device *);
extern int scsi_reset (Scsi_Cmnd *, int);
extern int max_scsi_hosts;
extern void proc_print_scsidevice(Scsi_Device *, char *, int *, int);
extern void print_command(unsigned char *);
extern void print_sense(const char *, Scsi_Cmnd *);
extern void print_driverbyte(int scsiresult);
extern void print_hostbyte(int scsiresult);
extern void scsi_mark_host_bus_reset(struct Scsi_Host *Host);
#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR)
#include "hosts.h"
static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
{
struct request * req;
struct buffer_head * bh;
req = &SCpnt->request;
if (!uptodate) {
printk(DEVICE_NAME " I/O error: dev %s, sector %lu\n",
kdevname(req->rq_dev), req->sector);
#ifdef MACH
req->errors = 1;
while (req->bh) {
bh = req->bh;
req->bh = bh->b_reqnext;
mark_buffer_uptodate(bh, 0);
unlock_buffer(bh);
}
goto done;
#endif
}
do {
if ((bh = req->bh) != NULL) {
req->bh = bh->b_reqnext;
req->nr_sectors -= bh->b_size >> 9;
req->sector += bh->b_size >> 9;
bh->b_reqnext = NULL;
mark_buffer_uptodate(bh, uptodate);
unlock_buffer(bh);
sectors -= bh->b_size >> 9;
if ((bh = req->bh) != NULL) {
req->current_nr_sectors = bh->b_size >> 9;
if (req->nr_sectors < req->current_nr_sectors) {
req->nr_sectors = req->current_nr_sectors;
printk("end_scsi_request: buffer-list destroyed\n");
}
}
}
} while(sectors && bh);
if (req->bh){
req->buffer = bh->b_data;
return SCpnt;
};
#ifdef MACH
req->errors = 0;
done:
#endif
DEVICE_OFF(req->rq_dev);
if (req->sem != NULL) {
up(req->sem);
}
if (SCpnt->host->block) {
struct Scsi_Host * next;
for (next = SCpnt->host->block; next != SCpnt->host;
next = next->block)
wake_up(&next->host_wait);
}
req->rq_status = RQ_INACTIVE;
#ifndef MACH
wake_up(&wait_for_request);
#endif
wake_up(&SCpnt->device->device_wait);
#ifdef MACH
{
unsigned long flags;
save_flags(flags);
cli();
(*blk_dev[MAJOR(req->rq_dev)].request_fn)();
restore_flags(flags);
}
#endif
return NULL;
}
/* This is just like INIT_REQUEST, but we need to be aware of the fact
* that an interrupt may start another request, so we run this with interrupts
* turned off
*/
#define INIT_SCSI_REQUEST \
if (!CURRENT) { \
CLEAR_INTR; \
restore_flags(flags); \
return; \
} \
if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
panic(DEVICE_NAME ": request list destroyed");\
if (CURRENT->bh) { \
if (!buffer_locked(CURRENT->bh)) \
panic(DEVICE_NAME ": block not locked"); \
}
#endif
#ifdef MACH
#define SCSI_SLEEP(QUEUE, CONDITION) { \
if (CONDITION) { \
struct wait_queue wait = { NULL, NULL}; \
add_wait_queue(QUEUE, &wait); \
for(;;) { \
if (CONDITION) { \
if (intr_count) \
panic("scsi: trying to call schedule() in interrupt" \
", file %s, line %d.\n", __FILE__, __LINE__); \
schedule(); \
} \
else \
break; \
} \
remove_wait_queue(QUEUE, &wait);\
}; }
#else /* ! MACH */
#define SCSI_SLEEP(QUEUE, CONDITION) { \
if (CONDITION) { \
struct wait_queue wait = { current, NULL}; \
add_wait_queue(QUEUE, &wait); \
for(;;) { \
current->state = TASK_UNINTERRUPTIBLE; \
if (CONDITION) { \
if (intr_count) \
panic("scsi: trying to call schedule() in interrupt" \
", file %s, line %d.\n", __FILE__, __LINE__); \
schedule(); \
} \
else \
break; \
} \
remove_wait_queue(QUEUE, &wait);\
current->state = TASK_RUNNING; \
}; }
#endif /* ! MACH */
#endif
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-indent-level: 4
* c-brace-imaginary-offset: 0
* c-brace-offset: -4
* c-argdecl-indent: 4
* c-label-offset: -4
* c-continued-statement-offset: 4
* c-continued-brace-offset: 0
* indent-tabs-mode: nil
* tab-width: 8
* End:
*/
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.