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