1 /* $NetBSD: ofdisk.c,v 1.5 1997/06/24 00:27:18 thorpej Exp $ */ 2 3 /* 4 * Copyright (C) 1995, 1996 Wolfgang Solfrank. 5 * Copyright (C) 1995, 1996 TooLs GmbH. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by TooLs GmbH. 19 * 4. The name of TooLs GmbH may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/buf.h> 36 #include <sys/device.h> 37 #include <sys/disklabel.h> 38 #include <sys/disk.h> 39 #include <sys/fcntl.h> 40 #include <sys/ioctl.h> 41 #include <sys/stat.h> 42 #include <sys/systm.h> 43 44 #include <dev/ofw/openfirm.h> 45 46 struct ofd_softc { 47 struct device sc_dev; 48 int sc_phandle; 49 int sc_unit; 50 int sc_flags; 51 struct disk sc_dk; 52 int sc_ihandle; 53 u_long max_transfer; 54 char sc_name[16]; 55 }; 56 57 /* sc_flags */ 58 #define OFDF_ISFLOPPY 0x01 /* we are a floppy drive */ 59 60 static int ofdprobe __P((struct device *, struct cfdata *, void *)); 61 static void ofdattach __P((struct device *, struct device *, void *)); 62 63 struct cfattach ofdisk_ca = { 64 sizeof(struct ofd_softc), ofdprobe, ofdattach 65 }; 66 67 struct cfdriver ofdisk_cd = { 68 NULL, "ofdisk", DV_DISK 69 }; 70 71 void ofdstrategy __P((struct buf *)); 72 73 struct dkdriver ofdkdriver = { ofdstrategy }; 74 75 static int 76 ofdprobe(parent, match, aux) 77 struct device *parent; 78 struct cfdata *match; 79 void *aux; 80 { 81 struct ofprobe *ofp = aux; 82 char type[8]; 83 int l; 84 85 if ((l = OF_getprop(ofp->phandle, "device_type", type, 86 sizeof type - 1)) < 0) 87 return 0; 88 if (l >= sizeof type) 89 return 0; 90 type[l] = 0; 91 return !strcmp(type, "block"); 92 } 93 94 static void 95 ofdattach(parent, self, aux) 96 struct device *parent, *self; 97 void *aux; 98 { 99 struct ofd_softc *of = (void *)self; 100 struct ofprobe *ofp = aux; 101 char child[64]; 102 int l; 103 104 if ((l = OF_getprop(ofp->phandle, "name", child, 105 sizeof child - 1)) < 0) 106 panic("device without name?"); 107 if (l >= sizeof child) 108 l = sizeof child - 1; 109 child[l] = 0; 110 111 of->sc_flags = 0; 112 of->sc_phandle = ofp->phandle; 113 of->sc_unit = ofp->unit; 114 of->sc_ihandle = 0; 115 of->sc_dk.dk_driver = &ofdkdriver; 116 of->sc_dk.dk_name = of->sc_name; 117 strcpy(of->sc_name, of->sc_dev.dv_xname); 118 disk_attach(&of->sc_dk); 119 dk_establish(&of->sc_dk, self); /* XXX */ 120 printf("\n"); 121 122 if (strcmp(child, "floppy") == 0) 123 of->sc_flags |= OFDF_ISFLOPPY; 124 } 125 126 int 127 ofdopen(dev, flags, fmt, p) 128 dev_t dev; 129 int flags; 130 int fmt; 131 struct proc *p; 132 { 133 int unit = DISKUNIT(dev); 134 struct ofd_softc *of; 135 char path[256]; 136 struct disklabel *lp; 137 char *errmes; 138 int l; 139 140 if (unit >= ofdisk_cd.cd_ndevs) 141 return ENXIO; 142 if (!(of = ofdisk_cd.cd_devs[unit])) 143 return ENXIO; 144 145 if (!of->sc_ihandle) { 146 if ((l = OF_package_to_path(of->sc_phandle, path, 147 sizeof path - 3)) < 0) 148 return ENXIO; 149 if (l >= sizeof path - 3) 150 return ENXIO; 151 path[l] = 0; 152 153 /* 154 * XXX This is for the benefit of SCSI/IDE disks that don't 155 * XXX have all their childs in the device tree. 156 * XXX YES, I DO THINK THIS IS A BUG IN OPENFIRMWARE!!! 157 * XXX And yes, this is a very gross hack! 158 * XXX See also ofscsi.c 159 */ 160 if (!strcmp(path + l - 4, "disk")) { 161 path[l++] = '@'; 162 path[l++] = '0' + of->sc_unit; 163 path[l] = 0; 164 } 165 166 strcat(path, ":0"); 167 168 if (!(of->sc_ihandle = OF_open(path))) 169 return ENXIO; 170 171 /* 172 * Try to get characteristics of the disk. 173 */ 174 of->max_transfer = OF_call_method_1("max-transfer", 175 of->sc_ihandle, 0); 176 if (of->max_transfer > MAXPHYS) 177 of->max_transfer = MAXPHYS; 178 179 lp = of->sc_dk.dk_label; 180 bzero(lp, sizeof *lp); 181 182 /* 183 * XXX Firmware bug? Asking for block size gives a 184 * XXX rediculous number! So we use what the boot program 185 * XXX uses. 186 */ 187 lp->d_secsize = DEV_BSIZE; 188 189 lp->d_secperunit = OF_call_method_1("#blocks", 190 of->sc_ihandle, 0); 191 if (lp->d_secperunit == (u_int32_t)-1) 192 lp->d_secperunit = 0x7fffffff; 193 194 lp->d_secpercyl = 1; 195 lp->d_nsectors = 1; 196 lp->d_ntracks = 1; 197 lp->d_ncylinders = lp->d_secperunit; 198 199 lp->d_partitions[RAW_PART].p_offset = 0; 200 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 201 lp->d_npartitions = RAW_PART + 1; 202 203 /* 204 * Don't read the disklabel on a floppy; simply 205 * assign all partitions the same size/offset as 206 * RAW_PART. (This is essentially what the ISA 207 * floppy driver does, but we don't deal with 208 * density stuff.) 209 */ 210 if (of->sc_flags & OFDF_ISFLOPPY) { 211 lp->d_npartitions = MAXPARTITIONS; 212 for (l = 0; l < lp->d_npartitions; l++) { 213 if (l == RAW_PART) 214 continue; 215 /* struct copy */ 216 lp->d_partitions[l] = 217 lp->d_partitions[RAW_PART]; 218 } 219 } else { 220 errmes = readdisklabel(MAKEDISKDEV(major(dev), 221 unit, RAW_PART), ofdstrategy, lp, 222 of->sc_dk.dk_cpulabel); 223 if (errmes != NULL) 224 printf("%s: %s\n", errmes); 225 } 226 } 227 228 switch (fmt) { 229 case S_IFCHR: 230 of->sc_dk.dk_copenmask |= 1 << DISKPART(dev); 231 break; 232 case S_IFBLK: 233 of->sc_dk.dk_bopenmask |= 1 << DISKPART(dev); 234 break; 235 } 236 of->sc_dk.dk_openmask = 237 of->sc_dk.dk_copenmask | of->sc_dk.dk_bopenmask; 238 239 return 0; 240 } 241 242 int 243 ofdclose(dev, flags, fmt, p) 244 dev_t dev; 245 int flags; 246 int fmt; 247 struct proc *p; 248 { 249 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(dev)]; 250 251 switch (fmt) { 252 case S_IFCHR: 253 of->sc_dk.dk_copenmask &= ~(1 << DISKPART(dev)); 254 break; 255 case S_IFBLK: 256 of->sc_dk.dk_bopenmask &= ~(1 << DISKPART(dev)); 257 break; 258 } 259 of->sc_dk.dk_openmask = of->sc_dk.dk_copenmask | of->sc_dk.dk_bopenmask; 260 261 #ifdef FIRMWORKSBUGS 262 /* 263 * This is a hack to get the firmware to flush its buffers. 264 */ 265 OF_seek(of->sc_ihandle, 0); 266 #endif 267 if (!of->sc_dk.dk_openmask) { 268 OF_close(of->sc_ihandle); 269 of->sc_ihandle = 0; 270 } 271 272 return 0; 273 } 274 275 void 276 ofdstrategy(bp) 277 struct buf *bp; 278 { 279 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(bp->b_dev)]; 280 struct partition *p; 281 u_quad_t off; 282 int read; 283 int (*OF_io)(int, void *, int); 284 daddr_t blkno = bp->b_blkno; 285 286 bp->b_resid = 0; 287 if (bp->b_bcount == 0) 288 goto done; 289 290 OF_io = bp->b_flags & B_READ ? OF_read : OF_write; 291 292 if (DISKPART(bp->b_dev) != RAW_PART) { 293 if (bounds_check_with_label(bp, of->sc_dk.dk_label, 0) <= 0) { 294 bp->b_resid = bp->b_bcount; 295 goto done; 296 } 297 p = &of->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)]; 298 blkno = bp->b_blkno + p->p_offset; 299 } 300 301 disk_busy(&of->sc_dk); 302 303 off = (u_quad_t)blkno * DEV_BSIZE; 304 read = -1; 305 do { 306 if (OF_seek(of->sc_ihandle, off) < 0) 307 break; 308 read = OF_io(of->sc_ihandle, bp->b_data, bp->b_bcount); 309 } while (read == -2); 310 311 if (read < 0) { 312 bp->b_error = EIO; 313 bp->b_flags |= B_ERROR; 314 bp->b_resid = bp->b_bcount; 315 } else 316 bp->b_resid = bp->b_bcount - read; 317 318 disk_unbusy(&of->sc_dk, bp->b_bcount - bp->b_resid); 319 320 done: 321 biodone(bp); 322 } 323 324 static void 325 ofminphys(bp) 326 struct buf *bp; 327 { 328 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(bp->b_dev)]; 329 330 if (bp->b_bcount > of->max_transfer) 331 bp->b_bcount = of->max_transfer; 332 } 333 334 int 335 ofdread(dev, uio) 336 dev_t dev; 337 struct uio *uio; 338 { 339 return physio(ofdstrategy, NULL, dev, B_READ, ofminphys, uio); 340 } 341 342 int 343 ofdwrite(dev, uio) 344 dev_t dev; 345 struct uio *uio; 346 { 347 return physio(ofdstrategy, NULL, dev, B_WRITE, ofminphys, uio); 348 } 349 350 int 351 ofdioctl(dev, cmd, data, flag, p) 352 dev_t dev; 353 u_long cmd; 354 caddr_t data; 355 int flag; 356 struct proc *p; 357 { 358 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(dev)]; 359 int error; 360 361 switch (cmd) { 362 case DIOCGDINFO: 363 *(struct disklabel *)data = *of->sc_dk.dk_label; 364 return 0; 365 366 case DIOCGPART: 367 ((struct partinfo *)data)->disklab = of->sc_dk.dk_label; 368 ((struct partinfo *)data)->part = 369 &of->sc_dk.dk_label->d_partitions[DISKPART(dev)]; 370 return 0; 371 372 case DIOCWDINFO: 373 case DIOCSDINFO: 374 if ((flag & FWRITE) == 0) 375 return EBADF; 376 377 error = setdisklabel(of->sc_dk.dk_label, 378 (struct disklabel *)data, /*of->sc_dk.dk_openmask */0, 379 of->sc_dk.dk_cpulabel); 380 if (error == 0 && cmd == DIOCWDINFO) 381 error = writedisklabel(MAKEDISKDEV(major(dev), 382 DISKUNIT(dev), RAW_PART), ofdstrategy, 383 of->sc_dk.dk_label, of->sc_dk.dk_cpulabel); 384 385 return error; 386 default: 387 return ENOTTY; 388 } 389 } 390 391 int 392 ofddump(dev, blkno, va, size) 393 dev_t dev; 394 daddr_t blkno; 395 caddr_t va; 396 size_t size; 397 { 398 return EINVAL; 399 } 400 401 int 402 ofdsize(dev) 403 dev_t dev; 404 { 405 struct ofd_softc *of; 406 struct disklabel *lp; 407 int size, part, omask, unit; 408 409 unit = DISKUNIT(dev); 410 if (unit >= ofdisk_cd.cd_ndevs || 411 (of = ofdisk_cd.cd_devs[unit]) == NULL) 412 return -1; 413 414 part = DISKPART(dev); 415 omask = of->sc_dk.dk_openmask & (1 << part); 416 lp = sc->sc_dk.dk_label; 417 418 if (omask == 0 && ofdopen(dev, 0, S_IFBLK, curproc) != 0) 419 return -1; 420 421 if (lp->d_partitions[part].p_fstype != FS_SWAP) 422 size = -1; 423 else 424 size = lp->d_partitions[part].p_size * 425 (lp->d_secsize / DEV_BSIZE); 426 427 if (omask == 0 && ofdclose(dev, 0, S_IFBLK, curproc) != 0) 428 return -1; 429 430 return size; 431 } 432