sdcd.c revision 1.9
1/*	$NetBSD: sdcd.c,v 1.9 2011/04/11 14:00:02 tsutsui 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
272	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
273	return 0;
274}
275
276int
277sdstrategy(void *arg, int rw, daddr_t dblk, size_t size,
278           void *buf, size_t *rsize)
279{
280	struct sdcd_softc *sc = arg;
281	uint32_t	start = sc->sc_partinfo.start + dblk;
282	size_t		nblks;
283	int		error;
284
285	if (size == 0) {
286		if (rsize)
287			*rsize = 0;
288		return 0;
289	}
290	nblks = howmany(size, 256 << current_blklen);
291
292	if ((dblk & 0x1fffff) == 0x1fffff && (nblks & 0xff) == nblks) {
293		if (rw & F_WRITE)
294			error = IOCS_S_WRITE(start, nblks, current_id,
295			                     current_blklen, buf);
296		else
297			error = IOCS_S_READ(start, nblks, current_id,
298			                    current_blklen, buf);
299	} else {
300		if (rw & F_WRITE)
301			error = IOCS_S_WRITEEXT(start, nblks, current_id,
302			                        current_blklen, buf);
303		else
304			error = IOCS_S_READEXT(start, nblks, current_id,
305			                       current_blklen, buf);
306	}
307	if (error < 0)
308		return EIO;
309
310	if (rsize)
311		*rsize = size;
312	return 0;
313}
314
315/* cdopen(struct open_file *f, int id, int part) */
316int
317cdopen(struct open_file *f, ...)
318{
319	int error;
320	struct sdcd_softc *sc;
321	int id, part;
322	va_list ap;
323
324	va_start(ap, f);
325	id   = va_arg(ap, int);
326	part = va_arg(ap, int);
327	va_end(ap);
328
329	if (id < 0 || id > 7)
330		return ENXIO;
331	if (part == 0 || part == 2)
332		return ENXIO;
333	if (current_id != id) {
334		error = check_unit(id);
335		if (error)
336			return error;
337	}
338
339	sc = alloc(sizeof(struct sdcd_softc));
340	current_npart = 3;
341	sc->sc_part = 0;
342	sc->sc_partinfo.size = sc->sc_partinfo.size = current_devsize;
343	sc->sc_blocksize = current_blklen << 9;
344	f->f_devdata = sc;
345	return 0;
346}
347
348int
349cdclose(struct open_file *f)
350{
351
352	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
353	return 0;
354}
355
356int
357cdstrategy(void *arg, int rw, daddr_t dblk, size_t size,
358           void *buf, size_t *rsize)
359{
360	struct sdcd_softc *sc = arg;
361
362	return sdstrategy(arg, rw, dblk * DEV_BSIZE / sc->sc_blocksize,
363	                  size, buf, rsize);
364}
365