+/*
+ * Determine capacity of a drive.
+ * Returns -1 on a failure, 0 on success, 1 on a failure that is probably
+ * due to missing media.
+ */
+int
+sdgetcapacity(sc, hd, dev)
+ struct sd_softc *sc;
+ struct hp_device *hd;
+ dev_t dev;
+{
+ static struct scsi_fmt_cdb cap = {
+ 10,
+ CMD_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ u_char *capbuf;
+ int i, capbufsize;
+
+ /*
+ * Cannot use stack space for this buffer since stack KVA may not
+ * be valid (i.e. in context of this process) when the operation
+ * actually starts.
+ */
+ capbufsize = 8;
+ capbuf = malloc(capbufsize, M_DEVBUF, M_WAITOK);
+
+ if (dev == NODEV) {
+ i = scsi_immed_command(hd->hp_ctlr, hd->hp_slave, sc->sc_punit,
+ &cap, capbuf, capbufsize, B_READ);
+ } else {
+ struct buf *bp;
+
+ /*
+ * XXX this is horrible
+ */
+ if (sc->sc_format_pid >= 0)
+ panic("sdgetcapacity");
+ bp = malloc(sizeof *bp, M_DEVBUF, M_WAITOK);
+ sc->sc_format_pid = curproc->p_pid;
+ bcopy((caddr_t)&cap, (caddr_t)&sdcmd[hd->hp_unit], sizeof cap);
+ bp->b_dev = dev;
+ bp->b_flags = B_READ | B_BUSY;
+ bp->b_un.b_addr = (caddr_t)capbuf;
+ bp->b_bcount = capbufsize;
+ sdstrategy(bp);
+ i = biowait(bp) ? sdsense[hd->hp_unit].status : 0;
+ free(bp, M_DEVBUF);
+ sc->sc_format_pid = -1;
+ }
+ if (i) {
+ if (i != STS_CHECKCOND || (sc->sc_flags & SDF_RMEDIA) == 0) {
+#ifdef DEBUG
+ if (sddebug & SDB_CAPACITY)
+ printf("sd%d: read_capacity returns %d\n",
+ hd->hp_unit, i);
+#endif
+ free(capbuf, M_DEVBUF);
+ return (-1);
+ }
+ /*
+ * XXX assume unformatted or non-existant media
+ */
+ sc->sc_blks = 0;
+ sc->sc_blksize = DEV_BSIZE;
+ sc->sc_bshift = 0;
+#ifdef DEBUG
+ if (sddebug & SDB_CAPACITY)
+ printf("sd%d: removable media not present\n",
+ hd->hp_unit);
+#endif
+ free(capbuf, M_DEVBUF);
+ return (1);
+ }
+ sc->sc_blks = *(u_int *)&capbuf[0];
+ sc->sc_blksize = *(int *)&capbuf[4];
+ free(capbuf, M_DEVBUF);
+ sc->sc_bshift = 0;
+
+ /* return value of read capacity is last valid block number */
+ sc->sc_blks++;
+
+ if (sc->sc_blksize != DEV_BSIZE) {
+ if (sc->sc_blksize < DEV_BSIZE) {
+ printf("sd%d: need at least %d byte blocks - %s\n",
+ hd->hp_unit, DEV_BSIZE, "drive ignored");
+ return (-1);
+ }
+ for (i = sc->sc_blksize; i > DEV_BSIZE; i >>= 1)
+ ++sc->sc_bshift;
+ sc->sc_blks <<= sc->sc_bshift;
+ }
+#ifdef DEBUG
+ if (sddebug & SDB_CAPACITY)
+ printf("sd%d: blks=%d, blksize=%d, bshift=%d\n", hd->hp_unit,
+ sc->sc_blks, sc->sc_blksize, sc->sc_bshift);
+#endif
+ return (0);
+}
+
+/*
+ * Read or constuct a disklabel
+ */
+int
+sdgetinfo(dev)
+ dev_t dev;
+{
+ int unit = sdunit(dev);
+ register struct sd_softc *sc = &sd_softc[unit];
+ register struct disklabel *lp = &sc->sc_info.si_label;
+ register struct partition *pi;
+ char *msg, *readdisklabel();
+#ifdef COMPAT_NOLABEL
+ int usedefault = 1;
+
+ /*
+ * For CD-ROM just define a single partition
+ */
+ if (sc->sc_type == 5)
+ usedefault = 0;
+#endif
+
+ bzero((caddr_t)lp, sizeof *lp);
+ msg = NULL;
+
+ /*
+ * If removable media or the size unavailable at boot time
+ * (i.e. unformatted hard disk), attempt to set the capacity
+ * now.
+ */
+ if ((sc->sc_flags & SDF_RMEDIA) || sc->sc_blks == 0) {
+ switch (sdgetcapacity(sc, sc->sc_hd, dev)) {
+ case 0:
+ break;
+ case -1:
+ /*
+ * Hard error, just return (open will fail).
+ */
+ return (EIO);
+ case 1:
+ /*
+ * XXX return 0 so open can continue just in case
+ * the media is unformatted and we want to format it.
+ * We set the error flag so they cannot do much else.
+ */
+ sc->sc_flags |= SDF_ERROR;
+ msg = "unformatted/missing media";
+#ifdef COMPAT_NOLABEL
+ usedefault = 0;
+#endif
+ break;
+ }
+ }
+
+ /*
+ * Set some default values to use while reading the label
+ * (or to use if there isn't a label) and try reading it.
+ */
+ if (msg == NULL) {
+ lp->d_type = DTYPE_SCSI;
+ lp->d_secsize = DEV_BSIZE;
+ lp->d_nsectors = 32;
+ lp->d_ntracks = 20;
+ lp->d_ncylinders = 1;
+ lp->d_secpercyl = 32*20;
+ lp->d_npartitions = 3;
+ lp->d_partitions[2].p_offset = 0;
+ /* XXX we can open a device even without SDF_ALIVE */
+ if (sc->sc_blksize == 0)
+ sc->sc_blksize = DEV_BSIZE;
+ /* XXX ensure size is at least one device block */
+ lp->d_partitions[2].p_size =
+ roundup(LABELSECTOR+1, btodb(sc->sc_blksize));
+ msg = readdisklabel(sdlabdev(dev), sdstrategy, lp);
+ if (msg == NULL)
+ return (0);
+ }
+
+ pi = lp->d_partitions;
+ printf("sd%d: WARNING: %s, ", unit, msg);
+#ifdef COMPAT_NOLABEL
+ if (usedefault) {
+ printf("using old default partitioning\n");
+ sdmakedisklabel(unit, lp);
+ return(0);
+ }
+#endif
+ printf("defining `c' partition as entire disk\n");
+ pi[2].p_size = sc->sc_blks;
+ /* XXX reset other info since readdisklabel screws with it */
+ lp->d_npartitions = 3;
+ pi[0].p_size = 0;
+ return(0);
+}
+