scsi_ch.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,716 行 · 第 1/3 页
C
1,716 行
/* * Copyright (c) 1997 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. * 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, * without modification, immediately at the beginning of the file. * 2. 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * $Id: scsi_ch.c,v 1.9.2.2 1999/05/09 01:27:36 ken Exp $ *//* * Derived from the NetBSD SCSI changer driver. * * $NetBSD: ch.c,v 1.32 1998/01/12 09:49:12 thorpej Exp $ * *//* * Copyright (c) 1996, 1997 Jason R. Thorpe <thorpej@and.com> * All rights reserved. * * Partially based on an autochanger driver written by Stefan Grefen * and on an autochanger driver written by the Systems Programming Group * at the University of Utah Computer Science Department. * * 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 acknowledgements: * This product includes software developed by Jason R. Thorpe * for And Communications, http://www.and.com/ * 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. */#include <sys/param.h>#include <sys/queue.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/types.h>#include <sys/dkbad.h>#include <sys/malloc.h>#include <sys/fcntl.h>#include <sys/stat.h>#include <sys/conf.h>#include <sys/buf.h>#include <sys/chio.h>#include <sys/errno.h>#include <sys/devicestat.h>#include <cam/cam.h>#include <cam/cam_ccb.h>#include <cam/cam_extend.h>#include <cam/cam_periph.h>#include <cam/cam_xpt_periph.h>#include <cam/cam_queue.h>#include <cam/cam_debug.h>#include <cam/scsi/scsi_all.h>#include <cam/scsi/scsi_message.h>#include <cam/scsi/scsi_ch.h>/* * Timeout definitions for various changer related commands. They may * be too short for some devices (especially the timeout for INITIALIZE * ELEMENT STATUS). */static const u_int32_t CH_TIMEOUT_MODE_SENSE = 6000;static const u_int32_t CH_TIMEOUT_MOVE_MEDIUM = 100000;static const u_int32_t CH_TIMEOUT_EXCHANGE_MEDIUM = 100000;static const u_int32_t CH_TIMEOUT_POSITION_TO_ELEMENT = 100000;static const u_int32_t CH_TIMEOUT_READ_ELEMENT_STATUS = 10000;static const u_int32_t CH_TIMEOUT_SEND_VOLTAG = 10000;static const u_int32_t CH_TIMEOUT_INITIALIZE_ELEMENT_STATUS = 500000;typedef enum { CH_FLAG_INVALID = 0x001, CH_FLAG_OPEN = 0x002} ch_flags;typedef enum { CH_STATE_PROBE, CH_STATE_NORMAL} ch_state;typedef enum { CH_CCB_PROBE, CH_CCB_WAITING} ch_ccb_types;typedef enum { CH_Q_NONE = 0x00, CH_Q_NO_DBD = 0x01} ch_quirks;#define ccb_state ppriv_field0#define ccb_bp ppriv_ptr1struct scsi_mode_sense_data { struct scsi_mode_header_6 header; struct scsi_mode_blk_desc blk_desc; union { struct page_element_address_assignment ea; struct page_transport_geometry_parameters tg; struct page_device_capabilities cap; } pages;};struct ch_softc { ch_flags flags; ch_state state; ch_quirks quirks; union ccb saved_ccb; struct devstat device_stats; int sc_picker; /* current picker */ /* * The following information is obtained from the * element address assignment page. */ int sc_firsts[4]; /* firsts, indexed by CHET_* */ int sc_counts[4]; /* counts, indexed by CHET_* */ /* * The following mask defines the legal combinations * of elements for the MOVE MEDIUM command. */ u_int8_t sc_movemask[4]; /* * As above, but for EXCHANGE MEDIUM. */ u_int8_t sc_exchangemask[4]; /* * Quirks; see below. XXX KDM not implemented yet */ int sc_settledelay; /* delay for settle */};#define CHUNIT(x) (minor((x)))#define CH_CDEV_MAJOR 17static d_open_t chopen;static d_close_t chclose;static d_ioctl_t chioctl;static periph_init_t chinit;static periph_ctor_t chregister;static periph_oninv_t choninvalidate;static periph_dtor_t chcleanup;static periph_start_t chstart;static void chasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg);static void chdone(struct cam_periph *periph, union ccb *done_ccb);static int cherror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags);static int chmove(struct cam_periph *periph, struct changer_move *cm);static int chexchange(struct cam_periph *periph, struct changer_exchange *ce);static int chposition(struct cam_periph *periph, struct changer_position *cp);static int chgetelemstatus(struct cam_periph *periph, struct changer_element_status_request *csr);static int chsetvoltag(struct cam_periph *periph, struct changer_set_voltag_request *csvr);static int chielem(struct cam_periph *periph, unsigned int timeout);static int chgetparams(struct cam_periph *periph);static struct periph_driver chdriver ={ chinit, "ch", TAILQ_HEAD_INITIALIZER(chdriver.units), /* generation */ 0};DATA_SET(periphdriver_set, chdriver);static struct cdevsw ch_cdevsw = { /*d_open*/ chopen, /*d_close*/ chclose, /*d_read*/ noread, /*d_write*/ nowrite, /*d_ioctl*/ chioctl, /*d_stop*/ nostop, /*d_reset*/ noreset, /*d_devtotty*/ nodevtotty, /*d_poll*/ seltrue, /*d_mmap*/ nommap, /*d_strategy*/ nostrategy, /*d_name*/ "ch", /*d_spare*/ NULL, /*d_maj*/ -1, /*d_dump*/ nodump, /*d_psize*/ nopsize, /*d_flags*/ 0, /*d_maxio*/ 0, /*b_maj*/ -1};static struct extend_array *chperiphs;voidchinit(void){ cam_status status; struct cam_path *path; /* * Create our extend array for storing the devices we attach to. */ chperiphs = cam_extend_new(); if (chperiphs == NULL) { printf("ch: Failed to alloc extend array!\n"); return; } /* * Install a global async callback. This callback will * receive async callbacks like "new device found". */ status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status == CAM_REQ_CMP) { struct ccb_setasync csa; xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_FOUND_DEVICE; csa.callback = chasync; csa.callback_arg = NULL; xpt_action((union ccb *)&csa); status = csa.ccb_h.status; xpt_free_path(path); } if (status != CAM_REQ_CMP) { printf("ch: Failed to attach master async callback " "due to status 0x%x!\n", status); } else { dev_t dev; /* If we were successfull, register our devsw */ dev = makedev(CH_CDEV_MAJOR, 0); cdevsw_add(&dev, &ch_cdevsw, NULL); }}static voidchoninvalidate(struct cam_periph *periph){ struct ch_softc *softc; struct ccb_setasync csa; softc = (struct ch_softc *)periph->softc; /* * De-register any async callbacks. */ xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = 0; csa.callback = chasync; csa.callback_arg = periph; xpt_action((union ccb *)&csa); softc->flags |= CH_FLAG_INVALID; xpt_print_path(periph->path); printf("lost device\n");}static voidchcleanup(struct cam_periph *periph){ struct ch_softc *softc; softc = (struct ch_softc *)periph->softc; devstat_remove_entry(&softc->device_stats); cam_extend_release(chperiphs, periph->unit_number); xpt_print_path(periph->path); printf("removing device entry\n"); free(softc, M_DEVBUF);}static voidchasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg){ struct cam_periph *periph; periph = (struct cam_periph *)callback_arg; switch(code) { case AC_FOUND_DEVICE: { struct ccb_getdev *cgd; cam_status status; cgd = (struct ccb_getdev *)arg; if (cgd->pd_type != T_CHANGER) break; /* * Allocate a peripheral instance for * this device and start the probe * process. */ status = cam_periph_alloc(chregister, choninvalidate, chcleanup, chstart, "ch", CAM_PERIPH_BIO, cgd->ccb_h.path, chasync, AC_FOUND_DEVICE, cgd); if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) printf("chasync: Unable to probe new device " "due to status 0x%x\n", status); break; } case AC_LOST_DEVICE: cam_periph_invalidate(periph); break; case AC_TRANSFER_NEG: case AC_SENT_BDR: case AC_SCSI_AEN: case AC_UNSOL_RESEL: case AC_BUS_RESET: default: break; }}static cam_statuschregister(struct cam_periph *periph, void *arg){ struct ch_softc *softc; struct ccb_setasync csa; struct ccb_getdev *cgd; cgd = (struct ccb_getdev *)arg; if (periph == NULL) { printf("chregister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } if (cgd == NULL) { printf("chregister: no getdev CCB, can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (struct ch_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT); if (softc == NULL) { printf("chregister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } bzero(softc, sizeof(*softc)); softc->state = CH_STATE_PROBE; periph->softc = softc; cam_extend_set(chperiphs, periph->unit_number, periph); softc->quirks = CH_Q_NONE; /* * Changers don't have a blocksize, and obviously don't support * tagged queueing. */ devstat_add_entry(&softc->device_stats, "ch", periph->unit_number, 0, DEVSTAT_NO_BLOCKSIZE | DEVSTAT_NO_ORDERED_TAGS, cgd->pd_type | DEVSTAT_TYPE_IF_SCSI, DEVSTAT_PRIORITY_OTHER); /* * Add an async callback so that we get * notified if this device goes away. */ xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = chasync; csa.callback_arg = periph; xpt_action((union ccb *)&csa); /* * Lock this peripheral until we are setup. * This first call can't block */ (void)cam_periph_lock(periph, PRIBIO); xpt_schedule(periph, /*priority*/5); return(CAM_REQ_CMP);}static intchopen(dev_t dev, int flags, int fmt, struct proc *p){ struct cam_periph *periph; struct ch_softc *softc; int unit, error; int s; unit = CHUNIT(dev); periph = cam_extend_get(chperiphs, unit); if (periph == NULL) return(ENXIO); softc = (struct ch_softc *)periph->softc; s = splsoftcam(); if (softc->flags & CH_FLAG_INVALID) { splx(s); return(ENXIO); } if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) { splx(s); return (error); } splx(s); if ((softc->flags & CH_FLAG_OPEN) == 0) { if (cam_periph_acquire(periph) != CAM_REQ_CMP) return(ENXIO); softc->flags |= CH_FLAG_OPEN; } /* * Load information about this changer device into the softc. */ if ((error = chgetparams(periph)) != 0) { softc->flags &= ~CH_FLAG_OPEN; cam_periph_unlock(periph); cam_periph_release(periph); return(error); } cam_periph_unlock(periph); return(error);}static intchclose(dev_t dev, int flag, int fmt, struct proc *p){ struct cam_periph *periph; struct ch_softc *softc; int unit, error; error = 0; unit = CHUNIT(dev); periph = cam_extend_get(chperiphs, unit); if (periph == NULL) return(ENXIO); softc = (struct ch_softc *)periph->softc; if ((error = cam_periph_lock(periph, PRIBIO)) != 0) return(error); softc->flags &= ~CH_FLAG_OPEN; cam_periph_unlock(periph); cam_periph_release(periph); return(0);}static voidchstart(struct cam_periph *periph, union ccb *start_ccb){ struct ch_softc *softc; int s; softc = (struct ch_softc *)periph->softc; switch (softc->state) { case CH_STATE_NORMAL: { s = splbio(); if (periph->immediate_priority <= periph->pinfo.priority){ start_ccb->ccb_h.ccb_state = CH_CCB_WAITING; SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, periph_links.sle); periph->immediate_priority = CAM_PRIORITY_NONE; splx(s); wakeup(&periph->ccb_list); } else splx(s); break; } case CH_STATE_PROBE: { int mode_buffer_len; void *mode_buffer; /* * Include the block descriptor when calculating the mode * buffer length, */ mode_buffer_len = sizeof(struct scsi_mode_header_6) + sizeof(struct scsi_mode_blk_desc) + sizeof(struct page_element_address_assignment); mode_buffer = malloc(mode_buffer_len, M_TEMP, M_NOWAIT); if (mode_buffer == NULL) { printf("chstart: couldn't malloc mode sense data\n"); break; } bzero(mode_buffer, mode_buffer_len); /* * Get the element address assignment page. */ scsi_mode_sense(&start_ccb->csio, /* retries */ 1, /* cbfcnp */ chdone, /* tag_action */ MSG_SIMPLE_Q_TAG, /* dbd */ (softc->quirks & CH_Q_NO_DBD) ? FALSE : TRUE, /* page_code */ SMS_PAGE_CTRL_CURRENT,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?