📄 aha_scsi.c
字号:
FORWARD _PROTOTYPE( int s_do_ioctl, (struct driver *dp, message *m_ptr) );
FORWARD _PROTOTYPE( int scsi_simple, (int opcode, int count) );
FORWARD _PROTOTYPE( void group0, (void) );
FORWARD _PROTOTYPE( void group1, (void) );
FORWARD _PROTOTYPE( int scsi_command, (phys_bytes data, vir_bytes len) );
FORWARD _PROTOTYPE( void aha_command, (int outlen, byte *outptr,
int inlen, byte *inptr) );
FORWARD _PROTOTYPE( int aha_reset, (void) );
FORWARD _PROTOTYPE( int s_handler, (int irq) );
FORWARD _PROTOTYPE( void h2b16, (big16 b, U16_t l) );
FORWARD _PROTOTYPE( void h2b24, (big24 b, u32_t l) );
FORWARD _PROTOTYPE( void h2b32, (big32 b, u32_t l) );
FORWARD _PROTOTYPE( u16_t b2h16, (big16 b) );
FORWARD _PROTOTYPE( u32_t b2h24, (big24 b) );
FORWARD _PROTOTYPE( u32_t b2h32, (big32 b) );
#if AHA_DEBUG & 2
FORWARD _PROTOTYPE( void errordump, (void) );
#else
#define errordump()
#endif
#if AHA_DEBUG & 4
FORWARD _PROTOTYPE( void show_req, (void) );
#else
#define show_req()
#endif
#if AHA_DEBUG & 8
FORWARD _PROTOTYPE( void dump_scsi_cmd, (void) );
#else
#define dump_scsi_cmd()
#endif
FORWARD _PROTOTYPE( void s_geometry, (struct partition *entry));
/* Entry points to this driver. */
PRIVATE struct driver s_dtab = {
s_name, /* current device's name */
s_do_open, /* open or mount request, initialize device */
s_do_close, /* release device */
s_do_ioctl, /* tape and partition ioctls */
s_prepare, /* prepare for I/O on a given minor device */
s_schedule, /* precompute SCSI transfer parameters, etc. */
s_finish, /* do the I/O */
nop_cleanup, /* no cleanup needed */
s_geometry /* tell the geometry of the disk */
};
/*===========================================================================*
* aha_scsi_task *
*===========================================================================*/
PUBLIC void aha_scsi_task()
{
/* Set target and logical unit numbers, then call the generic main loop. */
int i;
struct scsi *sp;
long v;
char *name;
static char fmt[] = "d,d";
for (i = 0; i < MAX_DEVICES; i++) {
(void) s_prepare(i * DEV_PER_DRIVE);
sp = s_sp;
/* Look into the environment for special parameters. */
name = s_name();
v = i;
(void) env_parse(name, fmt, 0, &v, 0L, 7L);
sp->targ = v;
v = 0;
(void) env_parse(name, fmt, 1, &v, 0L, 7L);
sp->lun = v;
}
driver_task(&s_dtab);
}
/*===========================================================================*
* s_prepare *
*===========================================================================*/
PRIVATE struct device *s_prepare(device)
int device;
{
/* Prepare for I/O on a device. */
rq->count = 0; /* no requests as yet */
s_must = TRUE; /* the first transfers must be done */
s_buf_blk = -1; /* invalidate s_buf_blk */
if (device < NR_DISKDEVS) { /* sd0, sd1, ... */
s_type = TYPE_SD;
s_sp = &scsi[device / DEV_PER_DRIVE];
s_dv = &s_sp->part[device % DEV_PER_DRIVE];
} else
if ((unsigned) (device - MINOR_hd1a) < NR_SUBDEVS) { /* sd1a, sd1b, ... */
device -= MINOR_hd1a;
s_type = TYPE_SD;
s_sp = &scsi[device / SUB_PER_DRIVE];
s_dv = &s_sp->subpart[device % SUB_PER_DRIVE];
} else
if ((unsigned) (device - MINOR_st0) < NR_TAPEDEVS) { /* nrst0, rst0, ... */
device -= MINOR_st0;
s_type = device & 1 ? TYPE_RST : TYPE_NRST;
s_sp = &scsi[device >> 1];
s_dv = &s_sp->dummypart;
} else {
return(NIL_DEV);
}
return(s_dv);
}
/*===========================================================================*
* s_name *
*===========================================================================*/
PRIVATE char *s_name()
{
/* Return a name for the current device. */
static char name[] = "sd35";
int n = (s_sp - scsi);
switch (s_type) {
case TYPE_SD: /* Disk device: sd* */
name[1] = 'd';
n *= DEV_PER_DRIVE;
break;
case TYPE_RST: /* Tape device: st* */
case TYPE_NRST:
name[1] = 't';
break;
}
if (n < 10) {
name[2] = '0' + n;
name[3] = 0;
} else {
name[2] = '0' + n / 10;
name[3] = '0' + n % 10;
}
return name;
}
/*===========================================================================*
* s_do_open *
*===========================================================================*/
PRIVATE int s_do_open(dp, m_ptr)
struct driver *dp;
message *m_ptr;
{
struct scsi *sp;
int r;
if (aha_irq == 0 && !aha_reset()) return(EIO); /* no controller, forget it */
if (s_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
sp = s_sp;
if ((r = scsi_probe()) != OK) return(r);
if (sp->state & S_RDONLY && m_ptr->COUNT & W_BIT) return(EACCES);
switch (sp->devtype) {
case SCSI_DEVDISK:
case SCSI_DEVWORM:
case SCSI_DEVCDROM:
case SCSI_DEVOPTICAL:
/* Read partition tables on first open. */
if (sp->open_ct == 0) {
partition(&s_dtab, (int) (sp-scsi) * DEV_PER_DRIVE, P_PRIMARY);
}
break;
case SCSI_DEVTAPE:
/* Make sure tape is not already open. */
if (sp->open_ct > 0) return(EBUSY);
sp->open_mode = m_ptr->COUNT;
/* If open(..., O_WRONLY) then write a filemark on close even if no
* write is done.
*/
sp->need_eof = ((sp->open_mode & (R_BIT|W_BIT)) == W_BIT);
break;
}
sp->open_ct++;
return(OK);
}
/*===========================================================================*
* scsi_probe *
*===========================================================================*/
PRIVATE int scsi_probe()
{
/* See if a device exists and if it is ready. */
struct scsi *sp = s_sp;
sense_t *sense;
int r, key;
/* Something out there? */
if ((r = scsi_sense()) != OK) {
if (sp->state & S_PRESENT) {
printf("%s: offline\n", s_name());
sp->state = 0;
}
return(r);
}
if (!(sp->state & S_PRESENT)) {
/* First contact with a new device, what type is it? */
if ((r = scsi_inquiry()) != OK) return(r);
sp->devtype = inqdata.devtype;
}
if (!(sp->state & S_READY)) {
/* If it's a disk: start it, if it's a tape: load it. */
(void) scsi_simple(SCSI_STRTSTP, 1);
}
/* See if the unit is ready for I/O. A disk may be spinning up, a
* floppy or tape drive may be empty.
*/
while ((key = scsi_simple(SCSI_UNITRDY, 0)) != SENSE_NO_SENSE) {
/* Not ready, why? */
sp->state &= ~S_READY;
switch (key) {
case SENSE_UNIT_ATT:
/* A media change or something, try again. */
break;
case SENSE_NOT_READY:
/* Look at the additional sense data to see why it isn't
* ready.
*/
sense = &ccb_sense(rq);
switch ((sense->add_code << 8) | sense->add_qual) {
case 0x0401:
/* "It is becoming ready." Fine, we wait. */
milli_delay(1000);
break;
case 0x0402:
/* "Initialization command required." So we tell it
* to spin up.
*/
if (scsi_simple(SCSI_STRTSTP, 1) != SENSE_NO_SENSE)
return(EIO);
break;
case 0x0403:
/* "Manual intervention required." */
case 0x3A00:
/* "No media present." */
printf("%s: no media loaded\n", s_name());
return(EIO);
default:
/* For some reason it is not usable. */
printf("%s: not ready\n", s_name());
return(EIO);
}
break;
default:
/* The device is in some odd state. */
if (key != SENSE_NOT_READY) {
printf("%s: hardware error\n", s_name());
return(EIO);
}
}
}
if (!(sp->state & S_PRESENT)) {
/* Do the inquiry again, the message may have changed. */
if (scsi_inquiry() != OK) return(EIO);
/* Tell what kind of device it is we have found. */
printf("%s: %-7s %.48s\n",
s_name(),
inqdata.devtype > SCSI_DEVMAX ? "UNKNOWN"
: scsi_devstr[inqdata.devtype],
inqdata.vendor /* + product + revision + extra */);
}
if (!(sp->state & S_READY)) {
/* Get the geometry, limits, etc. */
switch (sp->devtype) {
case SCSI_DEVDISK:
case SCSI_DEVWORM:
case SCSI_DEVCDROM:
case SCSI_DEVOPTICAL:
if (scsi_ndisk() != OK) return(EIO);
break;
case SCSI_DEVTAPE:
if (scsi_ntape() != OK) return(EIO);
break;
default:
printf("%s: unsupported\n", s_name());
return(EIO);
}
}
return(OK);
}
/*===========================================================================*
* scsi_sense *
*===========================================================================*/
PRIVATE int scsi_sense()
{
int key;
sense_t *sense = (sense_t *) tmp_buf;
/* Do a request sense to find out if a target exists or to check out
* a unit attention condition.
*/
key = scsi_simple(SCSI_REQSENSE, sizeof(sense_t));
if (rq->ccb.hastat == HST_TIMEOUT) return(ENXIO); /* nothing there */
if (rq->ccb.hastat != 0) return(EIO); /* something very bad */
/* There is something out there for sure. */
if (key == SENSE_UNIT_ATT || sense_key(sense->key) == SENSE_UNIT_ATT) {
/* Device is in a "look at me" state, probably changed media. */
s_sp->state &= ~S_READY;
}
return(OK);
}
/*===========================================================================*
* scsi_inquiry *
*===========================================================================*/
PRIVATE int scsi_inquiry()
{
/* Prefill with nulls. */
memset(tmp_buf, '\0', sizeof(inquiry_t));
/* Do a SCSI inquiry. */
if (scsi_simple(SCSI_INQUIRY, sizeof(inquiry_t)) != SENSE_NO_SENSE)
return(EIO);
inqdata = * (inquiry_t *) tmp_buf;
if (inqdata.len == 0) {
/* The device doesn't return meaningful text fields. */
strcpy(inqdata.vendor, "(unknown)");
}
/* The top three bits of devtype must be zero for the lun to exist. */
if ((inqdata.devtype & 0xE0) != 0) return(ENXIO);
return(OK);
}
/*===========================================================================*
* scsi_ndisk *
*===========================================================================*/
PRIVATE int scsi_ndisk()
{
/* Gather disk data, capacity and block size. */
struct scsi *sp = s_sp;
unsigned long capacity = -1, block_size = SECTOR_SIZE;
byte *buf = tmp_buf;
/* Minor device type must be for a disk. */
if (s_type != TYPE_SD) return(EIO);
if (sp->devtype == SCSI_DEVCDROM) {
/* Read-only by definition. */
sp->state |= S_RDONLY;
} else {
/* SCSI modesense to find out if the disk is write protected. */
if (scsi_simple(SCSI_MDSENSE, 255) != SENSE_NO_SENSE) return(EIO);
/* Write protected? */
sp->state &= ~S_RDONLY;
if (buf[2] & 0x80) sp->state |= S_RDONLY;
/* Don't write a worm disk, not wise at the moment. */
if (sp->devtype == SCSI_DEVWORM) sp->state |= S_RDONLY;
}
/* Get drive capacity and block size. */
group1();
rq->ccb.opcode = CCB_INIT;
ccb_cmd1(rq).scsi_op = SCSI_CAPACITY;
if (scsi_command(tmp_phys, 8) == SENSE_NO_SENSE) {
capacity = b2h32(buf + 0) + 1;
block_size = b2h32(buf + 4);
printf("%s: capacity %lu x %lu bytes\n",
s_name(), capacity, block_size);
} else {
printf("%s: unknown capacity\n", s_name());
}
/* We do not believe block sizes over 4 kb. */
if (block_size > 4096) {
printf("%s: can't handle %lu byte blocks\n", s_name(), block_size);
return(EIO);
}
sp->block_size = block_size;
#if _WORD_SIZE > 2
/* Keep it within reach of a group 0 command. */
sp->count_max = 0x100 * block_size;
#else
sp->count_max = block_size > UINT_MAX/0x100 ? UINT_MAX : 0x100 * block_size;
#endif
/* The fun ends at 4GB. */
if (capacity > ((unsigned long) -1) / block_size)
sp->part[0].dv_size = -1;
else
sp->part[0].dv_size = capacity * block_size;
/* Finally we recognize its existence. */
sp->state |= S_PRESENT|S_READY;
return(OK);
}
/*===========================================================================*
* scsi_ntape *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -