1 /* $NetBSD: sd.c,v 1.17 2023/02/12 08:25:09 tsutsui Exp $ */ 2 /* 3 * Copyright (c) 1994 Rolf Grossmann 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Rolf Grossmann. 17 * 4. The name of the author may not be used to endorse or promote products 18 * derived from this software without specific prior written permission 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/disklabel.h> 34 #include <sys/bootblock.h> 35 #include <dev/scsipi/scsi_spc.h> 36 #include <dev/scsipi/scsipi_all.h> 37 #include <dev/scsipi/scsi_all.h> 38 #include <dev/scsipi/scsipi_disk.h> 39 #include <lib/libsa/stand.h> 40 #include <lib/libkern/libkern.h> /* for bzero() */ 41 42 #include "dmareg.h" 43 #include "scsivar.h" 44 #include "samachdep.h" 45 46 #ifdef SD_DEBUG 47 #define DPRINTF(x) printf x; 48 #else 49 #define DPRINTF(x) 50 #endif 51 52 struct sdminilabel { 53 u_short npart; 54 long offset[MAXPARTITIONS+1]; /* offset from absolute block 0! */ 55 }; 56 57 struct sd_softc { 58 int sc_unit; 59 int sc_lun; 60 int sc_part; 61 int sc_dev_bsize; 62 struct sdminilabel sc_pinfo; 63 }; 64 65 #define NSD 7 66 #define MAXRETRIES 5 /* must be at least one */ 67 68 int sdprobe(char target, char lun); 69 int sdgetinfo(struct sd_softc *ss); 70 71 int 72 sdprobe(char target, char lun) 73 { 74 struct scsi_test_unit_ready cdb1; 75 struct scsipi_inquiry cdb2; 76 struct scsipi_inquiry_data inq; 77 int error, retries; 78 int count; 79 80 memset(&cdb1, 0, sizeof(cdb1)); 81 cdb1.opcode = SCSI_TEST_UNIT_READY; 82 83 retries = 0; 84 do { 85 count = 0; 86 error = scsiicmd(target, lun, (u_char *)&cdb1, sizeof(cdb1), NULL, &count); 87 if (error == -SCSI_BUSY) { 88 register int N = 10000000; while (--N > 0); 89 } 90 } while ((error == -SCSI_CHECK || error == -SCSI_BUSY) 91 && retries++ < MAXRETRIES); 92 93 if (error) 94 return error<0 ? ENODEV : error; 95 96 memset(&cdb2, 0, sizeof(cdb2)); 97 cdb2.opcode = INQUIRY; 98 cdb2.length = SCSIPI_INQUIRY_LENGTH_SCSI2; 99 count = SCSIPI_INQUIRY_LENGTH_SCSI2; 100 error = scsiicmd(target, lun, (u_char *)&cdb2, sizeof(cdb2), 101 (char *)&inq, &count); 102 if (error != 0) 103 return error<0 ? EHER : error; 104 105 if ((inq.device & SID_TYPE) != T_DIRECT 106 && (inq.device & SID_TYPE) != T_CDROM) 107 return EUNIT; /* not a disk */ 108 109 DPRINTF(("booting disk %s.\n", inq.vendor)); 110 111 return 0; 112 } 113 114 int 115 sdgetinfo(struct sd_softc *ss) 116 { 117 struct scsipi_read_capacity_10 cdb; 118 struct scsipi_read_capacity_10_data cap; 119 struct sdminilabel *pi = &ss->sc_pinfo; 120 struct next68k_disklabel *label; 121 int error, i, blklen; 122 char io_buf[NEXT68K_LABEL_SIZE+NEXT68K_LABEL_OFFSET]; 123 int count; 124 int sc_blkshift = 0; 125 126 memset(&cdb, 0, sizeof(cdb)); 127 cdb.opcode = READ_CAPACITY_10; 128 count = sizeof(cap); 129 error = scsiicmd(ss->sc_unit, ss->sc_lun, (u_char *)&cdb, sizeof(cdb), 130 (char *)&cap, &count); 131 if (error != 0) 132 return error<0 ? EHER : error; 133 blklen = (cap.length[0]<<24) + (cap.length[1]<<16) 134 + (cap.length[2]<<8) + cap.length[3]; 135 136 /* avoid division by zero trap even on possible xfer errors */ 137 if (blklen == 0) 138 blklen = DEV_BSIZE; 139 ss->sc_dev_bsize = blklen; 140 141 ss->sc_pinfo.offset[ss->sc_part] = 0; /* read absolute sector */ 142 error = sdstrategy(ss, F_READ, NEXT68K_LABEL_SECTOR, 143 NEXT68K_LABEL_SIZE+NEXT68K_LABEL_OFFSET, io_buf, (unsigned int *)&i); 144 if (error != 0) { 145 DPRINTF(("sdgetinfo: sdstrategy error %d\n", error)); 146 return(ERDLAB); 147 } 148 label = (struct next68k_disklabel *)(io_buf+NEXT68K_LABEL_OFFSET); 149 150 if (!IS_DISKLABEL(label)) /* || (label->cd_flags & CD_UNINIT)!=0) */ 151 return EUNLAB; /* bad magic */ 152 153 /* XXX calculate checksum ... for now we rely on the magic number */ 154 DPRINTF(("Disk is %s (%s, %s).\n", 155 label->cd_label,label->cd_name, label->cd_type)); 156 157 while (label->cd_secsize > blklen) 158 { 159 blklen <<= 1; 160 ++sc_blkshift; 161 } 162 if (label->cd_secsize < blklen) 163 { 164 printf("bad label sectorsize (%d) or device blocksize (%d).\n", 165 label->cd_secsize, blklen>>sc_blkshift); 166 return ENXIO; 167 } 168 pi->npart = 0; 169 for(i=0; i<MAXPARTITIONS; i++) { 170 if (label->cd_partitions[i].cp_size > 0) { 171 pi->offset[pi->npart] = (label->cd_partitions[i].cp_offset 172 + label->cd_front) << sc_blkshift; 173 } 174 else 175 pi->offset[pi->npart] = -1; 176 DPRINTF (("%d: [%d]=%ld\n", i, pi->npart, pi->offset[pi->npart])); 177 pi->npart++; 178 if (pi->npart == RAW_PART) 179 pi->npart++; 180 } 181 pi->offset[RAW_PART] = -1; 182 183 return 0; 184 } 185 186 int 187 sdopen(struct open_file *f, ...) 188 { 189 va_list ap; 190 int count, lun, part; 191 register struct sd_softc *ss; 192 char unit, cnt; 193 int error; 194 195 va_start(ap, f); 196 count = va_arg(ap, int); 197 lun = va_arg(ap, int); 198 part = va_arg(ap, int); 199 va_end(ap); 200 201 DPRINTF(("open: sd(%d,%d,%d)\n", count, lun, part)); 202 203 if (lun >= NSD) 204 return EUNIT; 205 206 scsi_init(); 207 208 for(cnt=0, unit=0; unit < NSD; unit++) 209 { 210 DPRINTF(("trying target %d lun %d.\n", unit, lun)); 211 error = sdprobe(unit, lun); 212 if (error == 0) 213 { 214 if (cnt++ == count) 215 break; 216 } 217 else if (error != EUNIT) 218 return error; 219 } 220 221 if (unit >= NSD) 222 return EUNIT; 223 224 ss = alloc(sizeof(struct sd_softc)); 225 ss->sc_unit = unit; 226 ss->sc_lun = lun; 227 ss->sc_part = part; 228 229 if ((error = sdgetinfo(ss)) != 0) 230 return error; 231 232 if ((unsigned char)part >= ss->sc_pinfo.npart 233 || ss->sc_pinfo.offset[(int)part] == -1) 234 return EPART; 235 236 f->f_devdata = ss; 237 return 0; 238 } 239 240 int 241 sdclose(struct open_file *f) 242 { 243 register struct sd_softc *ss = f->f_devdata; 244 245 dealloc(ss, sizeof(struct sd_softc)); 246 return 0; 247 } 248 249 int 250 sdstrategy(void *devdata, int rw, daddr_t dblk, size_t size, 251 void *buf, size_t *rsize) 252 { 253 struct sd_softc *ss = devdata; 254 u_long blk = dblk + ss->sc_pinfo.offset[ss->sc_part]; 255 struct scsipi_rw_10 cdb; 256 int error; 257 258 if (size == 0) 259 return 0; 260 261 if (rw != F_READ) 262 { 263 printf("sdstrategy: write not implemented.\n"); 264 return EOPNOTSUPP; 265 } 266 267 *rsize = 0; 268 while (size > 0) { 269 u_long nblks; 270 int tsize; 271 if (size > MAX_DMASIZE) 272 tsize = MAX_DMASIZE; 273 else 274 tsize = size; 275 276 nblks = howmany(tsize, ss->sc_dev_bsize); 277 278 DPRINTF(("sdstrategy: read block %ld, %d bytes (%ld blks a %d bytes).\n", 279 blk, tsize, nblks, ss->sc_dev_bsize)); 280 281 memset(&cdb, 0, sizeof(cdb)); 282 cdb.opcode = READ_10; 283 cdb.addr[0] = (blk & 0xff000000) >> 24; 284 cdb.addr[1] = (blk & 0xff0000) >> 16; 285 cdb.addr[2] = (blk & 0xff00) >> 8; 286 cdb.addr[3] = blk & 0xff; 287 cdb.length[0] = (nblks & 0xff00) >> 8; 288 cdb.length[1] = nblks & 0xff; 289 290 error = scsiicmd(ss->sc_unit, ss->sc_lun, 291 (u_char *)&cdb, sizeof(cdb), (char *)buf + *rsize, &tsize); 292 if (error != 0) 293 { 294 DPRINTF(("sdstrategy: scsiicmd failed: %d = %s.\n", error, strerror(error))); 295 return error<0 ? EIO : error; 296 } 297 *rsize += tsize; 298 size -= tsize; 299 blk += nblks; 300 } 301 DPRINTF(("sdstrategy: read %d bytes\n", *rsize)); 302 return 0; 303 } 304