1 /* $NetBSD: ofdisk.c,v 1.42 2009/05/12 13:17:37 cegger Exp $ */
4 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
5 * Copyright (C) 1995, 1996 TooLs GmbH.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: ofdisk.c,v 1.42 2009/05/12 13:17:37 cegger Exp $");
37 #include <sys/param.h>
39 #include <sys/device.h>
41 #include <sys/disklabel.h>
43 #include <sys/fcntl.h>
44 #include <sys/ioctl.h>
46 #include <sys/systm.h>
49 #include <dev/ofw/openfirm.h>
62 #define OFDF_ISFLOPPY 0x01 /* we are a floppy drive */
64 #define OFDISK_FLOPPY_P(of) ((of)->sc_flags & OFDF_ISFLOPPY)
66 static int ofdisk_match (device_t
, cfdata_t
, void *);
67 static void ofdisk_attach (device_t
, device_t
, void *);
69 CFATTACH_DECL(ofdisk
, sizeof(struct ofdisk_softc
),
70 ofdisk_match
, ofdisk_attach
, NULL
, NULL
);
72 extern struct cfdriver ofdisk_cd
;
74 dev_type_open(ofdisk_open
);
75 dev_type_close(ofdisk_close
);
76 dev_type_read(ofdisk_read
);
77 dev_type_write(ofdisk_write
);
78 dev_type_ioctl(ofdisk_ioctl
);
79 dev_type_strategy(ofdisk_strategy
);
80 dev_type_dump(ofdisk_dump
);
81 dev_type_size(ofdisk_size
);
83 const struct bdevsw ofdisk_bdevsw
= {
84 ofdisk_open
, ofdisk_close
, ofdisk_strategy
, ofdisk_ioctl
,
85 ofdisk_dump
, ofdisk_size
, D_DISK
88 const struct cdevsw ofdisk_cdevsw
= {
89 ofdisk_open
, ofdisk_close
, ofdisk_read
, ofdisk_write
, ofdisk_ioctl
,
90 nostop
, notty
, nopoll
, nommap
, nokqfilter
, D_DISK
93 static void ofminphys(struct buf
*);
95 struct dkdriver ofdisk_dkdriver
= { ofdisk_strategy
, ofminphys
};
97 void ofdisk_getdefaultlabel (struct ofdisk_softc
*, struct disklabel
*);
98 void ofdisk_getdisklabel (dev_t
);
101 ofdisk_match(device_t parent
, cfdata_t match
, void *aux
)
103 struct ofbus_attach_args
*oba
= aux
;
107 if (strcmp(oba
->oba_busname
, "ofw"))
109 if ((l
= OF_getprop(oba
->oba_phandle
, "device_type", type
,
110 sizeof type
- 1)) < 0)
112 if (l
>= sizeof type
)
115 return !strcmp(type
, "block");
119 ofdisk_attach(device_t parent
, device_t self
, void *aux
)
121 struct ofdisk_softc
*of
= device_private(self
);
122 struct ofbus_attach_args
*oba
= aux
;
126 if ((l
= OF_getprop(oba
->oba_phandle
, "name", child
,
127 sizeof child
- 1)) < 0)
128 panic("device without name?");
129 if (l
>= sizeof child
)
130 l
= sizeof child
- 1;
134 of
->sc_phandle
= oba
->oba_phandle
;
135 of
->sc_unit
= oba
->oba_unit
;
137 disk_init(&of
->sc_dk
, device_xname(&of
->sc_dev
), &ofdisk_dkdriver
);
138 disk_attach(&of
->sc_dk
);
141 if (strcmp(child
, "floppy") == 0)
142 of
->sc_flags
|= OFDF_ISFLOPPY
;
144 /* Discover wedges on this disk. */
145 dkwedge_discover(&of
->sc_dk
);
150 ofdisk_open(dev_t dev
, int flags
, int fmt
, struct lwp
*lwp
)
152 struct ofdisk_softc
*of
;
156 of
= device_lookup_private(&ofdisk_cd
, DISKUNIT(dev
));
160 part
= DISKPART(dev
);
162 mutex_enter(&of
->sc_dk
.dk_openlock
);
165 * If there are wedges, and this is not RAW_PART, then we
168 if (of
->sc_dk
.dk_nwedges
!= 0 && part
!= RAW_PART
) {
173 if (!of
->sc_ihandle
) {
174 if ((l
= OF_package_to_path(of
->sc_phandle
, path
,
175 sizeof path
- 3)) < 0 ||
176 l
>= sizeof path
- 3) {
183 * XXX This is for the benefit of SCSI/IDE disks that don't
184 * XXX have all their childs in the device tree.
185 * XXX YES, I DO THINK THIS IS A BUG IN OPENFIRMWARE!!!
186 * XXX And yes, this is a very gross hack!
187 * XXX See also ofscsi.c
189 if (!strcmp(path
+ l
- 4, "disk")) {
191 path
[l
++] = '0' + of
->sc_unit
;
195 strlcat(path
, ":0", sizeof(path
));
197 if ((of
->sc_ihandle
= OF_open(path
)) == -1) {
203 * Try to get characteristics of the disk.
205 of
->max_transfer
= OF_call_method_1("max-transfer",
207 if (of
->max_transfer
> MAXPHYS
)
208 of
->max_transfer
= MAXPHYS
;
210 ofdisk_getdisklabel(dev
);
215 of
->sc_dk
.dk_copenmask
|= 1 << part
;
218 of
->sc_dk
.dk_bopenmask
|= 1 << part
;
221 of
->sc_dk
.dk_openmask
=
222 of
->sc_dk
.dk_copenmask
| of
->sc_dk
.dk_bopenmask
;
227 mutex_exit(&of
->sc_dk
.dk_openlock
);
232 ofdisk_close(dev_t dev
, int flags
, int fmt
, struct lwp
*l
)
234 struct ofdisk_softc
*of
=
235 device_lookup_private(&ofdisk_cd
, DISKUNIT(dev
));
237 mutex_enter(&of
->sc_dk
.dk_openlock
);
241 of
->sc_dk
.dk_copenmask
&= ~(1 << DISKPART(dev
));
244 of
->sc_dk
.dk_bopenmask
&= ~(1 << DISKPART(dev
));
247 of
->sc_dk
.dk_openmask
= of
->sc_dk
.dk_copenmask
| of
->sc_dk
.dk_bopenmask
;
251 * This is a hack to get the firmware to flush its buffers.
253 OF_seek(of
->sc_ihandle
, 0);
255 if (!of
->sc_dk
.dk_openmask
) {
256 OF_close(of
->sc_ihandle
);
260 mutex_exit(&of
->sc_dk
.dk_openlock
);
265 ofdisk_strategy(struct buf
*bp
)
267 struct ofdisk_softc
*of
=
268 device_lookup_private(&ofdisk_cd
, DISKUNIT(bp
->b_dev
));
272 int (*OF_io
)(int, void *, int);
273 daddr_t blkno
= bp
->b_blkno
;
276 if (bp
->b_bcount
== 0)
279 OF_io
= bp
->b_flags
& B_READ
? OF_read
:
280 (int(*)(int, void*, int))OF_write
;
282 if (DISKPART(bp
->b_dev
) != RAW_PART
) {
283 if (bounds_check_with_label(&of
->sc_dk
, bp
, 0) <= 0) {
284 bp
->b_resid
= bp
->b_bcount
;
287 p
= &of
->sc_dk
.dk_label
->d_partitions
[DISKPART(bp
->b_dev
)];
288 blkno
= bp
->b_blkno
+ p
->p_offset
;
291 disk_busy(&of
->sc_dk
);
293 off
= (u_quad_t
)blkno
* DEV_BSIZE
;
296 if (OF_seek(of
->sc_ihandle
, off
) < 0)
298 read
= OF_io(of
->sc_ihandle
, bp
->b_data
, bp
->b_bcount
);
299 } while (read
== -2);
303 bp
->b_resid
= bp
->b_bcount
;
305 bp
->b_resid
= bp
->b_bcount
- read
;
307 disk_unbusy(&of
->sc_dk
, bp
->b_bcount
- bp
->b_resid
,
308 (bp
->b_flags
& B_READ
));
315 ofminphys(struct buf
*bp
)
317 struct ofdisk_softc
*of
=
318 device_lookup_private(&ofdisk_cd
, DISKUNIT(bp
->b_dev
));
320 if (bp
->b_bcount
> of
->max_transfer
)
321 bp
->b_bcount
= of
->max_transfer
;
325 ofdisk_read(dev_t dev
, struct uio
*uio
, int flags
)
327 return physio(ofdisk_strategy
, NULL
, dev
, B_READ
, ofminphys
, uio
);
331 ofdisk_write(dev_t dev
, struct uio
*uio
, int flags
)
333 return physio(ofdisk_strategy
, NULL
, dev
, B_WRITE
, ofminphys
, uio
);
337 ofdisk_ioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
339 struct ofdisk_softc
*of
=
340 device_lookup_private(&ofdisk_cd
, DISKUNIT(dev
));
342 #ifdef __HAVE_OLD_DISKLABEL
343 struct disklabel newlabel
;
348 *(struct disklabel
*)data
= *of
->sc_dk
.dk_label
;
350 #ifdef __HAVE_OLD_DISKLABEL
352 newlabel
= *of
->sc_dk
.dk_label
;
353 if (newlabel
.d_npartitions
> OLDMAXPARTITIONS
)
355 memcpy(data
, &newlabel
, sizeof (struct olddisklabel
));
360 ((struct partinfo
*)data
)->disklab
= of
->sc_dk
.dk_label
;
361 ((struct partinfo
*)data
)->part
=
362 &of
->sc_dk
.dk_label
->d_partitions
[DISKPART(dev
)];
367 #ifdef __HAVE_OLD_DISKLABEL
372 struct disklabel
*lp
;
374 #ifdef __HAVE_OLD_DISKLABEL
375 if (cmd
== ODIOCSDINFO
|| cmd
== ODIOCWDINFO
) {
376 memset(&newlabel
, 0, sizeof newlabel
);
377 memcpy(&newlabel
, data
, sizeof (struct olddisklabel
));
381 lp
= (struct disklabel
*)data
;
383 if ((flag
& FWRITE
) == 0)
386 mutex_enter(&of
->sc_dk
.dk_openlock
);
388 error
= setdisklabel(of
->sc_dk
.dk_label
,
389 lp
, /*of->sc_dk.dk_openmask */0,
390 of
->sc_dk
.dk_cpulabel
);
391 if (error
== 0 && cmd
== DIOCWDINFO
392 #ifdef __HAVE_OLD_DISKLABEL
393 || xfer
== ODIOCWDINFO
396 error
= writedisklabel(MAKEDISKDEV(major(dev
),
397 DISKUNIT(dev
), RAW_PART
), ofdisk_strategy
,
398 of
->sc_dk
.dk_label
, of
->sc_dk
.dk_cpulabel
);
400 mutex_exit(&of
->sc_dk
.dk_openlock
);
406 ofdisk_getdefaultlabel(of
, (struct disklabel
*)data
);
408 #ifdef __HAVE_OLD_DISKLABEL
410 ofdisk_getdefaultlabel(of
, &newlabel
);
411 if (newlabel
.d_npartitions
> OLDMAXPARTITIONS
)
413 memcpy(data
, &newlabel
, sizeof (struct olddisklabel
));
419 struct dkwedge_info
*dkw
= (void *) data
;
421 if (OFDISK_FLOPPY_P(of
))
424 if ((flag
& FWRITE
) == 0)
427 /* If the ioctl happens here, the parent is us. */
428 strlcpy(dkw
->dkw_parent
, device_xname(&of
->sc_dev
),
429 sizeof(dkw
->dkw_parent
));
430 return (dkwedge_add(dkw
));
435 struct dkwedge_info
*dkw
= (void *) data
;
437 if (OFDISK_FLOPPY_P(of
))
440 if ((flag
& FWRITE
) == 0)
443 /* If the ioctl happens here, the parent is us. */
444 strlcpy(dkw
->dkw_parent
, device_xname(&of
->sc_dev
),
445 sizeof(dkw
->dkw_parent
));
446 return (dkwedge_del(dkw
));
451 struct dkwedge_list
*dkwl
= (void *) data
;
453 if (OFDISK_FLOPPY_P(of
))
456 return (dkwedge_list(&of
->sc_dk
, dkwl
, l
));
465 ofdisk_dump(dev_t dev
, daddr_t blkno
, void *va
, size_t size
)
471 ofdisk_size(dev_t dev
)
473 struct ofdisk_softc
*of
;
474 struct disklabel
*lp
;
475 int size
, part
, omask
;
477 of
= device_lookup_private(&ofdisk_cd
, DISKUNIT(dev
));
481 part
= DISKPART(dev
);
482 omask
= of
->sc_dk
.dk_openmask
& (1 << part
);
483 lp
= of
->sc_dk
.dk_label
;
485 if (omask
== 0 && ofdisk_open(dev
, 0, S_IFBLK
, curlwp
) != 0)
488 if (lp
->d_partitions
[part
].p_fstype
!= FS_SWAP
)
491 size
= lp
->d_partitions
[part
].p_size
*
492 (lp
->d_secsize
/ DEV_BSIZE
);
494 if (omask
== 0 && ofdisk_close(dev
, 0, S_IFBLK
, curlwp
) != 0)
501 ofdisk_getdefaultlabel(struct ofdisk_softc
*of
, struct disklabel
*lp
)
504 memset(lp
, 0, sizeof *lp
);
507 * XXX Firmware bug? Asking for block size gives a
508 * XXX ridiculous number! So we use what the boot program
511 lp
->d_secsize
= DEV_BSIZE
;
513 lp
->d_secperunit
= OF_call_method_1("#blocks",
515 if (lp
->d_secperunit
== (u_int32_t
)-1)
516 lp
->d_secperunit
= 0x7fffffff;
521 lp
->d_ncylinders
= lp
->d_secperunit
;
523 lp
->d_partitions
[RAW_PART
].p_offset
= 0;
524 lp
->d_partitions
[RAW_PART
].p_size
= lp
->d_secperunit
;
525 lp
->d_npartitions
= RAW_PART
+ 1;
527 lp
->d_magic
= DISKMAGIC
;
528 lp
->d_magic2
= DISKMAGIC
;
529 lp
->d_checksum
= dkcksum(lp
);
533 ofdisk_getdisklabel(dev_t dev
)
535 int unit
= DISKUNIT(dev
);
536 struct ofdisk_softc
*of
=
537 device_lookup_private(&ofdisk_cd
, unit
);
538 struct disklabel
*lp
= of
->sc_dk
.dk_label
;
542 ofdisk_getdefaultlabel(of
, lp
);
545 * Don't read the disklabel on a floppy; simply
546 * assign all partitions the same size/offset as
547 * RAW_PART. (This is essentially what the ISA
548 * floppy driver does, but we don't deal with
551 if (OFDISK_FLOPPY_P(of
)) {
552 lp
->d_npartitions
= MAXPARTITIONS
;
553 for (l
= 0; l
< lp
->d_npartitions
; l
++) {
557 lp
->d_partitions
[l
] =
558 lp
->d_partitions
[RAW_PART
];
560 lp
->d_checksum
= dkcksum(lp
);
562 errmes
= readdisklabel(MAKEDISKDEV(major(dev
),
563 unit
, RAW_PART
), ofdisk_strategy
, lp
,
564 of
->sc_dk
.dk_cpulabel
);
566 printf("%s: %s\n", device_xname(&of
->sc_dev
), errmes
);