1 /* $NetBSD: ofdisk.c,v 1.3 1996/10/13 01:38:13 christos 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 struct disk sc_dk; 51 int sc_ihandle; 52 u_long max_transfer; 53 char sc_name[16]; 54 }; 55 56 static int ofdprobe __P((struct device *, void *, void *)); 57 static void ofdattach __P((struct device *, struct device *, void *)); 58 59 struct cfattach ofdisk_ca = { 60 sizeof(struct ofd_softc), ofdprobe, ofdattach 61 }; 62 63 struct cfdriver ofdisk_cd = { 64 NULL, "ofdisk", DV_DISK 65 }; 66 67 void ofdstrategy __P((struct buf *)); 68 69 struct dkdriver ofdkdriver = { ofdstrategy }; 70 71 static int 72 ofdprobe(parent, match, aux) 73 struct device *parent; 74 void *match, *aux; 75 { 76 struct ofprobe *ofp = aux; 77 char type[8]; 78 int l; 79 80 if ((l = OF_getprop(ofp->phandle, "device_type", type, sizeof type - 1)) < 0) 81 return 0; 82 if (l >= sizeof type) 83 return 0; 84 type[l] = 0; 85 return !strcmp(type, "block"); 86 } 87 88 static void 89 ofdattach(parent, self, aux) 90 struct device *parent, *self; 91 void *aux; 92 { 93 struct ofd_softc *of = (void *)self; 94 struct ofprobe *ofp = aux; 95 int l; 96 97 of->sc_phandle = ofp->phandle; 98 of->sc_unit = ofp->unit; 99 of->sc_ihandle = 0; 100 of->sc_dk.dk_driver = &ofdkdriver; 101 of->sc_dk.dk_name = of->sc_name; 102 strcpy(of->sc_name, of->sc_dev.dv_xname); 103 disk_attach(&of->sc_dk); 104 dk_establish(&of->sc_dk, self); /* XXX */ 105 printf("\n"); 106 } 107 108 int 109 ofdopen(dev, flags, fmt, p) 110 dev_t dev; 111 int flags; 112 int fmt; 113 struct proc *p; 114 { 115 int unit = DISKUNIT(dev); 116 struct ofd_softc *of; 117 char path[256]; 118 struct disklabel *lp; 119 int l; 120 121 if (unit >= ofdisk_cd.cd_ndevs) 122 return ENXIO; 123 if (!(of = ofdisk_cd.cd_devs[unit])) 124 return ENXIO; 125 126 if (!of->sc_ihandle) { 127 128 if ((l = OF_package_to_path(of->sc_phandle, path, sizeof path - 3)) < 0) 129 return ENXIO; 130 if (l >= sizeof path - 3) 131 return ENXIO; 132 path[l] = 0; 133 134 /* 135 * This is for the benefit of SCSI/IDE disks that don't 136 * have all their childs in the device tree. 137 * YES, I DO THINK THIS IS A BUG IN OPENFIRMWARE!!! 138 * And yes, this is a very gross hack! XXX 139 * See also ofscsi.c 140 */ 141 if (!strcmp(path + l - 4, "disk")) { 142 path[l++] = '@'; 143 path[l++] = '0' + of->sc_unit; 144 path[l] = 0; 145 } 146 147 strcat(path, ":0"); 148 149 if (!(of->sc_ihandle = OF_open(path))) 150 return ENXIO; 151 152 /* 153 * Try to get characteristics of the disk. 154 */ 155 of->max_transfer = OF_call_method_1("max-transfer", of->sc_ihandle, 0); 156 if (of->max_transfer > MAXPHYS) 157 of->max_transfer = MAXPHYS; 158 159 lp = of->sc_dk.dk_label; 160 bzero(lp, sizeof *lp); 161 162 lp->d_secsize = OF_call_method_1("block-size", of->sc_ihandle, 0); 163 if (lp->d_secsize == (u_int32_t)-1 || lp->d_secsize > MAXBSIZE) 164 lp->d_secsize = DEV_BSIZE; 165 166 lp->d_secperunit = OF_call_method_1("#blocks", of->sc_ihandle, 0); 167 if (lp->d_secperunit == (u_int32_t)-1) 168 lp->d_secperunit = 0x7fffffff; 169 170 lp->d_secpercyl = 1; 171 lp->d_nsectors = 1; 172 lp->d_ntracks = 1; 173 lp->d_ncylinders = lp->d_secperunit; 174 175 lp->d_partitions[RAW_PART].p_offset = 0; 176 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 177 178 readdisklabel(MAKEDISKDEV(major(dev), unit, RAW_PART), ofdstrategy, 179 lp, of->sc_dk.dk_cpulabel); 180 } 181 182 switch (fmt) { 183 case S_IFCHR: 184 of->sc_dk.dk_copenmask |= 1 << DISKPART(dev); 185 break; 186 case S_IFBLK: 187 of->sc_dk.dk_bopenmask |= 1 << DISKPART(dev); 188 break; 189 } 190 of->sc_dk.dk_openmask = of->sc_dk.dk_copenmask | of->sc_dk.dk_bopenmask; 191 192 return 0; 193 } 194 195 int 196 ofdclose(dev, flags, fmt, p) 197 dev_t dev; 198 int flags; 199 int fmt; 200 struct proc *p; 201 { 202 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(dev)]; 203 204 switch (fmt) { 205 case S_IFCHR: 206 of->sc_dk.dk_copenmask &= ~(1 << DISKPART(dev)); 207 break; 208 case S_IFBLK: 209 of->sc_dk.dk_bopenmask &= ~(1 << DISKPART(dev)); 210 break; 211 } 212 of->sc_dk.dk_openmask = of->sc_dk.dk_copenmask | of->sc_dk.dk_bopenmask; 213 214 #ifdef FIREPOWERBUGS 215 /* 216 * This is a hack to get the firmware to flush its buffers. 217 */ 218 OF_seek(of->sc_ihandle, 0); 219 #endif 220 if (!of->sc_dk.dk_openmask) { 221 OF_close(of->sc_ihandle); 222 of->sc_ihandle = 0; 223 } 224 225 return 0; 226 } 227 228 void 229 ofdstrategy(bp) 230 struct buf *bp; 231 { 232 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(bp->b_dev)]; 233 struct partition *p; 234 u_quad_t off; 235 int read; 236 int (*OF_io)(int, void *, int); 237 daddr_t blkno = bp->b_blkno; 238 239 bp->b_resid = 0; 240 if (bp->b_bcount == 0) 241 goto done; 242 243 OF_io = bp->b_flags & B_READ ? OF_read : OF_write; 244 245 if (DISKPART(bp->b_dev) != RAW_PART) { 246 if (bounds_check_with_label(bp, of->sc_dk.dk_label, 0) <= 0) { 247 bp->b_resid = bp->b_bcount; 248 goto done; 249 } 250 p = &of->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)]; 251 blkno = bp->b_blkno + p->p_offset; 252 } 253 254 disk_busy(&of->sc_dk); 255 256 off = (u_quad_t)blkno * DEV_BSIZE; 257 read = -1; 258 do { 259 if (OF_seek(of->sc_ihandle, off) < 0) 260 break; 261 read = OF_io(of->sc_ihandle, bp->b_data, bp->b_bcount); 262 } while (read == -2); 263 if (read < 0) { 264 bp->b_error = EIO; 265 bp->b_flags |= B_ERROR; 266 bp->b_resid = bp->b_bcount; 267 } else 268 bp->b_resid = bp->b_bcount - read; 269 270 disk_unbusy(&of->sc_dk, bp->b_bcount - bp->b_resid); 271 272 done: 273 biodone(bp); 274 } 275 276 static void 277 ofminphys(bp) 278 struct buf *bp; 279 { 280 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(bp->b_dev)]; 281 282 if (bp->b_bcount > of->max_transfer) 283 bp->b_bcount = of->max_transfer; 284 } 285 286 int 287 ofdread(dev, uio) 288 dev_t dev; 289 struct uio *uio; 290 { 291 return physio(ofdstrategy, NULL, dev, B_READ, ofminphys, uio); 292 } 293 294 int 295 ofdwrite(dev, uio) 296 dev_t dev; 297 struct uio *uio; 298 { 299 return physio(ofdstrategy, NULL, dev, B_WRITE, ofminphys, uio); 300 } 301 302 int 303 ofdioctl(dev, cmd, data, flag, p) 304 dev_t dev; 305 u_long cmd; 306 caddr_t data; 307 int flag; 308 struct proc *p; 309 { 310 struct ofd_softc *of = ofdisk_cd.cd_devs[DISKUNIT(dev)]; 311 int error; 312 313 switch (cmd) { 314 case DIOCGDINFO: 315 *(struct disklabel *)data = *of->sc_dk.dk_label; 316 return 0; 317 318 case DIOCGPART: 319 ((struct partinfo *)data)->disklab = of->sc_dk.dk_label; 320 ((struct partinfo *)data)->part = 321 &of->sc_dk.dk_label->d_partitions[DISKPART(dev)]; 322 return 0; 323 324 case DIOCWDINFO: 325 case DIOCSDINFO: 326 if ((flag & FWRITE) == 0) 327 return EBADF; 328 329 error = setdisklabel(of->sc_dk.dk_label, 330 (struct disklabel *)data, /*of->sc_dk.dk_openmask */0, 331 of->sc_dk.dk_cpulabel); 332 if (error == 0 && cmd == DIOCWDINFO) 333 error = writedisklabel(MAKEDISKDEV(major(dev), 334 DISKUNIT(dev), RAW_PART), 335 ofdstrategy, 336 of->sc_dk.dk_label, 337 of->sc_dk.dk_cpulabel); 338 339 return error; 340 default: 341 return ENOTTY; 342 } 343 } 344 345 int 346 ofddump(dev, blkno, va, size) 347 dev_t dev; 348 daddr_t blkno; 349 caddr_t va; 350 size_t size; 351 { 352 return EINVAL; 353 } 354 355 int 356 ofdsize(dev) 357 dev_t dev; 358 { 359 struct ofd_softc *of; 360 int part; 361 int size; 362 363 if (ofdopen(dev, 0, S_IFBLK) != 0) 364 return -1; 365 of = ofdisk_cd.cd_devs[DISKUNIT(dev)]; 366 part = DISKPART(dev); 367 if (of->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 368 size = -1; 369 else 370 size = of->sc_dk.dk_label->d_partitions[part].p_size; 371 if (ofdclose(dev, 0, S_IFBLK) != 0) 372 return -1; 373 return size; 374 } 375