diff options
| author | mycroft <mycroft@NetBSD.org> | 1993-11-24 04:52:44 +0000 |
|---|---|---|
| committer | mycroft <mycroft@NetBSD.org> | 1993-11-24 04:52:44 +0000 |
| commit | 37548328a127374ec0fe45b359c28b2234efd451 (patch) | |
| tree | 677535d657c3fa228d09bf919caf5a362aeb0722 /sys/dev | |
| parent | 2a9aacbf1cc2963a0155bf997b57e58ca4ebb331 (diff) | |
Under construction...
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/scsipi/scsi_base.c | 846 | ||||
| -rw-r--r-- | sys/dev/scsipi/scsipi_debug.h | 53 | ||||
| -rw-r--r-- | sys/dev/scsipi/scsipi_ioctl.c | 329 | ||||
| -rw-r--r-- | sys/dev/scsipi/su.c | 4 | ||||
| -rw-r--r-- | sys/dev/scsipi/uk.c | 157 |
5 files changed, 1389 insertions, 0 deletions
diff --git a/sys/dev/scsipi/scsi_base.c b/sys/dev/scsipi/scsi_base.c new file mode 100644 index 00000000000..ccc89e65395 --- /dev/null +++ b/sys/dev/scsipi/scsi_base.c @@ -0,0 +1,846 @@ +/* + * Written by Julian Elischer (julian@dialix.oz.au) + * $Id: scsi_base.c,v 1.1 1993/11/24 04:52:46 mycroft Exp $ + */ + +#define SPLSD splbio +#define ESUCCESS 0 +#include <sys/types.h> +#include <sys/param.h> +#include <sys/buf.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/errno.h> +#include <sys/device.h> +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +#ifdef __NetBSD__ +#ifdef DDB +int Debugger(); +#else /* DDB */ +#define Debugger() +#endif /* DDB */ +#else /* NetBSD */ +#include <ddb.h> +#if NDDB > 0 +int Debugger(); +#else /* NDDB > 0 */ +#define Debugger() +#endif /* NDDB > 0 */ +#endif + +void sc_print_addr __P((struct scsi_link *sc_link)); + +struct scsi_xfer *next_free_xs; + +/* + * Get a scsi transfer structure for the caller. Charge the structure + * to the device that is referenced by the sc_link structure. If the + * sc_link structure has no 'credits' then the device already has the + * maximum number or outstanding operations under way. In this stage, + * wait on the structure so that when one is freed, we are awoken again + * If the SCSI_NOSLEEP flag is set, then do not wait, but rather, return + * a NULL pointer, signifying that no slots were available + * Note in the link structure, that we are waiting on it. + */ + +struct scsi_xfer * +get_xs(sc_link, flags) + struct scsi_link *sc_link; /* who to charge the xs to */ + u_int32 flags; /* if this call can sleep */ +{ + struct scsi_xfer *xs; + u_int32 s; + + SC_DEBUG(sc_link, SDEV_DB3, ("get_xs\n")); + s = splbio(); + while (!sc_link->opennings) { + SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n")); + if (flags & SCSI_NOSLEEP) { + splx(s); + return 0; + } + sc_link->flags |= SDEV_WAITING; + sleep(sc_link, PRIBIO); + } + sc_link->opennings--; + if (xs = next_free_xs) { + next_free_xs = xs->next; + splx(s); + } else { + splx(s); + SC_DEBUG(sc_link, SDEV_DB3, ("making\n")); + xs = malloc(sizeof(*xs), M_TEMP, + ((flags & SCSI_NOSLEEP) ? M_NOWAIT : M_WAITOK)); + if (xs == NULL) { + sc_print_addr(sc_link); + printf("cannot allocate scsi xs\n"); + return (NULL); + } + } + SC_DEBUG(sc_link, SDEV_DB3, ("returning\n")); + xs->sc_link = sc_link; + return (xs); +} + +/* + * Given a scsi_xfer struct, and a device (referenced through sc_link) + * return the struct to the free pool and credit the device with it + * If another process is waiting for an xs, do a wakeup, let it proceed + */ +void +free_xs(xs, sc_link, flags) + struct scsi_xfer *xs; + struct scsi_link *sc_link; /* who to credit for returning it */ + u_int32 flags; +{ + xs->next = next_free_xs; + next_free_xs = xs; + + SC_DEBUG(sc_link, SDEV_DB3, ("free_xs\n")); + /* if was 0 and someone waits, wake them up */ + if ((!sc_link->opennings++) && (sc_link->flags & SDEV_WAITING)) { + wakeup(sc_link); + } else { + if (sc_link->device->start) { + SC_DEBUG(sc_link, SDEV_DB2, ("calling private start()\n")); + (*(sc_link->device->start)) (sc_link->dev_unit); + } + } +} + +/* + * Find out from the device what its capacity is. + */ +u_int32 +scsi_size(sc_link, flags) + struct scsi_link *sc_link; + u_int32 flags; +{ + struct scsi_read_cap_data rdcap; + struct scsi_read_capacity scsi_cmd; + u_int32 size; + + /* + * make up a scsi command and ask the scsi driver to do + * it for you. + */ + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = READ_CAPACITY; + + /* + * If the command works, interpret the result as a 4 byte + * number of blocks + */ + if (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) & rdcap, + sizeof(rdcap), + 2, + 20000, + NULL, + flags | SCSI_DATA_IN) != 0) { + + sc_print_addr(sc_link); + printf("could not get size\n"); + return (0); + } else { + size = rdcap.addr_0 + 1; + size += rdcap.addr_1 << 8; + size += rdcap.addr_2 << 16; + size += rdcap.addr_3 << 24; + } + return (size); +} + +/* + * Get scsi driver to send a "are you ready?" command + */ +errval +scsi_test_unit_ready(sc_link, flags) + struct scsi_link *sc_link; + u_int32 flags; +{ + struct scsi_test_unit_ready scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = TEST_UNIT_READY; + + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2, + 100000, + NULL, + flags)); +} + +/* + * Do a scsi operation, asking a device to run as SCSI-II if it can. + */ +errval +scsi_change_def(sc_link, flags) + struct scsi_link *sc_link; + u_int32 flags; +{ + struct scsi_changedef scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = CHANGE_DEFINITION; + scsi_cmd.how = SC_SCSI_2; + + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2, + 100000, + NULL, + flags)); +} + +/* + * Do a scsi operation asking a device what it is + * Use the scsi_cmd routine in the switch table. + */ +errval +scsi_inquire(sc_link, inqbuf, flags) + struct scsi_link *sc_link; + struct scsi_inquiry_data *inqbuf; + u_int32 flags; +{ + struct scsi_inquiry scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = INQUIRY; + scsi_cmd.length = sizeof(struct scsi_inquiry_data); + + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + (u_char *) inqbuf, + sizeof(struct scsi_inquiry_data), + 2, + 100000, + NULL, + SCSI_DATA_IN | flags)); +} + +/* + * Prevent or allow the user to remove the media + */ +errval +scsi_prevent(sc_link, type, flags) + struct scsi_link *sc_link; + u_int32 type, flags; +{ + struct scsi_prevent scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = PREVENT_ALLOW; + scsi_cmd.how = type; + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2, + 5000, + NULL, + flags)); +} + +/* + * Get scsi driver to send a "start up" command + */ +errval +scsi_start_unit(sc_link, flags) + struct scsi_link *sc_link; + u_int32 flags; +{ + struct scsi_start_stop scsi_cmd; + + bzero(&scsi_cmd, sizeof(scsi_cmd)); + scsi_cmd.op_code = START_STOP; + scsi_cmd.how = SSS_START; + + return (scsi_scsi_cmd(sc_link, + (struct scsi_generic *) &scsi_cmd, + sizeof(scsi_cmd), + 0, + 0, + 2, + 2000, + NULL, + flags)); +} + +/* + * This routine is called by the scsi interrupt when the transfer is complete. + */ +void +scsi_done(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *sc_link = xs->sc_link; + struct buf *bp = xs->bp; + errval retval; + + SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n")); +#ifdef SCSIDEBUG + if (sc_link->flags & SDEV_DB1) + { + show_scsi_cmd(xs); + } +#endif /*SCSIDEBUG */ + /* + * If it's a user level request, bypass all usual completion processing, + * let the user work it out.. We take reponsibility for freeing the + * xs when the user returns. (and restarting the device's queue). + */ + if (xs->flags & SCSI_USER) { + biodone(xs->bp); +#ifdef NOTNOW + SC_DEBUG(sc_link, SDEV_DB3, ("calling user done()\n")); + scsi_user_done(xs); /* to take a copy of the sense etc. */ + SC_DEBUG(sc_link, SDEV_DB3, ("returned from user done()\n ")); +#endif + free_xs(xs, sc_link, SCSI_NOSLEEP); /* restarts queue too */ + SC_DEBUG(sc_link, SDEV_DB3, ("returning to adapter\n")); + return; + } + /* + * If the device has it's own done routine, call it first. + * If it returns a legit error value, return that, otherwise + * it wants us to continue with normal processing. + */ + + if (sc_link->device->done) { + SC_DEBUG(sc_link, SDEV_DB2, ("calling private done()\n")); + retval = (*sc_link->device->done) (xs); + if (retval == -1) { + free_xs(xs, sc_link, SCSI_NOSLEEP); /*XXX */ + return; /* it did it all, finish up */ + } + if (retval == -2) { + return; /* it did it all, finish up */ + } + SC_DEBUG(sc_link, SDEV_DB3, ("continuing with generic done()\n")); + } + if ((bp = xs->bp) == NULL) { + /* + * if it's a normal upper level request, then ask + * the upper level code to handle error checking + * rather than doing it here at interrupt time + */ + wakeup(xs); + return; + } + /* + * Go and handle errors now. + * If it returns -1 then we should RETRY + */ + if ((retval = sc_err1(xs)) == -1) { + if ((*(sc_link->adapter->scsi_cmd)) (xs) + == SUCCESSFULLY_QUEUED) { /* don't wake the job, ok? */ + return; + } + xs->flags |= ITSDONE; + } + free_xs(xs, sc_link, SCSI_NOSLEEP); /* does a start if needed */ + biodone(bp); +} + +/* + * ask the scsi driver to perform a command for us. + * tell it where to read/write the data, and how + * long the data is supposed to be. If we have a buf + * to associate with the transfer, we need that too. + */ +errval +scsi_scsi_cmd(sc_link, scsi_cmd, cmdlen, data_addr, datalen, + retries, timeout, bp, flags) + struct scsi_link *sc_link; + struct scsi_generic *scsi_cmd; + u_int32 cmdlen; + u_char *data_addr; + u_int32 datalen; + u_int32 retries; + u_int32 timeout; + struct buf *bp; + u_int32 flags; +{ + struct scsi_xfer *xs; + errval retval; + u_int32 s; + + if (bp) flags |= SCSI_NOSLEEP; + SC_DEBUG(sc_link, SDEV_DB2, ("scsi_cmd\n")); + + xs = get_xs(sc_link, flags); /* should wait unless booting */ + if (!xs) return (ENOMEM); + /* + * Fill out the scsi_xfer structure. We don't know whose context + * the cmd is in, so copy it. + */ + bcopy(scsi_cmd, &(xs->cmdstore), cmdlen); + xs->flags = INUSE | flags; + xs->sc_link = sc_link; + xs->retries = retries; + xs->timeout = timeout; + xs->cmd = &xs->cmdstore; + xs->cmdlen = cmdlen; + xs->data = data_addr; + xs->datalen = datalen; + xs->resid = datalen; + xs->bp = bp; +/*XXX*/ /*use constant not magic number */ + if (datalen && ((caddr_t) data_addr < (caddr_t) 0xfe000000)) { + if (bp) { + printf("Data buffered space not in kernel context\n"); +#ifdef SCSIDEBUG + show_scsi_cmd(xs); +#endif /* SCSIDEBUG */ + retval = EFAULT; + goto bad; + } + xs->data = malloc(datalen, M_TEMP, M_WAITOK); + /* I think waiting is ok *//*XXX */ + switch (flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { + case 0: + printf("No direction flags, assuming both\n"); +#ifdef SCSIDEBUG + show_scsi_cmd(xs); +#endif /* SCSIDEBUG */ + case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */ + case SCSI_DATA_OUT: + bcopy(data_addr, xs->data, datalen); + break; + case SCSI_DATA_IN: + bzero(xs->data, datalen); + } + } +retry: + xs->error = XS_NOERROR; +#ifdef PARANOID + if (datalen && ((caddr_t) xs->data < (caddr_t) 0xfe000000)) { + printf("It's still wrong!\n"); + } +#endif /*PARANOID*/ +#ifdef SCSIDEBUG + if (sc_link->flags & SDEV_DB3) show_scsi_xs(xs); +#endif /* SCSIDEBUG */ + /* + * Do the transfer. If we are polling we will return: + * COMPLETE, Was poll, and scsi_done has been called + * TRY_AGAIN_LATER, Adapter short resources, try again + * + * if under full steam (interrupts) it will return: + * SUCCESSFULLY_QUEUED, will do a wakeup when complete + * TRY_AGAIN_LATER, (as for polling) + * After the wakeup, we must still check if it succeeded + * + * If we have a bp however, all the error proccessing + * and the buffer code both expect us to return straight + * to them, so as soon as the command is queued, return + */ + + retval = (*(sc_link->adapter->scsi_cmd)) (xs); + + switch (retval) { + case SUCCESSFULLY_QUEUED: + if (bp) + return retval; /* will sleep (or not) elsewhere */ + s = splbio(); + while (!(xs->flags & ITSDONE)) + sleep(xs, PRIBIO + 1); + splx(s); + /* fall through to check success of completed command */ + case COMPLETE: /* Polling command completed ok */ +/*XXX*/ case HAD_ERROR: /* Polling command completed with error */ + SC_DEBUG(sc_link, SDEV_DB3, ("back in cmd()\n")); + if ((retval = sc_err1(xs)) == -1) + goto retry; + break; + + case TRY_AGAIN_LATER: /* adapter resource shortage */ + SC_DEBUG(sc_link, SDEV_DB3, ("will try again \n")); + /* should sleep 1 sec here */ + if (xs->retries--) { + xs->flags &= ~ITSDONE; + goto retry; + } + default: + retval = EIO; + } + /* + * If we had to copy the data out of the user's context, + * then do the other half (copy it back or whatever) + * and free the memory buffer + */ + if (datalen && (xs->data != data_addr)) { + switch (flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { + case 0: + case SCSI_DATA_IN | SCSI_DATA_OUT: /* weird */ + case SCSI_DATA_IN: + bcopy(xs->data, data_addr, datalen); + break; + } + free(xs->data, M_TEMP); + } + /* + * we have finished with the xfer stuct, free it and + * check if anyone else needs to be started up. + */ +bad: + free_xs(xs, sc_link, flags); /* includes the 'start' op */ + if (bp && retval) { + bp->b_error = retval; + bp->b_flags |= B_ERROR; + biodone(bp); + } + return (retval); +} + +errval +sc_err1(xs) + struct scsi_xfer *xs; +{ + struct buf *bp = xs->bp; + errval retval; + + SC_DEBUG(xs->sc_link, SDEV_DB3, ("sc_err1,err = 0x%x \n", xs->error)); + /* + * If it has a buf, we might be working with + * a request from the buffer cache or some other + * piece of code that requires us to process + * errors at inetrrupt time. We have probably + * been called by scsi_done() + */ + switch (xs->error) { + case XS_NOERROR: /* nearly always hit this one */ + retval = ESUCCESS; + if (bp) { + bp->b_error = 0; + bp->b_resid = 0; + } + break; + + case XS_SENSE: + if (bp) { + bp->b_error = 0; + bp->b_resid = 0; + if (retval = (scsi_interpret_sense(xs))) { + bp->b_flags |= B_ERROR; + bp->b_error = retval; + bp->b_resid = bp->b_bcount; + } + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("scsi_interpret_sense (bp) returned %d\n", retval)); + } else { + retval = (scsi_interpret_sense(xs)); + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("scsi_interpret_sense (no bp) returned %d\n", retval)); + } + break; + + case XS_BUSY: + /*should somehow arange for a 1 sec delay here (how?) */ + case XS_TIMEOUT: + /* + * If we can, resubmit it to the adapter. + */ + if (xs->retries--) { + xs->error = XS_NOERROR; + xs->flags &= ~ITSDONE; + goto retry; + } + /* fall through */ + case XS_DRIVER_STUFFUP: + if (bp) { + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + } + retval = EIO; + break; + default: + retval = EIO; + sc_print_addr(xs->sc_link); + printf("unknown error category from scsi driver\n"); + } + return retval; +retry: + return (-1); +} + +/* + * Look at the returned sense and act on the error, determining + * the unix error number to pass back. (0 = report no error) + * + * THIS IS THE DEFAULT ERROR HANDLER + */ +errval +scsi_interpret_sense(xs) + struct scsi_xfer *xs; +{ + struct scsi_sense_data *sense; + struct scsi_link *sc_link = xs->sc_link; + u_int32 key; + u_int32 silent; + u_int32 info; + errval errcode; + + static char *error_mes[] = + {"soft error (corrected)", + "not ready", "medium error", + "non-media hardware failure", "illegal request", + "unit attention", "readonly device", + "no data found", "vendor unique", + "copy aborted", "command aborted", + "search returned equal", "volume overflow", + "verify miscompare", "unknown error key" + }; + + /* + * If the flags say errs are ok, then always return ok. + */ + if (xs->flags & SCSI_ERR_OK) + return (ESUCCESS); + + sense = &(xs->sense); +#ifdef SCSIDEBUG + if (sc_link->flags & SDEV_DB1) { + u_int32 count = 0; + printf("code%x valid%x ", + sense->error_code & SSD_ERRCODE, + sense->error_code & SSD_ERRCODE_VALID ? 1 : 0); + printf("seg%x key%x ili%x eom%x fmark%x\n", + sense->ext.extended.segment, + sense->ext.extended.flags & SSD_KEY, + sense->ext.extended.flags & SSD_ILI ? 1 : 0, + sense->ext.extended.flags & SSD_EOM ? 1 : 0, + sense->ext.extended.flags & SSD_FILEMARK ? 1 : 0); + printf("info: %x %x %x %x followed by %d extra bytes\n", + sense->ext.extended.info[0], + sense->ext.extended.info[1], + sense->ext.extended.info[2], + sense->ext.extended.info[3], + sense->ext.extended.extra_len); + printf("extra: "); + while (count < sense->ext.extended.extra_len) { + printf("%x ", sense->ext.extended.extra_bytes[count++]); + } + printf("\n"); + } +#endif /*SCSIDEBUG */ + /* + * If the device has it's own error handler, call it first. + * If it returns a legit error value, return that, otherwise + * it wants us to continue with normal error processing. + */ + if (sc_link->device->err_handler) { + SC_DEBUG(sc_link, SDEV_DB2, ("calling private err_handler()\n")); + errcode = (*sc_link->device->err_handler) (xs); + if (errcode != -1) + return errcode; /* errcode >= 0 better ? */ + } + /* otherwise use the default */ + silent = (xs->flags & SCSI_SILENT); + switch (sense->error_code & SSD_ERRCODE) { + /* + * If it's code 70, use the extended stuff and interpret the key + */ + case 0x71: /* delayed error */ + sc_print_addr(sc_link); + key = sense->ext.extended.flags & SSD_KEY; + printf(" DELAYED ERROR, key = 0x%x\n", key); + case 0x70: + if (sense->error_code & SSD_ERRCODE_VALID) { + info = ntohl(*((long *) sense->ext.extended.info)); + } else { + info = 0; + } + key = sense->ext.extended.flags & SSD_KEY; + + if (key && !silent) { + sc_print_addr(sc_link); + printf("%s", error_mes[key - 1]); + if (sense->error_code & SSD_ERRCODE_VALID) { + switch (key) { + case 0x2: /* NOT READY */ + case 0x5: /* ILLEGAL REQUEST */ + case 0x6: /* UNIT ATTENTION */ + case 0x7: /* DATA PROTECT */ + break; + case 0x8: /* BLANK CHECK */ + printf(", requested size: %d (decimal)", + info); + break; + default: + printf(", info = %d (decimal)", info); + } + } + printf("\n"); + } + switch (key) { + case 0x0: /* NO SENSE */ + case 0x1: /* RECOVERED ERROR */ + if (xs->resid == xs->datalen) + xs->resid = 0; /* not short read */ + case 0xc: /* EQUAL */ + return (ESUCCESS); + case 0x2: /* NOT READY */ + sc_link->flags &= ~SDEV_MEDIA_LOADED; + return (EBUSY); + case 0x5: /* ILLEGAL REQUEST */ + return (EINVAL); + case 0x6: /* UNIT ATTENTION */ + sc_link->flags &= ~SDEV_MEDIA_LOADED; + if (sc_link->flags & SDEV_OPEN) { + return (EIO); + } else { + return 0; + } + case 0x7: /* DATA PROTECT */ + return (EACCES); + case 0xd: /* VOLUME OVERFLOW */ + return (ENOSPC); + case 0x8: /* BLANK CHECK */ + return (ESUCCESS); + default: + return (EIO); + } + /* + * Not code 70, just report it + */ + default: + if (!silent) { + sc_print_addr(sc_link); + printf("error code %d", + sense->error_code & SSD_ERRCODE); + if (sense->error_code & SSD_ERRCODE_VALID) { + printf(" at block no. %d (decimal)", + (sense->ext.unextended.blockhi << 16) + + (sense->ext.unextended.blockmed << 8) + + (sense->ext.unextended.blocklow)); + } + printf("\n"); + } + return (EIO); + } +} + +/* + * Utility routines often used in SCSI stuff + */ + +/* + * convert a physical address to 3 bytes, + * MSB at the lowest address, + * LSB at the highest. + */ +void +lto3b(val, bytes) + int val; + u_char *bytes; +{ + *bytes++ = (val & 0xff0000) >> 16; + *bytes++ = (val & 0xff00) >> 8; + *bytes = val & 0xff; +} + +/* + * The reverse of lto3b + */ +int +_3btol(bytes) + u_char *bytes; +{ + u_int32 rc; + rc = (*bytes++ << 16); + rc += (*bytes++ << 8); + rc += *bytes; + return ((int) rc); +} + +/* + * Print out the scsi_link structure's address info. + */ +void +sc_print_addr(sc_link) + struct scsi_link *sc_link; +{ + + printf("%s%d(%s:%d:%d): ", + sc_link->device->name, sc_link->dev_unit, + ((struct device *)sc_link->adapter_softc)->dv_xname, + sc_link->target, sc_link->lun); +} + +#ifdef SCSIDEBUG +/* + * Given a scsi_xfer, dump the request, in all it's glory + */ +void +show_scsi_xs(xs) + struct scsi_xfer *xs; +{ + printf("xs(0x%x): ", xs); + printf("flg(0x%x)", xs->flags); + printf("sc_link(0x%x)", xs->sc_link); + printf("retr(0x%x)", xs->retries); + printf("timo(0x%x)", xs->timeout); + printf("cmd(0x%x)", xs->cmd); + printf("len(0x%x)", xs->cmdlen); + printf("data(0x%x)", xs->data); + printf("len(0x%x)", xs->datalen); + printf("res(0x%x)", xs->resid); + printf("err(0x%x)", xs->error); + printf("bp(0x%x)", xs->bp); + show_scsi_cmd(xs); +} + +void +show_scsi_cmd(struct scsi_xfer *xs) +{ + u_char *b = (u_char *) xs->cmd; + int i = 0; + + sc_print_addr(xs->sc_link); + printf("command: "); + + if (!(xs->flags & SCSI_RESET)) { + while (i < xs->cmdlen) { + if (i) + printf(","); + printf("%x", b[i++]); + } + printf("-[%d bytes]\n", xs->datalen); + if (xs->datalen) + show_mem(xs->data, min(64, xs->datalen)); + } else { + printf("-RESET-\n"); + } +} + +void +show_mem(address, num) + unsigned char *address; + u_int32 num; +{ + u_int32 x, y; + printf("------------------------------"); + for (y = 0; y < num; y += 1) { + if (!(y % 16)) + printf("\n%03d: ", y); + printf("%02x ", *address++); + } + printf("\n------------------------------\n"); +} +#endif /*SCSIDEBUG */ diff --git a/sys/dev/scsipi/scsipi_debug.h b/sys/dev/scsipi/scsipi_debug.h new file mode 100644 index 00000000000..8d9245dc46c --- /dev/null +++ b/sys/dev/scsipi/scsipi_debug.h @@ -0,0 +1,53 @@ +/*#define SCSIDEBUG 1*/ +/* + * Written by Julian Elischer (julian@tfs.com) + * + * $Id: scsipi_debug.h,v 1.1 1993/11/24 04:52:47 mycroft Exp $ + */ +#ifndef _SCSI_SCSI_DEBUG_H +#define _SCSI_SCSI_DEBUG_H 1 + +/* + * These are the new debug bits. (Sat Oct 2 12:46:46 WST 1993) + * the following DEBUG bits are defined to exist in the flags word of + * the scsi_link structure. + */ +#define SDEV_DB1 0x10 /* scsi commands, errors, data */ +#define SDEV_DB2 0x20 /* routine flow tracking */ +#define SDEV_DB3 0x40 /* internal to routine flows */ +#define SDEV_DB4 0x80 /* level 4 debugging for this dev */ + +/* target and LUN we want to debug */ +#define DEBUGTARG 9 /*9 = dissable*/ +#define DEBUGLUN 0 +#define DEBUGLEVEL (SDEV_DB1|SDEV_DB2) + +/* + * This is the usual debug macro for use with the above bits + */ +#ifdef SCSIDEBUG +#define SC_DEBUG(sc_link,Level,Printstuff) \ + if((sc_link)->flags & (Level)) \ + { \ + printf("%s%d(%s%d:%d:%d): ", \ + sc_link->device->name, \ + sc_link->dev_unit, \ + sc_link->adapter->name, \ + sc_link->adapter_unit, \ + sc_link->target, \ + sc_link->lun); \ + printf Printstuff; \ + } +#define SC_DEBUGN(sc_link,Level,Printstuff) \ + if((sc_link)->flags & (Level)) \ + { \ + printf Printstuff; \ + } +#else +#define SC_DEBUG(A,B,C) /* not included */ +#define SC_DEBUGN(A,B,C) /* not included */ +#endif + +#endif /*_SCSI_SCSI_DEBUG_H*/ +/* END OF FILE */ + diff --git a/sys/dev/scsipi/scsipi_ioctl.c b/sys/dev/scsipi/scsipi_ioctl.c new file mode 100644 index 00000000000..6d37df8afe8 --- /dev/null +++ b/sys/dev/scsipi/scsipi_ioctl.c @@ -0,0 +1,329 @@ +/* + * Contributed by HD Associates (hd@world.std.com). + * Copyright (c) 1992, 1993 HD Associates + * + * Berkeley style copyright. + * + * + */ +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/param.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#define b_screq av_forw /* XXX */ +#define b_sc_link av_back /* XXX */ +#include <sys/proc.h> +#include <sys/device.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <sys/scsiio.h> + + +/* + * We let the user interpret his own sense in the generic scsi world. + * This routine is called at interrupt time if the SCSI_USER bit was set + * in the flags passed to scsi_scsi_cmd(). No other completion processing + * takes place, even if we are running over another device driver. + * The lower level routines that call us here, will free the xs and restart + * the device's queue if such exists. + */ +#ifndef min +#define min(A,B) ((A<B) ? A : B ) +#endif + +void scsi_user_done(xs) +struct scsi_xfer *xs; +{ + + struct buf *bp; + scsireq_t *screq; + + bp = xs->bp; + if(!bp) { /* ALL user requests must have a buf */ + sc_print_addr(xs->sc_link); + printf("User command with no buf\n"); + return ; + } + screq = bp->b_screq; + if (!screq) { /* Is it one of ours? (the SCSI_USER bit says it is) */ + sc_print_addr(xs->sc_link); + printf("User command with no request\n"); + return ; + } + + SC_DEBUG(xs->sc_link,SDEV_DB2,("user-done\n")); + screq->retsts = 0; + screq->status = xs->status; + switch(xs->error) { + case XS_NOERROR: + SC_DEBUG(xs->sc_link,SDEV_DB3,("no error\n")); + screq->datalen_used = xs->datalen - xs->resid; /* probably rubbish */ + screq->retsts = SCCMD_OK; + break; + + case XS_SENSE: + SC_DEBUG(xs->sc_link,SDEV_DB3,("have sense\n")); + screq->senselen_used = min(sizeof(xs->sense),SENSEBUFLEN); + bcopy(&xs->sense,screq->sense,screq->senselen); + screq->retsts = SCCMD_SENSE; + break; + + case XS_DRIVER_STUFFUP: + sc_print_addr(xs->sc_link); + printf("host adapter code inconsistency\n"); + screq->retsts = SCCMD_UNKNOWN; + break; + + case XS_TIMEOUT: + SC_DEBUG(xs->sc_link,SDEV_DB3,("timeout\n")); + screq->retsts = SCCMD_TIMEOUT; + break; + + case XS_BUSY: + SC_DEBUG(xs->sc_link,SDEV_DB3,("busy\n")); + screq->retsts = SCCMD_BUSY; + break; + + default: + sc_print_addr(xs->sc_link); + printf("unknown error category from host adapter code\n"); + screq->retsts = SCCMD_UNKNOWN; + break; + } + biodone(bp); /* we're waiting on it in scsi_strategy() */ + return; /* it'll free the xs and restart any queue */ +} + + +/* Pseudo strategy function + * Called by scsi_do_ioctl() via physio/physstrat if there is to + * be data transfered, and directly if there is no data transfer. + * + * Should I reorganize this so it returns to physio instead + * of sleeping in scsiio_scsi_cmd? Is there any advantage, other + * than avoiding the probable duplicate wakeup in iodone? [PD] + * + * No, seems ok to me... [JRE] + * (I don't see any duplicate wakeups) + * + * Can't be used with block devices or raw_read/raw_write directly + * from the cdevsw/bdevsw tables because they couldn't have added + * the screq structure. [JRE] + */ +void scsistrategy(struct buf *bp) +{ + errval err; + struct scsi_link *sc_link = bp->b_sc_link; + scsireq_t *screq; + u_int32 flags = 0; + int s; + + + if(!sc_link) { + printf("user_strat: No link pointer\n"); + scsierr(bp,EINVAL); + return; + } + SC_DEBUG(sc_link,SDEV_DB2,("user_strategy\n")); + screq = bp->b_screq; + if(!screq) { + sc_print_addr(sc_link); + printf("No request block\n"); + scsierr(bp,EINVAL); + return; + } + + /* We're in trouble if physio tried to break up the + * transfer: + */ + if (bp->b_bcount != screq->datalen) { + sc_print_addr(sc_link); + printf("physio split the request.. cannot proceed\n"); + scsierr(bp, EIO); + return; + } + + if (screq->timeout == 0) { + scsierr(bp, EINVAL); + return; + } + + if (screq->cmdlen > sizeof(struct scsi_generic)) { + sc_print_addr(sc_link); + printf("cmdlen too big "); + scsierr(bp, EFAULT); + return; + } + + + if (screq->flags & SCCMD_READ) + flags |= SCSI_DATA_IN; + + if (screq->flags & SCCMD_WRITE) + flags |= SCSI_DATA_OUT; + + if (screq->flags & SCCMD_TARGET) + flags |= SCSI_TARGET; + + if (screq->flags & SCCMD_ESCAPE) + flags |= SCSI_ESCAPE; + err = scsi_scsi_cmd(sc_link, + (struct scsi_generic *)screq->cmd, + screq->cmdlen, + (u_char *)bp->b_un.b_addr, + screq->datalen, + 0, /* user must do the retries *//* ignored */ + screq->timeout, + bp, + flags | SCSI_USER); + + + + /*because there is a bp, scsi_scsi_cmd will return immediatly*/ + if (err) + { + scsierr(bp, err); + return; + } + SC_DEBUG(sc_link,SDEV_DB3,("about to sleep\n")); + s = splbio(); + while(!(bp->b_flags & B_DONE)) + { + sleep(bp,PRIBIO); + } + splx(s); + SC_DEBUG(sc_link,SDEV_DB3,("back from sleep\n")); + return; +} + +void scsiminphys(struct buf *bp) +{ + /*XXX*//* call the adapter's minphys */ +} + + +/* + * Something (e.g. another driver) has called us + * with an sc_link for a target/lun/adapter, and a scsi + * specific ioctl to perform, better try. + * If user-level type command, we must still be running + * in the context of the calling process + */ +errval scsi_do_ioctl(struct scsi_link *sc_link, int cmd, caddr_t addr, int f) +{ + errval ret = 0; + int phys; + + SC_DEBUG(sc_link,SDEV_DB2,("scsi_do_ioctl(0x%x)\n",cmd)); + switch(cmd) + { +#ifndef __NetBSD__ + case SCIOCCOMMAND: + { + /* + * You won't believe this, but the arg copied in + * from the user space, is on the kernel stack + * for this process, so we can't write + * to it at interrupt time.. + * we need to copy it in and out! + * Make a static copy using malloc! + */ + scsireq_t *screq2 = (scsireq_t *)addr; + scsireq_t *screq = (scsireq_t *)addr; + int rwflag = (screq->flags & SCCMD_READ) ? B_READ : B_WRITE; + struct buf *bp; + caddr_t d_addr; + int len; + + if((unsigned int)screq < (unsigned int)0xfe000000) + { + screq = malloc(sizeof(scsireq_t),M_TEMP,M_WAITOK); + bcopy(screq2,screq,sizeof(scsireq_t)); + } + bp = malloc(sizeof (struct buf),M_TEMP,M_WAITOK); + bzero(bp,sizeof(struct buf)); + d_addr = screq->databuf; + bp->b_bcount = len = screq->datalen; + bp->b_screq = screq; + bp->b_sc_link = sc_link; + if (len) { + /* have data, translate it. (physio)*/ +#ifdef __NetBSD__ +#error "dev, mincntfn & uio need defining" + ret = physio(scsistrategy, bp, dev, rwflag, + mincntfn, uio); +#else + ret = physio(scsistrategy,0,bp,0,rwflag, + d_addr,&len,curproc); +#endif + } else { + /* if no data, no need to translate it.. */ + bp->b_un.b_addr = 0; + bp->b_dev = -1; /* irrelevant info */ + bp->b_flags = 0; + + scsistrategy(bp); + ret = bp->b_error; + } + free(bp,M_TEMP); + if((unsigned int)screq2 < (unsigned int)0xfe000000) + { + bcopy(screq,screq2,sizeof(scsireq_t)); + free(screq,M_TEMP); + } + break; + } +#endif /* !NetBSD */ + case SCIOCDEBUG: + { + int level = *((int *)addr); + SC_DEBUG(sc_link,SDEV_DB3,("debug set to %d\n",level)); + sc_link->flags &= ~SDEV_DBX; /*clear debug bits */ + if(level & 1) sc_link->flags |= SDEV_DB1; + if(level & 2) sc_link->flags |= SDEV_DB2; + if(level & 4) sc_link->flags |= SDEV_DB3; + if(level & 8) sc_link->flags |= SDEV_DB4; + ret = 0; + break; + } + case SCIOCREPROBE: + { + extern int scsibus; + struct scsi_addr *sca = (struct scsi_addr *) addr; + + ret = scsi_probe_busses(sca->scbus,sca->target,sca->lun); + break; + } + case SCIOCRECONFIG: + case SCIOCDECONFIG: + ret = EINVAL; + break; + case SCIOCIDENTIFY: + { + struct scsi_addr *sca = (struct scsi_addr *) addr; + sca->scbus = sc_link->scsibus; + sca->target = sc_link->target; + sca->lun = sc_link->lun; + break; + } + + default: + ret = ENOTTY; + break; + } + + return ret; +} + +scsierr(bp,err) +struct buf *bp; +int err; +{ + bp->b_flags |= B_ERROR; + bp->b_error = err; + biodone(bp); + return; +} + diff --git a/sys/dev/scsipi/su.c b/sys/dev/scsipi/su.c new file mode 100644 index 00000000000..de4f0172dac --- /dev/null +++ b/sys/dev/scsipi/su.c @@ -0,0 +1,4 @@ + +/* this will be a special user scsi device */ +/* not written yet */ + diff --git a/sys/dev/scsipi/uk.c b/sys/dev/scsipi/uk.c new file mode 100644 index 00000000000..982deb13f9b --- /dev/null +++ b/sys/dev/scsipi/uk.c @@ -0,0 +1,157 @@ + +/* + * Dummy driver for a device we can't identify. + * by Julian Elischer (julian@tfs.com) + * + * $Id: uk.c,v 1.1 1993/11/24 04:52:53 mycroft Exp $ + */ + + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#define NUK 16 + +/* + * This driver is so simple it uses all the default services + */ +struct scsi_device uk_switch = +{ + NULL, + NULL, + NULL, + NULL, + "uk", + 0, + 0, 0 +}; + +struct uk_data { + u_int32 flags; + struct scsi_link *sc_link; /* all the inter level info */ +} uk_data[NUK]; + +#define UK_KNOWN 0x02 + +static u_int32 next_uk_unit = 0; + +/* + * The routine called by the low level scsi routine when it discovers + * a device suitable for this driver. + */ +errval +ukattach(sc_link) + struct scsi_link *sc_link; +{ + u_int32 unit, i, stat; + unsigned char *tbl; + + SC_DEBUG(sc_link, SDEV_DB2, ("ukattach: ")); + /* + * Check we have the resources for another drive + */ + unit = next_uk_unit++; + if (unit >= NUK) { + printf("Too many unknown devices..(%d > %d) reconfigure kernel\n", + (unit + 1), NUK); + return (0); + } + /* + * Store information needed to contact our base driver + */ + uk_data[unit].sc_link = sc_link; + sc_link->device = &uk_switch; + sc_link->dev_unit = unit; + + printf("uk%d: unknown device\n", unit); + uk_data[unit].flags = UK_KNOWN; + + return; + +} + +/* + * open the device. + */ +errval +ukopen(dev) +{ + errval errcode = 0; + u_int32 unit, mode; + struct scsi_link *sc_link; + unit = minor(dev); + + /* + * Check the unit is legal + */ + if (unit >= NUK) { + printf("uk%d: uk %d > %d\n", unit, unit, NUK); + return ENXIO; + } + + /* + * Make sure the device has been initialised + */ + if((uk_data[unit].flags & UK_KNOWN) == 0) { + printf("uk%d: not set up\n", unit); + return ENXIO; + } + + /* + * Only allow one at a time + */ + sc_link = uk_data[unit].sc_link; + if (sc_link->flags & SDEV_OPEN) { + printf("uk%d: already open\n", unit); + return ENXIO; + } + sc_link->flags |= SDEV_OPEN; + SC_DEBUG(sc_link, SDEV_DB1, ("ukopen: dev=0x%x (unit %d (of %d))\n" + ,dev, unit, NUK)); + /* + * Catch any unit attention errors. + */ + return 0; +} + +/* + * close the device.. only called if we are the LAST + * occurence of an open device + */ +errval +ukclose(dev) +{ + unsigned char unit, mode; + struct scsi_link *sc_link; + + sc_link = uk_data[unit].sc_link; + + SC_DEBUG(sc_link, SDEV_DB1, ("Closing device")); + sc_link->flags &= ~SDEV_OPEN; + return (0); +} + +/* + * Perform special action on behalf of the user + * Only does generic scsi ioctls. + */ +errval +ukioctl(dev, cmd, arg, mode) + dev_t dev; + u_int32 cmd; + caddr_t arg; +{ + unsigned char unit; + struct scsi_link *sc_link; + + /* + * Find the device that the user is talking about + */ + unit = minor(dev); + sc_link = uk_data[unit].sc_link; + return(scsi_do_ioctl(sc_link,cmd,arg,mode)); +} + |
