sdcd.c revision 1.8
1/*	$NetBSD: sdcd.c,v 1.8 2007/11/18 05:00:08 isaki Exp $	*/
2
3/*
4 * Copyright (c) 2001 MINOURA Makoto.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/param.h>
29#include <sys/disklabel.h>
30#include <machine/stdarg.h>
31#include <lib/libkern/libkern.h>
32#include <lib/libsa/stand.h>
33
34#include "libx68k.h"
35#include "sdcdvar.h"
36#include "iocs.h"
37
38
39static int current_id = -1;
40static int current_blklen, current_devsize, current_npart;
41static struct boot_partinfo partitions[MAXPARTITIONS];
42
43static int readdisklabel(int);
44static int check_unit(int);
45
46#ifdef DEBUG
47#define DPRINTF(x)	printf x
48#else
49#define DPRINTF(x)
50#endif
51
52static int
53check_unit(int id)
54{
55#define BUFFER_SIZE	8192
56	int error;
57	void *buffer = alloca(BUFFER_SIZE);
58
59	if (current_id == id)
60		return 0;
61
62	current_id = -1;
63
64	error = IOCS_S_TESTUNIT(id);
65	if (error < 0) {			/* not ready */
66		error = ENXIO;
67		goto out;
68	}
69
70	{
71		struct iocs_inquiry *inqdata = buffer;
72
73		error = IOCS_S_INQUIRY(100, id, inqdata);
74		if (error < 0) {		/* WHY??? */
75			error = ENXIO;
76			goto out;
77		}
78		if ((inqdata->unit != 0) &&	/* direct */
79		    (inqdata->unit != 7)) {	/* optical */
80			error = EUNIT;
81			goto out;
82		}
83	}
84
85	{
86		struct iocs_readcap *rcdata = buffer;
87
88		error = IOCS_S_READCAP(id, rcdata);
89		if (error < 0) {		/* WHY??? */
90			error = EUNIT;
91			goto out;
92		}
93		current_blklen = rcdata->size >> 9;
94		current_devsize = rcdata->block;
95	}
96
97	{
98		error = IOCS_S_READ(0, 1, id, current_blklen, buffer);
99		if (error < 0) {
100			error =  EIO;
101			goto out;
102		}
103		if (strncmp((char *)buffer, "X68SCSI1", 8) != 0) {
104			error = EUNLAB;
105			goto out;
106		}
107	}
108
109 out:
110	return error;
111}
112
113static int
114readdisklabel(int id)
115{
116	int error, i;
117	char *buffer;
118	struct disklabel *label;
119	struct dos_partition *parttbl;
120
121	if (current_id == id)
122		return 0;
123	current_id = -1;
124
125	error = check_unit(id);
126	if (error)
127		return error;
128	if (current_blklen > 4) {
129		printf ("FATAL: Unsupported block size %d.\n",
130			256 << current_blklen);
131		return ERDLAB;
132	}
133
134	/* Try BSD disklabel first */
135	buffer = alloca(2048);
136	error = IOCS_S_READ(LABELSECTOR, 1, id, current_blklen, buffer);
137	if (error < 0)
138		return EIO;
139	label = (void *)(buffer + LABELOFFSET);
140	if (label->d_magic == DISKMAGIC &&
141	    label->d_magic2 == DISKMAGIC) {
142		for (i = 0; i < label->d_npartitions; i++) {
143			partitions[i].start = label->d_partitions[i].p_offset;
144			partitions[i].size  = label->d_partitions[i].p_size;
145		}
146		current_npart = label->d_npartitions;
147
148		goto done;
149	}
150
151	/* Try Human68K-style partition table */
152#if 0
153	/* assumes 512byte/sec */
154	error = IOCS_S_READ(DOSPARTOFF, 2, id, current_blklen, buffer);
155#else
156	error = IOCS_S_READ(8 >> current_blklen, 8 >> current_blklen,
157			    id, current_blklen, buffer);
158#endif
159	if (error < 0)
160		return EIO;
161	parttbl = (void *)(buffer + DOSBBSECTOR);
162	if (strncmp(buffer, "X68K", 4) != 0)
163		return EUNLAB;
164	parttbl++;
165	for (current_npart = 0, i = 0;
166	     current_npart < MAXPARTITIONS && i < 15 && parttbl[i].dp_size;
167	     i++) {
168		partitions[current_npart].start
169			= parttbl[i].dp_start * 2;
170		partitions[current_npart].size
171			= parttbl[i].dp_size  * 2;
172		if (++current_npart == RAW_PART) {
173			partitions[current_npart].start = 0;
174			partitions[current_npart].size = -1; /* XXX */
175			current_npart++;
176		}
177	}
178done:
179#ifdef DEBUG
180	for (i = 0; i < current_npart; i++) {
181		printf ("%d: starts %d, size %d\n", i,
182			partitions[i].start,
183			partitions[i].size);
184	}
185#endif
186	current_id = id;
187
188	return 0;
189}
190
191int
192sd_getbsdpartition(int id, int humanpart)
193{
194	int error, i;
195	char *buffer;
196	struct dos_partition *parttbl;
197	unsigned parttop;
198
199	if (humanpart < 2)
200		humanpart++;
201
202	error = readdisklabel(id);
203	if (error) {
204		printf("Reading disklabel: %s\n", strerror(error));
205		return -1;
206	}
207	buffer = alloca(2048);
208	error = IOCS_S_READ(8 >> current_blklen, 8 >> current_blklen,
209			    id, current_blklen, buffer);
210	if (error < 0) {
211		printf("Reading partition table: %s\n", strerror(error));
212		return -1;
213	}
214	parttbl = (void *)(buffer + DOSBBSECTOR);
215	if (strncmp(buffer, "X68K", 4) != 0)
216		return 0;
217	parttop = parttbl[humanpart].dp_start;
218	parttop = parttop << (2 - current_blklen);
219
220	for (i = 0; i < current_npart; i++) {
221		if (partitions[i].start == parttop)
222			return i;
223	}
224
225	printf("Could not determine the boot partition.\n");
226
227	return -1;
228}
229
230struct sdcd_softc {
231	int			sc_part;
232	struct boot_partinfo	sc_partinfo;
233	int			sc_blocksize;
234};
235
236/* sdopen(struct open_file *f, int id, int part) */
237int
238sdopen(struct open_file *f, ...)
239{
240	int error;
241	struct sdcd_softc *sc;
242	int id, part;
243	va_list ap;
244
245	va_start(ap, f);
246	id   = va_arg(ap, int);
247	part = va_arg(ap, int);
248	va_end(ap);
249
250	if (id < 0 || id > 7)
251		return ENXIO;
252	if (current_id != id) {
253		error = readdisklabel(id);
254		if (error)
255			return error;
256	}
257	if (part >= current_npart)
258		return ENXIO;
259
260	sc = alloc(sizeof(struct sdcd_softc));
261	sc->sc_part = part;
262	sc->sc_partinfo = partitions[part];
263	sc->sc_blocksize = current_blklen << 9;
264	f->f_devdata = sc;
265	return 0;
266}
267
268int
269sdclose(struct open_file *f)
270{
271	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
272	return 0;
273}
274
275int
276sdstrategy(void *arg, int rw, daddr_t dblk, size_t size,
277           void *buf, size_t *rsize)
278{
279	struct sdcd_softc *sc = arg;
280	u_int32_t	start = sc->sc_partinfo.start + dblk;
281	size_t		nblks;
282	int		error;
283
284	if (size == 0) {
285		if (rsize)
286			*rsize = 0;
287		return 0;
288	}
289	nblks = howmany(size, 256 << current_blklen);
290
291	if ((dblk & 0x1fffff) == 0x1fffff && (nblks & 0xff) == nblks) {
292		if (rw & F_WRITE)
293			error = IOCS_S_WRITE(start, nblks, current_id,
294			                     current_blklen, buf);
295		else
296			error = IOCS_S_READ(start, nblks, current_id,
297			                    current_blklen, buf);
298	} else {
299		if (rw & F_WRITE)
300			error = IOCS_S_WRITEEXT(start, nblks, current_id,
301			                        current_blklen, buf);
302		else
303			error = IOCS_S_READEXT(start, nblks, current_id,
304			                       current_blklen, buf);
305	}
306	if (error < 0)
307		return EIO;
308
309	if (rsize)
310		*rsize = size;
311	return 0;
312}
313
314/* cdopen(struct open_file *f, int id, int part) */
315int
316cdopen(struct open_file *f, ...)
317{
318	int error;
319	struct sdcd_softc *sc;
320	int id, part;
321	va_list ap;
322
323	va_start(ap, f);
324	id   = va_arg(ap, int);
325	part = va_arg(ap, int);
326	va_end(ap);
327
328	if (id < 0 || id > 7)
329		return ENXIO;
330	if (part == 0 || part == 2)
331		return ENXIO;
332	if (current_id != id) {
333		error = check_unit(id);
334		if (error)
335			return error;
336	}
337
338	sc = alloc(sizeof(struct sdcd_softc));
339	current_npart = 3;
340	sc->sc_part = 0;
341	sc->sc_partinfo.size = sc->sc_partinfo.size = current_devsize;
342	sc->sc_blocksize = current_blklen << 9;
343	f->f_devdata = sc;
344	return 0;
345}
346
347int
348cdclose(struct open_file *f)
349{
350	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
351	return 0;
352}
353
354int
355cdstrategy(void *arg, int rw, daddr_t dblk, size_t size,
356           void *buf, size_t *rsize)
357{
358	struct sdcd_softc *sc = arg;
359
360	return sdstrategy(arg, rw, dblk * DEV_BSIZE / sc->sc_blocksize,
361	                  size, buf, rsize);
362}
363