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