1139749Simp/*-
294749Sbenno * Copyright (C) 2002 Benno Rice <benno@FreeBSD.org>
394749Sbenno * All rights reserved.
494749Sbenno *
594749Sbenno * Redistribution and use in source and binary forms, with or without
694749Sbenno * modification, are permitted provided that the following conditions
794749Sbenno * are met:
894749Sbenno * 1. Redistributions of source code must retain the above copyright
994749Sbenno *    notice, this list of conditions and the following disclaimer.
1094749Sbenno * 2. Redistributions in binary form must reproduce the above copyright
1194749Sbenno *    notice, this list of conditions and the following disclaimer in the
1294749Sbenno *    documentation and/or other materials provided with the distribution.
1394749Sbenno *
1494749Sbenno * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR
1594749Sbenno * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1694749Sbenno * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1794749Sbenno * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1894749Sbenno * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1994749Sbenno * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2094749Sbenno * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2194749Sbenno * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2294749Sbenno * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2394749Sbenno * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2494749Sbenno */
2594749Sbenno
26119418Sobrien#include <sys/cdefs.h>
27119418Sobrien__FBSDID("$FreeBSD$");
28119418Sobrien
2994749Sbenno#include <sys/param.h>
3094749Sbenno#include <sys/systm.h>
3194749Sbenno#include <sys/bio.h>
3294749Sbenno#include <sys/kernel.h>
33139100Sgrehan#include <sys/kthread.h>
34139100Sgrehan#include <sys/linker.h>
35139100Sgrehan#include <sys/lock.h>
36139100Sgrehan#include <sys/malloc.h>
37139100Sgrehan#include <sys/mutex.h>
38139100Sgrehan#include <sys/proc.h>
3994749Sbenno
40139100Sgrehan#include <geom/geom.h>
41139100Sgrehan
4294749Sbenno#include <dev/ofw/openfirm.h>
4394749Sbenno
4494749Sbenno#define	OFWD_BLOCKSIZE	512
4594749Sbenno
4694749Sbennostruct ofwd_softc
4794749Sbenno{
48194138Smarius	struct bio_queue_head ofwd_bio_queue;
49194138Smarius	struct mtx	ofwd_queue_mtx;
5094749Sbenno	ihandle_t	ofwd_instance;
51139100Sgrehan	off_t		ofwd_mediasize;
52194138Smarius	unsigned	ofwd_sectorsize;
53194138Smarius	unsigned	ofwd_fwheads;
54194138Smarius	unsigned	ofwd_fwsectors;
55194138Smarius	struct proc	*ofwd_procp;
56194138Smarius	struct g_geom	*ofwd_gp;
57194138Smarius	struct g_provider *ofwd_pp;
58139100Sgrehan} ofwd_softc;
5994749Sbenno
60139100Sgrehanstatic g_init_t g_ofwd_init;
61139100Sgrehanstatic g_start_t g_ofwd_start;
62139100Sgrehanstatic g_access_t g_ofwd_access;
6394749Sbenno
64139100Sgrehanstruct g_class g_ofwd_class = {
65139100Sgrehan	.name = "OFWD",
66139100Sgrehan	.version = G_VERSION,
67139100Sgrehan	.init = g_ofwd_init,
68139100Sgrehan	.start = g_ofwd_start,
69139100Sgrehan	.access = g_ofwd_access,
7094749Sbenno};
7194749Sbenno
72139100SgrehanDECLARE_GEOM_CLASS(g_ofwd_class, g_ofwd);
7394749Sbenno
74139372Sgrehanstatic int ofwd_enable = 0;
75139372SgrehanTUNABLE_INT("kern.ofw.disk", &ofwd_enable);
76139372Sgrehan
77139100Sgrehanstatic int
78139100Sgrehanofwd_startio(struct ofwd_softc *sc, struct bio *bp)
79139100Sgrehan{
80139100Sgrehan	u_int r;
8194749Sbenno
82139100Sgrehan	r = OF_seek(sc->ofwd_instance, bp->bio_offset);
83151894Sgrehan
84194138Smarius	switch (bp->bio_cmd) {
85194138Smarius	case BIO_READ:
86139100Sgrehan		r = OF_read(sc->ofwd_instance, (void *)bp->bio_data,
87194139Smarius		   bp->bio_length);
88194138Smarius		break;
89194138Smarius	case BIO_WRITE:
90139100Sgrehan		r = OF_write(sc->ofwd_instance, (void *)bp->bio_data,
91194139Smarius		   bp->bio_length);
92194138Smarius		break;
93194138Smarius	}
94139100Sgrehan	if (r != bp->bio_length)
95139100Sgrehan		panic("ofwd: incorrect i/o count");
9694749Sbenno
97194138Smarius	bp->bio_resid = 0;
98194138Smarius	return (0);
99139100Sgrehan}
10094749Sbenno
10194749Sbennostatic void
102139100Sgrehanofwd_kthread(void *arg)
10394749Sbenno{
104139100Sgrehan	struct ofwd_softc *sc;
105139100Sgrehan	struct bio *bp;
106139100Sgrehan	int error;
10794749Sbenno
108194138Smarius	sc = arg;
109194138Smarius	curthread->td_base_pri = PRIBIO;
11094749Sbenno
111194138Smarius	for (;;) {
112139100Sgrehan		mtx_lock(&sc->ofwd_queue_mtx);
113139100Sgrehan		bp = bioq_takefirst(&sc->ofwd_bio_queue);
114139100Sgrehan		if (!bp) {
115139100Sgrehan			msleep(sc, &sc->ofwd_queue_mtx, PRIBIO | PDROP,
116139100Sgrehan			    "ofwdwait", 0);
117194138Smarius			continue;
118139100Sgrehan		}
119194138Smarius		mtx_unlock(&sc->ofwd_queue_mtx);
120194138Smarius		if (bp->bio_cmd == BIO_GETATTR) {
121139100Sgrehan			error = EOPNOTSUPP;
122194138Smarius		} else
123139100Sgrehan			error = ofwd_startio(sc, bp);
12494749Sbenno
125139100Sgrehan		if (error != -1) {
126194138Smarius			bp->bio_completed = bp->bio_length;
127194138Smarius			g_io_deliver(bp, error);
128194138Smarius		}
12994749Sbenno	}
13094749Sbenno}
13194749Sbenno
132125435Sgrehanstatic void
133139100Sgrehang_ofwd_init(struct g_class *mp __unused)
134125435Sgrehan{
135194138Smarius	char path[128];
136194138Smarius	char fname[32];
137194138Smarius	phandle_t ofd;
138139100Sgrehan	struct ofwd_softc *sc;
139194138Smarius	struct g_geom *gp;
140194138Smarius	struct g_provider *pp;
141139100Sgrehan	ihandle_t ifd;
142194138Smarius	int error;
143125435Sgrehan
144139372Sgrehan	if (ofwd_enable == 0)
145139372Sgrehan		return;
146139372Sgrehan
147125435Sgrehan	ofd = OF_finddevice("ofwdisk");
148125435Sgrehan	if (ofd == -1)
149125435Sgrehan		return;
150125435Sgrehan
151139100Sgrehan	bzero(path, 128);
152139100Sgrehan	OF_package_to_path(ofd, path, 128);
153139100Sgrehan	OF_getprop(ofd, "file", fname, sizeof(fname));
154139100Sgrehan	printf("ofw_disk located at %s, file %s\n", path, fname);
155139100Sgrehan	ifd = OF_open(path);
156139100Sgrehan	if (ifd == -1) {
157139100Sgrehan		printf("ofw_disk: could not create instance\n");
158139100Sgrehan		return;
159139100Sgrehan	}
160125435Sgrehan
161139100Sgrehan	sc = (struct ofwd_softc *)malloc(sizeof *sc, M_DEVBUF,
162194139Smarius	    M_WAITOK | M_ZERO);
163139100Sgrehan	bioq_init(&sc->ofwd_bio_queue);
164194138Smarius	mtx_init(&sc->ofwd_queue_mtx, "ofwd bio queue", NULL, MTX_DEF);
165139100Sgrehan	sc->ofwd_instance = ifd;
166194138Smarius	sc->ofwd_mediasize = (off_t)2 * 33554432;
167139100Sgrehan	sc->ofwd_sectorsize = OFWD_BLOCKSIZE;
168139100Sgrehan	sc->ofwd_fwsectors = 0;
169139100Sgrehan	sc->ofwd_fwheads = 0;
170172836Sjulian	error = kproc_create(ofwd_kthread, sc, &sc->ofwd_procp, 0, 0,
171194138Smarius	    "ofwd0");
172194138Smarius	if (error != 0) {
173139100Sgrehan		free(sc, M_DEVBUF);
174194138Smarius		return;
175125435Sgrehan	}
176139100Sgrehan
177139100Sgrehan	gp = g_new_geomf(&g_ofwd_class, "ofwd0");
178139100Sgrehan	gp->softc = sc;
179139100Sgrehan	pp = g_new_providerf(gp, "ofwd0");
180139100Sgrehan	pp->mediasize = sc->ofwd_mediasize;
181139100Sgrehan	pp->sectorsize = sc->ofwd_sectorsize;
182139100Sgrehan	sc->ofwd_gp = gp;
183139100Sgrehan	sc->ofwd_pp = pp;
184139100Sgrehan	g_error_provider(pp, 0);
185125435Sgrehan}
186125435Sgrehan
187139100Sgrehanstatic void
188139100Sgrehang_ofwd_start(struct bio *bp)
18994749Sbenno{
190194138Smarius	struct ofwd_softc *sc;
19194749Sbenno
192194138Smarius	sc = bp->bio_to->geom->softc;
193194138Smarius	mtx_lock(&sc->ofwd_queue_mtx);
194194138Smarius	bioq_disksort(&sc->ofwd_bio_queue, bp);
195194138Smarius	mtx_unlock(&sc->ofwd_queue_mtx);
196194138Smarius	wakeup(sc);
19794749Sbenno}
19894749Sbenno
19994749Sbennostatic int
200139100Sgrehang_ofwd_access(struct g_provider *pp, int r, int w, int e)
20194749Sbenno{
20294749Sbenno
203139100Sgrehan	if (pp->geom->softc == NULL)
20494749Sbenno		return (ENXIO);
205194138Smarius	return (0);
20694749Sbenno}
207