📄 scsi_base.c
字号:
/* $OpenBSD: scsi_base.c,v 1.28 2001/02/18 22:38:44 fgsch Exp $ *//* $NetBSD: scsi_base.c,v 1.43 1997/04/02 02:29:36 mycroft Exp $ *//* * Copyright (c) 1994, 1995, 1997 Charles M. Hannum. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Charles M. Hannum. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *//* * Originally written by Julian Elischer (julian@dialix.oz.au) * Detailed SCSI error printing Copyright 1997 by Matthew Jacob. */#include <sys/types.h>#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/buf.h>#include <sys/uio.h>#include <sys/malloc.h>#include <sys/errno.h>#include <sys/device.h>#include <sys/proc.h>#include <scsi/scsi_all.h>#include <scsi/scsi_disk.h>#include <scsi/scsiconf.h>LIST_HEAD(xs_free_list, scsi_xfer) xs_free_list;static __inline struct scsi_xfer *scsi_make_xs __P((struct scsi_link *, struct scsi_generic *, int cmdlen, u_char *data_addr, int datalen, int retries, int timeout, struct buf *, int flags));static __inline void asc2ascii __P((u_char asc, u_char ascq, char *result));int sc_err1 __P((struct scsi_xfer *, int));int scsi_interpret_sense __P((struct scsi_xfer *));char *scsi_decode_sense __P((void *, int));/* * 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 *scsi_get_xs(sc_link, flags) struct scsi_link *sc_link; /* who to charge the xs to */ int flags; /* if this call can sleep */{ struct scsi_xfer *xs; int s; SC_DEBUG(sc_link, SDEV_DB3, ("scsi_get_xs\n")); s = splbio(); while (sc_link->openings <= 0) { SC_DEBUG(sc_link, SDEV_DB3, ("sleeping\n")); if ((flags & SCSI_NOSLEEP) != 0) { splx(s); return 0; } sc_link->flags |= SDEV_WAITING; (void) tsleep(sc_link, PRIBIO, "getxs", 0); } sc_link->openings--; if ((xs = xs_free_list.lh_first) != NULL) { LIST_REMOVE(xs, free_list); splx(s); } else { splx(s); SC_DEBUG(sc_link, SDEV_DB3, ("making\n")); xs = malloc(sizeof(*xs), M_DEVBUF, ((flags & SCSI_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK)); if (!xs) { sc_print_addr(sc_link); printf("cannot allocate scsi xs\n"); return 0; } } SC_DEBUG(sc_link, SDEV_DB3, ("returning\n")); xs->flags = flags; 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 scsi_free_xs(xs, flags) struct scsi_xfer *xs; int flags;{ struct scsi_link *sc_link = xs->sc_link; LIST_INSERT_HEAD(&xs_free_list, xs, free_list); SC_DEBUG(sc_link, SDEV_DB3, ("scsi_free_xs\n")); /* if was 0 and someone waits, wake them up */ sc_link->openings++; if ((sc_link->flags & SDEV_WAITING) != 0) { 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->device_softc); } }}/* * Make a scsi_xfer, and return a pointer to it. */static __inline struct scsi_xfer *scsi_make_xs(sc_link, scsi_cmd, cmdlen, data_addr, datalen, retries, timeout, bp, flags) struct scsi_link *sc_link; struct scsi_generic *scsi_cmd; int cmdlen; u_char *data_addr; int datalen; int retries; int timeout; struct buf *bp; int flags;{ struct scsi_xfer *xs; if ((xs = scsi_get_xs(sc_link, flags)) == NULL) return NULL; /* * Fill out the scsi_xfer structure. We don't know whose context * the cmd is in, so copy it. */ xs->sc_link = sc_link; bcopy(scsi_cmd, &xs->cmdstore, cmdlen); xs->cmd = &xs->cmdstore; xs->cmdlen = cmdlen; xs->data = data_addr; xs->datalen = datalen; xs->retries = retries; xs->timeout = timeout; xs->bp = bp; xs->req_sense_length = 0; /* XXX - not used by scsi internals */ /* * Set the LUN in the CDB. This may only be needed if we have an * older device. However, we also set it for more modern SCSI * devices "just in case". The old code assumed everything newer * than SCSI-2 would not need it, but why risk it? This was the * old conditional: * * if ((sc_link->scsi_version & SID_ANSII) <= 2) */ xs->cmd->bytes[0] &= ~SCSI_CMD_LUN_MASK; xs->cmd->bytes[0] |= ((sc_link->lun << SCSI_CMD_LUN_SHIFT) & SCSI_CMD_LUN_MASK); return xs;}/* * Find out from the device what its capacity is. */u_longscsi_size(sc_link, flags) struct scsi_link *sc_link; int flags;{ struct scsi_read_cap_data rdcap; struct scsi_read_capacity scsi_cmd; /* * make up a scsi command and ask the scsi driver to do * it for you. */ bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.opcode = 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; } return _4btol(rdcap.addr) + 1;}/* * Get scsi driver to send a "are you ready?" command */int scsi_test_unit_ready(sc_link, flags) struct scsi_link *sc_link; int flags;{ struct scsi_test_unit_ready scsi_cmd; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.opcode = TEST_UNIT_READY; return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), 0, 0, 5, 10000, NULL, flags);}/* * Do a scsi operation, asking a device to run as SCSI-II if it can. */int scsi_change_def(sc_link, flags) struct scsi_link *sc_link; int flags;{ struct scsi_changedef scsi_cmd; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.opcode = 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. */int scsi_inquire(sc_link, inqbuf, flags) struct scsi_link *sc_link; struct scsi_inquiry_data *inqbuf; int flags;{ struct scsi_inquiry scsi_cmd; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.opcode = 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, 10000, NULL, SCSI_DATA_IN | flags);}/* * Prevent or allow the user to remove the media */int scsi_prevent(sc_link, type, flags) struct scsi_link *sc_link; int type, flags;{ struct scsi_prevent scsi_cmd; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.opcode = 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 */int scsi_start(sc_link, type, flags) struct scsi_link *sc_link; int type, flags;{ struct scsi_start_stop scsi_cmd; if ((sc_link->quirks & SDEV_NOSTARTUNIT) == SDEV_NOSTARTUNIT) return 0; bzero(&scsi_cmd, sizeof(scsi_cmd)); scsi_cmd.opcode = START_STOP; scsi_cmd.byte2 = 0x00; scsi_cmd.how = type; return scsi_scsi_cmd(sc_link, (struct scsi_generic *) &scsi_cmd, sizeof(scsi_cmd), 0, 0, 2, type == SSS_START ? 30000 : 10000, 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; int error; SC_DEBUG(sc_link, SDEV_DB2, ("scsi_done\n"));#ifdef SCSIDEBUG if ((sc_link->flags & SDEV_DB1) != 0) show_scsi_cmd(xs);#endif /* SCSIDEBUG */#ifndef PMON /* * 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) != 0) { 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 ")); scsi_free_xs(xs, SCSI_NOSLEEP); /* restarts queue too */ SC_DEBUG(sc_link, SDEV_DB3, ("returning to adapter\n")); return; }#endif if (!((xs->flags & (SCSI_NOSLEEP | SCSI_POLL)) == SCSI_NOSLEEP)) { /* * 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 ERESTART then we should RETRY */retry: error = sc_err1(xs, 1); if (error == ERESTART) { switch ((*(sc_link->adapter->scsi_cmd)) (xs)) { case SUCCESSFULLY_QUEUED: return; case TRY_AGAIN_LATER: xs->error = XS_BUSY; case COMPLETE: goto retry; } } bp = xs->bp; if (bp) { if (error) { bp->b_error = error; bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; } else { bp->b_error = 0; bp->b_resid = xs->resid; } } if (sc_link->device->done) { /* * Tell the device the operation is actually complete. * No more will happen with this xfer. This for * notification of the upper-level driver only; they * won't be returning any meaningful information to us. */ (*sc_link->device->done)(xs); } scsi_free_xs(xs, SCSI_NOSLEEP); if (bp) biodone(bp);}intscsi_execute_xs(xs) struct scsi_xfer *xs;{ int error; int s; xs->flags &= ~ITSDONE; xs->error = XS_NOERROR; xs->resid = xs->datalen; xs->status = 0;retry: /* * 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 *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -