xref: /netbsd-src/sys/arch/x68k/stand/libsa/sdcd.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: sdcd.c,v 1.11 2011/07/17 20:54:49 joerg 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(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 
112 static int
113 readdisklabel(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 		goto done;
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 done:
178 #ifdef DEBUG
179 	for (i = 0; i < current_npart; i++) {
180 		printf ("%d: starts %d, size %d\n", i,
181 			partitions[i].start,
182 			partitions[i].size);
183 	}
184 #endif
185 	current_id = id;
186 
187 	return 0;
188 }
189 
190 int
191 sd_getbsdpartition(int id, int humanpart)
192 {
193 	int error, i;
194 	char *buffer;
195 	struct dos_partition *parttbl;
196 	unsigned parttop;
197 
198 	if (humanpart < 2)
199 		humanpart++;
200 
201 	error = readdisklabel(id);
202 	if (error) {
203 		printf("Reading disklabel: %s\n", strerror(error));
204 		return -1;
205 	}
206 	buffer = alloca(2048);
207 	error = IOCS_S_READ(8 >> current_blklen, 8 >> current_blklen,
208 			    id, current_blklen, buffer);
209 	if (error < 0) {
210 		printf("Reading partition table: %s\n", strerror(error));
211 		return -1;
212 	}
213 	parttbl = (void *)(buffer + DOSBBSECTOR);
214 	if (strncmp(buffer, "X68K", 4) != 0)
215 		return 0;
216 	parttop = parttbl[humanpart].dp_start;
217 	parttop = parttop << (2 - current_blklen);
218 
219 	for (i = 0; i < current_npart; i++) {
220 		if (partitions[i].start == parttop)
221 			return i;
222 	}
223 
224 	printf("Could not determine the boot partition.\n");
225 
226 	return -1;
227 }
228 
229 struct sdcd_softc {
230 	int			sc_part;
231 	struct boot_partinfo	sc_partinfo;
232 	int			sc_blocksize;
233 };
234 
235 /* sdopen(struct open_file *f, int id, int part) */
236 int
237 sdopen(struct open_file *f, ...)
238 {
239 	int error;
240 	struct sdcd_softc *sc;
241 	int id, part;
242 	va_list ap;
243 
244 	va_start(ap, f);
245 	id   = va_arg(ap, int);
246 	part = va_arg(ap, int);
247 	va_end(ap);
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 
267 int
268 sdclose(struct open_file *f)
269 {
270 
271 	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
272 	return 0;
273 }
274 
275 int
276 sdstrategy(void *arg, int rw, daddr_t dblk, size_t size,
277            void *buf, size_t *rsize)
278 {
279 	struct sdcd_softc *sc = arg;
280 	uint32_t	start = sc->sc_partinfo.start + dblk;
281 	size_t		nblks;
282 	int		error;
283 
284 	if (size == 0) {
285 		if (rsize)
286 			*rsize = 0;
287 		return 0;
288 	}
289 	nblks = howmany(size, 256 << current_blklen);
290 
291 	if ((dblk & 0x1fffff) == 0x1fffff && (nblks & 0xff) == nblks) {
292 		if (rw & F_WRITE)
293 			error = IOCS_S_WRITE(start, nblks, current_id,
294 			                     current_blklen, buf);
295 		else
296 			error = IOCS_S_READ(start, nblks, current_id,
297 			                    current_blklen, buf);
298 	} else {
299 		if (rw & F_WRITE)
300 			error = IOCS_S_WRITEEXT(start, nblks, current_id,
301 			                        current_blklen, buf);
302 		else
303 			error = IOCS_S_READEXT(start, nblks, current_id,
304 			                       current_blklen, buf);
305 	}
306 	if (error < 0)
307 		return EIO;
308 
309 	if (rsize)
310 		*rsize = size;
311 	return 0;
312 }
313 
314 /* cdopen(struct open_file *f, int id, int part) */
315 int
316 cdopen(struct open_file *f, ...)
317 {
318 	int error;
319 	struct sdcd_softc *sc;
320 	int id, part;
321 	va_list ap;
322 
323 	va_start(ap, f);
324 	id   = va_arg(ap, int);
325 	part = va_arg(ap, int);
326 	va_end(ap);
327 
328 	if (id < 0 || id > 7)
329 		return ENXIO;
330 	if (part == 0 || part == 2)
331 		return ENXIO;
332 	if (current_id != id) {
333 		error = check_unit(id);
334 		if (error)
335 			return error;
336 	}
337 
338 	sc = alloc(sizeof(struct sdcd_softc));
339 	current_npart = 3;
340 	sc->sc_part = 0;
341 	sc->sc_partinfo.size = current_devsize;
342 	sc->sc_blocksize = current_blklen << 9;
343 	f->f_devdata = sc;
344 	return 0;
345 }
346 
347 int
348 cdclose(struct open_file *f)
349 {
350 
351 	dealloc(f->f_devdata, sizeof(struct sdcd_softc));
352 	return 0;
353 }
354 
355 int
356 cdstrategy(void *arg, int rw, daddr_t dblk, size_t size,
357            void *buf, size_t *rsize)
358 {
359 	struct sdcd_softc *sc = arg;
360 
361 	return sdstrategy(arg, rw, dblk * DEV_BSIZE / sc->sc_blocksize,
362 	                  size, buf, rsize);
363 }
364