xref: /netbsd-src/sys/arch/x68k/stand/libsa/sdcd.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
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 
38 static int current_id = -1;
39 static int current_blklen, current_devsize, current_npart;
40 static struct boot_partinfo partitions[MAXPARTITIONS];
41 
42 static int readdisklabel(int);
43 static int check_unit(int);
44 
45 #ifdef DEBUG
46 #define DPRINTF(x)	printf x
47 #else
48 #define DPRINTF(x)
49 #endif
50 
51 static int
52 check_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 
113 static int
114 readdisklabel(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 	}
178 done:
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 
191 int
192 sd_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 
230 struct 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) */
237 int
238 sdopen(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 
268 int
269 sdclose(struct open_file *f)
270 {
271 
272 	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
273 	return 0;
274 }
275 
276 int
277 sdstrategy(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) */
316 int
317 cdopen(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 
351 int
352 cdclose(struct open_file *f)
353 {
354 
355 	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
356 	return 0;
357 }
358 
359 int
360 cdstrategy(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