1/*	$NetBSD$	*/
2
3/*-
4 * Copyright (c) 2001 Marcus Comstedt
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 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Marcus Comstedt.
18 * 4. Neither the name of The NetBSD Foundation nor the names of its
19 *    contributors may be used to endorse or promote products derived
20 *    from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
36__KERNEL_RCSID(0, "$NetBSD$");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/device.h>
41
42#include <sys/buf.h>
43#include <sys/bufq.h>
44#include <sys/ioctl.h>
45#include <sys/fcntl.h>
46#include <sys/disklabel.h>
47#include <sys/disk.h>
48#include <sys/cdio.h>
49#include <sys/proc.h>
50#include <sys/conf.h>
51
52#include <machine/sysasicvar.h>
53
54#include "ioconf.h"
55
56static int  gdrommatch(device_t, cfdata_t, void *);
57static void gdromattach(device_t, device_t, void *);
58
59dev_type_open(gdromopen);
60dev_type_close(gdromclose);
61dev_type_read(gdromread);
62dev_type_write(gdromwrite);
63dev_type_ioctl(gdromioctl);
64dev_type_strategy(gdromstrategy);
65
66const struct bdevsw gdrom_bdevsw = {
67	gdromopen, gdromclose, gdromstrategy, gdromioctl, nodump,
68	nosize, D_DISK
69};
70
71const struct cdevsw gdrom_cdevsw = {
72	gdromopen, gdromclose, gdromread, gdromwrite, gdromioctl,
73	nostop, notty, nopoll, nommap, nokqfilter, D_DISK
74};
75
76struct gdrom_softc {
77	device_t sc_dev;	/* generic device info */
78	struct disk sc_dk;	/* generic disk info */
79	struct bufq_state *sc_bufq;	/* device buffer queue */
80	struct buf curbuf;	/* state of current I/O operation */
81
82	bool is_open;
83	bool is_busy;
84	bool is_active;
85	int openpart_start;	/* start sector of currently open partition */
86
87	int cmd_active;
88	void *cmd_result_buf;	/* where to store result data (16 bit aligned) */
89	int cmd_result_size;	/* number of bytes allocated for buf */
90	int cmd_actual;		/* number of bytes actually read */
91	int cmd_cond;		/* resulting condition of command */
92};
93
94CFATTACH_DECL_NEW(gdrom, sizeof(struct gdrom_softc),
95    gdrommatch, gdromattach, NULL, NULL);
96
97struct dkdriver gdromdkdriver = { gdromstrategy };
98
99
100struct gd_toc {
101	unsigned int entry[99];
102	unsigned int first, last;
103	unsigned int leadout;
104};
105
106#ifdef GDROMDEBUG
107#define DPRINTF(x)	printf x
108#else
109#define DPRINTF(x)	/**/
110#endif
111
112#define TOC_LBA(n)	((n) & 0xffffff00)
113#define TOC_ADR(n)	((n) & 0x0f)
114#define TOC_CTRL(n)	(((n) & 0xf0) >> 4)
115#define TOC_TRACK(n)	(((n) & 0x0000ff00) >> 8)
116
117#define GDROM(o)	(*(volatile uint8_t *)(0xa05f7000 + (o)))
118
119#define GDSTATSTAT(n)	((n) & 0xf)
120#define GDSTATDISK(n)	(((n) >> 4) & 0xf)
121
122#define GDROM_BUSY	GDROM(0x18)
123#define GDROM_DATA	(*(volatile uint16_t *)(&GDROM(0x80)))
124#define GDROM_REGX	GDROM(0x84)
125#define GDROM_STAT	GDROM(0x8c)
126#define GDROM_CNTLO	GDROM(0x90)
127#define GDROM_CNTHI	GDROM(0x94)
128#define GDROM_COND	GDROM(0x9c)
129
130#if 0
131static int gdrom_getstat(void);
132#endif
133static int gdrom_do_command(struct gdrom_softc *, void *, void *,
134    unsigned int, int *);
135static int gdrom_command_sense(struct gdrom_softc *, void *, void *,
136    unsigned int, int *);
137static int gdrom_read_toc(struct gdrom_softc *, struct gd_toc *);
138static int gdrom_read_sectors(struct gdrom_softc *, void *, int, int,
139    int *);
140static int gdrom_mount_disk(struct gdrom_softc *);
141static int gdrom_intr(void *);
142static void gdrom_start(struct gdrom_softc *);
143
144#if 0
145int
146gdrom_getstat(void)
147{
148	uint8_t s1, s2, s3;
149
150	if (GDROM_BUSY & 0x80)
151		return -1;
152	s1 = GDROM_STAT;
153	s2 = GDROM_STAT;
154	s3 = GDROM_STAT;
155	if (GDROM_BUSY & 0x80)
156		return -1;
157	if (s1 == s2)
158		return s1;
159	else if (s2 == s3)
160		return s2;
161	else
162		return -1;
163}
164#endif
165
166int
167gdrom_intr(void *arg)
168{
169	struct gdrom_softc *sc = arg;
170	int s;
171	uint8_t cond;
172
173	s = splbio();
174	cond = GDROM_COND;
175	DPRINTF(("GDROM: cond = %x\n", cond));
176	if (!sc->cmd_active) {
177		DPRINTF(("GDROM: inactive IRQ!?\n"));
178		splx(s);
179		return 0;
180	}
181
182	if ((cond & 0x08) != 0) {
183		int cnt = (GDROM_CNTHI << 8) | GDROM_CNTLO;
184		DPRINTF(("GDROM: cnt = %d\n", cnt));
185		sc->cmd_actual += cnt;
186		if (cnt > 0 && sc->cmd_result_size > 0) {
187			int subcnt = (cnt > sc->cmd_result_size ?
188			    sc->cmd_result_size : cnt);
189			uint16_t *ptr = sc->cmd_result_buf;
190			sc->cmd_result_buf = ((uint8_t *)sc->cmd_result_buf) +
191			    subcnt;
192			sc->cmd_result_size -= subcnt;
193			cnt -= subcnt;
194			while (subcnt > 0) {
195				*ptr++ = GDROM_DATA;
196				subcnt -= 2;
197			}
198		}
199		while (cnt > 0) {
200			(void)GDROM_DATA;
201			cnt -= 2;
202		}
203	}
204	while ((GDROM_BUSY & 0x80) != 0);
205
206	if ((cond & 0x08) == 0) {
207		sc->cmd_cond = cond;
208		sc->cmd_active = 0;
209		wakeup(&sc->cmd_active);
210	}
211
212	splx(s);
213	return 1;
214}
215
216
217int
218gdrom_do_command(struct gdrom_softc *sc, void *req, void *buf,
219    unsigned int nbyt, int *resid)
220{
221	int i, s;
222	uint16_t *ptr = req;
223
224	while (GDROM_BUSY & 0x88)
225		;
226	if (buf != NULL) {
227		GDROM_CNTLO = nbyt & 0xff;
228		GDROM_CNTHI = (nbyt >> 8) & 0xff;
229		GDROM_REGX = 0;
230	}
231	sc->cmd_result_buf = buf;
232	sc->cmd_result_size = nbyt;
233
234	if (GDSTATSTAT(GDROM_STAT) == 0x06)
235		return -1;
236
237	GDROM_COND = 0xa0;
238	DELAY(1);
239	while ((GDROM_BUSY & 0x88) != 0x08)
240		;
241
242	s = splbio();
243
244	sc->cmd_actual = 0;
245	sc->cmd_active = 1;
246
247	for (i = 0; i < 6; i++)
248		GDROM_DATA = ptr[i];
249
250	while (sc->cmd_active)
251		tsleep(&sc->cmd_active, PRIBIO, "gdrom", 0);
252
253	splx(s);
254
255	if (resid != NULL)
256		*resid = sc->cmd_result_size;
257
258	return sc->cmd_cond;
259}
260
261
262int gdrom_command_sense(struct gdrom_softc *sc, void *req, void *buf,
263    unsigned int nbyt, int *resid)
264{
265	/*
266	 *  76543210 76543210
267	 *  0   0x13      -
268	 *  2    -      bufsz(hi)
269	 *  4 bufsz(lo)   -
270	 *  6    -        -
271	 *  8    -        -
272	 * 10    -        -
273	 */
274	uint16_t sense_data[5];
275	uint8_t cmd[12];
276	int cond, sense_key, sense_specific;
277
278	cond = gdrom_do_command(sc, req, buf, nbyt, resid);
279
280	if (cond < 0) {
281		DPRINTF(("GDROM: not ready (2:58)\n"));
282		return EIO;
283	}
284
285	if ((cond & 1) == 0) {
286		DPRINTF(("GDROM: no sense.  0:0\n"));
287		return 0;
288	}
289
290	memset(cmd, 0, sizeof(cmd));
291
292	cmd[0] = 0x13;
293	cmd[4] = sizeof(sense_data);
294
295	gdrom_do_command(sc, cmd, sense_data, sizeof(sense_data), NULL);
296
297	sense_key = sense_data[1] & 0xf;
298	sense_specific = sense_data[4];
299	if (sense_key == 11 && sense_specific == 0) {
300		DPRINTF(("GDROM: aborted (ignored).  0:0\n"));
301		return 0;
302	}
303
304	DPRINTF(("GDROM: SENSE %d:", sense_key));
305	DPRINTF(("GDROM: %d\n", sense_specific));
306
307	return sense_key == 0 ? 0 : EIO;
308}
309
310int gdrom_read_toc(struct gdrom_softc *sc, struct gd_toc *toc)
311{
312	/*
313	 *  76543210 76543210
314	 *  0   0x14      -
315	 *  2    -      bufsz(hi)
316	 *  4 bufsz(lo)   -
317	 *  6    -        -
318	 *  8    -        -
319	 * 10    -        -
320	 */
321	uint8_t cmd[12];
322
323	memset(cmd, 0, sizeof(cmd));
324
325	cmd[0] = 0x14;
326	cmd[3] = sizeof(struct gd_toc) >> 8;
327	cmd[4] = sizeof(struct gd_toc) & 0xff;
328
329	return gdrom_command_sense(sc, cmd, toc, sizeof(struct gd_toc), NULL);
330}
331
332int gdrom_read_sectors(struct gdrom_softc *sc, void *buf, int sector, int cnt,
333    int *resid)
334{
335	/*
336	 *  76543210 76543210
337	 *  0   0x30    datafmt
338	 *  2  sec(hi)  sec(mid)
339	 *  4  sec(lo)    -
340	 *  6    -        -
341	 *  8  cnt(hi)  cnt(mid)
342	 * 10  cnt(lo)    -
343	 */
344	uint8_t cmd[12];
345
346	memset(cmd, 0, sizeof(cmd));
347
348	cmd[0]  = 0x30;
349	cmd[1]  = 0x20;
350	cmd[2]  = sector >> 16;
351	cmd[3]  = sector >>  8;
352	cmd[4]  = sector;
353	cmd[8]  = cnt >> 16;
354	cmd[9]  = cnt >>  8;
355	cmd[10] = cnt;
356
357	return gdrom_command_sense(sc, cmd, buf, cnt << 11, resid);
358}
359
360int gdrom_mount_disk(struct gdrom_softc *sc)
361{
362	/*
363	 *  76543210 76543210
364	 *  0   0x70      -
365	 *  2   0x1f      -
366	 *  4    -        -
367	 *  6    -        -
368	 *  8    -        -
369	 * 10    -        -
370	 */
371	uint8_t cmd[12];
372
373	memset(cmd, 0, sizeof(cmd));
374
375	cmd[0] = 0x70;
376	cmd[1] = 0x1f;
377
378	return gdrom_command_sense(sc, cmd, NULL, 0, NULL);
379}
380
381int
382gdrommatch(device_t parent, cfdata_t cf, void *aux)
383{
384	static int gdrom_matched = 0;
385
386	/* Allow only once instance. */
387	if (gdrom_matched)
388		return 0;
389	gdrom_matched = 1;
390
391	return 1;
392}
393
394void
395gdromattach(device_t parent, device_t self, void *aux)
396{
397	struct gdrom_softc *sc;
398	uint32_t p, x;
399
400	sc = device_private(self);
401	sc->sc_dev = self;
402
403	bufq_alloc(&sc->sc_bufq, "disksort", BUFQ_SORT_RAWBLOCK);
404
405	/*
406	 * Initialize and attach the disk structure.
407	 */
408	disk_init(&sc->sc_dk, device_xname(self), &gdromdkdriver);
409	disk_attach(&sc->sc_dk);
410
411	/*
412	 * reenable disabled drive
413	 */
414	*((volatile uint32_t *)0xa05f74e4) = 0x1fffff;
415	for (p = 0; p < 0x200000 / 4; p++)
416		x = ((volatile uint32_t *)0xa0000000)[p];
417
418	printf(": %s\n", sysasic_intr_string(SYSASIC_IRL9));
419	sysasic_intr_establish(SYSASIC_EVENT_GDROM, IPL_BIO, SYSASIC_IRL9,
420	    gdrom_intr, sc);
421}
422
423int
424gdromopen(dev_t dev, int flags, int devtype, struct lwp *l)
425{
426	struct gdrom_softc *sc;
427	int s, error, unit, cnt;
428	struct gd_toc toc;
429
430	DPRINTF(("GDROM: open\n"));
431
432	unit = DISKUNIT(dev);
433
434	sc = device_lookup_private(&gdrom_cd, unit);
435	if (sc == NULL)
436		return ENXIO;
437
438	if (sc->is_open)
439		return EBUSY;
440
441	s = splbio();
442	while (sc->is_busy)
443		tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0);
444	sc->is_busy = true;
445	splx(s);
446
447	for (cnt = 0; cnt < 5; cnt++)
448		if ((error = gdrom_mount_disk(sc)) == 0)
449			break;
450
451	if (error == 0)
452		error = gdrom_read_toc(sc, &toc);
453
454	sc->is_busy = false;
455	wakeup(&sc->is_busy);
456
457	if (error != 0)
458		return error;
459
460	sc->is_open = true;
461	sc->openpart_start = 150;
462
463	DPRINTF(("GDROM: open OK\n"));
464	return 0;
465}
466
467int
468gdromclose(dev_t dev, int flags, int devtype, struct lwp *l)
469{
470	struct gdrom_softc *sc;
471	int unit;
472
473	DPRINTF(("GDROM: close\n"));
474
475	unit = DISKUNIT(dev);
476	sc = device_lookup_private(&gdrom_cd, unit);
477
478	sc->is_open = false;
479
480	return 0;
481}
482
483void
484gdromstrategy(struct buf *bp)
485{
486	struct gdrom_softc *sc;
487	int s, unit;
488
489	DPRINTF(("GDROM: strategy\n"));
490
491	unit = DISKUNIT(bp->b_dev);
492	sc = device_lookup_private(&gdrom_cd, unit);
493
494	if (bp->b_bcount == 0)
495		goto done;
496
497	bp->b_rawblkno = bp->b_blkno / (2048 / DEV_BSIZE) + sc->openpart_start;
498
499	DPRINTF(("GDROM: read_sectors(%p, %lld, %d) [%d bytes]\n",
500	    bp->b_data, bp->b_rawblkno,
501	    bp->b_bcount >> 11, bp->b_bcount));
502
503	s = splbio();
504	bufq_put(sc->sc_bufq, bp);
505	splx(s);
506	if (!sc->is_active)
507		gdrom_start(sc);
508	return;
509
510 done:
511	bp->b_resid = bp->b_bcount;
512	biodone(bp);
513}
514
515void
516gdrom_start(struct gdrom_softc *sc)
517{
518	struct buf *bp;
519	int error, resid, s;
520
521	sc->is_active = true;
522
523	for (;;) {
524		s = splbio();
525		bp = bufq_get(sc->sc_bufq);
526		if (bp == NULL) {
527			splx(s);
528			break;
529		}
530
531		while (sc->is_busy)
532			tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0);
533		sc->is_busy = true;
534		disk_busy(&sc->sc_dk);
535		splx(s);
536
537		error = gdrom_read_sectors(sc, bp->b_data, bp->b_rawblkno,
538		    bp->b_bcount >> 11, &resid);
539		bp->b_error = error;
540		bp->b_resid = resid;
541		if (error != 0)
542			bp->b_resid = bp->b_bcount;
543
544		sc->is_busy = false;
545		wakeup(&sc->is_busy);
546
547		s = splbio();
548		disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid,
549		    (bp->b_flags & B_READ) != 0);
550		splx(s);
551		biodone(bp);
552	}
553
554	sc->is_active = false;
555}
556
557int
558gdromioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
559{
560	struct gdrom_softc *sc;
561	int unit, error;
562
563	DPRINTF(("GDROM: ioctl %lx\n", cmd));
564
565	unit = DISKUNIT(dev);
566	sc = device_lookup_private(&gdrom_cd, unit);
567
568	switch (cmd) {
569	case CDIOREADMSADDR: {
570		int s, track, sessno = *(int *)addr;
571		struct gd_toc toc;
572
573		if (sessno != 0)
574			return EINVAL;
575
576		s = splbio();
577		while (sc->is_busy)
578			tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0);
579		sc->is_busy = true;
580		splx(s);
581
582		error = gdrom_read_toc(sc, &toc);
583
584		sc->is_busy = false;
585		wakeup(&sc->is_busy);
586
587		if (error != 0)
588			return error;
589#ifdef GDROMDEBUGTOC
590		{ /* Dump the GDROM TOC */
591		unsigned char *ptr = (unsigned char *)&toc;
592		int i;
593
594		printf("gdrom: TOC\n");
595		for(i = 0; i < sizeof(toc); ++i) {
596			printf("%02x", *ptr++);
597			if( i%32 == 31)
598				printf("\n");
599			else if( i%4 == 3)
600				printf(",");
601		}
602		printf("\n");
603		}
604#endif
605		for (track = TOC_TRACK(toc.last);
606		    track >= TOC_TRACK(toc.first);
607		    --track) {
608			if (track < 1 || track > 100)
609				return ENXIO;
610			if (TOC_CTRL(toc.entry[track - 1]))
611				break;
612		}
613
614#ifdef GDROMDEBUGTOC
615		printf("gdrom: Using track %d, LBA %u\n", track,
616		    TOC_LBA(toc.entry[track - 1]));
617#endif
618
619		*(int *)addr = htonl(TOC_LBA(toc.entry[track - 1])) -
620		    sc->openpart_start;
621
622		return 0;
623	}
624	default:
625		return ENOTTY;
626	}
627
628#ifdef DIAGNOSTIC
629	panic("gdromioctl: impossible");
630#endif
631}
632
633
634int
635gdromread(dev_t dev, struct uio *uio, int flags)
636{
637
638	DPRINTF(("GDROM: read\n"));
639	return physio(gdromstrategy, NULL, dev, B_READ, minphys, uio);
640}
641
642int
643gdromwrite(dev_t dev, struct uio *uio, int flags)
644{
645
646	return EROFS;
647}
648