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