Home | History | Annotate | Line # | Download | only in dev
gdrom.c revision 1.36
      1 /*	$NetBSD: gdrom.c,v 1.36 2014/03/14 08:50:08 martin 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 __KERNEL_RCSID(0, "$NetBSD: gdrom.c,v 1.36 2014/03/14 08:50:08 martin Exp $");
     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 
     56 static int  gdrommatch(device_t, cfdata_t, void *);
     57 static void gdromattach(device_t, device_t, void *);
     58 
     59 dev_type_open(gdromopen);
     60 dev_type_close(gdromclose);
     61 dev_type_read(gdromread);
     62 dev_type_write(gdromwrite);
     63 dev_type_ioctl(gdromioctl);
     64 dev_type_strategy(gdromstrategy);
     65 
     66 const struct bdevsw gdrom_bdevsw = {
     67 	gdromopen, gdromclose, gdromstrategy, gdromioctl, nodump,
     68 	nosize, D_DISK
     69 };
     70 
     71 const struct cdevsw gdrom_cdevsw = {
     72 	gdromopen, gdromclose, gdromread, gdromwrite, gdromioctl,
     73 	nostop, notty, nopoll, nommap, nokqfilter, D_DISK
     74 };
     75 
     76 struct 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 
     94 CFATTACH_DECL_NEW(gdrom, sizeof(struct gdrom_softc),
     95     gdrommatch, gdromattach, NULL, NULL);
     96 
     97 struct dkdriver gdromdkdriver = { gdromstrategy };
     98 
     99 
    100 struct 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
    131 static int gdrom_getstat(void);
    132 #endif
    133 static int gdrom_do_command(struct gdrom_softc *, void *, void *,
    134     unsigned int, int *);
    135 static int gdrom_command_sense(struct gdrom_softc *, void *, void *,
    136     unsigned int, int *);
    137 static int gdrom_read_toc(struct gdrom_softc *, struct gd_toc *);
    138 static int gdrom_read_sectors(struct gdrom_softc *, void *, int, int,
    139     int *);
    140 static int gdrom_mount_disk(struct gdrom_softc *);
    141 static int gdrom_intr(void *);
    142 static void gdrom_start(struct gdrom_softc *);
    143 
    144 #if 0
    145 int
    146 gdrom_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 
    166 int
    167 gdrom_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 
    217 int
    218 gdrom_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 
    262 int 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 
    310 int 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 
    332 int 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 
    360 int 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 
    381 int
    382 gdrommatch(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 
    394 void
    395 gdromattach(device_t parent, device_t self, void *aux)
    396 {
    397 	struct gdrom_softc *sc;
    398 	uint32_t p;
    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 		(void)((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 
    423 int
    424 gdromopen(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 
    467 int
    468 gdromclose(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 
    483 void
    484 gdromstrategy(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 
    515 void
    516 gdrom_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 
    557 int
    558 gdromioctl(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 
    634 int
    635 gdromread(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 
    642 int
    643 gdromwrite(dev_t dev, struct uio *uio, int flags)
    644 {
    645 
    646 	return EROFS;
    647 }
    648