+/*
+ * 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);
+}
+