sdcd.c revision 1.15
1/*	$NetBSD: sdcd.c,v 1.15 2014/02/11 08:06:07 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 <lib/libkern/libkern.h>
31#include <lib/libsa/stand.h>
32
33#include "libx68k.h"
34#include "sdcdvar.h"
35#include "iocs.h"
36
37
38static int current_id = -1;
39static int current_blklen, current_devsize, current_npart;
40static struct boot_partinfo partitions[MAXPARTITIONS];
41
42static int readdisklabel(int);
43static int check_unit(int);
44
45#ifdef DEBUG
46#define DPRINTF(x)	printf x
47#else
48#define DPRINTF(x)
49#endif
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(sizeof(*inqdata), id, inqdata);
73		if (error < 0) {		/* WHY??? */
74			error = ENXIO;
75			goto out;
76		}
77		if ((inqdata->unit != 0) &&	/* direct */
78		    (inqdata->unit != 5) &&	/* cdrom */
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.start = 0;
343	sc->sc_partinfo.size = current_devsize;
344	sc->sc_blocksize = current_blklen << 9;
345	f->f_devdata = sc;
346	current_id = id;
347
348	return 0;
349}
350
351int
352cdclose(struct open_file *f)
353{
354
355	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
356	return 0;
357}
358
359int
360cdstrategy(void *arg, int rw, daddr_t dblk, size_t size,
361           void *buf, size_t *rsize)
362{
363	struct sdcd_softc *sc = arg;
364
365	/* cast dblk to avoid divdi3; 32bit is enough even for BD-ROMs.  */
366	return sdstrategy(arg, rw,
367			  (unsigned int) dblk / (sc->sc_blocksize/DEV_BSIZE),
368	                  size, buf, rsize);
369}
370