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