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