sdcd.c revision 1.2
1/*	$NetBSD: sdcd.c,v 1.2 2001/09/29 03:50:13 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		return 0;
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	}
177#ifdef DEBUG
178	for (i = 0; i < current_npart; i++) {
179		printf ("%d: starts %d, size %d\n", i,
180			partitions[i].start,
181			partitions[i].size);
182	}
183#endif
184	current_id = id;
185
186	return 0;
187}
188
189int
190sd_getbsdpartition (int id, int humanpart)
191{
192	int error, i;
193	char *buffer;
194	struct dos_partition *parttbl;
195	unsigned parttop;
196
197	if (humanpart < 2)
198		humanpart++;
199
200	error = readdisklabel(id);
201	if (error) {
202		printf ("Reading disklabel: %s\n", strerror(error));
203		return -1;
204	}
205	buffer = alloca(2048);
206	error = IOCS_S_READ(8 >> current_blklen, 8 >> current_blklen,
207			    id, current_blklen, buffer);
208	if (error < 0) {
209		printf ("Reading partition table: %s\n", strerror(error));
210		return -1;
211	}
212	parttbl = (void*) (buffer + DOSBBSECTOR);
213	if (strncmp (buffer, "X68K", 4) != 0)
214		return 0;
215	parttop = parttbl[humanpart].dp_start;
216	parttop = parttop<<(2-current_blklen);
217
218	for (i = 0; i < current_npart; i++) {
219		if (partitions[i].start == parttop)
220			return i;
221	}
222
223	printf ("Could not determine the boot partition.\n");
224
225	return -1;
226}
227
228struct sdcd_softc {
229	int			sc_part;
230	struct boot_partinfo	sc_partinfo;
231	int			sc_blocksize;
232};
233
234int
235sdopen (struct open_file *f, int id, int part)
236{
237	int error;
238	struct sdcd_softc *sc;
239
240	if (id < 0 || id > 7)
241		return ENXIO;
242	if (current_id != id) {
243		error = readdisklabel(id);
244		if (error)
245			return error;
246	}
247	if (part >= current_npart)
248		return ENXIO;
249
250	sc = alloc (sizeof (struct sdcd_softc));
251	sc->sc_part = part;
252	sc->sc_partinfo = partitions[part];
253	sc->sc_blocksize = current_blklen << 9;
254	f->f_devdata = sc;
255	return 0;
256}
257
258int
259sdclose (struct open_file *f)
260{
261	free (f->f_devdata, sizeof (struct sdcd_softc));
262	return 0;
263}
264
265int
266sdstrategy (void *arg, int rw, daddr_t dblk, size_t size,
267	    void *buf, size_t *rsize)
268{
269	struct sdcd_softc *sc = arg;
270	u_int32_t	start = sc->sc_partinfo.start + dblk;
271	size_t		nblks;
272	int		error;
273
274	if (size == 0) {
275		if (rsize)
276			*rsize = 0;
277		return 0;
278	}
279	nblks = howmany (size, 256 << current_blklen);
280
281	if (dblk & 0x1fffff == 0x1fffff && (nblks & 0xff) == nblks) {
282		if (rw & F_WRITE)
283			error = IOCS_S_WRITE (start, nblks, current_id,
284					      current_blklen, buf);
285		else
286			error = IOCS_S_READ (start, nblks, current_id,
287					     current_blklen, buf);
288	} else {
289		if (rw & F_WRITE)
290			error = IOCS_S_WRITEEXT (start, nblks, current_id,
291						 current_blklen, buf);
292		else
293			error = IOCS_S_READEXT (start, nblks, current_id,
294						 current_blklen, buf);
295	}
296	if (error < 0)
297		return EIO;
298
299	if (rsize)
300		*rsize = size;
301	return 0;
302}
303
304int
305cdopen (struct open_file *f, int id, int part)
306{
307	int error;
308	struct sdcd_softc *sc;
309
310	if (id < 0 || id > 7)
311		return ENXIO;
312	if (part == 0 || part == 2)
313		return ENXIO;
314	if (current_id != id) {
315		error = check_unit(id);
316		if (error)
317			return error;
318	}
319
320	sc = alloc (sizeof (struct sdcd_softc));
321	current_npart = 3;
322	sc->sc_part = 0;
323	sc->sc_partinfo.size = sc->sc_partinfo.size = current_devsize;
324	sc->sc_blocksize = current_blklen << 9;
325	f->f_devdata = sc;
326	return 0;
327}
328
329int
330cdclose (struct open_file *f)
331{
332	free (f->f_devdata, sizeof (struct sdcd_softc));
333	return 0;
334}
335
336int
337cdstrategy (void *arg, int rw, daddr_t dblk, size_t size,
338	    void *buf, size_t *rsize)
339{
340	struct sdcd_softc *sc = arg;
341
342	return sdstrategy (arg, rw, dblk * DEV_BSIZE / sc->sc_blocksize,
343			   size, buf, rsize);
344}
345