sdcd.c revision 1.3
1/*	$NetBSD: sdcd.c,v 1.3 2001/10/15 16:07:20 minoura 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 <lib/libkern/libkern.h>
31#include <lib/libsa/stand.h>
32
33#include "sdcdvar.h"
34#include "iocs.h"
35
36
37static int current_id = -1;
38static int current_blklen, current_devsize, current_npart;
39static struct boot_partinfo partitions[MAXPARTITIONS];
40
41static int readdisklabel(int);
42static int check_unit(int);
43
44#ifdef DEBUG
45#define DPRINTF(x)	printf x
46#else
47#define DPRINTF(x)
48#endif
49#define alloca		__builtin_alloca
50
51static int
52check_unit(int id)
53{
54#define BUFFER_SIZE	8192
55	int error;
56	void *buffer = alloca(BUFFER_SIZE);
57
58	if (current_id == id)
59		return 0;
60
61	current_id = -1;
62
63	error = IOCS_S_TESTUNIT(id);
64	if (error < 0) {			/* not ready */
65		error = ENXIO;
66		goto out;
67	}
68
69	{
70		struct iocs_inquiry *inqdata = buffer;
71
72		error = IOCS_S_INQUIRY(100, id, inqdata);
73		if (error < 0) {		/* WHY??? */
74			error = ENXIO;
75			goto out;
76		}
77		if ((inqdata->unit != 0) &&	/* direct */
78		    (inqdata->unit != 7)) {	/* optical */
79			error = EUNIT;
80			goto out;
81		}
82	}
83
84	{
85		struct iocs_readcap *rcdata = buffer;
86
87		error = IOCS_S_READCAP(id, rcdata);
88		if (error < 0) {		/* WHY??? */
89			error = EUNIT;
90			goto out;
91		}
92		current_blklen = rcdata->size >> 9;
93		current_devsize = rcdata->block;
94	}
95
96	{
97		error = IOCS_S_READ(0, 1, id, current_blklen, buffer);
98		if (error < 0) {
99			error =  EIO;
100			goto out;
101		}
102		if (strncmp((char*) buffer, "X68SCSI1", 8) != 0) {
103			error = EUNLAB;
104			goto out;
105		}
106	}
107
108 out:
109	return error;
110}
111
112static int
113readdisklabel (int id)
114{
115	int error, i;
116	char *buffer;
117	struct disklabel *label;
118	struct dos_partition *parttbl;
119
120	if (current_id == id)
121		return 0;
122	current_id = -1;
123
124	error = check_unit(id);
125	if (error)
126		return error;
127	if (current_blklen > 4) {
128		printf ("FATAL: Unsupported block size %d.\n",
129			256 << current_blklen);
130		return ERDLAB;
131	}
132
133	/* Try BSD disklabel first */
134	buffer = alloca(2048);
135	error = IOCS_S_READ(LABELSECTOR, 1, id, current_blklen, buffer);
136	if (error < 0)
137		return EIO;
138	label = (void*) (buffer + LABELOFFSET);
139	if (label->d_magic == DISKMAGIC &&
140	    label->d_magic2 == DISKMAGIC) {
141		for (i = 0; i < label->d_npartitions; i++) {
142			partitions[i].start = label->d_partitions[i].p_offset;
143			partitions[i].size  = label->d_partitions[i].p_size;
144		}
145		current_npart = label->d_npartitions;
146
147		goto done;
148	}
149
150	/* Try Human68K-style partition table */
151#if 0
152	/* assumes 512byte/sec */
153	error = IOCS_S_READ(DOSPARTOFF, 2, id, current_blklen, buffer);
154#else
155	error = IOCS_S_READ(8 >> current_blklen, 8 >> current_blklen,
156			    id, current_blklen, buffer);
157#endif
158	if (error < 0)
159		return EIO;
160	parttbl = (void*) (buffer + DOSBBSECTOR);
161	if (strncmp (buffer, "X68K", 4) != 0)
162		return EUNLAB;
163	parttbl++;
164	for (current_npart = 0, i = 0;
165	     current_npart < MAXPARTITIONS && i < 15 && parttbl[i].dp_size;
166	     i++) {
167		partitions[current_npart].start
168			= parttbl[i].dp_start * 2;
169		partitions[current_npart].size
170			= parttbl[i].dp_size  * 2;
171		if (++current_npart == RAW_PART) {
172			partitions[current_npart].start = 0;
173			partitions[current_npart].size = -1; /* XXX */
174			current_npart++;
175		}
176	}
177done:
178#ifdef DEBUG
179	for (i = 0; i < current_npart; i++) {
180		printf ("%d: starts %d, size %d\n", i,
181			partitions[i].start,
182			partitions[i].size);
183	}
184#endif
185	current_id = id;
186
187	return 0;
188}
189
190int
191sd_getbsdpartition (int id, int humanpart)
192{
193	int error, i;
194	char *buffer;
195	struct dos_partition *parttbl;
196	unsigned parttop;
197
198	if (humanpart < 2)
199		humanpart++;
200
201	error = readdisklabel(id);
202	if (error) {
203		printf ("Reading disklabel: %s\n", strerror(error));
204		return -1;
205	}
206	buffer = alloca(2048);
207	error = IOCS_S_READ(8 >> current_blklen, 8 >> current_blklen,
208			    id, current_blklen, buffer);
209	if (error < 0) {
210		printf ("Reading partition table: %s\n", strerror(error));
211		return -1;
212	}
213	parttbl = (void*) (buffer + DOSBBSECTOR);
214	if (strncmp (buffer, "X68K", 4) != 0)
215		return 0;
216	parttop = parttbl[humanpart].dp_start;
217	parttop = parttop<<(2-current_blklen);
218
219	for (i = 0; i < current_npart; i++) {
220		if (partitions[i].start == parttop)
221			return i;
222	}
223
224	printf ("Could not determine the boot partition.\n");
225
226	return -1;
227}
228
229struct sdcd_softc {
230	int			sc_part;
231	struct boot_partinfo	sc_partinfo;
232	int			sc_blocksize;
233};
234
235int
236sdopen (struct open_file *f, int id, int part)
237{
238	int error;
239	struct sdcd_softc *sc;
240
241	if (id < 0 || id > 7)
242		return ENXIO;
243	if (current_id != id) {
244		error = readdisklabel(id);
245		if (error)
246			return error;
247	}
248	if (part >= current_npart)
249		return ENXIO;
250
251	sc = alloc (sizeof (struct sdcd_softc));
252	sc->sc_part = part;
253	sc->sc_partinfo = partitions[part];
254	sc->sc_blocksize = current_blklen << 9;
255	f->f_devdata = sc;
256	return 0;
257}
258
259int
260sdclose (struct open_file *f)
261{
262	free (f->f_devdata, sizeof (struct sdcd_softc));
263	return 0;
264}
265
266int
267sdstrategy (void *arg, int rw, daddr_t dblk, size_t size,
268	    void *buf, size_t *rsize)
269{
270	struct sdcd_softc *sc = arg;
271	u_int32_t	start = sc->sc_partinfo.start + dblk;
272	size_t		nblks;
273	int		error;
274
275	if (size == 0) {
276		if (rsize)
277			*rsize = 0;
278		return 0;
279	}
280	nblks = howmany (size, 256 << current_blklen);
281
282	if (dblk & 0x1fffff == 0x1fffff && (nblks & 0xff) == nblks) {
283		if (rw & F_WRITE)
284			error = IOCS_S_WRITE (start, nblks, current_id,
285					      current_blklen, buf);
286		else
287			error = IOCS_S_READ (start, nblks, current_id,
288					     current_blklen, buf);
289	} else {
290		if (rw & F_WRITE)
291			error = IOCS_S_WRITEEXT (start, nblks, current_id,
292						 current_blklen, buf);
293		else
294			error = IOCS_S_READEXT (start, nblks, current_id,
295						 current_blklen, buf);
296	}
297	if (error < 0)
298		return EIO;
299
300	if (rsize)
301		*rsize = size;
302	return 0;
303}
304
305int
306cdopen (struct open_file *f, int id, int part)
307{
308	int error;
309	struct sdcd_softc *sc;
310
311	if (id < 0 || id > 7)
312		return ENXIO;
313	if (part == 0 || part == 2)
314		return ENXIO;
315	if (current_id != id) {
316		error = check_unit(id);
317		if (error)
318			return error;
319	}
320
321	sc = alloc (sizeof (struct sdcd_softc));
322	current_npart = 3;
323	sc->sc_part = 0;
324	sc->sc_partinfo.size = sc->sc_partinfo.size = current_devsize;
325	sc->sc_blocksize = current_blklen << 9;
326	f->f_devdata = sc;
327	return 0;
328}
329
330int
331cdclose (struct open_file *f)
332{
333	free (f->f_devdata, sizeof (struct sdcd_softc));
334	return 0;
335}
336
337int
338cdstrategy (void *arg, int rw, daddr_t dblk, size_t size,
339	    void *buf, size_t *rsize)
340{
341	struct sdcd_softc *sc = arg;
342
343	return sdstrategy (arg, rw, dblk * DEV_BSIZE / sc->sc_blocksize,
344			   size, buf, rsize);
345}
346