xref: /netbsd-src/sys/dev/ofw/ofdisk.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
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