ofw_disk.c revision 123543
1/*
2 * Copyright (C) 2002 Benno Rice <benno@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 *
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/sys/dev/ofw/ofw_disk.c 123543 2003-12-15 09:53:53Z grehan $");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/bio.h>
34#include <sys/bus.h>
35#include <sys/conf.h>
36#include <sys/kernel.h>
37#include <geom/geom_disk.h>
38
39#include <dev/ofw/openfirm.h>
40
41#include <machine/bus.h>
42#include <machine/md_var.h>
43#include <machine/nexusvar.h>
44
45#define	OFWD_BLOCKSIZE	512
46
47struct ofwd_softc
48{
49	device_t	ofwd_dev;
50	struct		disk ofwd_disk;
51	phandle_t	ofwd_package;
52	ihandle_t	ofwd_instance;
53};
54
55/*
56 * Disk device bus interface.
57 */
58static int	ofwd_probe(device_t);
59static int	ofwd_attach(device_t);
60
61static device_method_t	ofwd_methods[] = {
62	DEVMETHOD(device_probe, 	ofwd_probe),
63	DEVMETHOD(device_attach,	ofwd_attach),
64	{ 0, 0 }
65};
66
67static driver_t ofwd_driver = {
68	"ofwd",
69	ofwd_methods,
70	sizeof(struct ofwd_softc)
71};
72
73static devclass_t	ofwd_devclass;
74
75DRIVER_MODULE(ofwd, nexus, ofwd_driver, ofwd_devclass, 0, 0);
76
77/*
78 * Disk device control interface.
79 */
80static disk_strategy_t	ofwd_strategy;
81
82/*
83 * Handle an I/O request.
84 */
85static void
86ofwd_strategy(struct bio *bp)
87{
88	struct	ofwd_softc *sc;
89	long	r;
90
91	sc = (struct ofwd_softc *)bp->bio_disk->d_drv1;
92
93	if (sc == NULL) {
94		printf("ofwd: bio for invalid disk!\n");
95		biofinish(bp, NULL, EINVAL);
96		return;
97	}
98
99	r = OF_seek(sc->ofwd_instance, bp->bio_offset);
100	if (r == -1) {
101		bp->bio_resid = bp->bio_bcount;
102		device_printf(sc->ofwd_dev, "seek failed\n");
103		biofinish(bp, NULL, EIO);
104		return;
105	}
106
107	if (bp->bio_cmd == BIO_READ) {
108		r = OF_read(sc->ofwd_instance, (void *)bp->bio_data,
109		    bp->bio_bcount);
110	} else {
111		r = OF_write(sc->ofwd_instance, (void *)bp->bio_data,
112			    bp->bio_bcount);
113	}
114
115	if (r > bp->bio_bcount)
116		panic("ofwd: more bytes read/written than requested");
117	if (r == -1) {
118		device_printf(sc->ofwd_dev, "r (%ld) < bp->bio_bcount (%ld)\n",
119		    r, bp->bio_bcount);
120		biofinish(bp, NULL, EIO);
121		return;
122	}
123
124	bp->bio_resid -= r;
125
126	if (r < bp->bio_bcount) {
127		device_printf(sc->ofwd_dev, "r (%ld) < bp->bio_bcount (%ld)\n",
128		    r, bp->bio_bcount);
129		biofinish(bp, NULL, EIO);	/* XXX: probably not an error */
130		return;
131	}
132	biodone(bp);
133	return;
134}
135
136/*
137 * Probe for an OpenFirmware disk.
138 */
139static int
140ofwd_probe(device_t dev)
141{
142	char		*type;
143	char		fname[32];
144	phandle_t	node;
145
146	type = nexus_get_device_type(dev);
147	node = nexus_get_node(dev);
148
149	if (type == NULL ||
150	    (strcmp(type, "disk") != 0 && strcmp(type, "block") != 0))
151		return (ENXIO);
152
153	if (OF_getprop(node, "file", fname, sizeof(fname)) == -1)
154		return (ENXIO);
155
156	device_set_desc(dev, "OpenFirmware disk");
157	return (0);
158}
159
160static int
161ofwd_attach(device_t dev)
162{
163	struct	ofwd_softc *sc;
164	char	path[128];
165	char	fname[32];
166
167	sc = device_get_softc(dev);
168	sc->ofwd_dev = dev;
169
170	bzero(path, 128);
171	OF_package_to_path(nexus_get_node(dev), path, 128);
172	OF_getprop(nexus_get_node(dev), "file", fname, sizeof(fname));
173	device_printf(dev, "located at %s, file %s\n", path, fname);
174	sc->ofwd_instance = OF_open(path);
175	if (sc->ofwd_instance == -1) {
176		device_printf(dev, "could not create instance\n");
177		return (ENXIO);
178	}
179
180	sc->ofwd_disk.d_strategy = ofwd_strategy;
181	sc->ofwd_disk.d_name = "ofwd";
182	sc->ofwd_disk.d_sectorsize = OFWD_BLOCKSIZE;
183	sc->ofwd_disk.d_mediasize = (off_t)33554432 * OFWD_BLOCKSIZE;
184	sc->ofwd_disk.d_fwsectors = 0;
185	sc->ofwd_disk.d_fwheads = 0;
186	sc->ofwd_disk.d_drv1 = sc;
187	sc->ofwd_disk.d_maxsize = PAGE_SIZE;
188	disk_create(device_get_unit(dev), &sc->ofwd_disk, 0, NULL, NULL);
189
190	return (0);
191}
192