📄 sd.c
字号:
#ident "@(#)sd.c 1.1 92/07/30 SMI"/* * Copyright (c) 1988, 1989, 1990 by Sun Microsystems, Inc. */#include "sd.h"#if NSD > 0/* * SCSI fixed disk driver * *//* * * Includes, Declarations and Local Data * */#include <scsi/scsi.h>#include <scsi/targets/sddef.h>#include <scsi/impl/uscsi.h>#include <vm/hat.h>#include <vm/seg.h>#include <vm/as.h>/* * * Global Data Definitions and references * */extern int sd_error_level, sd_io_time, sd_fmt_time;extern int sd_retry_count, sdnspecial;extern struct sd_drivetype sdspecial[];extern int maxphys, dkn;static int sdmaxphys;#ifdef B_KLUSTERextern int klustsort(), klustdone();extern void klustbust();#endif /* B_KLUSTER *//* * * Local Static Data * */static struct scsi_device *sdunits[SD_MAXUNIT];static struct scsi_address *sdsvdaddr[SD_MAXUNIT];static int sddebug = 0;static int sdpri = 0;static char *diskokay = "disk okay";/* * Forward reference definitions */extern char *sd_cmds[];/* * Configuration Data */#ifdef OPENPROMS/* * Device driver ops vector */int sdslave(), sdattach(), sdopen(), sdclose(), sdread(), sdwrite();int sdstrategy(), sddump(), sdioctl(), sdsize();struct dev_ops sd_ops = { 1, sdslave, sdattach, sdopen, sdclose, sdread, sdwrite, sdstrategy, sddump, sdsize, sdioctl};#else OPENPROMS/* * sddriver is only used to attach */int sdslave(), sdattach(), sdopen(), sdclose(), sdread(), sdwrite();int sdstrategy(), sddump(), sdioctl(), sdsize();extern int nulldev(), nodev();struct mb_driver sddriver = { nulldev, sdslave, sdattach, nodev, nodev, nulldev, 0, "sd", 0, 0, 0, 0};#endif/* * * Local Function Declarations * */static void inq_fill(), sdintr(), sd_doattach();static void sderrmsg(), sddone(), sd_offline(), sd_lock_unlock();static void make_sd_cmd(), sdstart(), sddone();static int sdrunout(), sd_findslave(), sd_winchester_exists();static int sd_unit_ready();static void sdprintf(), sdlog();static void sd_setlink();static int sd_testlink(), sd_maptouscsi();#ifdef ADAPTECstatic void sdintr_adaptec();#endif /* ADAPTEC *//* * * Autoconfiguration Routines * */intsdslave(devp)struct scsi_device *devp;{ int r, unit;#ifdef sun4m sdmaxphys = 0x3f000;#else sdmaxphys = maxphys;#endif /* sun4m */ unit = DUNIT; /* * fill in our local array */ if (unit >= SD_MAXUNIT || sdunits[unit]) return (0); sdunits[unit] = devp;#ifdef OPENPROMS sdpri = MAX(sdpri, ipltospl(devp->sd_dev->devi_intr->int_pri));#else sdpri = MAX(sdpri, pritospl(devp->sd_dev->md_intpri));#endif /* * Turn around and call real slave routine. If it returns -1, * then try and save the address structure for a possible later open. */ r = sd_findslave(devp, 0); if (r < 0) { sdsvdaddr[unit] = (struct scsi_address *) kmem_zalloc(sizeof (struct scsi_address)); if (sdsvdaddr[unit]) { sdunits[unit] = 0; *sdsvdaddr[unit] = devp->sd_address; } else { r = 0; } } return (r);}static intsd_findslave(devp, canwait)register struct scsi_device *devp;int canwait;{ static char *nonccs = "non-CCS device found at target %d lun %d on %s%d"; auto struct scsi_capacity cbuf; register struct scsi_pkt *rqpkt = (struct scsi_pkt *) 0; register struct scsi_disk *un = (struct scsi_disk *) 0; register struct sd_drivetype *dp; register int made_dp; /* * Call the routine scsi_slave to do some of the dirty work. * All scsi_slave does is do a TEST UNIT READY (and possibly * a non-extended REQUEST SENSE or two), and an INQUIRY command. * If the INQUIRY command succeeds, the field sd_inq in the * device structure will be filled in. The sd_sense structure * will also be allocated. * */ switch (scsi_slave(devp, canwait)) { default: case SCSIPROBE_NOMEM: case SCSIPROBE_FAILURE: case SCSIPROBE_NORESP: if (devp->sd_inq) { IOPBFREE (devp->sd_inq, SUN_INQSIZE); devp->sd_inq = (struct scsi_inquiry *) 0; } return (-1); case SCSIPROBE_NONCCS:#ifdef ADAPTEC /* * Okay, the INQUIRY command failed. * How hard do we want to work at seeing * if this is the right device, and how * much can we assume about this target? */ bzero((caddr_t) devp->sd_inq, SUN_INQSIZE); devp->sd_inq->inq_dtype = DTYPE_DIRECT; devp->sd_inq->inq_rdf = RDF_LEVEL0; bcopy("ADAPTEC", devp->sd_inq->inq_vid, 7); bcopy("ACB4000", devp->sd_inq->inq_pid, 7); sdlog(devp, LOG_WARNING, nonccs, Tgt(devp), Lun(devp), CNAME, CUNIT); break;#else /* ADAPTEC */ sdlog(devp, LOG_ERR, nonccs, Tgt(devp), Lun(devp), CNAME, CUNIT); devp->sd_inq->inq_dtype = DTYPE_NOTPRESENT; /* FALLTHROUGH */#endif /* ADAPTEC */ case SCSIPROBE_EXISTS: switch (devp->sd_inq->inq_dtype) { case DTYPE_DIRECT: break; case DTYPE_NOTPRESENT: default: IOPBFREE (devp->sd_inq, SUN_INQSIZE); devp->sd_inq = (struct scsi_inquiry *) 0; return (-1); } } /* * Allocate a request sense packet. */ rqpkt = get_pktiopb(ROUTE, (caddr_t *) &devp->sd_sense, CDB_GROUP0, 1, SENSE_LENGTH, B_READ, (canwait)? SLEEP_FUNC: NULL_FUNC); if (!rqpkt) { return (0); } rqpkt->pkt_pmon = -1; makecom_g0(rqpkt, devp, FLAG_NOPARITY, SCMD_REQUEST_SENSE, 0, SENSE_LENGTH); /* * If the device is not a removable media device, make sure that * it can be started (if possible) with the START command, and * attempt to read it's capacity too (if possible). */ cbuf.capacity = -1; cbuf.lbasize = 0; if (devp->sd_inq->inq_rmb == 0) { if (sd_winchester_exists(devp, rqpkt, &cbuf, canwait)) { goto error; } } /* * The actual unit is present. * The attach routine will check validity of label * (and print out whether it is there). * Now is the time to fill in the rest of our info.. */ un = (struct scsi_disk *) kmem_zalloc(sizeof (struct scsi_disk)); if (!(UPTR = un)) { goto error; } un->un_sbufp = (struct buf *) kmem_zalloc(sizeof (struct buf)); if (un->un_sbufp == (struct buf *) NULL) { goto error; } /* * Look through our list of special drives to see whether * this drive is known to have special problems. */ made_dp = 0; for (dp = sdspecial; dp < &sdspecial[sdnspecial]; dp++) { /* * It turns out that order is important for strcmp()! */ if (bcmp(devp->sd_inq->inq_vid, dp->id, strlen(dp->id)) == 0) { un->un_dp = dp; break; } } if (un->un_dp == 0) { /* * Assume CCS drive, assume parity */ made_dp = 1; un->un_dp = (struct sd_drivetype *) kmem_zalloc(sizeof (struct sd_drivetype)); if (!un->un_dp) { goto error; } un->un_dp->id = kmem_alloc((u_int)12); if (!un->un_dp->id) goto error; bcopy(devp->sd_inq->inq_vid, un->un_dp->id, 12); un->un_dp->ctype = CTYPE_CCS; } if ((un->un_dp->options & SD_NOPARITY) == 0) rqpkt->pkt_flags &= ~FLAG_NOPARITY; devp->sd_present = 1; if (cbuf.capacity != (u_long) -1) { /* * The returned capacity is the LBA of the last * addressable logical block, so the real capacity * is one greater */ un->un_capacity = cbuf.capacity+1; un->un_lbasize = cbuf.lbasize; } else { un->un_capacity = 0; un->un_lbasize = SECSIZE; } rqpkt->pkt_comp = sdintr; rqpkt->pkt_time = sd_io_time; if (un->un_dp->options & SD_NODISC) rqpkt->pkt_flags |= FLAG_NODISCON; un->un_rqs = rqpkt; un->un_sd = devp; un->un_dkn = (char) -1;#ifdef OPENPROMS devp->sd_dev->devi_driver = &sd_ops;#endif OPENPROMS return (1);error: if (un) { if (un->un_sbufp) { (void) kmem_free((caddr_t)un->un_sbufp, sizeof (struct buf)); } if (made_dp) { if (un->un_dp->id) { (void) kmem_free(un->un_dp->id, (u_int) 12); } if (un->un_dp) { (void) kmem_free((caddr_t) un->un_dp, (u_int) sizeof (struct sd_drivetype)); } } (void) kmem_free((caddr_t) un, sizeof (struct scsi_disk)); UPTR = (struct scsi_disk *) 0; } if (rqpkt) { free_pktiopb(rqpkt, (caddr_t) devp->sd_sense, SENSE_LENGTH); devp->sd_sense = (struct scsi_extended_sense *) 0; } return (0);}static intsd_winchester_exists(devp, rqpkt, cptr, canwait)struct scsi_device *devp;struct scsi_pkt *rqpkt;struct scsi_capacity *cptr;int canwait;{ static char *emustring = "EMULEX MD21/S2"; register struct scsi_pkt *pkt; auto caddr_t wrkbuf; register rval = -1; /* * Get a work packet to play with. Get one with a buffer big * enough for another INQUIRY command, and get one with * a cdb big enough for the READ CAPACITY command. */ pkt = get_pktiopb(ROUTE, &wrkbuf, CDB_GROUP1, 1, SUN_INQSIZE, B_READ, (canwait)? SLEEP_FUNC: NULL_FUNC); if (pkt == NULL) { return (rval); } /* * Send a throwaway START UNIT command. * * If we fail on this, we don't care present what * precisely is wrong. Fire off a throwaway REQUEST * SENSE command if the failure is a CHECK_CONDITION. * */ makecom_g0(pkt, devp, FLAG_NOPARITY, SCMD_START_STOP, 0, 1); (void) scsi_poll(pkt); if (SCBP(pkt)->sts_chk) { (void) scsi_poll (rqpkt); } /* * Send another Inquiry command to the target. This is necessary * for non-removable media direct access devices because their * Inquiry data may not be fully qualified until they are spun up * (perhaps via the START command above) */ bzero(wrkbuf, SUN_INQSIZE); makecom_g0(pkt, devp, FLAG_NOPARITY, SCMD_INQUIRY, 0, SUN_INQSIZE); if (scsi_poll(pkt) < 0) { goto out; } else if (SCBP(pkt)->sts_chk) { (void) scsi_poll (rqpkt); } else { if ((pkt->pkt_state & STATE_XFERRED_DATA) && (SUN_INQSIZE - pkt->pkt_resid) >= SUN_MIN_INQLEN) { bcopy(wrkbuf, (caddr_t) devp->sd_inq, SUN_INQSIZE); } } /* * It would be nice to attempt to make sure that there is a disk there, * but that turns out to be very hard- We used to use the REZERO * command to verify that a disk was attached, but some SCSI disks * can't handle a REZERO command. We can't use a READ command * because the disk may not be formatted yet. Therefore, we just * have to believe a disk is there until proven otherwise, *except* * (hack hack hack) for the MD21. */ if (bcmp(devp->sd_inq->inq_vid, emustring, strlen(emustring)) == 0) { makecom_g0(pkt, devp, FLAG_NOPARITY, SCMD_REZERO_UNIT, 0, 0); if (scsi_poll(pkt) || SCBP_C(pkt) != STATUS_GOOD) { if (SCBP(pkt)->sts_chk) { (void) scsi_poll (rqpkt); } goto out; } } /* * At this point, we've 'succeeded' with this winchester */ rval = 0; /* * Attempt to read the drive's capacity. * It's okay to fail with this. */ makecom_g1(pkt, devp, FLAG_NOPARITY, SCMD_READ_CAPACITY, 0, 0); if (scsi_poll(pkt) >= 0 && SCBP_C(pkt) == STATUS_GOOD && (pkt->pkt_state & STATE_XFERRED_DATA) && (pkt->pkt_resid == SUN_INQSIZE-(sizeof (struct scsi_capacity)))) { cptr->capacity = ((struct scsi_capacity *)wrkbuf)->capacity; cptr->lbasize = ((struct scsi_capacity *)wrkbuf)->lbasize; } else if (SCBP(pkt)->sts_chk) { (void) scsi_poll (rqpkt); }out: free_pktiopb(pkt, wrkbuf, SUN_INQSIZE); return (rval);}/* * Attach disk. The controller is there. Is the label valid? * This is a wrapper for sd_doattach. */intsdattach(devp)struct scsi_device *devp;{ sd_doattach(devp, NULL_FUNC); /* * If this is a removable media device, * invalidate the geometry in order to * force the geometry to be re-examined * at open time. */ if (devp->sd_inq->inq_rmb && UPTR) UPTR->un_gvalid = 0;}static voidsd_doattach(devp, f)register struct scsi_device *devp;int (*f)();{ void sd_uselabel(); auto struct dk_label *dkl; register struct scsi_pkt *pkt; register struct scsi_disk *un; int unit, fnoparity; auto char *label = 0, labelstring[128]; if (!(un = UPTR)) return; /* * FIX ME: */ if (un->un_capacity > 0 && un->un_lbasize != SECSIZE) { sdlog(devp, LOG_ERR, "logical block size %d not supported", un->un_lbasize); return; } unit = DUNIT; if (un->un_dp->options & SD_NOPARITY) fnoparity = FLAG_NOPARITY; else fnoparity = 0; /* * Only DIRECT ACCESS devices will have Sun labels */ if (devp->sd_inq->inq_dtype == DTYPE_DIRECT) { int i; pkt = get_pktiopb(ROUTE, (caddr_t *) &dkl, CDB_GROUP0, 1, SECSIZE, B_READ, f); if (pkt == NULL) { sdlog(devp, LOG_CRIT, "no memory for disk label"); return; } bzero ((caddr_t) dkl, SECSIZE); makecom_g0(pkt, devp, fnoparity, SCMD_READ, 0, 1); /* * Ok, it's ready - try to read and use the label. */ un->un_gvalid = 0; for (i = 0; i < 3; i++) { if (scsi_poll(pkt) || SCBP_C(pkt) != STATUS_GOOD || (pkt->pkt_state & STATE_XFERRED_DATA) == 0 || (pkt->pkt_resid != 0)) { if (i > 2 && un->un_state == SD_STATE_NIL) sdlog(devp, LOG_ERR, "unable to read label"); if (SCBP(pkt)->sts_chk) { (void) scsi_poll (un->un_rqs); } } else { /* * sd_uselabel will establish * that the geometry is valid */ sd_uselabel(devp, dkl); break; } } /* * XXX: Use Mode Sense to determine things like * XXX; rpm, geometry, from SCSI-2 compliant * XXX: peripherals */ if (un->un_g.dkg_rpm == 0) un->un_g.dkg_rpm = 3600; bcopy (dkl->dkl_asciilabel, labelstring, 128); label = labelstring; free_pktiopb(pkt, (caddr_t) dkl, SECSIZE); } else if (un->un_capacity < 0) { return; } else { /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -