1 /* $NetBSD: sd.c,v 1.2 1999/03/26 06:54:40 dbj 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 <dev/scsipi/scsipi_all.h> 35 #include <dev/scsipi/scsi_all.h> 36 #include <dev/scsipi/scsipi_disk.h> 37 #include <lib/libsa/stand.h> 38 #include <lib/libkern/libkern.h> /* for bzero() */ 39 40 #ifdef SD_DEBUG 41 #define DPRINTF(x) printf x; 42 #else 43 #define DPRINTF(x) 44 #endif 45 46 struct sdminilabel { 47 u_short npart; 48 long offset[MAXPARTITIONS]; /* offset from absolute block 0! */ 49 }; 50 51 struct sd_softc { 52 int sc_unit; 53 int sc_lun; 54 int sc_part; 55 short sc_blkshift; 56 int sc_dev_bsize; 57 struct sdminilabel sc_pinfo; 58 }; 59 60 #define NSD 7 61 #define MAXRETRIES 5 /* must be at least one */ 62 63 void scsi_init(void); 64 int scsiicmd(char, char, u_char *, int, char *, int); 65 int sdstrategy(struct sd_softc *ss, int rw, daddr_t dblk, size_t size, 66 void *buf, size_t *rsize); 67 int sdprobe(char target, char lun); 68 int sdgetinfo(struct sd_softc *ss); 69 int sdopen(struct open_file *f, char count, char lun, char part); 70 int sdclose(struct open_file *f); 71 72 int 73 sdprobe(char target, char lun) 74 { 75 struct scsipi_test_unit_ready cdb1; 76 struct scsipi_inquiry cdb2; 77 struct scsipi_inquiry_data inq; 78 int error, retries; 79 80 bzero(&cdb1, sizeof(cdb1)); 81 cdb1.opcode = TEST_UNIT_READY; 82 83 retries = 0; 84 do { 85 error = scsiicmd(target, lun, (u_char *)&cdb1, sizeof(cdb1), NULL, 0); 86 if (error == -SCSI_BUSY) { 87 register int N = 10000000; while (--N > 0); 88 } 89 } while ((error == -SCSI_CHECK || error == -SCSI_BUSY) 90 && retries++ < MAXRETRIES); 91 92 if (error) 93 return error<0 ? ENODEV : error; 94 95 bzero(&cdb2, sizeof(cdb2)); 96 cdb2.opcode = INQUIRY; 97 cdb2.length = sizeof(inq); 98 error = scsiicmd(target, lun, (u_char *)&cdb2, sizeof(cdb2), 99 (char *)&inq, sizeof(inq)); 100 if (error != 0) 101 return error<0 ? EHER : error; 102 103 if ((inq.device & SID_TYPE) != T_DIRECT 104 && (inq.device & SID_TYPE) != T_CDROM) 105 return EUNIT; /* not a disk */ 106 107 DPRINTF(("booting disk %s.\n", inq.vendor)); 108 109 return 0; 110 } 111 112 int 113 sdgetinfo(struct sd_softc *ss) 114 { 115 struct scsipi_read_capacity cdb; 116 struct scsipi_read_cap_data cap; 117 struct sdminilabel *pi = &ss->sc_pinfo; 118 struct cpu_disklabel *label; 119 int error, i, blklen; 120 char io_buf[LABELSIZE+LABELOFFSET]; 121 122 bzero(&cdb, sizeof(cdb)); 123 cdb.opcode = READ_CAPACITY; 124 error = scsiicmd(ss->sc_unit, ss->sc_lun, (u_char *)&cdb, sizeof(cdb), 125 (char *)&cap, sizeof(cap)); 126 if (error != 0) 127 return error<0 ? EHER : error; 128 129 blklen = (cap.length[0]<<24) + (cap.length[1]<<16) 130 + (cap.length[2]<<8) + cap.length[3]; 131 ss->sc_dev_bsize = blklen; 132 ss->sc_blkshift = 0; 133 ss->sc_pinfo.offset[ss->sc_part] = 0; /* read absolute sector */ 134 error = sdstrategy(ss, F_READ, LABELSECTOR, 135 LABELSIZE+LABELOFFSET, io_buf, &i); 136 if (error != 0) { 137 DPRINTF(("sdgetinfo: sdstrategy error %d\n", error)); 138 return(ERDLAB); 139 } 140 label = (struct cpu_disklabel *)(io_buf+LABELOFFSET); 141 142 if (!IS_DISKLABEL(label) || (label->cd_flags & CD_UNINIT)!=0) 143 return EUNLAB; /* bad magic */ 144 145 /* XXX calculate checksum ... for now we rely on the magic number */ 146 DPRINTF(("Disk is %s (%s, %s).\n", 147 label->cd_label,label->cd_name, label->cd_type)); 148 149 while (label->cd_secsize > blklen) 150 { 151 blklen <<= 1; 152 ++ss->sc_blkshift; 153 } 154 if (label->cd_secsize < blklen) 155 { 156 printf("bad label sectorsize (%d) or device blocksize (%d).\n", 157 label->cd_secsize, blklen>>ss->sc_blkshift); 158 return ENXIO; 159 } 160 pi->npart = MAXPARTITIONS; 161 for(i=0; i<MAXPARTITIONS; i++) { 162 if (label->cd_partitions[i].cp_size > 0) 163 pi->offset[i] = label->cd_partitions[i].cp_offset 164 + label->cd_front; 165 else 166 pi->offset[i] = -1; 167 } 168 169 return 0; 170 } 171 172 int 173 sdopen(struct open_file *f, char count, char lun, char part) 174 { 175 register struct sd_softc *ss; 176 char unit, cnt; 177 int error; 178 179 DPRINTF(("open: sd(%d,%d,%d)\n", count, lun, part)); 180 181 if (lun >= NSD) 182 return EUNIT; 183 184 scsi_init(); 185 186 for(cnt=0, unit=0; unit < NSD; unit++) 187 { 188 DPRINTF(("trying target %d lun %d.\n", unit, lun)); 189 error = sdprobe(unit, lun); 190 if (error == 0) 191 { 192 if (cnt++ == count) 193 break; 194 } 195 else if (error != EUNIT) 196 return error; 197 } 198 199 if (unit >= NSD) 200 return EUNIT; 201 202 ss = alloc(sizeof(struct sd_softc)); 203 ss->sc_unit = unit; 204 ss->sc_lun = lun; 205 ss->sc_part = part; 206 207 if ((error = sdgetinfo(ss)) != 0) 208 return error; 209 210 if ((unsigned char)part >= ss->sc_pinfo.npart 211 || ss->sc_pinfo.offset[(int)part] == -1) 212 return EPART; 213 214 f->f_devdata = ss; 215 return 0; 216 } 217 218 int 219 sdclose(struct open_file *f) 220 { 221 register struct sd_softc *ss = f->f_devdata; 222 223 free(ss, sizeof(struct sd_softc)); 224 return 0; 225 } 226 227 int 228 sdstrategy(struct sd_softc *ss, int rw, daddr_t dblk, size_t size, 229 void *buf, size_t *rsize) 230 { 231 u_long blk = (dblk + ss->sc_pinfo.offset[ss->sc_part]) << ss->sc_blkshift; 232 u_long nblks = howmany(size, ss->sc_dev_bsize); 233 struct scsipi_rw_big cdb; 234 int error; 235 236 if (size == 0) 237 return 0; 238 239 if (rw != F_READ) 240 { 241 printf("sdstrategy: write not implemented.\n"); 242 return EOPNOTSUPP; 243 } 244 245 DPRINTF(("sdstrategy: read block %ld, %d bytes (%ld blks).\n", 246 blk, size, nblks)); 247 248 bzero(&cdb, sizeof(cdb)); 249 cdb.opcode = READ_BIG; 250 cdb.addr[0] = (blk & 0xff000000) >> 24; 251 cdb.addr[1] = (blk & 0xff0000) >> 16; 252 cdb.addr[2] = (blk & 0xff00) >> 8; 253 cdb.addr[3] = blk & 0xff; 254 cdb.length[0] = (nblks & 0xff00) >> 8; 255 cdb.length[1] = nblks & 0xff; 256 257 error = scsiicmd(ss->sc_unit, ss->sc_lun, 258 (u_char *)&cdb, sizeof(cdb), buf, size); 259 if (error != 0) 260 { 261 DPRINTF(("sdstrategy: scsiicmd failed: %d = %s.\n", error, strerror(error))); 262 return error<0 ? EIO : error; 263 } 264 *rsize = size; 265 return 0; 266 } 267