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