xref: /netbsd/sys/arch/dreamcast/dev/gdrom.c (revision bf9ec67e)
1 /*	$NetBSD: gdrom.c,v 1.8 2002/03/25 18:59:39 uch Exp $	*/
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 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/device.h>
40 
41 #include <sys/buf.h>
42 #include <sys/ioctl.h>
43 #include <sys/fcntl.h>
44 #include <sys/disklabel.h>
45 #include <sys/disk.h>
46 #include <sys/cdio.h>
47 #include <sys/proc.h>
48 
49 #include <machine/sysasicvar.h>
50 
51 int	gdrommatch(struct device *, struct cfdata *, void *);
52 void	gdromattach(struct device *, struct device *, void *);
53 int	gdromopen(dev_t, int, int, struct proc *);
54 int	gdromclose(dev_t, int, int, struct proc *);
55 void	gdromstrategy(struct buf *);
56 int	gdromioctl(dev_t, u_long, caddr_t, int, struct proc *);
57 int	gdromdump(dev_t, daddr_t, caddr_t, size_t);
58 int	gdromsize(dev_t);
59 int	gdromread(dev_t, struct uio *, int);
60 int	gdromwrite(dev_t, struct uio *, int);
61 
62 struct gdrom_softc {
63 	struct device sc_dv;	/* generic device info; must come first */
64 	struct disk dkdev;	/* generic disk info */
65 	struct buf_queue bufq;	/* queue pending I/O operations */
66 	struct buf curbuf;	/* state of current I/O operation */
67 
68 	int is_open, is_busy;
69 	int openpart_start;	/* start sector of currently open partition */
70 
71 	int cmd_active;
72 	void *cmd_result_buf;	/* where to store result data (16 bit aligned) */
73 	int cmd_result_size;	/* number of bytes allocated for buf */
74 	int cmd_actual;		/* number of bytes actually read */
75 	int cmd_cond;		/* resulting condition of command */
76 };
77 
78 struct cfattach gdrom_ca = {
79 	sizeof(struct gdrom_softc), gdrommatch, gdromattach
80 };
81 
82 struct dkdriver gdromdkdriver = { gdromstrategy };
83 
84 extern struct cfdriver gdrom_cd;
85 
86 
87 struct gd_toc {
88 	unsigned int entry[99];
89 	unsigned int first, last;
90 	unsigned int leadout;
91 };
92 
93 #define TOC_LBA(n)	((n) & 0xffffff00)
94 #define TOC_ADR(n)	((n) & 0x0f)
95 #define TOC_CTRL(n)	(((n) & 0xf0) >> 4)
96 #define TOC_TRACK(n)	(((n) & 0x0000ff00) >> 8)
97 
98 #define GDROM(o)	(*(volatile unsigned char *)(0xa05f7000 + (o)))
99 
100 #define GDSTATSTAT(n)	((n) & 0xf)
101 #define GDSTATDISK(n)	(((n) >> 4) & 0xf)
102 
103 #define GDROM_BUSY	GDROM(0x18)
104 #define GDROM_DATA	(*(volatile short *) & GDROM(0x80))
105 #define GDROM_REGX	GDROM(0x84)
106 #define GDROM_STAT	GDROM(0x8c)
107 #define GDROM_CNTLO	GDROM(0x90)
108 #define GDROM_CNTHI	GDROM(0x94)
109 #define GDROM_COND	GDROM(0x9c)
110 
111 int	gdrom_getstat(void);
112 int	gdrom_do_command(struct gdrom_softc *, void *, void *, unsigned int);
113 int	gdrom_command_sense(struct gdrom_softc *, void *, void *, unsigned int);
114 int	gdrom_read_toc(struct gdrom_softc *, struct gd_toc *);
115 int	gdrom_read_sectors(struct gdrom_softc *, void *, int, int);
116 int	gdrom_mount_disk(struct gdrom_softc *);
117 int	gdrom_intr(void *);
118 
119 int gdrom_getstat()
120 {
121 	int s1, s2, s3;
122 
123 	if (GDROM_BUSY & 0x80)
124 		return (-1);
125 	s1 = GDROM_STAT;
126 	s2 = GDROM_STAT;
127 	s3 = GDROM_STAT;
128 	if(GDROM_BUSY & 0x80)
129 		return (-1);
130 	if(s1 == s2)
131 		return (s1);
132 	else if(s2 == s3)
133 		return (s2);
134 	else
135 		return (-1);
136 }
137 
138 int
139 gdrom_intr(void *arg)
140 {
141 	struct gdrom_softc *sc = arg;
142 	int s, cond;
143 
144 	s = splbio();
145 	cond = GDROM_COND;
146 #ifdef GDROMDEBUG
147 	printf("GDROM: cond = %x\n", cond);
148 #endif
149 	if(!sc->cmd_active) {
150 #ifdef GDROMDEBUG
151 		printf("GDROM: inactive IRQ!?\n");
152 #endif
153 		splx(s);
154 		return (0);
155 	}
156 
157 	if((cond & 8)) {
158 		int cnt = (GDROM_CNTHI << 8) | GDROM_CNTLO;
159 #ifdef GDROMDEBUG
160 		printf("GDROM: cnt = %d\n", cnt);
161 #endif
162 		sc->cmd_actual += cnt;
163 		if(cnt > 0 && sc->cmd_result_size > 0) {
164 			int subcnt = (cnt > sc->cmd_result_size ?
165 			    sc->cmd_result_size : cnt);
166 			int16_t *ptr = sc->cmd_result_buf;
167 			sc->cmd_result_buf = ((char *)sc->cmd_result_buf) +
168 			    subcnt;
169 			sc->cmd_result_size -= subcnt;
170 			cnt -= subcnt;
171 			while (subcnt > 0) {
172 				*ptr++ = GDROM_DATA;
173 				subcnt -= 2;
174 			}
175 		}
176 		while (cnt > 0) {
177 			__volatile int16_t tmp;
178 			tmp = GDROM_DATA;
179 			cnt -= 2;
180 		}
181 	}
182 	while (GDROM_BUSY & 0x80);
183 
184 	if ((cond & 8) == 0) {
185 		sc->cmd_cond = cond;
186 		sc->cmd_active = 0;
187 		wakeup(&sc->cmd_active);
188 	}
189 
190 	splx(s);
191 	return (0);
192 }
193 
194 
195 int gdrom_do_command(struct gdrom_softc *sc, void *req, void *buf,
196     unsigned int nbyt)
197 {
198 	int i, s;
199 	short *ptr = req;
200 
201 	while (GDROM_BUSY & 0x88)
202 		;
203 	if (buf != NULL) {
204 		GDROM_CNTLO = nbyt & 0xff;
205 		GDROM_CNTHI = (nbyt >> 8) & 0xff;
206 		GDROM_REGX = 0;
207 	}
208 	sc->cmd_result_buf = buf;
209 	sc->cmd_result_size = nbyt;
210 
211 	if (GDSTATSTAT(GDROM_STAT) == 6)
212 		return (-1);
213 
214 	GDROM_COND = 0xa0;
215 	for (i = 0; i < 64; i++)
216 		;
217 	while ((GDROM_BUSY & 0x88) != 8)
218 		;
219 
220 	s = splbio();
221 
222 	sc->cmd_actual = 0;
223 	sc->cmd_active = 1;
224 
225 	for (i = 0; i< 6; i++)
226 		GDROM_DATA = ptr[i];
227 
228 	while (sc->cmd_active)
229 		tsleep(&sc->cmd_active, PRIBIO, "gdrom", 0);
230 
231 	splx(s);
232 
233 	return (sc->cmd_cond);
234 }
235 
236 
237 int gdrom_command_sense(struct gdrom_softc *sc, void *req, void *buf,
238     unsigned int nbyt)
239 {
240 	/* 76543210 76543210
241 	   0   0x13      -
242 	   2    -      bufsz(hi)
243 	   4 bufsz(lo)   -
244 	   6    -        -
245 	   8    -        -
246 	   10    -        -        */
247 	unsigned short sense_data[5];
248 	unsigned char cmd[12];
249 	int sense_key, sense_specific;
250 
251 	int cond = gdrom_do_command(sc, req, buf, nbyt);
252 
253 	if (cond < 0) {
254 #ifdef GDROMDEBUG
255 		printf("GDROM: not ready (2:58)\n");
256 #endif
257 		return (EIO);
258 	}
259 
260 	if ((cond & 1) == 0) {
261 #ifdef GDROMDEBUG
262 		printf("GDROM: no sense.  0:0\n");
263 #endif
264 		return (0);
265 	}
266 
267 	memset(cmd, 0, sizeof(cmd));
268 
269 	cmd[0] = 0x13;
270 	cmd[4] = sizeof(sense_data);
271 
272 	gdrom_do_command(sc, cmd, sense_data, sizeof(sense_data));
273 
274 	sense_key = sense_data[1] & 0xf;
275 	sense_specific = sense_data[4];
276 	if (sense_key == 11 && sense_specific == 0) {
277 #ifdef GDROMDEBUG
278 		printf("GDROM: aborted (ignored).  0:0\n");
279 #endif
280 		return (0);
281 	}
282 
283 #ifdef GDROMDEBUG
284 	printf("GDROM: SENSE %d:", sense_key);
285 	printf("GDROM: %d\n", sense_specific);
286 #endif
287 
288 	return (sense_key == 0 ? 0 : EIO);
289 }
290 
291 int gdrom_read_toc(struct gdrom_softc *sc, struct gd_toc *toc)
292 {
293 	/* 76543210 76543210
294 	   0   0x14      -
295 	   2    -      bufsz(hi)
296 	   4 bufsz(lo)   -
297 	   6    -        -
298 	   8    -        -
299 	   10    -        -        */
300 	unsigned char cmd[12];
301 
302 	memset(cmd, 0, sizeof(cmd));
303 
304 	cmd[0] = 0x14;
305 	cmd[3] = sizeof(struct gd_toc) >> 8;
306 	cmd[4] = sizeof(struct gd_toc) & 0xff;
307 
308 	return (gdrom_command_sense(sc, cmd, toc, sizeof(struct gd_toc)));
309 }
310 
311 int gdrom_read_sectors(struct gdrom_softc *sc, void *buf, int sector, int cnt)
312 {
313 	/* 76543210 76543210
314 	   0   0x30    datafmt
315 	   2  sec(hi)  sec(mid)
316 	   4  sec(lo)    -
317 	   6    -        -
318 	   8  cnt(hi)  cnt(mid)
319 	   10  cnt(lo)    -        */
320 	unsigned char cmd[12];
321 
322 	memset(cmd, 0, sizeof(cmd));
323 
324 	cmd[0] = 0x30;
325 	cmd[1] = 0x20;
326 	cmd[2] = sector>>16;
327 	cmd[3] = sector>>8;
328 	cmd[4] = sector;
329 	cmd[8] = cnt>>16;
330 	cmd[9] = cnt>>8;
331 	cmd[10] = cnt;
332 
333 	return (gdrom_command_sense(sc, cmd, buf, cnt << 11));
334 }
335 
336 int gdrom_mount_disk(struct gdrom_softc *sc)
337 {
338 	/* 76543210 76543210
339 	   0   0x70      -
340 	   2   0x1f      -
341 	   4    -        -
342 	   6    -        -
343 	   8    -        -
344 	   10    -        -        */
345 	unsigned char cmd[12];
346 
347 	memset(cmd, 0, sizeof(cmd));
348 
349 	cmd[0] = 0x70;
350 	cmd[1] = 0x1f;
351 
352 	return (gdrom_command_sense(sc, cmd, NULL, 0));
353 }
354 
355 int
356 gdrommatch(struct device *parent, struct cfdata *cf, void *aux)
357 {
358 	static int gdrom_matched = 0;
359 
360 	/* Allow only once instance. */
361 	if (strcmp("gdrom", cf->cf_driver->cd_name) || gdrom_matched)
362 		return (0);
363 	gdrom_matched = 1;
364 
365 	return (1);
366 }
367 
368 void
369 gdromattach(struct device *parent, struct device *self, void *aux)
370 {
371 	struct gdrom_softc *sc;
372 	u_int32_t p, x;
373 
374 	sc = (struct gdrom_softc *)self;
375 
376 	BUFQ_INIT(&sc->bufq);
377 
378 	printf(": SH4 IRL 9\n");
379 
380 	/*
381 	 * Initialize and attach the disk structure.
382 	 */
383 	sc->dkdev.dk_name = sc->sc_dv.dv_xname;
384 	sc->dkdev.dk_driver = &gdromdkdriver;
385 	disk_attach(&sc->dkdev);
386 
387 	/*
388 	 * reenable disabled drive
389 	 */
390 	*((__volatile u_int32_t *)0xa05f74e4) = 0x1fffff;
391 	for (p = 0; p < 0x200000 / 4; p++)
392 		x = ((__volatile u_int32_t *)0xa0000000)[p];
393 
394 	sysasic_intr_establish(SYSASIC_EVENT_GDROM, gdrom_intr, sc);
395 }
396 
397 int
398 gdromopen(dev_t dev, int flags, int devtype, struct proc *p)
399 {
400 	struct gdrom_softc *sc;
401 	int s, error, unit, cnt;
402 	struct gd_toc toc;
403 
404 #ifdef GDROMDEBUG
405 	printf("GDROM: open\n");
406 #endif
407 
408 	unit = DISKUNIT(dev);
409 	if (unit >= gdrom_cd.cd_ndevs)
410 		return (ENXIO);
411 
412 	sc = gdrom_cd.cd_devs[unit];
413 	if (sc == NULL)
414 		return (ENXIO);
415 
416 	if (sc->is_open)
417 		return (EBUSY);
418 
419 	s = splbio();
420 	while(sc->is_busy)
421 		tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0);
422 	sc->is_busy = 1;
423 	splx(s);
424 
425 	for (cnt = 0; cnt < 5; cnt++)
426 		if ((error = gdrom_mount_disk(sc)) == 0)
427 			break;
428 
429 	if (!error)
430 		error = gdrom_read_toc(sc, &toc);
431 
432 	sc->is_busy = 0;
433 	wakeup(&sc->is_busy);
434 
435 	if (error)
436 		return error;
437 
438 	sc->is_open = 1;
439 	sc->openpart_start = 150;
440 
441 #ifdef GDROMDEBUG
442 	printf("GDROM: open OK\n");
443 #endif
444 	return (0);
445 }
446 
447 int
448 gdromclose(dev_t dev, int flags, int devtype, struct proc *p)
449 {
450 	struct gdrom_softc *sc;
451 	int unit;
452 #ifdef GDROMDEBUG
453 	printf("GDROM: close\n");
454 #endif
455 	unit = DISKUNIT(dev);
456 	sc = gdrom_cd.cd_devs[unit];
457 
458 	sc->is_open = 0;
459 
460 	return (0);
461 }
462 
463 void
464 gdromstrategy(struct buf *bp)
465 {
466 	struct gdrom_softc *sc;
467 	int s, unit, error;
468 #ifdef GDROMDEBUG
469 	printf("GDROM: strategy\n");
470 #endif
471 
472 	unit = DISKUNIT(bp->b_dev);
473 	sc = gdrom_cd.cd_devs[unit];
474 
475 	if (bp->b_bcount == 0)
476 		goto done;
477 
478 	bp->b_rawblkno = bp->b_blkno / (2048 / DEV_BSIZE) + sc->openpart_start;
479 
480 #ifdef GDROMDEBUG
481 	printf("GDROM: read_sectors(%p, %d, %ld) [%ld bytes]\n",
482 	    bp->b_data, bp->b_rawblkno,
483 	    bp->b_bcount>>11, bp->b_bcount);
484 #endif
485 	s = splbio();
486 	while (sc->is_busy)
487 		tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0);
488 	sc->is_busy = 1;
489 	splx(s);
490 
491 	if ((error = gdrom_read_sectors(sc, bp->b_data, bp->b_rawblkno,
492 	    bp->b_bcount >> 11))) {
493 		bp->b_error = error;
494 		bp->b_flags |= B_ERROR;
495 	}
496 
497 	sc->is_busy = 0;
498 	wakeup(&sc->is_busy);
499 
500  done:
501 	bp->b_resid = bp->b_bcount;
502 	biodone(bp);
503 }
504 
505 int
506 gdromioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
507 {
508 	struct gdrom_softc *sc;
509 	int unit, error;
510 #ifdef GDROMDEBUG
511 	printf("GDROM: ioctl %lx\n", cmd);
512 #endif
513 
514 	unit = DISKUNIT(dev);
515 	sc = gdrom_cd.cd_devs[unit];
516 
517 	switch (cmd) {
518 	case CDIOREADMSADDR: {
519 		int s, track, sessno = *(int *)addr;
520 		struct gd_toc toc;
521 
522 		if (sessno != 0)
523 			return (EINVAL);
524 
525 		s = splbio();
526 		while (sc->is_busy)
527 			tsleep(&sc->is_busy, PRIBIO, "gdbusy", 0);
528 		sc->is_busy = 1;
529 		splx(s);
530 
531 		error = gdrom_read_toc(sc, &toc);
532 
533 		sc->is_busy = 0;
534 		wakeup(&sc->is_busy);
535 
536 		if (error)
537 			return error;
538 
539 		for (track = TOC_TRACK(toc.last);
540 		    track >= TOC_TRACK(toc.first);
541 		    --track)
542 			if (TOC_CTRL(toc.entry[track-1]))
543 				break;
544 
545 		if (track < TOC_TRACK(toc.first) || track > 100)
546 			return (ENXIO);
547 
548 		*(int *)addr = htonl(TOC_LBA(toc.entry[track-1])) -
549 		    sc->openpart_start;
550 
551 		return (0);
552 	}
553 	default:
554 		return (EINVAL);
555 	}
556 
557 #ifdef DIAGNOSTIC
558 	panic("gdromioctl: impossible");
559 #endif
560 }
561 
562 
563 /*
564  * Can't dump to CD; read only media...
565  */
566 int
567 gdromdump(dev_t	dev, daddr_t blkno, caddr_t va, size_t size)
568 {
569 
570 	return (EINVAL);
571 }
572 
573 int
574 gdromsize(dev_t dev)
575 {
576 
577 	return (-1);
578 }
579 
580 int
581 gdromread(dev_t dev, struct uio *uio, int flags)
582 {
583 #ifdef GDROMDEBUG
584 	printf("GDROM: read\n");
585 #endif
586 	return (physio(gdromstrategy, NULL, dev, B_READ, minphys, uio));
587 }
588 
589 int
590 gdromwrite(dev_t dev, struct uio *uio, int flags)
591 {
592 
593 	return (EROFS);
594 }
595